From d0865ed2a2ba126c6eb1d30ed17cd0abce7ed097 Mon Sep 17 00:00:00 2001 From: Banana Joe <qtc-committer@nokia.com> Date: Mon, 9 Feb 2009 10:55:46 +0100 Subject: [PATCH] Initial revision of the cdbdebugger playground. --- tests/manual/cdbdebugger/cdbdebugger.pro | 20 ++ tests/manual/cdbdebugger/debugger.cpp | 305 ++++++++++++++++++ tests/manual/cdbdebugger/debugger.h | 71 ++++ tests/manual/cdbdebugger/main.cpp | 11 + tests/manual/cdbdebugger/mainwindow.cpp | 90 ++++++ tests/manual/cdbdebugger/mainwindow.h | 26 ++ tests/manual/cdbdebugger/mainwindow.ui | 174 ++++++++++ tests/manual/cdbdebugger/outputcallback.cpp | 61 ++++ tests/manual/cdbdebugger/outputcallback.h | 30 ++ .../cdbdebugger/windbgeventcallback.cpp | 186 +++++++++++ .../manual/cdbdebugger/windbgeventcallback.h | 126 ++++++++ tests/manual/cdbdebugger/windbgthread.cpp | 150 +++++++++ tests/manual/cdbdebugger/windbgthread.h | 49 +++ 13 files changed, 1299 insertions(+) create mode 100644 tests/manual/cdbdebugger/cdbdebugger.pro create mode 100644 tests/manual/cdbdebugger/debugger.cpp create mode 100644 tests/manual/cdbdebugger/debugger.h create mode 100644 tests/manual/cdbdebugger/main.cpp create mode 100644 tests/manual/cdbdebugger/mainwindow.cpp create mode 100644 tests/manual/cdbdebugger/mainwindow.h create mode 100644 tests/manual/cdbdebugger/mainwindow.ui create mode 100644 tests/manual/cdbdebugger/outputcallback.cpp create mode 100644 tests/manual/cdbdebugger/outputcallback.h create mode 100644 tests/manual/cdbdebugger/windbgeventcallback.cpp create mode 100644 tests/manual/cdbdebugger/windbgeventcallback.h create mode 100644 tests/manual/cdbdebugger/windbgthread.cpp create mode 100644 tests/manual/cdbdebugger/windbgthread.h diff --git a/tests/manual/cdbdebugger/cdbdebugger.pro b/tests/manual/cdbdebugger/cdbdebugger.pro new file mode 100644 index 00000000000..cc9b4c63182 --- /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 00000000000..14df5688b0b --- /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 00000000000..f170caff67e --- /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 00000000000..fb18f90e12e --- /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 00000000000..3cce2118922 --- /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 00000000000..9831b508bbd --- /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 00000000000..56b0706f2d2 --- /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>&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> diff --git a/tests/manual/cdbdebugger/outputcallback.cpp b/tests/manual/cdbdebugger/outputcallback.cpp new file mode 100644 index 00000000000..4a0ca81bec1 --- /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 00000000000..73004c7be67 --- /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 00000000000..39826f99a59 --- /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 00000000000..b405f77014a --- /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 00000000000..a21014216eb --- /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 00000000000..2e34fae8923 --- /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; +}; -- GitLab