aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/debugger
diff options
context:
space:
mode:
authorFriedemann Kleint <[email protected]>2011-04-13 13:52:03 +0200
committerFriedemann Kleint <[email protected]>2011-04-13 13:56:53 +0200
commit292119bb6a4627d1e88a6ae80ea7b357ee7d1ceb (patch)
treef98e0fe1f52a674ee2ab038867db45869289aa2d /src/plugins/debugger
parentd34c81a2a6dfa71f16015613b5d669b546884e0c (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/debugger')
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp6
-rw-r--r--src/plugins/debugger/cdb/cdbparsehelpers.cpp154
-rw-r--r--src/plugins/debugger/cdb/cdbparsehelpers.h3
-rw-r--r--src/plugins/debugger/disassemblerlines.cpp35
-rw-r--r--src/plugins/debugger/disassemblerlines.h6
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp22
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, &currentFunction, &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));