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

@@ -216,7 +216,8 @@ QT_END_NAMESPACE
// this can be mangled typenames of nested templates, each char-by-char
// comma-separated integer list
static char qDumpInBuffer[10000];
static char qDumpBuffer[1000];
static char qDumpOutBuffer[100000];
static char qDumpSize[20];
namespace {
@@ -443,27 +444,28 @@ QDumper::~QDumper()
{
flush();
char buf[30];
int len = qsnprintf(buf, sizeof(buf) - 1, "%d^done\n", token);
write(buf, len);
//char buf[30];
//int len = qsnprintf(buf, sizeof(buf) - 1, "%d^done\n", token);
//write(buf, len);
}
void QDumper::write(const void *buf, int len)
{
::fwrite(buf, len, 1, stdout);
::fflush(stdout);
//::fwrite(buf, len, 1, stdout);
//::fflush(stdout);
}
void QDumper::flush()
{
if (pos != 0) {
char buf[30];
int len = qsnprintf(buf, sizeof(buf) - 1, "%d#%d,", token, pos);
write(buf, len);
write(qDumpBuffer, pos);
write("\n", 1);
pos = 0;
}
put(0);
//if (pos != 0) {
// char buf[30];
// int len = qsnprintf(buf, sizeof(buf) - 1, "%d#%d,", token, pos);
// write(buf, len);
// write(qDumpOutBuffer, pos);
// write("\n", 1);
// pos = 0;
// }
}
void QDumper::setupTemplateParameters()
@@ -489,49 +491,49 @@ void QDumper::setupTemplateParameters()
QDumper &QDumper::operator<<(unsigned long long c)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%llu", c);
pos += sprintf(qDumpOutBuffer + pos, "%llu", c);
return *this;
}
QDumper &QDumper::operator<<(unsigned long c)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%lu", c);
pos += sprintf(qDumpOutBuffer + pos, "%lu", c);
return *this;
}
QDumper &QDumper::operator<<(float d)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%f", d);
pos += sprintf(qDumpOutBuffer + pos, "%f", d);
return *this;
}
QDumper &QDumper::operator<<(double d)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%f", d);
pos += sprintf(qDumpOutBuffer + pos, "%f", d);
return *this;
}
QDumper &QDumper::operator<<(unsigned int i)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%u", i);
pos += sprintf(qDumpOutBuffer + pos, "%u", i);
return *this;
}
QDumper &QDumper::operator<<(long c)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%ld", c);
pos += sprintf(qDumpOutBuffer + pos, "%ld", c);
return *this;
}
QDumper &QDumper::operator<<(int i)
{
checkFill();
pos += sprintf(qDumpBuffer + pos, "%d", i);
pos += sprintf(qDumpOutBuffer + pos, "%d", i);
return *this;
}
@@ -555,21 +557,21 @@ QDumper &QDumper::operator<<(const void *p)
void QDumper::checkFill()
{
if (pos >= int(sizeof(qDumpBuffer)) - 100)
if (pos >= int(sizeof(qDumpOutBuffer)) - 100)
flush();
}
void QDumper::put(char c)
{
checkFill();
qDumpBuffer[pos++] = c;
qDumpOutBuffer[pos++] = c;
}
void QDumper::addCommaIfNeeded()
{
if (pos == 0)
return;
char c = qDumpBuffer[pos - 1];
char c = qDumpOutBuffer[pos - 1];
if (c == '}' || c == '"' || c == ']')
put(',');
}

View File

@@ -928,7 +928,7 @@ void DebuggerPlugin::readSettings()
m->m_skipKnownFrames = s->value("SkipKnownFrames", false).toBool();
m->m_debugDumpers = s->value("DebugDumpers", false).toBool();
m->m_useCustomDumpers = s->value("UseCustomDupers", false).toBool();
m->m_useCustomDumpers = s->value("UseCustomDumpers", true).toBool();
m->m_useFastStart = s->value("UseFastStart", false).toBool();
m->m_useToolTips = s->value("UseToolTips", false).toBool();
m->m_useTerminal = s->value("UseTerminal", false).toBool();

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);
}

View File

@@ -307,8 +307,11 @@ private:
const WatchData &cookie);
void handleQueryDataDumper1(const GdbResultRecord &record);
void handleQueryDataDumper2(const GdbResultRecord &record);
void handleQueryDataDumper3(const GdbResultRecord &record);
void handleDumpCustomValue2(const GdbResultRecord &record,
const WatchData &cookie);
void handleDumpCustomValue3(const GdbResultRecord &record,
const WatchData &cookie);
void handleDumpCustomEditValue(const GdbResultRecord &record);
void handleDumpCustomSetup(const GdbResultRecord &record);
void handleStackListLocals(const GdbResultRecord &record);

View File

@@ -46,20 +46,20 @@ QTextStream & operator<<(QTextStream & os, const GdbMi & mi)
return os << mi.toString();
}
//static void skipSpaces(const GdbMi::Char *&from, const GdbMi::Char *to)
//static void skipSpaces(const char *&from, const char *to)
//{
// while (from != to && QChar(*from).isSpace())
// ++from;
//}
void GdbMi::parseResultOrValue(const Char *&from, const Char *to)
void GdbMi::parseResultOrValue(const char *&from, const char *to)
{
//skipSpaces(from, to);
while (from != to && QChar(*from).isSpace())
++from;
//qDebug() << "parseResultOrValue: " << QByteArray::fromLatin1(from, to - from);
//qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
parseValue(from, to);
if (isValid()) {
//qDebug() << "no valid result in " << QByteArray::fromLatin1(from, to - from);
@@ -67,7 +67,7 @@ void GdbMi::parseResultOrValue(const Char *&from, const Char *to)
}
if (from == to || *from == '(')
return;
const Char *ptr = from;
const char *ptr = from;
while (ptr < to && *ptr != '=') {
//qDebug() << "adding" << QChar(*ptr) << "to name";
++ptr;
@@ -80,7 +80,7 @@ void GdbMi::parseResultOrValue(const Char *&from, const Char *to)
}
}
QByteArray GdbMi::parseCString(const Char *&from, const Char *to)
QByteArray GdbMi::parseCString(const char *&from, const char *to)
{
QByteArray result;
//qDebug() << "parseCString: " << QByteArray::fromUtf16(from, to - from);
@@ -88,7 +88,7 @@ QByteArray GdbMi::parseCString(const Char *&from, const Char *to)
qDebug() << "MI Parse Error, double quote expected";
return QByteArray();
}
const Char *ptr = from;
const char *ptr = from;
++ptr;
while (ptr < to) {
if (*ptr == '"') {
@@ -115,7 +115,7 @@ QByteArray GdbMi::parseCString(const Char *&from, const Char *to)
return result;
}
void GdbMi::parseValue(const Char *&from, const Char *to)
void GdbMi::parseValue(const char *&from, const char *to)
{
//qDebug() << "parseValue: " << QByteArray::fromUtf16(from, to - from);
switch (*from) {
@@ -135,7 +135,7 @@ void GdbMi::parseValue(const Char *&from, const Char *to)
}
void GdbMi::parseTuple(const Char *&from, const Char *to)
void GdbMi::parseTuple(const char *&from, const char *to)
{
//qDebug() << "parseTuple: " << QByteArray::fromUtf16(from, to - from);
QTC_ASSERT(*from == '{', /**/);
@@ -143,7 +143,7 @@ void GdbMi::parseTuple(const Char *&from, const Char *to)
parseTuple_helper(from, to);
}
void GdbMi::parseTuple_helper(const Char *&from, const Char *to)
void GdbMi::parseTuple_helper(const char *&from, const char *to)
{
//qDebug() << "parseTuple_helper: " << QByteArray::fromUtf16(from, to - from);
m_type = Tuple;
@@ -163,7 +163,7 @@ void GdbMi::parseTuple_helper(const Char *&from, const Char *to)
}
}
void GdbMi::parseList(const Char *&from, const Char *to)
void GdbMi::parseList(const char *&from, const char *to)
{
//qDebug() << "parseList: " << QByteArray::fromUtf16(from, to - from);
QTC_ASSERT(*from == '[', /**/);
@@ -267,8 +267,8 @@ QByteArray GdbMi::toString(bool multiline, int indent) const
void GdbMi::fromString(const QByteArray &ba)
{
const Char *from = ba.constBegin();
const Char *to = ba.constEnd();
const char *from = ba.constBegin();
const char *to = ba.constEnd();
parseResultOrValue(from, to);
}
@@ -449,16 +449,16 @@ static struct Tester {
}
for (int i = from; i < to; ++i) {
if (str[i] == '{')
result += "{\n" + QByteArray(2*++indent + 1, QChar(' '));
result += "{\n" + QByteArray(2*++indent + 1, ' ');
else if (str[i] == '}') {
if (!result.isEmpty() && result[result.size() - 1] != '\n')
result += "\n";
result += QByteArray(2*--indent + 1, QChar(' ')) + "}\n";
result += QByteArray(2*--indent + 1, ' ') + "}\n";
}
else if (str[i] == ',') {
if (true || !result.isEmpty() && result[result.size() - 1] != '\n')
result += "\n";
result += QByteArray(2*indent, QChar(' '));
result += QByteArray(2*indent, ' ');
}
else
result += str[i];

View File

@@ -34,8 +34,6 @@
#ifndef DEBUGGER_GDBMI_H
#define DEBUGGER_GDBMI_H
#include <qglobal.h>
#include <QtCore/QByteArray>
#include <QtCore/QList>
@@ -125,8 +123,8 @@ public:
inline const QList<GdbMi> &children() const { return m_children; }
inline int childCount() const { return m_children.size(); }
const GdbMi & childAt(int index) const { return m_children[index]; }
GdbMi & childAt(int index) { return m_children[index]; }
const GdbMi &childAt(int index) const { return m_children[index]; }
GdbMi &childAt(int index) { return m_children[index]; }
GdbMi findChild(const QByteArray &name) const;
GdbMi findChild(const QByteArray &name, const QByteArray &defaultString) const;
@@ -138,14 +136,12 @@ private:
friend class GdbResultRecord;
friend class GdbEngine;
//typedef ushort Char;
typedef char Char;
static QByteArray parseCString(const Char *&from, const Char *to);
void parseResultOrValue(const Char *&from, const Char *to);
void parseValue(const Char *&from, const Char *to);
void parseTuple(const Char *&from, const Char *to);
void parseTuple_helper(const Char *&from, const Char *to);
void parseList(const Char *&from, const Char *to);
static QByteArray parseCString(const char *&from, const char *to);
void parseResultOrValue(const char *&from, const char *to);
void parseValue(const char *&from, const char *to);
void parseTuple(const char *&from, const char *to);
void parseTuple_helper(const char *&from, const char *to);
void parseList(const char *&from, const char *to);
void dumpChildren(QByteArray *str, bool multiline, int indent) const;
};
@@ -171,8 +167,6 @@ public:
int token;
GdbResultClass resultClass;
GdbMi data;
private:
friend class GdbMi;
};
} // namespace Internal