forked from qt-creator/qt-creator
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).
This commit is contained in:
@@ -823,6 +823,7 @@ void CdbEngine::setupInferior()
|
|||||||
qDebug("setupInferior");
|
qDebug("setupInferior");
|
||||||
attemptBreakpointSynchronization();
|
attemptBreakpointSynchronization();
|
||||||
postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
|
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);
|
postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1507,10 +1508,7 @@ void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
|
QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
|
||||||
DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
|
DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
|
||||||
DisassemblerLines disassemblerLines;
|
agent->setContents(parseCdbDisassembler(command->reply));
|
||||||
foreach(const QByteArray &line, command->reply)
|
|
||||||
disassemblerLines.appendUnparsed(QString::fromLatin1(line));
|
|
||||||
agent->setContents(disassemblerLines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
|
void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
|
||||||
|
@@ -37,19 +37,22 @@
|
|||||||
#include "registerhandler.h"
|
#include "registerhandler.h"
|
||||||
#include "bytearrayinputstream.h"
|
#include "bytearrayinputstream.h"
|
||||||
#include "gdb/gdbmi.h"
|
#include "gdb/gdbmi.h"
|
||||||
|
#include "disassemblerlines.h"
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
# include "shared/dbgwinutils.h"
|
# include "shared/dbgwinutils.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
|
enum { debugDisAsm = 0 };
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -435,5 +438,152 @@ QDebug operator<<(QDebug s, const WinException &e)
|
|||||||
return s;
|
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 Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
@@ -54,6 +54,7 @@ class BreakpointParameters;
|
|||||||
struct ThreadData;
|
struct ThreadData;
|
||||||
class Register;
|
class Register;
|
||||||
class GdbMi;
|
class GdbMi;
|
||||||
|
class DisassemblerLines;
|
||||||
|
|
||||||
// Perform mapping on parts of the source tree as reported by/passed to debugger
|
// 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.
|
// 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 debugByteArray(const QByteArray &a);
|
||||||
QString StringFromBase64EncodedUtf16(const QByteArray &a);
|
QString StringFromBase64EncodedUtf16(const QByteArray &a);
|
||||||
|
|
||||||
|
DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a);
|
||||||
|
|
||||||
// Model EXCEPTION_RECORD + firstchance
|
// Model EXCEPTION_RECORD + firstchance
|
||||||
struct WinException
|
struct WinException
|
||||||
{
|
{
|
||||||
|
@@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QRegExp>
|
#include <QtCore/QRegExp>
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QTextStream>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -95,6 +97,39 @@ void DisassemblerLines::appendLine(const DisassemblerLine &dl)
|
|||||||
m_rowCache[dl.address] = m_data.size();
|
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)
|
void DisassemblerLines::appendUnparsed(const QString &unparsed)
|
||||||
{
|
{
|
||||||
QString line = unparsed.trimmed();
|
QString line = unparsed.trimmed();
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
#ifndef DEBUGGER_DISASSEMBLERLINES_H
|
#ifndef DEBUGGER_DISASSEMBLERLINES_H
|
||||||
#define DEBUGGER_DISASSEMBLERLINES_H
|
#define DEBUGGER_DISASSEMBLERLINES_H
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QStringList>
|
||||||
#include <QtCore/QHash>
|
#include <QtCore/QHash>
|
||||||
#include <QtCore/QVector>
|
#include <QtCore/QVector>
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
QString function; // (ass) Function to which current instruction belongs.
|
QString function; // (ass) Function to which current instruction belongs.
|
||||||
uint offset; // (ass) Offset of instruction in relation to current function.
|
uint offset; // (ass) Offset of instruction in relation to current function.
|
||||||
uint lineNumber; // (src) Line number in source.
|
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.
|
QString data; // (ass) Instruction text, (src) source text, (cmt) arbitrary.
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,6 +77,9 @@ public:
|
|||||||
bool coversAddress(quint64 address) const;
|
bool coversAddress(quint64 address) const;
|
||||||
void appendUnparsed(const QString &line);
|
void appendUnparsed(const QString &line);
|
||||||
void appendLine(const DisassemblerLine &dl);
|
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(); }
|
int size() const { return m_data.size(); }
|
||||||
const DisassemblerLine &at(int i) const { return m_data.at(i); }
|
const DisassemblerLine &at(int i) const { return m_data.at(i); }
|
||||||
int lineForAddress(quint64 address) const;
|
int lineForAddress(quint64 address) const;
|
||||||
|
@@ -4137,30 +4137,14 @@ DisassemblerLines GdbEngine::parseMiDisassembler(const GdbMi &lines)
|
|||||||
// {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
|
// {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
|
||||||
// {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
|
// {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
|
||||||
|
|
||||||
QStringList fileContents;
|
|
||||||
bool fileLoaded = false;
|
|
||||||
DisassemblerLines result;
|
DisassemblerLines result;
|
||||||
|
|
||||||
// FIXME: Performance?
|
// FIXME: Performance?
|
||||||
foreach (const GdbMi &child, lines.children()) {
|
foreach (const GdbMi &child, lines.children()) {
|
||||||
if (child.hasName("src_and_asm_line")) {
|
if (child.hasName("src_and_asm_line")) {
|
||||||
// Mixed mode.
|
const QString fileName = QFile::decodeName(child.findChild("file").data());
|
||||||
if (!fileLoaded) {
|
const uint line = child.findChild("line").data().toUInt();
|
||||||
QString fileName = QFile::decodeName(child.findChild("file").data());
|
result.appendSourceLine(fileName, line);
|
||||||
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);
|
|
||||||
}
|
|
||||||
GdbMi insn = child.findChild("line_asm_insn");
|
GdbMi insn = child.findChild("line_asm_insn");
|
||||||
foreach (const GdbMi &item, insn.children())
|
foreach (const GdbMi &item, insn.children())
|
||||||
result.appendLine(parseLine(item));
|
result.appendLine(parseLine(item));
|
||||||
|
Reference in New Issue
Block a user