Commit ced7e3a0 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Added profiles for manual tests, cleaned up.

parent f0f9635d
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
DEFINES +=
LIBS += Dbghelp.lib dbgeng.lib
HEADERS += mainwindow.h \
debugger.h \
outputcallback.h \
windbgeventcallback.h \
windbgthread.h
FORMS += mainwindow.ui
SOURCES += main.cpp mainwindow.cpp \
debugger.cpp \
outputcallback.cpp \
windbgeventcallback.cpp \
windbgthread.cpp
#include "debugger.h"
#include "windbgthread.h"
#include "outputcallback.h"
#include "windbgeventcallback.h"
#include <QFileInfo>
#include <QTimerEvent>
#include <QDebug>
Debugger::Debugger(QObject* parent)
: QObject(parent),
m_callbackEvent(this),
m_watchTimer(-1),
m_hDebuggeeProcess(0)
{
HRESULT hr;
hr = DebugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_pDebugClient));
if (FAILED(hr)) m_pDebugClient = 0;
hr = DebugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl));
if (FAILED(hr)) m_pDebugControl = 0;
hr = DebugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_pDebugSystemObjects));
if (FAILED(hr)) m_pDebugSystemObjects = 0;
hr = DebugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_pDebugSymbols));
if (FAILED(hr)) m_pDebugSymbols = 0;
hr = DebugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters));
if (FAILED(hr)) m_pDebugRegisters = 0;
m_pDebugClient->SetOutputCallbacks(&g_outputCallbacks);
m_pDebugClient->SetEventCallbacks(&m_callbackEvent);
}
Debugger::~Debugger()
{
killTimer(m_watchTimer);
if (m_pDebugClient)
m_pDebugClient->Release();
if (m_pDebugControl)
m_pDebugControl->Release();
if (m_pDebugSystemObjects)
m_pDebugSystemObjects->Release();
if (m_pDebugSymbols)
m_pDebugSymbols->Release();
if (m_pDebugRegisters)
m_pDebugRegisters->Release();
}
void Debugger::timerEvent(QTimerEvent* te)
{
if (te->timerId() != m_watchTimer)
return;
HRESULT hr;
hr = m_pDebugControl->WaitForEvent(0, 1);
switch (hr) {
case S_OK:
//qDebug() << "S_OK";
//hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_BREAK);
killTimer(m_watchTimer);
m_watchTimer = -1;
handleDebugEvent();
break;
case S_FALSE:
//qDebug() << "S_FALSE";
break;
case E_PENDING:
qDebug() << "S_PENDING";
break;
case E_UNEXPECTED:
killTimer(m_watchTimer);
m_watchTimer = -1;
break;
case E_FAIL:
qDebug() << "E_FAIL";
break;
default:
qDebug() << "asser welljuh";
}
}
void Debugger::openProcess(const QString& filename)
{
DEBUG_CREATE_PROCESS_OPTIONS dbgopts;
memset(&dbgopts, 0, sizeof(dbgopts));
dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
HRESULT hr;
QFileInfo fi(filename);
m_pDebugSymbols->AppendImagePathWide(fi.absolutePath().replace('/','\\').utf16());
//m_pDebugSymbols->AppendSymbolPathWide(fi.absolutePath().replace('/','\\').utf16());
//m_pDebugSymbols->AppendSymbolPathWide(L"D:\\dev\\qt\\4.4.3\\lib");
m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
hr = m_pDebugClient->CreateProcess2Wide(NULL,
const_cast<PWSTR>(filename.utf16()),
&dbgopts,
sizeof(dbgopts),
NULL, // TODO: think about the initial directory
NULL); // TODO: think about setting the environment
if (FAILED(hr)) {
qWarning("CreateProcess2Wide failed");
return;
}
m_watchTimer = startTimer(0);
}
void Debugger::closeProcess()
{
m_pDebugClient->TerminateCurrentProcess();
}
void Debugger::breakAtCurrentPosition()
{
if (!m_hDebuggeeProcess)
return;
if (!DebugBreakProcess(m_hDebuggeeProcess))
qWarning("DebugBreakProcess failed.");
}
void Debugger::continueProcess()
{
m_watchTimer = startTimer(0);
}
void Debugger::handleDebugEvent()
{
HRESULT hr;
ULONG numberOfThreads;
hr = m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads);
const ULONG maxThreadIds = 200;
ULONG threadIds[maxThreadIds];
ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1);
hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0);
for (ULONG threadId = 0; threadId <= biggestThreadId; ++threadId) {
qDebug() << "dumping stack for thread" << threadId;
m_pDebugSystemObjects->SetCurrentThreadId(threadId);
ULONG64 frameOffset, instructionOffset, stackOffset;
if (FAILED(m_pDebugRegisters->GetFrameOffset2(DEBUG_REGSRC_DEBUGGEE, &frameOffset)) ||
FAILED(m_pDebugRegisters->GetInstructionOffset2(DEBUG_REGSRC_DEBUGGEE, &instructionOffset)) ||
FAILED(m_pDebugRegisters->GetStackOffset2(DEBUG_REGSRC_DEBUGGEE, &stackOffset)))
{
frameOffset = instructionOffset = stackOffset = 0;
}
//frameOffset = instructionOffset = stackOffset = 0;
const ULONG numFrames = 100;
ULONG numFramesFilled = 0;
DEBUG_STACK_FRAME frames[numFrames];
hr = m_pDebugControl->GetStackTrace(frameOffset, stackOffset, instructionOffset, frames, numFrames, &numFramesFilled);
if (FAILED(hr))
qDebug() << "GetStackTrace failed";
const size_t buflen = 1024;
WCHAR wszBuf[buflen];
for (ULONG i=0; i < numFramesFilled; ++i) {
m_pDebugSymbols->GetNameByOffsetWide(frames[i].InstructionOffset, wszBuf, buflen, 0, 0);
qDebug() << QString::fromUtf16(wszBuf);
}
//m_pDebugSymbols->GetImagePathWide(wszBuf, buflen, 0);
//qDebug() << "ImagePath" << QString::fromUtf16(wszBuf);
//m_pDebugSymbols->GetSymbolPathWide(wszBuf, buflen, 0);
//qDebug() << "SymbolPath" << QString::fromUtf16(wszBuf);
//m_pDebugControl->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT, 0, 2, DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_FRAME_NUMBERS);
//m_pDebugControl->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT, frames, numFramesFilled, DEBUG_STACK_SOURCE_LINE);
}
}
void Debugger::handleCreateProcessEvent(DEBUG_EVENT* e)
{
//qDebug() << "CREATE_PROCESS_DEBUG_EVENT";
//m_hDebuggeeProcess = e->u.CreateProcessInfo.hProcess;
//m_hDebuggeeThread = e->u.CreateProcessInfo.hThread;
//m_hDebuggeeImage = e->u.CreateProcessInfo.hFile;
//QFileInfo fi(m_pDbgProcess->processFileName());
//BOOL bSuccess;
//bSuccess = SymInitialize(m_hDebuggeeProcess, fi.absolutePath().utf16(), FALSE);
//if (!bSuccess)
// qWarning("SymInitialize failed");
//else {
// SymSetOptions(SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
// if (!SymLoadModule64(m_hDebuggeeProcess, m_hDebuggeeImage, NULL, NULL, NULL, NULL))
// qDebug() << "SymLoadModule64 failed w/ error code" << GetLastError();
//}
}
void Debugger::handleExceptionEvent(DEBUG_EVENT* e)
{
//BOOL bSuccess;
//SuspendThread(m_hDebuggeeThread);
//CONTEXT context;
//memset(&context, 0, sizeof(context));
//context.ContextFlags = CONTEXT_ALL;
//bSuccess = GetThreadContext(m_hDebuggeeThread, &context);
//if (!bSuccess)
// qDebug() << "GetThreadContext failed w/ error code" << GetLastError();
//ResumeThread(m_hDebuggeeThread);
//STACKFRAME64 stackFrame;
//stackFrame.AddrPC.Offset = context.Eip;
//stackFrame.AddrPC.Mode = AddrModeFlat;
//stackFrame.AddrFrame.Offset = context.Ebp;
//stackFrame.AddrFrame.Mode = AddrModeFlat;
//stackFrame.AddrStack.Offset = context.Esp;
//stackFrame.AddrStack.Mode = AddrModeFlat;
//m_currentStackTrace.clear();
//do {
// StackFrame sf;
// bSuccess = StackWalk64(IMAGE_FILE_MACHINE_I386, m_hDebuggeeProcess, m_hDebuggeeThread, &stackFrame,
// &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
// if (bSuccess) {
// qDebug() << "StackWalk";
// IMAGEHLP_MODULE64 moduleInfo;
// moduleInfo.SizeOfStruct = sizeof(moduleInfo);
// if (SymGetModuleInfo64(m_hDebuggeeProcess, stackFrame.AddrPC.Offset, &moduleInfo))
// qDebug() << "SymGetModuleInfo64 success!";
// else
// qDebug() << "SymGetModuleInfo64 failed w/ error code" << GetLastError();
// }
// if (stackFrame.AddrPC.Offset) {
// DWORD64 dwDisplacement;
// const size_t bufferSize = 200;
// class MySymbol : public IMAGEHLP_SYMBOL64
// {
// public:
// private:
// char buffer[bufferSize];
// };
// MySymbol img;
// ZeroMemory(&img, sizeof(img));
// img.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
// img.MaxNameLength = bufferSize;
// BOOL bSuccess;
// bSuccess = SymGetSymFromAddr64(m_hDebuggeeProcess,
// stackFrame.AddrPC.Offset,
// &dwDisplacement,
// &img);
// if (bSuccess) {
// qDebug() << "SymGetSymFromAddr64:" << img.Name;
// sf.symbol = QString::fromLocal8Bit(img.Name);
// }
// else
// qDebug() << "SymGetSymFromAddr64 failed w/ error code" << GetLastError();
// }
// if (stackFrame.AddrPC.Offset) {
// DWORD dwDisplacement;
// IMAGEHLP_LINE64 line;
// ZeroMemory(&line, sizeof(line));
// line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
// BOOL bSuccess;
// bSuccess = SymGetLineFromAddr64(m_hDebuggeeProcess,
// stackFrame.AddrPC.Offset,
// &dwDisplacement,
// &line);
// if (bSuccess) {
// //qDebug() << "SymGetLineFromAddr64:" << QString::fromUtf16((ushort*)line.FileName) << line.LineNumber;
// sf.filename = QString::fromUtf16((ushort*)line.FileName);
// sf.line = line.LineNumber;
// } else
// qDebug() << "SymGetLineFromAddr64 failed w/ error code" << GetLastError();
// m_currentStackTrace.append(sf);
// }
//} while (bSuccess);
//emit debuggeePaused();
}
void Debugger::handleOutputDebugStringEvent(DEBUG_EVENT* e)
{
//qDebug() << "OUTPUT_DEBUG_STRING_EVENT";
//BOOL bSuccess;
//SIZE_T nNumberOfBytesRead;
//void* buffer;
//QString result;
//if (e->u.DebugString.fUnicode) {
// buffer = malloc(e->u.DebugString.nDebugStringLength * sizeof(WCHAR));
//} else {
// buffer = malloc(e->u.DebugString.nDebugStringLength * sizeof(char));
//}
//bSuccess = ReadProcessMemory(m_hDebuggeeProcess, e->u.DebugString.lpDebugStringData,
// buffer, e->u.DebugString.nDebugStringLength, &nNumberOfBytesRead);
//if (bSuccess) {
// if (e->u.DebugString.fUnicode)
// result = QString::fromUtf16(reinterpret_cast<ushort*>(buffer), nNumberOfBytesRead);
// else
// result = QString::fromLocal8Bit(reinterpret_cast<char*>(buffer), nNumberOfBytesRead);
// emit debugOutput(result);
//}
//free(buffer);
}
#pragma once
#include "windbgeventcallback.h"
#include <QObject>
#include <QVector>
#define DBGHELP_TRANSLATE_TCHAR
#include <Dbghelp.h>
class WinDbgThread;
class Debugger : public QObject
{
Q_OBJECT
public:
Debugger(QObject* parent = 0);
~Debugger();
void openProcess(const QString& filename);
void closeProcess();
void breakAtCurrentPosition();
void continueProcess();
struct StackFrame
{
QString symbol;
QString filename;
uint line;
};
typedef QVector<StackFrame> StackTrace;
StackTrace stackTrace() { return m_currentStackTrace; }
signals:
void debugOutput(const QString&);
void debuggeePaused();
protected:
void timerEvent(QTimerEvent*);
private:
void handleDebugEvent();
void handleCreateProcessEvent(DEBUG_EVENT* e);
void handleExceptionEvent(DEBUG_EVENT* e);
void handleOutputDebugStringEvent(DEBUG_EVENT* e);
private:
HANDLE m_hDebuggeeProcess;
HANDLE m_hDebuggeeThread;
HANDLE m_hDebuggeeImage;
StackTrace m_currentStackTrace;
//DWORD64 m_dwModuleBaseAddress;
int m_watchTimer;
IDebugClient5* m_pDebugClient;
IDebugControl4* m_pDebugControl;
IDebugSystemObjects4* m_pDebugSystemObjects;
IDebugSymbols3* m_pDebugSymbols;
IDebugRegisters2* m_pDebugRegisters;
WinDbgEventCallback m_callbackEvent;
//struct ThreadInfo
//{
// ULONG64 handle, dataOffset, startOffset;
//};
//QVector<ThreadInfo> m_threadlist;
friend class WinDbgEventCallback;
};
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow mw;
if (argc >= 2) mw.setDebuggee(argv[1]);
mw.show();
return app.exec();
}
#include "mainwindow.h"
#include <QFileDialog>
#include <QTextStream>
#include <QDebug>
MainWindow::MainWindow()
: QMainWindow(0, 0)
{
setupUi(this);
connect(&m_debugger, SIGNAL(debugOutput(const QString&)), SLOT(appendOutput(const QString&)));
connect(&m_debugger, SIGNAL(debuggeePaused()), SLOT(onDebuggeePaused()));
}
void MainWindow::setDebuggee(const QString& filename)
{
m_debugger.openProcess(filename);
}
void MainWindow::on_actionOpen_triggered()
{
QString exeName;
exeName = QFileDialog::getOpenFileName(this, "Open Executable", ".", "*.exe");
if (!exeName.isNull())
m_debugger.openProcess(exeName);
}
void MainWindow::on_actionExit_triggered()
{
close();
}
void MainWindow::on_actionBreak_triggered()
{
m_debugger.breakAtCurrentPosition();
}
void MainWindow::on_actionRun_triggered()
{
m_debugger.continueProcess();
}
void MainWindow::on_lstStack_itemClicked(QListWidgetItem* item)
{
Debugger::StackFrame sf = m_stackTrace[ lstStack->row(item) ];
QFile f(sf.filename);
if (!f.exists())
return;
f.open(QFile::ReadOnly);
QTextStream ts(&f);
int cursorPos = 0;
int currentLine = 0;
QString fullText;
do {
QString strLine = ts.readLine();
currentLine++;
if (currentLine < sf.line)
cursorPos += strLine.length();
fullText.append(strLine + "\n");
} while (!ts.atEnd());
codeWindow->setPlainText(fullText);
//QList<QTextEdit::ExtraSelection> extraSelections;
//extraSelections.append(QTextEdit::ExtraSelection());
//QTextEdit::ExtraSelection& exsel = extraSelections.first();
//exsel.cursor.setPosition(cursorPos, QTextCursor::MoveAnchor);
//exsel.cursor.select(QTextCursor::LineUnderCursor);
//exsel.format.setBackground(Qt::red);
//exsel.format.setFontUnderline(true);
//codeWindow->setExtraSelections(extraSelections);
}
void MainWindow::appendOutput(const QString& str)
{
teOutput->setPlainText(teOutput->toPlainText() + str);
}
void MainWindow::onDebuggeePaused()
{
lstStack->clear();
m_stackTrace = m_debugger.stackTrace();
foreach (Debugger::StackFrame sf, m_stackTrace) {
QString str = sf.symbol;
if (!sf.filename.isEmpty())
str.append(" at " + sf.filename + ":" + QString::number(sf.line));
lstStack->addItem(str);
}
}
#pragma once
#include "ui_mainwindow.h"
#include "debugger.h"
class MainWindow : public QMainWindow, private Ui_MainWindow
{
Q_OBJECT
public:
MainWindow();
void setDebuggee(const QString& filename);
private slots:
void on_actionOpen_triggered();
void on_actionExit_triggered();
void on_actionBreak_triggered();
void on_actionRun_triggered();
void on_lstStack_itemClicked(QListWidgetItem*);
void appendOutput(const QString&);
void onDebuggeePaused();
private:
Debugger m_debugger;
Debugger::StackTrace m_stackTrace;
};
\ No newline at end of file
<ui version="4.0" >
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>599</width>
<height>606</height>
</rect>
</property>
<property name="windowTitle" >
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget" >
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="QPlainTextEdit" name="codeWindow" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="undoRedoEnabled" >
<bool>false</bool>
</property>
<property name="lineWrapMode" >
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
<property name="textInteractionFlags" >
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex" >
<number>1</number>
</property>
<widget class="QWidget" name="tab" >
<attribute name="title" >
<string>Threads</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QListWidget" name="lstThreads" />
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2" >
<attribute name="title" >
<string>Stack</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2" >
<item>
<widget class="QListWidget" name="lstStack" />
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3" >
<attribute name="title" >
<string>Output</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3" >
<item>
<widget class="QPlainTextEdit" name="teOutput" />
</item>
</layout>
</widget>