forked from qt-creator/qt-creator
Initial revision of the cdbdebugger playground.
This commit is contained in:
20
tests/manual/cdbdebugger/cdbdebugger.pro
Normal file
20
tests/manual/cdbdebugger/cdbdebugger.pro
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
305
tests/manual/cdbdebugger/debugger.cpp
Normal file
305
tests/manual/cdbdebugger/debugger.cpp
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
#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);
|
||||||
|
}
|
71
tests/manual/cdbdebugger/debugger.h
Normal file
71
tests/manual/cdbdebugger/debugger.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#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;
|
||||||
|
};
|
11
tests/manual/cdbdebugger/main.cpp
Normal file
11
tests/manual/cdbdebugger/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#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();
|
||||||
|
}
|
90
tests/manual/cdbdebugger/mainwindow.cpp
Normal file
90
tests/manual/cdbdebugger/mainwindow.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
26
tests/manual/cdbdebugger/mainwindow.h
Normal file
26
tests/manual/cdbdebugger/mainwindow.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#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;
|
||||||
|
};
|
174
tests/manual/cdbdebugger/mainwindow.ui
Normal file
174
tests/manual/cdbdebugger/mainwindow.ui
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<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>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>599</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menu_Debug" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>&Debug</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionRun" />
|
||||||
|
<addaction name="actionBreak" />
|
||||||
|
<addaction name="actionStepOver" />
|
||||||
|
<addaction name="actionStopDebugging" />
|
||||||
|
<addaction name="actionStepInto" />
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menu_File" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>&File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionOpen" />
|
||||||
|
<addaction name="actionClose" />
|
||||||
|
<addaction name="separator" />
|
||||||
|
<addaction name="actionExit" />
|
||||||
|
</widget>
|
||||||
|
<addaction name="menu_File" />
|
||||||
|
<addaction name="menu_Debug" />
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar" />
|
||||||
|
<action name="actionOpen" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Open...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut" >
|
||||||
|
<string>Ctrl+O</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionClose" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Close</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionExit" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>E&xit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRun" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Run</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut" >
|
||||||
|
<string>F5</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionBreak" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Break</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionStepOver" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Step over</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut" >
|
||||||
|
<string>F10</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionStepInto" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Step into</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut" >
|
||||||
|
<string>F11</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionStopDebugging" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Stop debugging</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut" >
|
||||||
|
<string>Shift+F5</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
61
tests/manual/cdbdebugger/outputcallback.cpp
Normal file
61
tests/manual/cdbdebugger/outputcallback.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <dbgeng.h>
|
||||||
|
|
||||||
|
#include "outputcallback.h"
|
||||||
|
|
||||||
|
WinDbgOutputCallback g_outputCallbacks;
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
WinDbgOutputCallback::QueryInterface(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*Interface = NULL;
|
||||||
|
|
||||||
|
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||||
|
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
|
||||||
|
{
|
||||||
|
*Interface = (IDebugOutputCallbacks *)this;
|
||||||
|
AddRef();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG)
|
||||||
|
WinDbgOutputCallback::AddRef(
|
||||||
|
THIS
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG)
|
||||||
|
WinDbgOutputCallback::Release(
|
||||||
|
THIS
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
WinDbgOutputCallback::Output(
|
||||||
|
THIS_
|
||||||
|
IN ULONG Mask,
|
||||||
|
IN PCSTR Text
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(Mask);
|
||||||
|
fputs(Text, stdout);
|
||||||
|
return S_OK;
|
||||||
|
}
|
30
tests/manual/cdbdebugger/outputcallback.h
Normal file
30
tests/manual/cdbdebugger/outputcallback.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __OUT_HPP__
|
||||||
|
#define __OUT_HPP__
|
||||||
|
|
||||||
|
class WinDbgOutputCallback : public IDebugOutputCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// IUnknown.
|
||||||
|
STDMETHOD(QueryInterface)(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, AddRef)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, Release)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
|
||||||
|
// IDebugOutputCallbacks.
|
||||||
|
STDMETHOD(Output)(
|
||||||
|
THIS_
|
||||||
|
IN ULONG Mask,
|
||||||
|
IN PCSTR Text
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern WinDbgOutputCallback g_outputCallbacks;
|
||||||
|
|
||||||
|
#endif // #ifndef __OUT_HPP__
|
186
tests/manual/cdbdebugger/windbgeventcallback.cpp
Normal file
186
tests/manual/cdbdebugger/windbgeventcallback.cpp
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#include "windbgeventcallback.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
WinDbgEventCallback::QueryInterface(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*Interface = NULL;
|
||||||
|
|
||||||
|
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||||
|
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
|
||||||
|
{
|
||||||
|
*Interface = (IDebugOutputCallbacks *)this;
|
||||||
|
AddRef();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG)
|
||||||
|
WinDbgEventCallback::AddRef(
|
||||||
|
THIS
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG)
|
||||||
|
WinDbgEventCallback::Release(
|
||||||
|
THIS
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::GetInterestMask(
|
||||||
|
THIS_
|
||||||
|
__out PULONG Mask
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::Breakpoint(
|
||||||
|
THIS_
|
||||||
|
__in PDEBUG_BREAKPOINT Bp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::Exception(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG FirstChance
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::CreateThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 DataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//Debugger::ThreadInfo ti;
|
||||||
|
//ti.handle = Handle;
|
||||||
|
//ti.dataOffset = DataOffset;
|
||||||
|
//ti.startOffset = StartOffset;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::ExitThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::CreateProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCSTR ModuleName,
|
||||||
|
__in_opt PCSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp,
|
||||||
|
__in ULONG64 InitialThreadHandle,
|
||||||
|
__in ULONG64 ThreadDataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_pDebugger->m_hDebuggeeProcess = (HANDLE)Handle;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::ExitProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_pDebugger->m_hDebuggeeProcess = 0;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::LoadModule(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCSTR ModuleName,
|
||||||
|
__in_opt PCSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::UnloadModule(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCSTR ImageBaseName,
|
||||||
|
__in ULONG64 BaseOffset
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::SystemError(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Error,
|
||||||
|
__in ULONG Level
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::SessionStatus(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Status
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::ChangeDebuggeeState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::ChangeEngineState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP WinDbgEventCallback::ChangeSymbolState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
126
tests/manual/cdbdebugger/windbgeventcallback.h
Normal file
126
tests/manual/cdbdebugger/windbgeventcallback.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <dbgeng.h>
|
||||||
|
|
||||||
|
class Debugger;
|
||||||
|
|
||||||
|
class WinDbgEventCallback : public IDebugEventCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WinDbgEventCallback(Debugger* dbg)
|
||||||
|
: m_pDebugger(dbg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// IUnknown.
|
||||||
|
STDMETHOD(QueryInterface)(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, AddRef)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, Release)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
|
||||||
|
// IDebugEventCallbacks.
|
||||||
|
|
||||||
|
STDMETHOD(GetInterestMask)(
|
||||||
|
THIS_
|
||||||
|
__out PULONG Mask
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Breakpoint)(
|
||||||
|
THIS_
|
||||||
|
__in PDEBUG_BREAKPOINT Bp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Exception)(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG FirstChance
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 DataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
STDMETHOD(ExitThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCSTR ModuleName,
|
||||||
|
__in_opt PCSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp,
|
||||||
|
__in ULONG64 InitialThreadHandle,
|
||||||
|
__in ULONG64 ThreadDataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ExitProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(LoadModule)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCSTR ModuleName,
|
||||||
|
__in_opt PCSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(UnloadModule)(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCSTR ImageBaseName,
|
||||||
|
__in ULONG64 BaseOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(SystemError)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Error,
|
||||||
|
__in ULONG Level
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(SessionStatus)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Status
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeDebuggeeState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeEngineState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeSymbolState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Debugger* m_pDebugger;
|
||||||
|
};
|
||||||
|
|
150
tests/manual/cdbdebugger/windbgthread.cpp
Normal file
150
tests/manual/cdbdebugger/windbgthread.cpp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include "windbgthread.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#define DBGHELP_TRANSLATE_TCHAR
|
||||||
|
#include <Dbghelp.h>
|
||||||
|
|
||||||
|
WinDbgThread::WinDbgThread(QObject* parent)
|
||||||
|
: QThread(parent),
|
||||||
|
m_state(Idle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WinDbgThread::~WinDbgThread()
|
||||||
|
{
|
||||||
|
stopProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::startProcess(const QString& filename)
|
||||||
|
{
|
||||||
|
stopProcess();
|
||||||
|
m_bOwnsProcess = true;
|
||||||
|
m_processFileName = filename;
|
||||||
|
m_pi.dwProcessId = 0;
|
||||||
|
QThread::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::stopProcess()
|
||||||
|
{
|
||||||
|
if (!QThread::isRunning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (m_state)
|
||||||
|
{
|
||||||
|
case ProcessRunning:
|
||||||
|
if (m_bOwnsProcess) {
|
||||||
|
m_bOwnsProcess = false; // don't terminate in the loop again
|
||||||
|
TerminateProcess(m_pi.hProcess, 0);
|
||||||
|
}
|
||||||
|
// don't break here
|
||||||
|
case ProcessPaused:
|
||||||
|
m_bAbortEventPollingLoop = true;
|
||||||
|
resume();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QThread::wait(5000);
|
||||||
|
if (QThread::isRunning()) {
|
||||||
|
qWarning("WinDbgThread still running... terminating!");
|
||||||
|
QThread::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::attachToProcess(DWORD processId)
|
||||||
|
{
|
||||||
|
m_bOwnsProcess = false;
|
||||||
|
m_processFileName = QString();
|
||||||
|
m_pi.dwProcessId = processId;
|
||||||
|
QThread::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::run()
|
||||||
|
{
|
||||||
|
qDebug() << "WinDbgThread started";
|
||||||
|
// start process or attach process
|
||||||
|
if (m_bOwnsProcess) {
|
||||||
|
// create new process
|
||||||
|
internalStartProcess();
|
||||||
|
} else {
|
||||||
|
// attach to process
|
||||||
|
qWarning("attach to process not yet implemented");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hThisThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, GetCurrentThreadId());
|
||||||
|
if (!m_hThisThread) {
|
||||||
|
qWarning("WinDbgThread: can't open thread handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_EVENT debugEvent;
|
||||||
|
m_bAbortEventPollingLoop = false;
|
||||||
|
while (WaitForDebugEvent(&debugEvent, INFINITE)) {
|
||||||
|
setState(ProcessPaused);
|
||||||
|
emit debugEventOccured(&debugEvent);
|
||||||
|
suspend();
|
||||||
|
if (m_bAbortEventPollingLoop)
|
||||||
|
break;
|
||||||
|
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
|
||||||
|
setState(ProcessRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(Idle);
|
||||||
|
if (m_bOwnsProcess) {
|
||||||
|
TerminateProcess(m_pi.hProcess, 0);
|
||||||
|
}
|
||||||
|
CloseHandle(m_pi.hProcess);
|
||||||
|
CloseHandle(m_pi.hThread);
|
||||||
|
CloseHandle(m_hThisThread);
|
||||||
|
|
||||||
|
qDebug() << "WinDbgThread finished";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::continueProcess()
|
||||||
|
{
|
||||||
|
if (m_state == ProcessPaused)
|
||||||
|
resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::pauseProcess()
|
||||||
|
{
|
||||||
|
if (m_state == ProcessRunning)
|
||||||
|
DebugBreakProcess(m_pi.hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::internalStartProcess()
|
||||||
|
{
|
||||||
|
BOOL bSuccess;
|
||||||
|
|
||||||
|
STARTUPINFO si;
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.wShowWindow = TRUE;
|
||||||
|
|
||||||
|
ZeroMemory(&m_pi, sizeof(m_pi));
|
||||||
|
|
||||||
|
DWORD dwCreationFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
|
||||||
|
bSuccess = CreateProcess(m_processFileName.utf16(), NULL, NULL, NULL, FALSE,
|
||||||
|
dwCreationFlags,
|
||||||
|
NULL, NULL, &si, &m_pi
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bSuccess)
|
||||||
|
setState(ProcessRunning);
|
||||||
|
else
|
||||||
|
setState(Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::setState(State s)
|
||||||
|
{
|
||||||
|
m_state = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::suspend()
|
||||||
|
{
|
||||||
|
SuspendThread(m_hThisThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinDbgThread::resume()
|
||||||
|
{
|
||||||
|
ResumeThread(m_hThisThread);
|
||||||
|
}
|
49
tests/manual/cdbdebugger/windbgthread.h
Normal file
49
tests/manual/cdbdebugger/windbgthread.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
class WinDbgThread : protected QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
WinDbgThread(QObject* parent = 0);
|
||||||
|
~WinDbgThread();
|
||||||
|
|
||||||
|
void startProcess(const QString& filename);
|
||||||
|
void attachToProcess(DWORD processId);
|
||||||
|
void continueProcess();
|
||||||
|
void pauseProcess();
|
||||||
|
void stopProcess();
|
||||||
|
const QString& processFileName() { return m_processFileName; }
|
||||||
|
|
||||||
|
QObject* asQObject() { return this; }
|
||||||
|
//using QThread::isRunning;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void debugEventOccured(void*);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void internalStartProcess();
|
||||||
|
void suspend();
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Idle,
|
||||||
|
ProcessRunning,
|
||||||
|
ProcessPaused
|
||||||
|
};
|
||||||
|
|
||||||
|
void setState(State s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
State m_state;
|
||||||
|
QString m_processFileName;
|
||||||
|
HANDLE m_hThisThread;
|
||||||
|
PROCESS_INFORMATION m_pi;
|
||||||
|
bool m_bOwnsProcess;
|
||||||
|
bool m_bAbortEventPollingLoop;
|
||||||
|
};
|
Reference in New Issue
Block a user