1
0
Fork 0

GUI: thread list view (completed 100%)

BRIDGE: added thread list API
DBG: almost completed thread list (needs LastError + WaitReason)
This commit is contained in:
Mr. eXoDia 2014-03-16 01:48:14 +01:00
parent 87491800c4
commit e71d10203a
21 changed files with 488 additions and 6 deletions

View File

@ -565,6 +565,11 @@ BRIDGE_IMPEXP bool DbgStackCommentGet(duint addr, STACK_COMMENT* comment)
return (bool)(duint)_dbg_sendmessage(DBG_STACK_COMMENT_GET, (void*)addr, comment);
}
BRIDGE_IMPEXP void DbgGetThreadList(THREADLIST* list)
{
_dbg_sendmessage(DBG_GET_THREAD_LIST, list, 0);
}
//GUI
BRIDGE_IMPEXP void GuiDisasmAt(duint addr, duint cip)
{
@ -748,6 +753,11 @@ BRIDGE_IMPEXP void GuiUpdateDumpView()
_gui_sendmessage(GUI_UPDATE_DUMP_VIEW, 0, 0);
}
BRIDGE_IMPEXP void GuiUpdateThreadView()
{
_gui_sendmessage(GUI_UPDATE_THREAD_VIEW, 0, 0);
}
//Main
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{

View File

@ -114,8 +114,9 @@ enum DBGMSG
DBG_SYMBOL_ENUM, // param1=SYMBOLCBINFO* cbInfo, param2=unused
DBG_ASSEMBLE_AT, // param1=duint addr, param2=const char* instruction
DBG_MODBASE_FROM_NAME, // param1=const char* modname, param2=unused
DBG_DISASM_AT, // param1=duint addr, param2=DISASM_INSTR* instr
DBG_STACK_COMMENT_GET // param1=duint addr, param2=STACK_COMMENT* comment
DBG_DISASM_AT, // param1=duint addr, param2=DISASM_INSTR* instr
DBG_STACK_COMMENT_GET, // param1=duint addr, param2=STACK_COMMENT* comment
DBG_GET_THREAD_LIST // param1=THREADALLINFO* list, param2=unused
};
enum SCRIPTLINETYPE
@ -147,6 +148,59 @@ enum STRING_TYPE
str_unicode
};
enum THREADPRIORITY
{
PriorityIdle = -15,
PriorityAboveNormal = 1,
PriorityBelowNormal = -1,
PriorityHighest = 2,
PriorityLowest = -2,
PriorityNormal = 0,
PriorityTimeCritical = 15,
PriorityUnknown = 0x7FFFFFFF
};
enum THREADWAITREASON
{
Executive = 0,
FreePage = 1,
PageIn = 2,
PoolAllocation = 3,
DelayExecution = 4,
Suspended = 5,
UserRequest = 6,
WrExecutive = 7,
WrFreePage = 8,
WrPageIn = 9,
WrPoolAllocation = 10,
WrDelayExecution = 11,
WrSuspended = 12,
WrUserRequest = 13,
WrEventPair = 14,
WrQueue = 15,
WrLpcReceive = 16,
WrLpcReply = 17,
WrVirtualMemory = 18,
WrPageOut = 19,
WrRendezvous = 20,
Spare2 = 21,
Spare3 = 22,
Spare4 = 23,
Spare5 = 24,
WrCalloutStack = 25,
WrKernel = 26,
WrResource = 27,
WrPushLock = 28,
WrMutex = 29,
WrQuantumEnd = 30,
WrDispatchInt = 31,
WrPreempted = 32,
WrYieldExecution = 33,
WrFastMutex = 34,
WrGuardedMutex = 35,
WrRundown = 36,
};
//Debugger typedefs
struct SYMBOLINFO;
@ -295,6 +349,32 @@ struct STACK_COMMENT
char comment[MAX_COMMENT_SIZE];
};
struct THREADINFO
{
int ThreadNumber;
HANDLE hThread;
DWORD dwThreadId;
duint ThreadStartAddress;
duint ThreadLocalBase;
};
struct THREADALLINFO
{
THREADINFO BasicInfo;
duint ThreadCip;
DWORD SuspendCount;
THREADPRIORITY Priority;
THREADWAITREASON WaitReason;
DWORD LastError;
};
struct THREADLIST
{
int count;
THREADALLINFO* list;
int CurrentThread;
};
//Debugger functions
BRIDGE_IMPEXP const char* DbgInit();
BRIDGE_IMPEXP bool DbgMemRead(duint va, unsigned char* dest, duint size);
@ -340,6 +420,7 @@ BRIDGE_IMPEXP bool DbgAssembleAt(duint addr, const char* instruction);
BRIDGE_IMPEXP duint DbgModBaseFromName(const char* name);
BRIDGE_IMPEXP void DbgDisasmAt(duint addr, DISASM_INSTR* instr);
BRIDGE_IMPEXP bool DbgStackCommentGet(duint addr, STACK_COMMENT* comment);
BRIDGE_IMPEXP void DbgGetThreadList(THREADLIST* list);
//Gui enums
enum GUIMSG
@ -378,7 +459,8 @@ enum GUIMSG
GUI_REF_SETSINGLESELECTION, // param1=int index, param2=bool scroll
GUI_REF_SETPROGRESS, // param1=int progress, param2=unused
GUI_STACK_DUMP_AT, // param1=duint addr, param2=duint csp
GUI_UPDATE_DUMP_VIEW // param1=unused, param2=unused
GUI_UPDATE_DUMP_VIEW, // param1=unused, param2=unused
GUI_UPDATE_THREAD_VIEW // param1=unused, param2=unused
};
//GUI structures
@ -425,6 +507,7 @@ BRIDGE_IMPEXP void GuiReferenceSetSingleSelection(int index, bool scroll);
BRIDGE_IMPEXP void GuiReferenceSetProgress(int progress);
BRIDGE_IMPEXP void GuiStackDumpAt(duint addr, duint csp);
BRIDGE_IMPEXP void GuiUpdateDumpView();
BRIDGE_IMPEXP void GuiUpdateThreadView();
#ifdef __cplusplus
}

View File

@ -11,6 +11,7 @@
#include "symbolinfo.h"
#include "assemble.h"
#include "stackinfo.h"
#include "thread.h"
extern "C" DLL_EXPORT duint _dbg_memfindbaseaddr(duint addr, duint* size)
{
@ -619,6 +620,12 @@ extern "C" DLL_EXPORT uint _dbg_sendmessage(DBGMSG type, void* param1, void* par
return stackcommentget((uint)param1, (STACK_COMMENT*)param2);
}
break;
case DBG_GET_THREAD_LIST:
{
threadgetlist((THREADLIST*)param1);
}
break;
}
return 0;
}

View File

@ -57,6 +57,7 @@ struct PLUG_CB_CREATETHREAD
struct PLUG_CB_EXITTHREAD
{
EXIT_THREAD_DEBUG_INFO* ExitThread;
DWORD dwThreadId;
};
struct PLUG_CB_SYSTEMBREAKPOINT

View File

@ -12,6 +12,7 @@
#include "x64_dbg.h"
#include "disasm_helper.h"
#include "symbolinfo.h"
#include "thread.h"
#include "BeaEngine\BeaEngine.h"
@ -492,6 +493,13 @@ static void cbCreateProcess(CREATE_PROCESS_DEBUG_INFO* CreateProcessInfo)
callbackInfo.DebugFileName=DebugFileName;
callbackInfo.fdProcessInfo=fdProcessInfo;
plugincbcall(CB_CREATEPROCESS, &callbackInfo);
//update thread list
CREATE_THREAD_DEBUG_INFO threadInfo;
threadInfo.hThread=CreateProcessInfo->hThread;
threadInfo.lpStartAddress=CreateProcessInfo->lpStartAddress;
threadInfo.lpThreadLocalBase=CreateProcessInfo->lpThreadLocalBase;
threadcreate(&threadInfo);
}
static void cbExitProcess(EXIT_PROCESS_DEBUG_INFO* ExitProcess)
@ -505,6 +513,7 @@ static void cbExitProcess(EXIT_PROCESS_DEBUG_INFO* ExitProcess)
static void cbCreateThread(CREATE_THREAD_DEBUG_INFO* CreateThread)
{
threadcreate(CreateThread); //update thread list
PLUG_CB_CREATETHREAD callbackInfo;
callbackInfo.CreateThread=CreateThread;
plugincbcall(CB_CREATETHREAD, &callbackInfo);
@ -512,9 +521,12 @@ static void cbCreateThread(CREATE_THREAD_DEBUG_INFO* CreateThread)
static void cbExitThread(EXIT_THREAD_DEBUG_INFO* ExitThread)
{
DWORD dwThreadId=((DEBUG_EVENT*)GetDebugData())->dwThreadId;
PLUG_CB_EXITTHREAD callbackInfo;
callbackInfo.ExitThread=ExitThread;
callbackInfo.dwThreadId=dwThreadId;
plugincbcall(CB_EXITTHREAD, &callbackInfo);
threadexit(dwThreadId);
}
static void cbSystemBreakpoint(void* ExceptionData)
@ -529,6 +541,7 @@ static void cbSystemBreakpoint(void* ExceptionData)
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
GuiUpdateThreadView();
//lock
lock(WAITID_RUN);
bSkipExceptions=false;

65
x64_dbg_dbg/thread.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "thread.h"
#include "console.h"
static std::vector<THREADINFO> threadList;
static int threadNum;
static int currentThread;
void threadcreate(CREATE_THREAD_DEBUG_INFO* CreateThread)
{
THREADINFO curInfo;
curInfo.ThreadNumber=threadNum;
curInfo.hThread=CreateThread->hThread;
curInfo.dwThreadId=((DEBUG_EVENT*)GetDebugData())->dwThreadId;
curInfo.ThreadStartAddress=(uint)CreateThread->lpStartAddress;
curInfo.ThreadLocalBase=(uint)CreateThread->lpThreadLocalBase;
threadList.push_back(curInfo);
threadNum++;
GuiUpdateThreadView();
}
void threadexit(DWORD dwThreadId)
{
for(int i=0; i<threadList.size(); i++)
if(threadList.at(i).dwThreadId==dwThreadId)
{
threadList.erase(threadList.begin()+i);
break;
}
GuiUpdateThreadView();
}
void threadclear()
{
threadNum=0;
std::vector<THREADINFO>().swap(threadList);
GuiUpdateThreadView();
}
static THREADWAITREASON GetThreadWaitReason(DWORD dwThreadId)
{
return Executive;
}
void threadgetlist(THREADLIST* list)
{
int count=threadList.size();
list->count=count;
if(!count)
return;
list->list=(THREADALLINFO*)BridgeAlloc(count*sizeof(THREADALLINFO));
for(int i=0; i<count; i++)
{
if(((DEBUG_EVENT*)GetDebugData())->dwThreadId==threadList.at(i).dwThreadId)
currentThread=i;
memset(&list->list[i], 0, sizeof(THREADALLINFO));
memcpy(&list->list[i].BasicInfo, &threadList.at(i), sizeof(THREADINFO));
HANDLE hThread=list->list[i].BasicInfo.hThread;
list->list[i].ThreadCip=GetContextDataEx(hThread, UE_CIP);
list->list[i].SuspendCount=SuspendThread(hThread);
ResumeThread(hThread);
list->list[i].Priority=(THREADPRIORITY)GetThreadPriority(list->list[i].BasicInfo.hThread);
list->list[i].WaitReason=GetThreadWaitReason(list->list[i].BasicInfo.dwThreadId);
}
list->CurrentThread=currentThread;
}

13
x64_dbg_dbg/thread.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _THREAD_H
#define _THREAD_H
#include "_global.h"
#include "debugger.h"
//functions
void threadcreate(CREATE_THREAD_DEBUG_INFO* CreateThread);
void threadexit(DWORD dwThreadId);
void threadclear();
void threadgetlist(THREADLIST* list);
#endif //_THREAD_H

View File

@ -117,6 +117,8 @@
<Unit filename="stackinfo.h" />
<Unit filename="symbolinfo.cpp" />
<Unit filename="symbolinfo.h" />
<Unit filename="thread.cpp" />
<Unit filename="thread.h" />
<Unit filename="threading.cpp" />
<Unit filename="threading.h" />
<Unit filename="value.cpp" />

View File

@ -29,6 +29,7 @@
<ClCompile Include="sqlhelper.cpp" />
<ClCompile Include="stackinfo.cpp" />
<ClCompile Include="symbolinfo.cpp" />
<ClCompile Include="thread.cpp" />
<ClCompile Include="threading.cpp" />
<ClCompile Include="value.cpp" />
<ClCompile Include="variable.cpp" />
@ -57,6 +58,7 @@
<ClInclude Include="sqlhelper.h" />
<ClInclude Include="stackinfo.h" />
<ClInclude Include="symbolinfo.h" />
<ClInclude Include="thread.h" />
<ClInclude Include="threading.h" />
<ClInclude Include="value.h" />
<ClInclude Include="variable.h" />

View File

@ -90,6 +90,9 @@
<ClCompile Include="stackinfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="thread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="_exports.h">
@ -173,5 +176,8 @@
<ClInclude Include="stackinfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="thread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -56,7 +56,8 @@ SOURCES += \
Src/BasicView/SymbolView.cpp \
Src/BasicView/RegistersView.cpp \
Src/BasicView/SearchListView.cpp \
Src/BasicView/ReferenceView.cpp
Src/BasicView/ReferenceView.cpp \
Src/BasicView/ThreadView.cpp
HEADERS += \
@ -93,7 +94,8 @@ HEADERS += \
Src/Gui/CPUStack.h \
Src/BasicView/SymbolView.h \
Src/BasicView/SearchListView.h \
Src/BasicView/ReferenceView.h
Src/BasicView/ReferenceView.h \
Src/BasicView/ThreadView.h
INCLUDEPATH += \
Src \

View File

@ -45,7 +45,7 @@ QString ScriptView::paintContent(QPainter* painter, int_t rowBase, int rowOffset
if(DbgScriptBpGet(line)) //breakpoint
painter->setPen(QPen(QColor("#FF0000"))); //red address
else
painter->setPen(QPen(QColor("#FFFFFF"))); //black address
painter->setPen(QPen(QColor("#FFFFFF"))); //white address
}
else if(DbgScriptBpGet(line)) //breakpoint
{

View File

@ -0,0 +1,210 @@
#include "ThreadView.h"
ThreadView::ThreadView(StdTable *parent) : StdTable(parent)
{
int charwidth=QFontMetrics(this->font()).width(QChar(' '));
addColumnAt(8+charwidth*sizeof(unsigned int)*2, "Number", false);
addColumnAt(8+charwidth*sizeof(unsigned int)*2, "ID", false);
addColumnAt(8+charwidth*sizeof(uint_t)*2, "Entry", false);
addColumnAt(8+charwidth*sizeof(uint_t)*2, "TEB", false);
#ifdef _WIN64
addColumnAt(8+charwidth*sizeof(uint_t)*2, "RIP", false);
#else
addColumnAt(8+charwidth*sizeof(uint_t)*2, "EIP", false);
#endif //_WIN64
addColumnAt(8+charwidth*14, "Suspend Count", false);
addColumnAt(8+charwidth*20, "Priority", false);
addColumnAt(8+charwidth*16, "WaitReason", false);
addColumnAt(8+charwidth*10, "LastError", false);
addColumnAt(0, "", false);
connect(Bridge::getBridge(), SIGNAL(updateThreads()), this, SLOT(updateThreadList()));
}
void ThreadView::updateThreadList()
{
THREADLIST threadList;
memset(&threadList, 0, sizeof(THREADLIST));
DbgGetThreadList(&threadList);
setRowCount(threadList.count);
for(int i=0; i<threadList.count; i++)
{
if(!threadList.list[i].BasicInfo.ThreadNumber)
setCellContent(i, 0, "Main");
else
setCellContent(i, 0, QString("%1").arg(threadList.list[i].BasicInfo.ThreadNumber, 0, 10));
setCellContent(i, 1, QString("%1").arg(threadList.list[i].BasicInfo.dwThreadId, 0, 16).toUpper());
setCellContent(i, 2, QString("%1").arg(threadList.list[i].BasicInfo.ThreadStartAddress, sizeof(int_t) * 2, 16, QChar('0')).toUpper());
setCellContent(i, 3, QString("%1").arg(threadList.list[i].BasicInfo.ThreadLocalBase, sizeof(int_t) * 2, 16, QChar('0')).toUpper());
setCellContent(i, 4, QString("%1").arg(threadList.list[i].ThreadCip, sizeof(int_t) * 2, 16, QChar('0')).toUpper());
setCellContent(i, 5, QString("%1").arg(threadList.list[i].SuspendCount, 0, 10));
QString priorityString;
switch(threadList.list[i].Priority)
{
case PriorityIdle:
priorityString="Idle";
break;
case PriorityAboveNormal:
priorityString="AboveNormal";
break;
case PriorityBelowNormal:
priorityString="BelowNormal";
break;
case PriorityHighest:
priorityString="Highest";
break;
case PriorityLowest:
priorityString="Lowest";
break;
case PriorityNormal:
priorityString="Normal";
break;
case PriorityTimeCritical:
priorityString="TimeCritical";
break;
default:
priorityString="Unknown";
break;
}
setCellContent(i, 6, priorityString);
QString waitReasonString;
switch(threadList.list[i].WaitReason)
{
case Executive:
waitReasonString="Executive";
break;
case FreePage:
waitReasonString="FreePage";
break;
case PageIn:
waitReasonString="PageIn";
break;
case PoolAllocation:
waitReasonString="PoolAllocation";
break;
case DelayExecution:
waitReasonString="DelayExecution";
break;
case Suspended:
waitReasonString="Suspended";
break;
case UserRequest:
waitReasonString="UserRequest";
break;
case WrExecutive:
waitReasonString="WrExecutive";
break;
case WrFreePage:
waitReasonString="WrFreePage";
break;
case WrPageIn:
waitReasonString="WrPageIn";
break;
case WrPoolAllocation:
waitReasonString="WrPoolAllocation";
break;
case WrDelayExecution:
waitReasonString="WrDelayExecution";
break;
case WrSuspended:
waitReasonString="WrSuspended";
break;
case WrUserRequest:
waitReasonString="WrUserRequest";
break;
case WrEventPair:
waitReasonString="WrEventPair";
break;
case WrQueue:
waitReasonString="WrQueue";
break;
case WrLpcReceive:
waitReasonString="WrLpcReceive";
break;
case WrLpcReply:
waitReasonString="WrLpcReply";
break;
case WrVirtualMemory:
waitReasonString="WrVirtualMemory";
break;
case WrPageOut:
waitReasonString="WrPageOut";
break;
case WrRendezvous:
waitReasonString="WrRendezvous";
break;
case Spare2:
waitReasonString="Spare2";
break;
case Spare3:
waitReasonString="Spare3";
break;
case Spare4:
waitReasonString="Spare4";
break;
case Spare5:
waitReasonString="Spare5";
break;
case WrCalloutStack:
waitReasonString="WrCalloutStack";
break;
case WrKernel:
waitReasonString="WrKernel";
break;
case WrResource:
waitReasonString="WrResource";
break;
case WrPushLock:
waitReasonString="WrPushLock";
break;
case WrMutex:
waitReasonString="WrMutex";
break;
case WrQuantumEnd:
waitReasonString="WrQuantumEnd";
break;
case WrDispatchInt:
waitReasonString="WrDispatchInt";
break;
case WrPreempted:
waitReasonString="WrPreempted";
break;
case WrYieldExecution:
waitReasonString="WrYieldExecution";
break;
case WrFastMutex:
waitReasonString="WrFastMutex";
break;
case WrGuardedMutex:
waitReasonString="WrGuardedMutex";
break;
case WrRundown:
waitReasonString="WrRundown";
break;
default:
waitReasonString="Unknown";
break;
}
setCellContent(i, 7, waitReasonString);
setCellContent(i, 8, QString("%1").arg(threadList.list[i].LastError, sizeof(unsigned int) * 2, 16, QChar('0')).toUpper());
}
if(threadList.count)
BridgeFree(threadList.list);
mCurrentThread=threadList.CurrentThread;
reloadData();
}
QString ThreadView::paintContent(QPainter* painter, int_t rowBase, int rowOffset, int col, int x, int y, int w, int h)
{
QString ret=StdTable::paintContent(painter, rowBase, rowOffset, col, x, y, w, h);
if(rowBase+rowOffset==mCurrentThread && !col)
{
painter->save();
painter->fillRect(QRect(x, y, w, h), QBrush(QColor("#000000")));
painter->setPen(QPen(QColor("#ffffff"))); //white text
painter->drawText(QRect(x + 4, y , w - 4 , h), Qt::AlignVCenter | Qt::AlignLeft, ret);
painter->restore();
ret="";
}
return ret;
}

View File

@ -0,0 +1,21 @@
#ifndef THREADVIEW_H
#define THREADVIEW_H
#include "StdTable.h"
#include "Bridge.h"
class ThreadView : public StdTable
{
Q_OBJECT
public:
explicit ThreadView(StdTable* parent = 0);
QString paintContent(QPainter* painter, int_t rowBase, int rowOffset, int col, int x, int y, int w, int h);
public slots:
void updateThreadList();
private:
int mCurrentThread;
};
#endif // THREADVIEW_H

View File

@ -215,6 +215,11 @@ void Bridge::emitUpdateDump()
emit updateDump();
}
void Bridge::emitUpdateThreads()
{
emit updateThreads();
}
/************************************************************************************
Static Functions
************************************************************************************/
@ -451,6 +456,12 @@ __declspec(dllexport) void* _gui_sendmessage(GUIMSG type, void* param1, void* pa
}
break;
case GUI_UPDATE_THREAD_VIEW:
{
Bridge::getBridge()->emitUpdateThreads();
}
break;
default:
{
}

View File

@ -59,6 +59,7 @@ public:
void emitReferenceSetProgress(int progress);
void emitStackDumpAt(uint_t va, uint_t csp);
void emitUpdateDump();
void emitUpdateThreads();
//Public variables
void* winId;
@ -99,6 +100,7 @@ signals:
void referenceSetProgress(int progress);
void stackDumpAt(uint_t va, uint_t csp);
void updateDump();
void updateThreads();
private:
QMutex mBridgeMutex;

View File

@ -67,6 +67,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
mReferenceView->setWindowTitle("References");
mReferenceView->setWindowIcon(QIcon(":/icons/images/search.png"));
mThreadView = new ThreadView();
mThreadView->setWindowTitle("Threads");
mThreadView->setWindowIcon(QIcon(":/icons/images/arrow-threads.png"));
//Create the tab widget
mTabWidget = new QTabWidget();
@ -78,6 +82,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
mTabWidget->addTab(mScriptView, mScriptView->windowIcon(), mScriptView->windowTitle());
mTabWidget->addTab(mSymbolView, mSymbolView->windowIcon(), mSymbolView->windowTitle());
mTabWidget->addTab(mReferenceView, mReferenceView->windowIcon(),mReferenceView->windowTitle());
mTabWidget->addTab(mThreadView, mThreadView->windowIcon(), mThreadView->windowTitle());
setCentralWidget(mTabWidget);
@ -119,6 +124,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
connect(ui->actionSymbolInfo,SIGNAL(triggered()),this,SLOT(displaySymbolWidget()));
connect(mSymbolView,SIGNAL(showCpu()),this,SLOT(displayCpuWidget()));
connect(ui->actionReferences,SIGNAL(triggered()),this,SLOT(displayReferencesWidget()));
connect(ui->actionThreads,SIGNAL(triggered()),this,SLOT(displayThreadsWidget()));
connect(Bridge::getBridge(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString)));
connect(Bridge::getBridge(), SIGNAL(updateCPUTitle(QString)), this, SLOT(updateCPUTitleSlot(QString)));
@ -491,3 +497,10 @@ void MainWindow::displayReferencesWidget()
mReferenceView->setFocus();
setTab(mReferenceView);
}
void MainWindow::displayThreadsWidget()
{
mThreadView->show();
mThreadView->setFocus();
setTab(mThreadView);
}

View File

@ -15,6 +15,7 @@
#include "ScriptView.h"
#include "SymbolView.h"
#include "ReferenceView.h"
#include "ThreadView.h"
namespace Ui {
class MainWindow;
@ -55,6 +56,7 @@ public slots:
void displayCpuWidget();
void displaySymbolWidget();
void displayReferencesWidget();
void displayThreadsWidget();
private:
Ui::MainWindow *ui;
@ -68,6 +70,7 @@ private:
BreakpointsView* mBreakpointsView;
ScriptView* mScriptView;
ReferenceView* mReferenceView;
ThreadView* mThreadView;
StatusLabel* mStatusLabel;
StatusLabel* mLastLogLabel;

View File

@ -52,6 +52,7 @@
<addaction name="actionScript"/>
<addaction name="actionSymbolInfo"/>
<addaction name="actionReferences"/>
<addaction name="actionThreads"/>
</widget>
<widget class="QMenu" name="menuDebug">
<property name="title">
@ -126,6 +127,7 @@
<addaction name="actionScript"/>
<addaction name="actionSymbolInfo"/>
<addaction name="actionReferences"/>
<addaction name="actionThreads"/>
<addaction name="separator"/>
<addaction name="actionScylla"/>
</widget>
@ -447,6 +449,21 @@
<string>Alt+R</string>
</property>
</action>
<action name="actionThreads">
<property name="icon">
<iconset resource="../../resource.qrc">
<normaloff>:/icons/images/arrow-threads.png</normaloff>:/icons/images/arrow-threads.png</iconset>
</property>
<property name="text">
<string>Threads</string>
</property>
<property name="toolTip">
<string>Threads</string>
</property>
<property name="shortcut">
<string>Alt+T</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

View File

@ -27,5 +27,6 @@
<file>images/breakpoint.png</file>
<file>images/pdb.png</file>
<file>images/search.png</file>
<file>images/arrow-threads.png</file>
</qresource>
</RCC>