Use gdb's 'print' command to access dumper output.

Simple solution after all. This idea got initially dropped early in the
process because it does not handle binary data and using x/x was
way to slow. But since we use only printable characters now thanks
to base64 encoding etc using 'p' becomes feasible again and simplifies
the process vastly as no additional communication channel is needed anymore.
This commit is contained in:
hjk
2009-01-21 11:12:12 +01:00
parent f1a7c95813
commit 076488bfbb
6 changed files with 243 additions and 57 deletions

View File

@@ -113,6 +113,7 @@ enum GdbCommandType
GdbInfoProc,
GdbQueryDataDumper1,
GdbQueryDataDumper2,
GdbQueryDataDumper3,
BreakCondition = 200,
BreakEnablePending,
@@ -144,7 +145,8 @@ enum GdbCommandType
WatchToolTip,
WatchDumpCustomSetup,
WatchDumpCustomValue1, // waiting for gdb ack
WatchDumpCustomValue2, // waiting for actual data
WatchDumpCustomValue2, // waiting for actual data (fd version)
WatchDumpCustomValue3, // waiting for actual data (buffer version)
WatchDumpCustomEditValue,
};
@@ -807,6 +809,9 @@ void GdbEngine::handleResult(const GdbResultRecord & record, int type,
case GdbQueryDataDumper2:
handleQueryDataDumper2(record);
break;
case GdbQueryDataDumper3:
handleQueryDataDumper3(record);
break;
case BreakList:
handleBreakList(record);
@@ -883,6 +888,9 @@ void GdbEngine::handleResult(const GdbResultRecord & record, int type,
case WatchDumpCustomValue2:
handleDumpCustomValue2(record, cookie.value<WatchData>());
break;
case WatchDumpCustomValue3:
handleDumpCustomValue3(record, cookie.value<WatchData>());
break;
case WatchDumpCustomSetup:
handleDumpCustomSetup(record);
break;
@@ -1562,6 +1570,7 @@ bool GdbEngine::startDebugger()
//sendCommand("set confirm off");
//sendCommand("set pagination off");
sendCommand("set breakpoint pending on", BreakEnablePending);
sendCommand("set print elements 10000");
// one of the following is needed to prevent crashes in gdb on code like:
// template <class T> T foo() { return T(0); }
@@ -3081,11 +3090,12 @@ void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
// create response slot for socket data
QVariant var;
var.setValue(data);
sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var);
//sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var);
// this increases the probability that gdb spits out output it
// has collected so far
//sendCommand("p qDumpInBuffer");
sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue3, var);
}
void GdbEngine::createGdbVariable(const WatchData &data)
@@ -3360,6 +3370,73 @@ void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
//qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
}
void GdbEngine::handleQueryDataDumper3(const GdbResultRecord &record)
{
#if 1
// is this the official gdb response. However, it won't contain
// interesting data other than the information that 'real' data
// either already arrived or is still in the pipe. So we do
// _not_ register this result for counting purposes, this will
// be done by the 'real' result (with resultClass == GdbResultCustomDone)
//qDebug() << "DATA DUMPER TRIAL:" << record.toString();
//GdbMi output = record.data.findChild("customvaluecontents");
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
out = out.mid(out.indexOf('"') + 1);
out = out.replace('\\', "");
out = out.left(out.lastIndexOf('"'));
out = "result={" + out + "}";
qDebug() << "OUTPUT: " << out;
GdbMi contents;
contents.fromString(out);
GdbMi simple = contents.findChild("dumpers");
m_namespace = contents.findChild("namespace").data();
GdbMi qtversion = contents.findChild("qtversion");
if (qtversion.children().size() == 3) {
m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
+ (qtversion.childAt(1).data().toInt() << 8)
+ qtversion.childAt(2).data().toInt();
//qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
} else {
m_qtVersion = 0;
}
qDebug() << "OUTPUT: " << out;
qDebug() << "CONTENTS: " << contents.toString();
qDebug() << "SIMPLE DUMPERS: " << simple.toString();
m_availableSimpleDumpers.clear();
foreach (const GdbMi &item, simple.children())
m_availableSimpleDumpers.append(item.data());
if (m_availableSimpleDumpers.isEmpty()) {
m_dataDumperState = DataDumperUnavailable;
QMessageBox::warning(q->mainWindow(),
tr("Cannot find special data dumpers"),
tr("The debugged binary does not contain information needed for "
"nice display of Qt data types.\n\n"
"Try might want to try include the file\n\n"
".../ide/main/bin/gdbmacros/gdbmacros.cpp'\n\n"
"into your project directly.")
);
} else {
m_dataDumperState = DataDumperAvailable;
}
qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
#else
// divert this to the fd version
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
out = out.mid(out.indexOf('=') + 3);
out = out.replace("\\\\", "\\");
out = out.left(out.lastIndexOf('"'));
out = "dummy={customvaluecontents=\"{" + out + "}\"}";
GdbResultRecord record1 = record;
record1.data = GdbMi();
record1.data.fromString(out);
handleQueryDataDumper2(record1);
#endif
}
void GdbEngine::sendWatchParameters(const QByteArray &params0)
{
QByteArray params = params0;
@@ -3596,6 +3673,115 @@ void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
}
}
void GdbEngine::handleDumpCustomValue3(const GdbResultRecord &record,
const WatchData &data0)
{
WatchData data = data0;
QTC_ASSERT(data.isValid(), return);
qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
if (record.resultClass == GdbResultDone) {
//GdbMi output = record.data.findChild("customvaluecontents");
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
out = out.mid(out.indexOf('"') + 1);
out = out.replace('\\', "");
out = out.left(out.lastIndexOf('"'));
out = "result={" + out + "}";
qDebug() << "OUTPUT: " << out;
GdbMi contents;
contents.fromString(out);
//qDebug() << "HANDLE VALUE CONTENTS: " << output.toString(true);
if (!contents.isValid()) {
qDebug() << "INVALID";
// custom dumper produced no output
if (data.isValueNeeded())
data.setValue("<unknown>");
if (data.isTypeNeeded())
data.setType("<unknown>");
if (data.isChildrenNeeded())
data.setChildCount(0);
if (data.isChildCountNeeded())
data.setChildCount(0);
data.setValueToolTip("<custom dumper produced no output>");
insertData(data);
} else {
//GdbMi contents;
//qDebug() << "OUTPUT" << output.toString(true);
//contents.fromString(output.data());
qDebug() << "CONTENTS" << contents.toString(true);
setWatchDataType(data, contents.findChild("type"));
setWatchDataValue(data, contents.findChild("value"),
contents.findChild("valueencoded").data().toInt());
setWatchDataAddress(data, contents.findChild("addr"));
setWatchDataChildCount(data, contents.findChild("numchild"));
setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
setWatchDataEditValue(data, contents.findChild("editvalue"));
if (qq->watchHandler()->isDisplayedIName(data.iname)) {
GdbMi editvalue = contents.findChild("editvalue");
if (editvalue.isValid()) {
setWatchDataEditValue(data, editvalue);
qq->watchHandler()->showEditValue(data);
}
}
if (!qq->watchHandler()->isExpandedIName(data.iname))
data.setChildrenUnneeded();
GdbMi children = contents.findChild("children");
if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
data.setChildrenUnneeded();
data.setValueUnneeded();
// try not to repeat data too often
WatchData childtemplate;
setWatchDataType(childtemplate, contents.findChild("childtype"));
setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
//qDebug() << "DATA: " << data.toString();
insertData(data);
foreach (GdbMi item, children.children()) {
WatchData data1 = childtemplate;
data1.name = item.findChild("name").data();
data1.iname = data.iname + "." + data1.name;
//qDebug() << "NAMEENCODED: " << item.findChild("nameencoded").data()
// << item.findChild("nameencoded").data()[1];
if (item.findChild("nameencoded").data()[0] == '1')
data1.name = QByteArray::fromBase64(data1.name.toUtf8());
QString key = item.findChild("key").data();
if (!key.isEmpty())
data1.name += " (" + key + ")";
setWatchDataType(data1, item.findChild("type"));
setWatchDataExpression(data1, item.findChild("exp"));
setWatchDataChildCount(data1, item.findChild("numchild"));
setWatchDataValue(data1, item.findChild("value"),
item.findChild("valueencoded").data().toInt());
setWatchDataAddress(data1, item.findChild("addr"));
setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
if (!qq->watchHandler()->isExpandedIName(data1.iname))
data1.setChildrenUnneeded();
//qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
insertData(data1);
}
}
//qDebug() << "HANDLE CUSTOM VALUE CONTENTS: " << data.toString();
} else if (record.resultClass == GdbResultError) {
// FIXME: Should not happen here, i.e. could be removed
QString msg = record.data.findChild("msg").data();
//qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
if (msg.startsWith("The program being debugged was sig"))
msg = strNotInScope;
if (msg.startsWith("The program being debugged stopped while"))
msg = strNotInScope;
data.setError(msg);
insertData(data);
} else {
qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
}
}
void GdbEngine::updateLocals()
{
setTokenBarrier();
@@ -4021,7 +4207,8 @@ void GdbEngine::tryLoadCustomDumpers()
sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
GdbQueryDataDumper1);
// create response slot for socket data
sendCommand(QString(), GdbQueryDataDumper2);
//sendCommand(QString(), GdbQueryDataDumper2);
sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper3);
}