diff --git a/src/bridge/bridgemain.cpp b/src/bridge/bridgemain.cpp index 8bcd254b..b1d37bb5 100644 --- a/src/bridge/bridgemain.cpp +++ b/src/bridge/bridgemain.cpp @@ -1779,6 +1779,12 @@ BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack) _gui_sendmessage(GUI_MENU_SET_ENTRY_HOTKEY, (void*)hEntry, (void*)hack); } + +BRIDGE_IMPEXP void GuiShowThreads() +{ + _gui_sendmessage(GUI_SHOW_THREADS, 0, 0); +} + BRIDGE_IMPEXP void GuiShowCpu() { _gui_sendmessage(GUI_SHOW_CPU, 0, 0); diff --git a/src/bridge/bridgemain.h b/src/bridge/bridgemain.h index 1fe7e6ac..05ade979 100644 --- a/src/bridge/bridgemain.h +++ b/src/bridge/bridgemain.h @@ -1152,6 +1152,7 @@ typedef enum GUI_GRAPH, GUI_MEMMAP, GUI_SYMMOD, + GUI_THREADS, } GUISELECTIONTYPE; #define GUI_MAX_LINE_SIZE 65536 @@ -1281,6 +1282,7 @@ typedef enum GUI_SAVE_LOG, // param1=const char* file name,param2=unused GUI_REDIRECT_LOG, // param1=const char* file name,param2=unused GUI_STOP_REDIRECT_LOG, // param1=unused, param2=unused + GUI_SHOW_THREADS, // param1=unused, param2=unused } GUIMSG; //GUI Typedefs @@ -1423,6 +1425,7 @@ BRIDGE_IMPEXP void GuiMenuSetName(int hMenu, const char* name); BRIDGE_IMPEXP void GuiMenuSetEntryName(int hEntry, const char* name); BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack); BRIDGE_IMPEXP void GuiShowCpu(); +BRIDGE_IMPEXP void GuiShowThreads(); BRIDGE_IMPEXP void GuiAddQWidgetTab(void* qWidget); BRIDGE_IMPEXP void GuiShowQWidgetTab(void* qWidget); BRIDGE_IMPEXP void GuiCloseQWidgetTab(void* qWidget); diff --git a/src/dbg/commands/cmd-gui.cpp b/src/dbg/commands/cmd-gui.cpp index 949d140d..c8a9fe74 100644 --- a/src/dbg/commands/cmd-gui.cpp +++ b/src/dbg/commands/cmd-gui.cpp @@ -8,6 +8,60 @@ #include "value.h" #include "variable.h" +bool cbShowThreadId(int argc, char* argv[]) +{ + duint threadID = 0; + + if(argc > 2) + { + dprintf(QT_TRANSLATE_NOOP("DBG", "Too many arguments specified. Only 1 argument can be passed, or none.\n")); + return false; + } + else if(argc == 2) + { + threadID = strtoll(argv[1], NULL, 10); + if(threadID == 0) + { + dprintf(QT_TRANSLATE_NOOP("DBG", "Could not search for thread ID 0 or argument was not a number in decimal base.\n")); + return false; + } + } + else if(argc == 1) + { + GuiShowThreads(); + return true; + } + + THREADLIST threads_head{}; + DbgGetThreadList(&threads_head); + GuiUpdateThreadView(); // To be sure that we have the freshest stuff + + bool threadFound = false; + uint32_t row; + for(row = 0; row < threads_head.count; row++) + { + THREADALLINFO currThread = threads_head.list[row]; + if(currThread.BasicInfo.ThreadId == threadID) + { + threadFound = true; + break; + } + } + + BridgeFree(threads_head.list); + if(!threadFound) + { + dprintf(QT_TRANSLATE_NOOP("DBG", "Could not find provided thread ID: %llu\n"), threadID); + return false; + } + + SELECTIONDATA foundThreadGUIRow{row, row}; + + GuiShowThreads(); + GuiSelectionSet(GUI_THREADS, &foundThreadGUIRow); + return true; +} + bool cbDebugDisasm(int argc, char* argv[]) { duint addr = 0; diff --git a/src/dbg/commands/cmd-gui.h b/src/dbg/commands/cmd-gui.h index 2db2f2ff..a954388e 100644 --- a/src/dbg/commands/cmd-gui.h +++ b/src/dbg/commands/cmd-gui.h @@ -2,6 +2,7 @@ #include "command.h" +bool cbShowThreadId(int argc, char* argv[]); bool cbDebugDisasm(int argc, char* argv[]); bool cbDebugDump(int argc, char* argv[]); bool cbDebugStackDump(int argc, char* argv[]); diff --git a/src/dbg/x64dbg.cpp b/src/dbg/x64dbg.cpp index 290d60b8..b81ed904 100644 --- a/src/dbg/x64dbg.cpp +++ b/src/dbg/x64dbg.cpp @@ -415,6 +415,7 @@ static void registercommands() dbgcmdnew("scriptcmd", cbScriptCmd, false); // execute a script command TODO: undocumented //gui + dbgcmdnew("showthreadid", cbShowThreadId, false); // show given thread in threads dbgcmdnew("disasm,dis,d", cbDebugDisasm, true); //doDisasm dbgcmdnew("dump", cbDebugDump, true); //dump at address dbgcmdnew("sdump", cbDebugStackDump, true); //dump at stack address diff --git a/src/gui/Src/Bridge/Bridge.cpp b/src/gui/Src/Bridge/Bridge.cpp index b39cf7f1..0d29bdba 100644 --- a/src/gui/Src/Bridge/Bridge.cpp +++ b/src/gui/Src/Bridge/Bridge.cpp @@ -579,6 +579,9 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2) case GUI_MEMMAP: emit selectionMemmapSet(selection); break; + case GUI_THREADS: + emit selectionThreadsSet(selection); + break; default: return (void*)false; } @@ -644,6 +647,10 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2) emit loadSourceFile(QString((const char*)param1), (duint)param2); break; + case GUI_SHOW_THREADS: + emit showThreads(); + break; + case GUI_SHOW_CPU: emit showCpu(); break; diff --git a/src/gui/Src/Bridge/Bridge.h b/src/gui/Src/Bridge/Bridge.h index a3a5fddb..67691ac1 100644 --- a/src/gui/Src/Bridge/Bridge.h +++ b/src/gui/Src/Bridge/Bridge.h @@ -125,6 +125,7 @@ signals: void selectionMemmapGet(SELECTIONDATA* selection); void selectionMemmapSet(const SELECTIONDATA* selection); void selectionSymmodGet(SELECTIONDATA* selection); + void selectionThreadsSet(const SELECTIONDATA* selection); void getStrWindow(const QString title, QString* text); void autoCompleteAddCmd(const QString cmd); void autoCompleteDelCmd(const QString cmd); @@ -139,6 +140,7 @@ signals: void symbolRefreshCurrent(); void loadSourceFile(const QString path, duint addr); void showCpu(); + void showThreads(); void addQWidgetTab(QWidget* qWidget); void showQWidgetTab(QWidget* qWidget); void closeQWidgetTab(QWidget* qWidget); diff --git a/src/gui/Src/Gui/CallStackView.cpp b/src/gui/Src/Gui/CallStackView.cpp index d4e061b0..a381e8ec 100644 --- a/src/gui/Src/Gui/CallStackView.cpp +++ b/src/gui/Src/Gui/CallStackView.cpp @@ -7,7 +7,7 @@ CallStackView::CallStackView(StdTable* parent) : StdIconTable(parent) { int charwidth = getCharWidth(); - addColumnAt(8 * charwidth, tr("Thread ID"), false); + addColumnAt(32 * charwidth, tr("Thread ID"), false); addColumnAt(8 + charwidth * sizeof(dsint) * 2, tr("Address"), false); //address in the stack addColumnAt(8 + charwidth * sizeof(dsint) * 2, tr("To"), false); //return to addColumnAt(8 + charwidth * sizeof(dsint) * 2, tr("From"), false); //return from @@ -56,6 +56,7 @@ void CallStackView::setupContextMenu() // TODO: Is Label/Comment/Bookmark useful? mCommonActions->build(mMenuBuilder, CommonActions::ActionBreakpoint); mMenuBuilder->addSeparator(); + QAction* showSuspectedCallStack = makeAction(tr("Show Suspected Call Stack Frame"), SLOT(showSuspectedCallStack())); mMenuBuilder->addAction(showSuspectedCallStack, [showSuspectedCallStack](QMenu*) { @@ -68,6 +69,20 @@ void CallStackView::setupContextMenu() showSuspectedCallStack->setText(tr("Show Suspected Call Stack Frame")); return true; }); + + mMenuBuilder->addSeparator(); + QAction* followInThreads = makeAction(tr("Follow in Threads"), SLOT(followInThreads())); + mMenuBuilder->addAction(followInThreads, [this](QMenu*) + { + return isThreadHeaderSelected(); + }); + + QAction* renameThread = makeAction(tr("Rename Thread"), SLOT(renameThread())); + mMenuBuilder->addAction(renameThread, [this](QMenu*) + { + return isThreadHeaderSelected(); + }); + MenuBuilder* mCopyMenu = new MenuBuilder(this); setupCopyMenu(mCopyMenu); // Column count cannot be zero @@ -146,7 +161,14 @@ void CallStackView::updateCallStack() memset(&callstack, 0, sizeof(DBGCALLSTACK)); DbgFunctions()->GetCallStackByThread(threadList.list[currentIndexToDraw].BasicInfo.Handle, &callstack); setRowCount(currentRow + callstack.total + 1); - setCellContent(currentRow, ColThread, ToDecString(threadList.list[currentIndexToDraw].BasicInfo.ThreadId)); + + QString threadName = threadList.list[currentIndexToDraw].BasicInfo.threadName; + QString colThreadString = ToDecString(threadList.list[currentIndexToDraw].BasicInfo.ThreadId); + + if(threadName.size() > 0) + colThreadString += " - " + threadName; // The " - " is crucial here, because later split is happening + + setCellContent(currentRow, ColThread, colThreadString); currentRow++; @@ -223,6 +245,27 @@ void CallStackView::followFrom() DbgCmdExecDirect(QString("disasm " + addrText)); } +void CallStackView::renameThread() +{ + const QStringList threadIDName = getCellContent(getInitialSelection(), 0).split("-", QString::SplitBehavior::SkipEmptyParts); + const duint threadID = threadIDName[0].toInt(); + + const QString windowTitle = QStringLiteral("%1 - %2").arg(tr("Changing Threads Name")).arg(threadID); + QString newThreadsName(""); + + SimpleInputBox(this, windowTitle, "", newThreadsName, "Place new threads name here..."); + QString escapedName = newThreadsName.replace("\"", "\\\""); + DbgCmdExec(QString("setthreadname %1, \"%2\"").arg(ToHexString(threadID)).arg(escapedName)); +} +void CallStackView::followInThreads() +{ + QStringList threadIDName = getCellContent(getInitialSelection(), ColThread).split(" - "); + if(threadIDName[0].size() == 0) + return; + + DbgCmdExecDirect(QString("showthreadid " + threadIDName[0])); +} + void CallStackView::showSuspectedCallStack() { duint i; @@ -235,6 +278,11 @@ void CallStackView::showSuspectedCallStack() emit Bridge::getBridge()->updateDump(); } +bool CallStackView::isThreadHeaderSelected() +{ + return !getCellContent(getInitialSelection(), ColThread).isEmpty(); +} + bool CallStackView::isSelectionValid() { return getCellContent(getInitialSelection(), ColThread).isEmpty(); diff --git a/src/gui/Src/Gui/CallStackView.h b/src/gui/Src/Gui/CallStackView.h index 0463d945..f149926f 100644 --- a/src/gui/Src/Gui/CallStackView.h +++ b/src/gui/Src/Gui/CallStackView.h @@ -21,6 +21,8 @@ protected slots: void followTo(); void followFrom(); void showSuspectedCallStack(); + void followInThreads(); + void renameThread(); private: enum @@ -37,4 +39,5 @@ private: MenuBuilder* mMenuBuilder; CommonActions* mCommonActions; bool isSelectionValid(); + bool CallStackView::isThreadHeaderSelected(); }; diff --git a/src/gui/Src/Gui/MainWindow.cpp b/src/gui/Src/Gui/MainWindow.cpp index 48420c01..449d5287 100644 --- a/src/gui/Src/Gui/MainWindow.cpp +++ b/src/gui/Src/Gui/MainWindow.cpp @@ -91,6 +91,7 @@ MainWindow::MainWindow(QWidget* parent) connect(Bridge::getBridge(), SIGNAL(getStrWindow(QString, QString*)), this, SLOT(getStrWindow(QString, QString*))); connect(Bridge::getBridge(), SIGNAL(showCpu()), this, SLOT(displayCpuWidget())); connect(Bridge::getBridge(), SIGNAL(showReferences()), this, SLOT(displayReferencesWidget())); + connect(Bridge::getBridge(), SIGNAL(showThreads()), this, SLOT(displayThreadsWidget())); connect(Bridge::getBridge(), SIGNAL(addQWidgetTab(QWidget*)), this, SLOT(addQWidgetTab(QWidget*))); connect(Bridge::getBridge(), SIGNAL(showQWidgetTab(QWidget*)), this, SLOT(showQWidgetTab(QWidget*))); connect(Bridge::getBridge(), SIGNAL(closeQWidgetTab(QWidget*)), this, SLOT(closeQWidgetTab(QWidget*))); @@ -368,6 +369,7 @@ MainWindow::MainWindow(QWidget* parent) connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySourceManagerWidget()), this, SLOT(displaySourceViewWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySymbolsWidget()), this, SLOT(displaySymbolWidget())); + connect(mThreadView, SIGNAL(displayThreadsView()), this, SLOT(displayThreadsWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(showPatches()), this, SLOT(patchWindow())); connect(mCpuWidget->getGraphWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget())); @@ -1363,6 +1365,11 @@ void MainWindow::displayCpuWidget() showQWidgetTab(mCpuWidget); } +void MainWindow::displayThreadsWidget() +{ + showQWidgetTab(mThreadView); +} + void MainWindow::displaySymbolWidget() { showQWidgetTab(mSymbolView); @@ -1378,11 +1385,6 @@ void MainWindow::displayReferencesWidget() showQWidgetTab(mReferenceManager); } -void MainWindow::displayThreadsWidget() -{ - showQWidgetTab(mThreadView); -} - void MainWindow::displayGraphWidget() { showQWidgetTab(mCpuWidget); diff --git a/src/gui/Src/Gui/MainWindow.h b/src/gui/Src/Gui/MainWindow.h index 716f3259..f5736150 100644 --- a/src/gui/Src/Gui/MainWindow.h +++ b/src/gui/Src/Gui/MainWindow.h @@ -80,12 +80,12 @@ public slots: void displayBreakpointWidget(); void updateWindowTitleSlot(QString filename); void runSlot(); + void displayThreadsWidget(); void displayCpuWidget(); void displayCpuWidgetShowCpu(); void displaySymbolWidget(); void displaySourceViewWidget(); void displayReferencesWidget(); - void displayThreadsWidget(); void displayVariables(); void displayGraphWidget(); void displayTraceWidget(); diff --git a/src/gui/Src/Gui/ThreadView.cpp b/src/gui/Src/Gui/ThreadView.cpp index 64319dc1..86f23be7 100644 --- a/src/gui/Src/Gui/ThreadView.cpp +++ b/src/gui/Src/Gui/ThreadView.cpp @@ -174,7 +174,7 @@ ThreadView::ThreadView(StdTable* parent) : StdTable(parent) loadColumnFromConfig("Thread"); //setCopyMenuOnly(true); - + connect(Bridge::getBridge(), SIGNAL(selectionThreadsSet(const SELECTIONDATA*)), this, SLOT(selectionThreadsSet(const SELECTIONDATA*))); connect(Bridge::getBridge(), SIGNAL(updateThreads()), this, SLOT(updateThreadList())); connect(this, SIGNAL(doubleClickedSignal()), this, SLOT(doubleClickedSlot())); connect(this, SIGNAL(contextMenuSignal(QPoint)), this, SLOT(contextMenuSlot(QPoint))); @@ -184,6 +184,23 @@ ThreadView::ThreadView(StdTable* parent) : StdTable(parent) new DisassemblyPopup(this, Bridge::getArchitecture()); } +void ThreadView::selectionThreadsSet(const SELECTIONDATA* selection) +{ + if(selection->start >= getRowCount() || + selection->end >= getRowCount() || + selection->start > selection->end) + { + Bridge::getBridge()->setResult(BridgeResult::SelectionSet, 0); + return; + } + + mSelection.firstSelectedIndex = selection->start; + mSelection.fromIndex = selection->start; + mSelection.toIndex = selection->end; + Bridge::getBridge()->setResult(BridgeResult::SelectionSet, 1); + emit selectionChanged(mSelection.firstSelectedIndex); +} + void ThreadView::updateThreadList() { THREADLIST threadList; @@ -394,12 +411,13 @@ void ThreadView::doubleClickedSlot() void ThreadView::SetNameSlot() { - duint threadId = getCellUserdata(getInitialSelection(), 1); - LineEditDialog mLineEdit(this); - mLineEdit.setWindowTitle(tr("Name") + threadId); - mLineEdit.setText(getCellContent(getInitialSelection(), 13)); - if(mLineEdit.exec() != QDialog::Accepted) - return; - QString escapedName = mLineEdit.editText.replace("\"", "\\\""); - DbgCmdExec(QString("setthreadname %1, \"%2\"").arg(ToHexString(threadId)).arg(escapedName)); + const duint threadID = getCellUserdata(getInitialSelection(), 1); + const QString currentThreadsName = getCellContent(getInitialSelection(), 13); + + const QString windowTitle = QStringLiteral("%1 - %2").arg(tr("Changing Threads Name")).arg(threadID); + QString newThreadsName(""); + + SimpleInputBox(this, windowTitle, "", newThreadsName, "Place new threads name here..."); + QString escapedName = newThreadsName.replace("\"", "\\\""); + DbgCmdExec(QString("setthreadname %1, \"%2\"").arg(ToHexString(threadID)).arg(escapedName)); } diff --git a/src/gui/Src/Gui/ThreadView.h b/src/gui/Src/Gui/ThreadView.h index f016ee0d..54a3f409 100644 --- a/src/gui/Src/Gui/ThreadView.h +++ b/src/gui/Src/Gui/ThreadView.h @@ -10,8 +10,11 @@ public: explicit ThreadView(StdTable* parent = nullptr); QString paintContent(QPainter* painter, duint row, duint col, int x, int y, int w, int h) override; void setupContextMenu(); +signals: + void displayThreadsView(); public slots: + void selectionThreadsSet(const SELECTIONDATA* selection); void updateThreadList(); void doubleClickedSlot(); void ExecCommand();