summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/manual/cdbdebugger/cdbdebugger.pro20
-rw-r--r--tests/manual/cdbdebugger/debugger.cpp305
-rw-r--r--tests/manual/cdbdebugger/debugger.h71
-rw-r--r--tests/manual/cdbdebugger/main.cpp11
-rw-r--r--tests/manual/cdbdebugger/mainwindow.cpp90
-rw-r--r--tests/manual/cdbdebugger/mainwindow.h26
-rw-r--r--tests/manual/cdbdebugger/mainwindow.ui174
-rw-r--r--tests/manual/cdbdebugger/outputcallback.cpp61
-rw-r--r--tests/manual/cdbdebugger/outputcallback.h30
-rw-r--r--tests/manual/cdbdebugger/windbgeventcallback.cpp186
-rw-r--r--tests/manual/cdbdebugger/windbgeventcallback.h126
-rw-r--r--tests/manual/cdbdebugger/windbgthread.cpp150
-rw-r--r--tests/manual/cdbdebugger/windbgthread.h49
13 files changed, 1299 insertions, 0 deletions
diff --git a/tests/manual/cdbdebugger/cdbdebugger.pro b/tests/manual/cdbdebugger/cdbdebugger.pro
new file mode 100644
index 0000000000..cc9b4c6318
--- /dev/null
+++ b/tests/manual/cdbdebugger/cdbdebugger.pro
@@ -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
diff --git a/tests/manual/cdbdebugger/debugger.cpp b/tests/manual/cdbdebugger/debugger.cpp
new file mode 100644
index 0000000000..14df5688b0
--- /dev/null
+++ b/tests/manual/cdbdebugger/debugger.cpp
@@ -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);
+}
diff --git a/tests/manual/cdbdebugger/debugger.h b/tests/manual/cdbdebugger/debugger.h
new file mode 100644
index 0000000000..f170caff67
--- /dev/null
+++ b/tests/manual/cdbdebugger/debugger.h
@@ -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;
+};
diff --git a/tests/manual/cdbdebugger/main.cpp b/tests/manual/cdbdebugger/main.cpp
new file mode 100644
index 0000000000..fb18f90e12
--- /dev/null
+++ b/tests/manual/cdbdebugger/main.cpp
@@ -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();
+}
diff --git a/tests/manual/cdbdebugger/mainwindow.cpp b/tests/manual/cdbdebugger/mainwindow.cpp
new file mode 100644
index 0000000000..3cce211892
--- /dev/null
+++ b/tests/manual/cdbdebugger/mainwindow.cpp
@@ -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);
+ }
+}
diff --git a/tests/manual/cdbdebugger/mainwindow.h b/tests/manual/cdbdebugger/mainwindow.h
new file mode 100644
index 0000000000..9831b508bb
--- /dev/null
+++ b/tests/manual/cdbdebugger/mainwindow.h
@@ -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;
+}; \ No newline at end of file
diff --git a/tests/manual/cdbdebugger/mainwindow.ui b/tests/manual/cdbdebugger/mainwindow.ui
new file mode 100644
index 0000000000..56b0706f2d
--- /dev/null
+++ b/tests/manual/cdbdebugger/mainwindow.ui
@@ -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>&amp;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>&amp;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>&amp;Open...</string>
+ </property>
+ <property name="shortcut" >
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="actionClose" >
+ <property name="text" >
+ <string>&amp;Close</string>
+ </property>
+ </action>
+ <action name="actionExit" >
+ <property name="text" >
+ <string>E&amp;xit</string>
+ </property>
+ </action>
+ <action name="actionRun" >
+ <property name="text" >
+ <string>&amp;Run</string>
+ </property>
+ <property name="shortcut" >
+ <string>F5</string>
+ </property>
+ </action>
+ <action name="actionBreak" >
+ <property name="text" >
+ <string>&amp;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>
diff --git a/tests/manual/cdbdebugger/outputcallback.cpp b/tests/manual/cdbdebugger/outputcallback.cpp
new file mode 100644
index 0000000000..4a0ca81bec
--- /dev/null
+++ b/tests/manual/cdbdebugger/outputcallback.cpp
@@ -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;
+}
diff --git a/tests/manual/cdbdebugger/outputcallback.h b/tests/manual/cdbdebugger/outputcallback.h
new file mode 100644
index 0000000000..73004c7be6
--- /dev/null
+++ b/tests/manual/cdbdebugger/outputcallback.h
@@ -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__
diff --git a/tests/manual/cdbdebugger/windbgeventcallback.cpp b/tests/manual/cdbdebugger/windbgeventcallback.cpp
new file mode 100644
index 0000000000..39826f99a5
--- /dev/null
+++ b/tests/manual/cdbdebugger/windbgeventcallback.cpp
@@ -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;
+}
diff --git a/tests/manual/cdbdebugger/windbgeventcallback.h b/tests/manual/cdbdebugger/windbgeventcallback.h
new file mode 100644
index 0000000000..b405f77014
--- /dev/null
+++ b/tests/manual/cdbdebugger/windbgeventcallback.h
@@ -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;
+};
+
diff --git a/tests/manual/cdbdebugger/windbgthread.cpp b/tests/manual/cdbdebugger/windbgthread.cpp
new file mode 100644
index 0000000000..a21014216e
--- /dev/null
+++ b/tests/manual/cdbdebugger/windbgthread.cpp
@@ -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);
+}
diff --git a/tests/manual/cdbdebugger/windbgthread.h b/tests/manual/cdbdebugger/windbgthread.h
new file mode 100644
index 0000000000..2e34fae892
--- /dev/null
+++ b/tests/manual/cdbdebugger/windbgthread.h
@@ -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;
+};