diff options
author | Friedemann Kleint <[email protected]> | 2011-04-13 13:52:03 +0200 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2011-04-13 13:56:53 +0200 |
commit | 292119bb6a4627d1e88a6ae80ea7b357ee7d1ceb (patch) | |
tree | f98e0fe1f52a674ee2ab038867db45869289aa2d /src/plugins | |
parent | d34c81a2a6dfa71f16015613b5d669b546884e0c (diff) |
Debugger[CDB]: Use new disassembler structures.
Parse CDB's ASM output, set it up with source line.
Move convenience to add source lines into DisassemblerLines
(cached).
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/debugger/cdb/cdbengine.cpp | 6 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbparsehelpers.cpp | 154 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbparsehelpers.h | 3 | ||||
-rw-r--r-- | src/plugins/debugger/disassemblerlines.cpp | 35 | ||||
-rw-r--r-- | src/plugins/debugger/disassemblerlines.h | 6 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 22 |
6 files changed, 200 insertions, 26 deletions
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 7b93d25872a..587c786b0d0 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -823,6 +823,7 @@ void CdbEngine::setupInferior() qDebug("setupInferior"); attemptBreakpointSynchronization(); postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions. + postCommand(".asm source_line", 0); // Source line in assembly postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid); } @@ -1507,10 +1508,7 @@ void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command) { QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;) DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie); - DisassemblerLines disassemblerLines; - foreach(const QByteArray &line, command->reply) - disassemblerLines.appendUnparsed(QString::fromLatin1(line)); - agent->setContents(disassemblerLines); + agent->setContents(parseCdbDisassembler(command->reply)); } void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length) diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.cpp b/src/plugins/debugger/cdb/cdbparsehelpers.cpp index 446e50abf07..16957271781 100644 --- a/src/plugins/debugger/cdb/cdbparsehelpers.cpp +++ b/src/plugins/debugger/cdb/cdbparsehelpers.cpp @@ -37,19 +37,22 @@ #include "registerhandler.h" #include "bytearrayinputstream.h" #include "gdb/gdbmi.h" +#include "disassemblerlines.h" #ifdef Q_OS_WIN # include "shared/dbgwinutils.h" #endif +#include <utils/qtcassert.h> + #include <QtCore/QByteArray> #include <QtCore/QVariant> #include <QtCore/QString> #include <QtCore/QDir> #include <QtCore/QDebug> -#include <utils/qtcassert.h> - #include <cctype> +enum { debugDisAsm = 0 }; + namespace Debugger { namespace Internal { @@ -435,5 +438,152 @@ QDebug operator<<(QDebug s, const WinException &e) return s; } +/*! + \fn DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a) + + \brief Parse CDB disassembler output into DisassemblerLines (with helpers) + + Expected options (prepend source file line): + \code + .asm source_line + .lines + \endcode + + should cause the 'u' command to produce: + + \code +gitgui!Foo::MainWindow::on_actionPtrs_triggered+0x1f9 [c:\qt\projects\gitgui\app\mainwindow.cpp @ 758]: + 225 00000001`3fcebfe9 488b842410050000 mov rax,qword ptr [rsp+510h] + 225 00000001`3fcebff1 8b4030 mov eax,dword ptr [rax+30h] + 226 00000001`3fcebff4 ffc0 inc eax + 00000001`3fcebff6 488b8c2410050000 mov rcx,qword ptr [rsp+510h] +... +QtCored4!QTextStreamPrivate::putString+0x34: + 10 00000000`6e5e7f64 90 nop +... +\endcode + + The algorithm checks for a function line and grabs the function name, offset and (optional) + source file from it. + Instruction lines are checked for address and source line number. + When the source line changes, the source instruction is inserted. +*/ + +// Parse a function header line: Match: 'nsp::foo::+0x<offset> [<file> @ <line>]:' +// or 'nsp::foo::+0x<offset>:' +// Do not use regexp here as it is hard for functions like operator+, operator[]. +bool parseCdbDisassemblerFunctionLine(const QString &l, + QString *currentFunction, quint64 *functionOffset, + QString *sourceFile) +{ + if (l.isEmpty() || !l.endsWith(QLatin1Char(':')) || l.at(0).isDigit() || l.at(0).isSpace()) + return false; + const int offsetPos = l.indexOf(QLatin1String("+0x")); + if (offsetPos == -1) + return false; + sourceFile->clear(); + *currentFunction = l.left(offsetPos); + if (!l.endsWith(QLatin1String("]:"))) { // No source file. + *functionOffset = l.mid(offsetPos + 3, l.size() - offsetPos - 4).trimmed().toULongLong(0, 16); + if (debugDisAsm) + qDebug() << "Function:" << l << currentFunction << functionOffset; + return true; + } + // Parse file and line. + const int filePos = l.indexOf(QLatin1Char('['), offsetPos + 1); + const int linePos = filePos != -1 ? l.indexOf(QLatin1String(" @ "), filePos + 1) : -1; + if (linePos == -1) + return false; + *functionOffset = l.mid(offsetPos + 3, filePos - offsetPos - 4).trimmed().toULongLong(0, 16); + *sourceFile = l.mid(filePos + 1, linePos - filePos - 1).trimmed(); + if (debugDisAsm) + qDebug() << "Function with source: " << l << currentFunction + << functionOffset << sourceFile; + return true; +} + +// Parse an instruction line: +// ' 21 00000001`3fcebff1 8b4030 mov eax,dword ptr [rax+30h]' +// '<source_line> <address> <raw data> <instruction> +bool parseCdbDisassemblerLine(const QString &lineIn, DisassemblerLine *dLine, uint *sourceLine) +{ + *sourceLine = 0; + if (lineIn.size() < 6) + return false; + // Check source line: right-padded 5 digits + const bool hasSourceLine = lineIn.at(4).isDigit(); + const QString line = lineIn.trimmed(); + int addressPos = 0; + const QChar blank = QLatin1Char(' '); + // Optional source line. + if (hasSourceLine) { + const int sourceLineEnd = line.indexOf(blank); + if (sourceLineEnd == -1) + return false; + *sourceLine = line.left(sourceLineEnd).toUInt(); + addressPos = sourceLineEnd + 1; + } + // Find positions of address/raw data/instruction + const int addressEnd = line.indexOf(blank, addressPos + 1); + if (addressEnd < 0) + return false; + const int rawDataPos = addressEnd + 1; + const int rawDataEnd = line.indexOf(blank, rawDataPos + 1); + if (rawDataEnd < 0) + return false; + const int instructionPos = rawDataEnd + 1; + bool ok; + QString addressS = line.mid(addressPos, addressEnd - addressPos); + if (addressS.size() > 9 && addressS.at(8) == QLatin1Char('`')) + addressS.remove(8, 1); + dLine->address = addressS.toULongLong(&ok, 16); + if (!ok) + return false; + dLine->rawData = QByteArray::fromHex(line.mid(rawDataPos, rawDataEnd - rawDataPos).toAscii()); + dLine->data = line.right(line.size() - instructionPos).trimmed(); + return true; +} + +DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a) +{ + DisassemblerLines result; + quint64 functionAddress = 0; + uint lastSourceLine = 0; + QString currentFunction; + quint64 functionOffset = 0; + QString sourceFile; + + foreach (const QByteArray &lineBA, a) { + const QString line = QString::fromLatin1(lineBA); + // New function? + if (parseCdbDisassemblerFunctionLine(line, ¤tFunction, &functionOffset, &sourceFile)) { + functionAddress = 0; + } else { + DisassemblerLine disassemblyLine; + uint sourceLine; + if (parseCdbDisassemblerLine(line, &disassemblyLine, &sourceLine)) { + // New source line: Add source code if available. + if (sourceLine && sourceLine != lastSourceLine) { + lastSourceLine = sourceLine; + result.appendSourceLine(sourceFile, sourceLine); + } + } else { + qWarning("Unable to parse assembly line '%s'", lineBA.constData()); + disassemblyLine.fromString(line); + } + // Determine address of function from the first assembler line after a + // function header line. + if (!functionAddress && disassemblyLine.address && functionOffset) { + functionAddress = disassemblyLine.address - functionOffset; + } + if (functionAddress && disassemblyLine.address) + disassemblyLine.offset = disassemblyLine.address - functionAddress; + disassemblyLine.function = currentFunction; + result.appendLine(disassemblyLine); + } + } + return result; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.h b/src/plugins/debugger/cdb/cdbparsehelpers.h index 65f93627067..6f761c5e2ab 100644 --- a/src/plugins/debugger/cdb/cdbparsehelpers.h +++ b/src/plugins/debugger/cdb/cdbparsehelpers.h @@ -54,6 +54,7 @@ class BreakpointParameters; struct ThreadData; class Register; class GdbMi; +class DisassemblerLines; // Perform mapping on parts of the source tree as reported by/passed to debugger // in case the user has specified such mappings in the global settings. @@ -81,6 +82,8 @@ QByteArray cdbWriteMemoryCommand(quint64 addr, const QByteArray &data); QString debugByteArray(const QByteArray &a); QString StringFromBase64EncodedUtf16(const QByteArray &a); +DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a); + // Model EXCEPTION_RECORD + firstchance struct WinException { diff --git a/src/plugins/debugger/disassemblerlines.cpp b/src/plugins/debugger/disassemblerlines.cpp index c9cba4f896b..0f8523a8926 100644 --- a/src/plugins/debugger/disassemblerlines.cpp +++ b/src/plugins/debugger/disassemblerlines.cpp @@ -36,6 +36,8 @@ #include <QtCore/QDebug> #include <QtCore/QRegExp> +#include <QtCore/QFile> +#include <QtCore/QTextStream> namespace Debugger { namespace Internal { @@ -95,6 +97,39 @@ void DisassemblerLines::appendLine(const DisassemblerLine &dl) m_rowCache[dl.address] = m_data.size(); } +// Append source line: Cache current file +struct SourceFileCache +{ + QString fileName; + QStringList lines; +}; + +Q_GLOBAL_STATIC(SourceFileCache, sourceFileCache) + +void DisassemblerLines::appendSourceLine(const QString &fileName, uint lineNumber) +{ + + if (fileName.isEmpty() || lineNumber == 0) + return; + lineNumber--; // fix 1..n range. + SourceFileCache *cache = sourceFileCache(); + if (fileName != cache->fileName) { + cache->fileName = fileName; + cache->lines.clear(); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + QTextStream ts(&file); + cache->lines = ts.readAll().split(QLatin1Char('\n')); + } // open + } // different file + if (lineNumber >= uint(cache->lines.size())) + return; + DisassemblerLine dl; + dl.lineNumber = lineNumber; + dl.data = cache->lines.at(lineNumber); + appendLine(dl); +} + void DisassemblerLines::appendUnparsed(const QString &unparsed) { QString line = unparsed.trimmed(); diff --git a/src/plugins/debugger/disassemblerlines.h b/src/plugins/debugger/disassemblerlines.h index 9d17f4b943c..e175b9c5ef5 100644 --- a/src/plugins/debugger/disassemblerlines.h +++ b/src/plugins/debugger/disassemblerlines.h @@ -34,7 +34,7 @@ #ifndef DEBUGGER_DISASSEMBLERLINES_H #define DEBUGGER_DISASSEMBLERLINES_H -#include <QtCore/QString> +#include <QtCore/QStringList> #include <QtCore/QHash> #include <QtCore/QVector> @@ -65,6 +65,7 @@ public: QString function; // (ass) Function to which current instruction belongs. uint offset; // (ass) Offset of instruction in relation to current function. uint lineNumber; // (src) Line number in source. + QByteArray rawData; // (ass) Raw bytes of the instruction QString data; // (ass) Instruction text, (src) source text, (cmt) arbitrary. }; @@ -76,6 +77,9 @@ public: bool coversAddress(quint64 address) const; void appendUnparsed(const QString &line); void appendLine(const DisassemblerLine &dl); + // Mixed source/assembly: Retrieve contents of source (cached) + void appendSourceLine(const QString &fileName, uint line); + int size() const { return m_data.size(); } const DisassemblerLine &at(int i) const { return m_data.at(i); } int lineForAddress(quint64 address) const; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 3e52db0fbce..52098edfe9b 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4137,30 +4137,14 @@ DisassemblerLines GdbEngine::parseMiDisassembler(const GdbMi &lines) // {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"}, // {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"}, - QStringList fileContents; - bool fileLoaded = false; DisassemblerLines result; // FIXME: Performance? foreach (const GdbMi &child, lines.children()) { if (child.hasName("src_and_asm_line")) { - // Mixed mode. - if (!fileLoaded) { - QString fileName = QFile::decodeName(child.findChild("file").data()); - fileName = cleanupFullName(fileName); - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QTextStream ts(&file); - fileContents = ts.readAll().split(QLatin1Char('\n')); - fileLoaded = true; - } - int line = child.findChild("line").data().toInt(); - if (line >= 1 && line <= fileContents.size()) { - DisassemblerLine dl; - dl.lineNumber = line; - dl.data = fileContents.at(line - 1); - result.appendLine(dl); - } + const QString fileName = QFile::decodeName(child.findChild("file").data()); + const uint line = child.findChild("line").data().toUInt(); + result.appendSourceLine(fileName, line); GdbMi insn = child.findChild("line_asm_insn"); foreach (const GdbMi &item, insn.children()) result.appendLine(parseLine(item)); |