Implement disassembler for cdb.

Filter debugging output correctly, some glitches. Extract
base class for debugging output to be able to intercept
debugging output for other purposes (such as disassembling).
This commit is contained in:
Friedemann Kleint
2009-04-14 15:04:19 +02:00
parent b95af44c0d
commit d4ccc16f9f
11 changed files with 313 additions and 39 deletions

View File

@@ -28,15 +28,20 @@
**************************************************************************/
#include "cdbassembler.h"
#include "registerhandler.h"
#include "cdbdebugoutput.h"
#include "cdbdebugengine_p.h"
#include "cdbsymbolgroupcontext.h"
#include "disassemblerhandler.h"
#include "registerhandler.h"
#include <QtCore/QVector>
namespace Debugger {
namespace Internal {
typedef QList<DisassemblerLine> DisassemblerLineList;
bool getRegisters(IDebugControl4 *ctl,
IDebugRegisters2 *ireg,
QList<Register> *registers,
@@ -79,5 +84,151 @@ bool getRegisters(IDebugControl4 *ctl,
return true;
}
// Output parser for disassembler lines.
// It uses the source file lines as symbol until it encounters
// a C++ symbol (function entered), from which then on
// it uses that symbol.
class DisassemblerOutputParser
{
public:
explicit DisassemblerOutputParser(DisassemblerLineList *list);
void parse(const QStringList &l);
private:
enum ParseResult { ParseOk, ParseIgnore, ParseFailed };
ParseResult parseDisassembled(const QString &in, DisassemblerLine* l);
DisassemblerLineList *m_list;
QString m_sourceSymbol;
int m_sourceSymbolOffset;
};
DisassemblerOutputParser::DisassemblerOutputParser(DisassemblerLineList *list) :
m_list(list),
m_sourceSymbolOffset(0)
{
}
// Parse a disassembler line:
// module!class::foo:
// 004017cf cc int 3
// 77 mainwindow.cpp 004018ff 8d4da8 lea ecx,[ebp-0x58]
DisassemblerOutputParser::ParseResult
DisassemblerOutputParser::parseDisassembled(const QString &in, DisassemblerLine* l)
{
l->clear();
// Check if there is a source file
if (in.size() < 7)
return ParseIgnore;
const bool hasSourceFile = !in.at(6).isSpace();
// Sometimes, empty lines occur
const QString simplified = in.simplified();
if (simplified.isEmpty())
return ParseIgnore;
QStringList tokens = simplified.split(QLatin1Char(' '), QString::SkipEmptyParts);
// Check for symbols as 'module!class::foo:' (start of function encountered)
if (tokens.size() == 1) {
QString symbol = tokens.front();
if (symbol.endsWith(QLatin1Char(':')) && symbol.contains(QLatin1Char('!'))) {
symbol.truncate(symbol.size() - 1);
m_sourceSymbol = symbol;
m_sourceSymbolOffset = 0;
}
return ParseIgnore;
}
if (tokens.size() < 2)
return ParseIgnore;
// Symbol display: Do we know a symbol?
if (!m_sourceSymbol.isEmpty()) {
l->symbol = QString(QLatin1Char('<'));
l->symbol += m_sourceSymbol;
if (m_sourceSymbolOffset) {
l->symbol += QLatin1Char('+');
l->symbol += QString::number(m_sourceSymbolOffset);
}
l->symbol += QLatin1Char('>');
m_sourceSymbolOffset++;
}
// Read source file information: If we don't know a symbol yet,
// use the source file.
if (hasSourceFile) {
if (l->symbol.isEmpty()) {
l->symbol = tokens.at(1);
l->symbol += QLatin1Char('+');
l->symbol += tokens.front();
}
tokens.pop_front();
tokens.pop_front();
}
l->symbolDisplay = l->symbol;
// Get offset address and instruction
if (tokens.size() < 3)
return ParseFailed;
l->addressDisplay = l->address = tokens.front();
tokens.pop_front();
// The rest is effective address & instructions
if (tokens.size() > 1)
tokens.pop_front();
l->mnemonic = tokens.join(QString(QLatin1Char(' ')));
return ParseOk;
}
void DisassemblerOutputParser::parse(const QStringList &l)
{
DisassemblerLine dLine;
foreach(const QString &line, l) {
switch (parseDisassembled(line, &dLine)) {
case ParseOk:
m_list->push_back(dLine);
break;
case ParseIgnore:
break;
case ParseFailed:
qWarning("Failed to parse '%s'\n", qPrintable(line));
break;
}
}
}
bool dissassemble(IDebugClient5 *client,
IDebugControl4 *ctl,
ULONG64 offset,
unsigned long beforeLines,
unsigned long afterLines,
QList<DisassemblerLine> *lines,
QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << offset;
lines->clear();
const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME;
// Catch the output by temporarily setting another handler.
// We use the method that outputs to the output handler as it
// conveniently provides the 'beforeLines' context (stepping back
// in assembler code). We build a complete string first as line breaks
// may occur in-between messages.
StringOutputHandler stringHandler;
IDebugOutputCallbacksWide *oldHandler = CdbDebugOutputBase::getOutputCallback(client);
client->SetOutputCallbacksWide(&stringHandler);
// For some reason, we need to output to "all clients"
const HRESULT hr = ctl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS,
beforeLines, beforeLines + afterLines,
offset, flags, 0, 0, 0, 0);
client->SetOutputCallbacksWide(oldHandler);
if (FAILED(hr)) {
*errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2").
arg(QString::number(offset, 16), msgComFailed("OutputDisassemblyLines", hr));
return false;
}
DisassemblerOutputParser parser(lines);
parser.parse(stringHandler.result().split(QLatin1Char('\n')));
return true;
}
}
}