Commit d4ccc16f authored by Friedemann Kleint's avatar Friedemann Kleint

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).
parent b95af44c
......@@ -84,16 +84,17 @@ void BreakWindow::resizeEvent(QResizeEvent *ev)
void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
QModelIndex index = indexAt(ev->pos());
const QModelIndex index = indexAt(ev->pos());
const bool indexIsValid = index.isValid();
QAction *act0 = new QAction(tr("Delete breakpoint"), &menu);
act0->setEnabled(index.isValid());
act0->setEnabled(indexIsValid);
QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu);
QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu);
act2->setCheckable(true);
act2->setChecked(m_alwaysResizeColumnsToContents);
QAction *act3 = new QAction(tr("Edit condition..."), &menu);
act0->setEnabled(index.isValid());
QAction *act4 = new QAction(tr("Syncronize breakpoints"), &menu);
act3->setEnabled(indexIsValid);
QAction *act4 = new QAction(tr("Synchronize breakpoints"), &menu);
menu.addAction(act0);
menu.addAction(act3);
......
......@@ -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;
}
}
}
......@@ -39,6 +39,8 @@
namespace Debugger {
namespace Internal {
class DisassemblerLine;
// Utilities related to assembler code.
class Register;
......@@ -47,6 +49,14 @@ bool getRegisters(IDebugControl4 *ctl,
QList<Register> *registers,
QString *errorMessage,
int base = 10 /* 16 for hex, etc */);
bool dissassemble(IDebugClient5 *client,
IDebugControl4 *ctl,
ULONG64 offset,
unsigned long beforeLines,
unsigned long afterLines,
QList<DisassemblerLine> *lines,
QString *errorMessage);
}
}
......
......@@ -42,6 +42,7 @@
#include "watchhandler.h"
#include "registerhandler.h"
#include "moduleshandler.h"
#include "disassemblerhandler.h"
#include "watchutils.h"
#include <utils/qtcassert.h>
......@@ -56,6 +57,7 @@
#include <QtCore/QCoreApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtGui/QApplication>
#define DBGHELP_TRANSLATE_TCHAR
#include <inc/Dbghelp.h>
......@@ -195,7 +197,6 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn
m_breakEventMode(BreakEventHandle),
m_watchTimer(-1),
m_debugEventCallBack(engine),
m_debugOutputCallBack(engine),
m_pDebugClient(0),
m_pDebugControl(0),
m_pDebugSystemObjects(0),
......@@ -225,7 +226,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage)
return false;
}
m_pDebugClient->SetOutputCallbacks(&m_debugOutputCallBack);
m_pDebugClient->SetOutputCallbacksWide(&m_debugOutputCallBack);
m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack);
hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl));
......@@ -317,6 +318,10 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent) :
m_d->m_debuggerManager, SLOT(showDebuggerOutput(QString,QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerInputPrompt(QString,QString)),
m_d->m_debuggerManager, SLOT(showDebuggerInput(QString,QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeOutput(QString)),
m_d->m_debuggerManager, SLOT(showApplicationOutput(QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeInputPrompt(QString)),
m_d->m_debuggerManager, SLOT(showApplicationOutput(QString)));
}
CdbDebugEngine::~CdbDebugEngine()
......@@ -1040,7 +1045,31 @@ void CdbDebugEngine::saveSessionData()
}
void CdbDebugEngine::reloadDisassembler()
{
{
enum { ContextLines = 40 };
// Do we have a top stack frame?
const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0);
if (debugCDB)
qDebug() << Q_FUNC_INFO << offset;
DisassemblerHandler *dh = m_d->m_debuggerManagerAccess->disassemblerHandler();
if (offset) {
QList<DisassemblerLine> lines;
QString errorMessage;
QApplication::setOverrideCursor(Qt::WaitCursor);
const bool drc = dissassemble(m_d->m_pDebugClient, m_d->m_pDebugControl, offset,
ContextLines, ContextLines, &lines, &errorMessage);
QApplication::restoreOverrideCursor();
if (drc) {
dh->setLines(lines);
if (lines.size() > ContextLines)
dh->setCurrentLine(ContextLines);
} else {
qWarning("reloadDisassembler: %s\n", qPrintable(errorMessage));
}
} else {
dh->setLines(QList<DisassemblerLine>());
}
}
void CdbDebugEngine::reloadModules()
......
......@@ -27,7 +27,6 @@
**
**************************************************************************/
#include "cdbdebugoutput.h"
#include "cdbdebugengine.h"
#include "cdbdebugengine_p.h"
......@@ -38,12 +37,11 @@
namespace Debugger {
namespace Internal {
CdbDebugOutput::CdbDebugOutput(CdbDebugEngine* engine) :
m_pEngine(engine)
CdbDebugOutputBase::CdbDebugOutputBase()
{
}
STDMETHODIMP CdbDebugOutput::QueryInterface(
STDMETHODIMP CdbDebugOutputBase::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
......@@ -52,9 +50,9 @@ STDMETHODIMP CdbDebugOutput::QueryInterface(
*Interface = NULL;
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide)))
{
*Interface = (IDebugOutputCallbacks *)this;
*Interface = (IDebugOutputCallbacksWide*)this;
AddRef();
return S_OK;
} else {
......@@ -62,27 +60,43 @@ STDMETHODIMP CdbDebugOutput::QueryInterface(
}
}
STDMETHODIMP_(ULONG) CdbDebugOutput::AddRef(THIS)
STDMETHODIMP_(ULONG) CdbDebugOutputBase::AddRef(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 1;
}
STDMETHODIMP_(ULONG) CdbDebugOutput::Release(THIS)
STDMETHODIMP_(ULONG) CdbDebugOutputBase::Release(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 0;
}
STDMETHODIMP CdbDebugOutputBase::Output(
THIS_
IN ULONG mask,
IN PCWSTR text
)
{
output(mask, QString::fromUtf16(text));
return S_OK;
}
IDebugOutputCallbacksWide *CdbDebugOutputBase::getOutputCallback(IDebugClient5 *client)
{
IDebugOutputCallbacksWide *rc;
if (FAILED(client->GetOutputCallbacksWide(&rc)))
return 0;
return rc;
}
// ------------------------- CdbDebugOutput
// Return a prefix for debugger messages
static QString prefix(ULONG mask)
{
if (mask & (DEBUG_OUTPUT_DEBUGGEE|DEBUG_OUTPUT_DEBUGGEE_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) {
static const QString p = QLatin1String("target:");
return p;
}
if (mask & (DEBUG_OUTPUT_PROMPT_REGISTERS)) {
static const QString p = QLatin1String("registers:");
return p;
......@@ -103,23 +117,42 @@ static QString prefix(ULONG mask)
return commonPrefix;
}
STDMETHODIMP CdbDebugOutput::Output(
THIS_
IN ULONG mask,
IN PCSTR text
)
enum OutputKind { DebuggerOutput, DebuggerPromptOutput, DebuggeeOutput, DebuggeePromptOutput };
static inline OutputKind outputKind(ULONG mask)
{
if (mask & DEBUG_OUTPUT_DEBUGGEE)
return DebuggeeOutput;
if (mask & DEBUG_OUTPUT_DEBUGGEE_PROMPT)
return DebuggeePromptOutput;
if (mask & DEBUG_OUTPUT_PROMPT)
return DebuggerPromptOutput;
return DebuggerOutput;
}
CdbDebugOutput::CdbDebugOutput()
{
const QString msg = QString::fromLocal8Bit(text);
}
void CdbDebugOutput::output(ULONG mask, const QString &msg)
{
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << "\n " << msg;
if (mask & (DEBUG_OUTPUT_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) {
switch (outputKind(mask)) {
case DebuggerOutput:
debuggerOutput(prefix(mask), msg);
break;
case DebuggerPromptOutput:
emit debuggerInputPrompt(prefix(mask), msg);
} else {
emit debuggerOutput(prefix(mask), msg);
break;
case DebuggeeOutput:
emit debuggeeOutput(msg);
break;
case DebuggeePromptOutput:
emit debuggeeInputPrompt(msg);
break;
}
return S_OK;
}
} // namespace Internal
......
......@@ -38,14 +38,12 @@
namespace Debugger {
namespace Internal {
class CdbDebugEngine;
// CdbDebugOutputBase is a base class for output handlers
// that takes care of the Active X magic and conversion to QString.
class CdbDebugOutput : public QObject, public IDebugOutputCallbacks
class CdbDebugOutputBase : public IDebugOutputCallbacksWide
{
Q_OBJECT
public:
explicit CdbDebugOutput(CdbDebugEngine* engine);
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
......@@ -63,15 +61,48 @@ public:
STDMETHOD(Output)(
THIS_
IN ULONG mask,
IN PCSTR text
IN PCWSTR text
);
// Helpers to retrieve the output callbacks IF
static IDebugOutputCallbacksWide *getOutputCallback(IDebugClient5 *client);
protected:
CdbDebugOutputBase();
virtual void output(ULONG mask, const QString &message) = 0;
};
// Standard CDB output handler
class CdbDebugOutput : public QObject, public CdbDebugOutputBase
{
Q_OBJECT
public:
CdbDebugOutput();
protected:
virtual void output(ULONG mask, const QString &message);
signals:
void debuggerOutput(const QString &prefix, const QString &message);
void debuggerInputPrompt(const QString &prefix, const QString &message);
void debuggeeOutput(const QString &message);
void debuggeeInputPrompt(const QString &message);
};
// An output handler that adds lines to a string (to be
// used for cases in which linebreaks occur in-between calls
// to output).
class StringOutputHandler : public CdbDebugOutputBase
{
public:
StringOutputHandler() {}
QString result() const { return m_result; }
protected:
virtual void output(ULONG, const QString &message) { m_result += message; }
private:
CdbDebugEngine* m_pEngine;
QString m_result;
};
} // namespace Internal
......
......@@ -40,7 +40,8 @@ namespace Internal {
CdbStackTraceContext::CdbStackTraceContext(IDebugSystemObjects4* pDebugSystemObjects,
IDebugSymbols3* pDebugSymbols) :
m_pDebugSystemObjects(pDebugSystemObjects),
m_pDebugSymbols(pDebugSymbols)
m_pDebugSymbols(pDebugSymbols),
m_instructionOffset(0)
{
}
......@@ -95,6 +96,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
for (ULONG i=0; i < frameCount; ++i) {
StackFrame frame(i);
const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset;
if (i == 0)
m_instructionOffset = instructionOffset;
frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16);
m_pDebugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0);
......
......@@ -66,6 +66,9 @@ public:
QList<StackFrame> frames() const { return m_frames; }
inline int frameCount() const { return m_frames.size(); }
// Top-Level instruction offset for disassembler
ULONG64 instructionOffset() const { return m_instructionOffset; }
CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage);
private:
......@@ -78,6 +81,7 @@ private:
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
QVector <CdbSymbolGroupContext*> m_symbolContexts;
QList<StackFrame> m_frames;
ULONG64 m_instructionOffset;
};
}
......
......@@ -213,6 +213,8 @@ void DebuggerManager::init()
QAbstractItemView *disassemblerView =
qobject_cast<QAbstractItemView *>(m_disassemblerWindow);
disassemblerView->setModel(m_disassemblerHandler->model());
connect(m_disassemblerWindow, SIGNAL(reloadDisassemblerRequested()),
this, SLOT(reloadDisassembler()));
// Breakpoints
m_breakHandler = new BreakHandler;
......
......@@ -39,6 +39,14 @@
using namespace Debugger;
using namespace Debugger::Internal;
void DisassemblerLine::clear()
{
address.clear();
symbol.clear();
addressDisplay.clear();
symbolDisplay.clear();
mnemonic.clear();
}
//////////////////////////////////////////////////////////////////
//
......
......@@ -41,6 +41,8 @@ namespace Internal {
class DisassemblerLine
{
public:
void clear();
QString address;
QString symbol;
QString addressDisplay;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment