forked from qt-creator/qt-creator
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:
@@ -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 ¶ms0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user