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");
|
||||
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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
@@ -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));
|
||||
|
Reference in New Issue
Block a user