mirror of
https://github.com/x64dbg/x64dbg.git
synced 2025-02-18 12:08:15 +00:00
Merge pull request #2556 from torusrxxx/patch000000b2
Run to selection+breakpoint in call stack view
This commit is contained in:
commit
3a339698a5
@ -107,6 +107,12 @@ static void _getcallstack(DBGCALLSTACK* callstack)
|
||||
stackgetcallstack(GetContextDataEx(hActiveThread, UE_CSP), (CALLSTACK*)callstack);
|
||||
}
|
||||
|
||||
static void _getcallstackbythread(HANDLE thread, DBGCALLSTACK* callstack)
|
||||
{
|
||||
if(thread)
|
||||
stackgetcallstackbythread(thread, (CALLSTACK*)callstack);
|
||||
}
|
||||
|
||||
static void _getsehchain(DBGSEHCHAIN* sehchain)
|
||||
{
|
||||
std::vector<duint> SEHList;
|
||||
@ -532,4 +538,5 @@ void dbgfunctionsinit()
|
||||
_dbgfunctions.RefreshModuleList = _refreshmodulelist;
|
||||
_dbgfunctions.GetAddrFromLineEx = _getaddrfromlineex;
|
||||
_dbgfunctions.ModSymbolStatus = _modsymbolstatus;
|
||||
_dbgfunctions.GetCallStackByThread = _getcallstackbythread;
|
||||
}
|
||||
|
@ -207,6 +207,7 @@ typedef int(*SYMAUTOCOMPLETE)(const char* Search, char** Buffer, int MaxSymbols)
|
||||
typedef void(*REFRESHMODULELIST)();
|
||||
typedef duint(*GETADDRFROMLINEEX)(duint mod, const char* szSourceFile, int line);
|
||||
typedef MODULESYMBOLSTATUS(*MODSYMBOLSTATUS)(duint mod);
|
||||
typedef void(*GETCALLSTACKBYTHREAD)(HANDLE thread, DBGCALLSTACK* callstack);
|
||||
|
||||
//The list of all the DbgFunctions() return value.
|
||||
//WARNING: This list is append only. Do not insert things in the middle or plugins would break.
|
||||
@ -285,6 +286,7 @@ typedef struct DBGFUNCTIONS_
|
||||
REFRESHMODULELIST RefreshModuleList;
|
||||
GETADDRFROMLINEEX GetAddrFromLineEx;
|
||||
MODSYMBOLSTATUS ModSymbolStatus;
|
||||
GETCALLSTACKBYTHREAD GetCallStackByThread;
|
||||
} DBGFUNCTIONS;
|
||||
|
||||
#ifdef BUILD_DBG
|
||||
|
@ -357,6 +357,103 @@ void stackgetcallstack(duint csp, std::vector<CALLSTACKENTRY> & callstackVector,
|
||||
CallstackCache[csp] = callstackVector;
|
||||
}
|
||||
|
||||
void stackgetcallstackbythread(HANDLE thread, CALLSTACK* callstack)
|
||||
{
|
||||
std::vector<CALLSTACKENTRY> callstackVector;
|
||||
duint csp = GetContextDataEx(thread, UE_CSP);
|
||||
// Gather context data
|
||||
CONTEXT context;
|
||||
memset(&context, 0, sizeof(CONTEXT));
|
||||
|
||||
context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
|
||||
|
||||
if(SuspendThread(thread) == -1)
|
||||
return;
|
||||
|
||||
if(!GetThreadContext(thread, &context))
|
||||
return;
|
||||
|
||||
if(ResumeThread(thread) == -1)
|
||||
return;
|
||||
|
||||
if(ShowSuspectedCallStack)
|
||||
{
|
||||
stackgetsuspectedcallstack(csp, callstackVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set up all frame data
|
||||
STACKFRAME64 frame;
|
||||
ZeroMemory(&frame, sizeof(STACKFRAME64));
|
||||
|
||||
#ifdef _M_IX86
|
||||
DWORD machineType = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context.Eip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Ebp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = csp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = csp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#endif
|
||||
|
||||
const int MaxWalks = 50;
|
||||
// Container for each callstack entry (50 pre-allocated entries)
|
||||
callstackVector.clear();
|
||||
callstackVector.reserve(MaxWalks);
|
||||
|
||||
for(auto i = 0; i < MaxWalks; i++)
|
||||
{
|
||||
if(!SafeStackWalk64(
|
||||
machineType,
|
||||
fdProcessInfo->hProcess,
|
||||
thread,
|
||||
&frame,
|
||||
&context,
|
||||
StackReadProcessMemoryProc64,
|
||||
StackSymFunctionTableAccess64,
|
||||
StackGetModuleBaseProc64,
|
||||
StackTranslateAddressProc64))
|
||||
{
|
||||
// Maybe it failed, maybe we have finished walking the stack
|
||||
break;
|
||||
}
|
||||
|
||||
if(frame.AddrPC.Offset != 0)
|
||||
{
|
||||
// Valid frame
|
||||
CALLSTACKENTRY entry;
|
||||
memset(&entry, 0, sizeof(CALLSTACKENTRY));
|
||||
|
||||
StackEntryFromFrame(&entry, (duint)frame.AddrFrame.Offset + sizeof(duint), (duint)frame.AddrPC.Offset, (duint)frame.AddrReturn.Offset);
|
||||
callstackVector.push_back(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Base reached
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callstack->total = (int)callstackVector.size();
|
||||
|
||||
if(callstack->total > 0)
|
||||
{
|
||||
callstack->entries = (CALLSTACKENTRY*)BridgeAlloc(callstack->total * sizeof(CALLSTACKENTRY));
|
||||
|
||||
// Copy data directly from the vector
|
||||
memcpy(callstack->entries, callstackVector.data(), callstack->total * sizeof(CALLSTACKENTRY));
|
||||
}
|
||||
}
|
||||
|
||||
void stackgetcallstack(duint csp, CALLSTACK* callstack)
|
||||
{
|
||||
std::vector<CALLSTACKENTRY> callstackVector;
|
||||
|
@ -22,6 +22,7 @@ bool stackcommentget(duint addr, STACK_COMMENT* comment);
|
||||
void stackupdatecallstack(duint csp);
|
||||
void stackgetcallstack(duint csp, CALLSTACK* callstack);
|
||||
void stackgetcallstack(duint csp, std::vector<CALLSTACKENTRY> & callstack, bool cache);
|
||||
void stackgetcallstackbythread(HANDLE thread, CALLSTACK* callstack);
|
||||
void stackupdatesettings();
|
||||
|
||||
#endif //_STACKINFO_H
|
@ -1,10 +1,12 @@
|
||||
#include "CallStackView.h"
|
||||
#include "CommonActions.h"
|
||||
#include "Bridge.h"
|
||||
|
||||
CallStackView::CallStackView(StdTable* parent) : StdTable(parent)
|
||||
{
|
||||
int charwidth = getCharWidth();
|
||||
|
||||
addColumnAt(8 * charwidth, tr("Thread ID"), true);
|
||||
addColumnAt(8 + charwidth * sizeof(dsint) * 2, tr("Address"), true); //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
|
||||
@ -26,16 +28,29 @@ void CallStackView::setupContextMenu()
|
||||
{
|
||||
return DbgIsDebugging();
|
||||
});
|
||||
mCommonActions = new CommonActions(this, getActionHelperFuncs(), [this]()
|
||||
{
|
||||
return getSelectionVa();
|
||||
});
|
||||
QIcon icon = DIcon(ArchValue("processor32.png", "processor64.png"));
|
||||
mMenuBuilder->addAction(makeAction(icon, tr("Follow &Address"), SLOT(followAddress())));
|
||||
mMenuBuilder->addAction(makeAction(icon, tr("Follow &To"), SLOT(followTo())));
|
||||
mMenuBuilder->addAction(makeAction(icon, tr("Follow &Address"), SLOT(followAddress())), [this](QMenu*)
|
||||
{
|
||||
return isSelectionValid();
|
||||
});
|
||||
mMenuBuilder->addAction(makeAction(icon, tr("Follow &To"), SLOT(followTo())), [this](QMenu*)
|
||||
{
|
||||
return isSelectionValid();
|
||||
});
|
||||
QAction* mFollowFrom = mMenuBuilder->addAction(makeAction(icon, tr("Follow &From"), SLOT(followFrom())), [this](QMenu*)
|
||||
{
|
||||
return !getCellContent(getInitialSelection(), 2).isEmpty();
|
||||
return !getCellContent(getInitialSelection(), ColFrom).isEmpty() && isSelectionValid();
|
||||
});
|
||||
mFollowFrom->setShortcutContext(Qt::WidgetShortcut);
|
||||
mFollowFrom->setShortcut(QKeySequence("enter"));
|
||||
connect(this, SIGNAL(enterPressedSignal()), this, SLOT(followFrom()));
|
||||
// Breakpoint menu
|
||||
// TODO: Is Label/Comment/Bookmark useful?
|
||||
mCommonActions->build(mMenuBuilder, CommonActions::ActionBreakpoint);
|
||||
mMenuBuilder->addSeparator();
|
||||
QAction* wShowSuspectedCallStack = makeAction(tr("Show Suspected Call Stack Frame"), SLOT(showSuspectedCallStack()));
|
||||
mMenuBuilder->addAction(wShowSuspectedCallStack, [wShowSuspectedCallStack](QMenu*)
|
||||
@ -57,46 +72,100 @@ void CallStackView::setupContextMenu()
|
||||
mMenuBuilder->loadFromConfig();
|
||||
}
|
||||
|
||||
QString CallStackView::paintContent(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h)
|
||||
{
|
||||
QString ret = getCellContent(rowBase + rowOffset, col);
|
||||
|
||||
if(isSelected(rowBase, rowOffset))
|
||||
painter->fillRect(QRect(x, y, w, h), QBrush(mSelectionColor));
|
||||
|
||||
bool isSpaceRow = !getCellContent(rowBase + rowOffset, ColThread).isEmpty();
|
||||
|
||||
if(!col && !(rowBase + rowOffset) && !ret.isEmpty())
|
||||
{
|
||||
painter->fillRect(QRect(x, y, w, h), QBrush(ConfigColor("ThreadCurrentBackgroundColor")));
|
||||
painter->setPen(QPen(ConfigColor("ThreadCurrentColor")));
|
||||
painter->drawText(QRect(x + 4, y, w - 4, h), Qt::AlignVCenter | Qt::AlignLeft, ret);
|
||||
ret = "";
|
||||
}
|
||||
|
||||
else if(col > ColThread && isSpaceRow)
|
||||
{
|
||||
auto mid = h / 2.0;
|
||||
painter->drawLine(QPointF(x, y + mid), QPointF(x + w, y + mid));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CallStackView::updateCallStack()
|
||||
{
|
||||
DBGCALLSTACK callstack;
|
||||
memset(&callstack, 0, sizeof(DBGCALLSTACK));
|
||||
if(!DbgFunctions()->GetCallStack)
|
||||
if(!DbgFunctions()->GetCallStackByThread)
|
||||
return;
|
||||
DbgFunctions()->GetCallStack(&callstack);
|
||||
setRowCount(callstack.total);
|
||||
for(int i = 0; i < callstack.total; i++)
|
||||
|
||||
THREADLIST threadList;
|
||||
memset(&threadList, 0, sizeof(THREADLIST));
|
||||
DbgGetThreadList(&threadList);
|
||||
|
||||
int currentRow = 0;
|
||||
int currentIndexToDraw = 0;
|
||||
setRowCount(0);
|
||||
for(int j = 0; j < threadList.count; j++)
|
||||
{
|
||||
QString addrText = ToPtrString(callstack.entries[i].addr);
|
||||
setCellContent(i, 0, addrText);
|
||||
addrText = ToPtrString(callstack.entries[i].to);
|
||||
setCellContent(i, 1, addrText);
|
||||
if(callstack.entries[i].from)
|
||||
{
|
||||
addrText = ToPtrString(callstack.entries[i].from);
|
||||
setCellContent(i, 2, addrText);
|
||||
}
|
||||
if(i != callstack.total - 1)
|
||||
setCellContent(i, 3, ToHexString(callstack.entries[i + 1].addr - callstack.entries[i].addr));
|
||||
if(!j)
|
||||
currentIndexToDraw = threadList.CurrentThread; // Draw the current thread first
|
||||
else if(j == threadList.CurrentThread)
|
||||
currentIndexToDraw = 0; // Draw the previously skipped thread
|
||||
else
|
||||
setCellContent(i, 3, "");
|
||||
setCellContent(i, 4, callstack.entries[i].comment);
|
||||
int party = DbgFunctions()->ModGetParty(callstack.entries[i].to);
|
||||
switch(party)
|
||||
currentIndexToDraw = j;
|
||||
|
||||
DBGCALLSTACK callstack;
|
||||
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));
|
||||
|
||||
currentRow++;
|
||||
|
||||
for(int i = 0; i < callstack.total; i++, currentRow++)
|
||||
{
|
||||
case mod_user:
|
||||
setCellContent(i, 5, tr("User"));
|
||||
break;
|
||||
case mod_system:
|
||||
setCellContent(i, 5, tr("System"));
|
||||
break;
|
||||
default:
|
||||
setCellContent(i, 5, QString::number(party));
|
||||
break;
|
||||
QString addrText = ToPtrString(callstack.entries[i].addr);
|
||||
setCellContent(currentRow, ColAddress, addrText);
|
||||
addrText = ToPtrString(callstack.entries[i].to);
|
||||
setCellContent(currentRow, ColTo, addrText);
|
||||
if(callstack.entries[i].from)
|
||||
{
|
||||
addrText = ToPtrString(callstack.entries[i].from);
|
||||
setCellContent(currentRow, ColFrom, addrText);
|
||||
}
|
||||
setCellUserdata(currentRow, ColFrom, callstack.entries[i].from);
|
||||
if(i != callstack.total - 1)
|
||||
setCellContent(currentRow, ColSize, ToHexString(callstack.entries[i + 1].addr - callstack.entries[i].addr));
|
||||
else
|
||||
setCellContent(currentRow, ColSize, "");
|
||||
setCellContent(currentRow, ColComment, callstack.entries[i].comment);
|
||||
int party = DbgFunctions()->ModGetParty(callstack.entries[i].to);
|
||||
switch(party)
|
||||
{
|
||||
case mod_user:
|
||||
setCellContent(currentRow, ColParty, tr("User"));
|
||||
break;
|
||||
case mod_system:
|
||||
setCellContent(currentRow, ColParty, tr("System"));
|
||||
break;
|
||||
default:
|
||||
setCellContent(currentRow, ColParty, QString::number(party));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(callstack.total)
|
||||
BridgeFree(callstack.entries);
|
||||
|
||||
}
|
||||
if(callstack.total)
|
||||
BridgeFree(callstack.entries);
|
||||
|
||||
if(threadList.count)
|
||||
BridgeFree(threadList.list);
|
||||
|
||||
reloadData();
|
||||
}
|
||||
|
||||
@ -110,19 +179,19 @@ void CallStackView::contextMenuSlot(const QPoint pos)
|
||||
|
||||
void CallStackView::followAddress()
|
||||
{
|
||||
QString addrText = getCellContent(getInitialSelection(), 0);
|
||||
QString addrText = getCellContent(getInitialSelection(), ColAddress);
|
||||
DbgCmdExecDirect(QString("sdump " + addrText));
|
||||
}
|
||||
|
||||
void CallStackView::followTo()
|
||||
{
|
||||
QString addrText = getCellContent(getInitialSelection(), 1);
|
||||
QString addrText = getCellContent(getInitialSelection(), ColTo);
|
||||
DbgCmdExecDirect(QString("disasm " + addrText));
|
||||
}
|
||||
|
||||
void CallStackView::followFrom()
|
||||
{
|
||||
QString addrText = getCellContent(getInitialSelection(), 2);
|
||||
QString addrText = getCellContent(getInitialSelection(), ColFrom);
|
||||
DbgCmdExecDirect(QString("disasm " + addrText));
|
||||
}
|
||||
|
||||
@ -137,3 +206,17 @@ void CallStackView::showSuspectedCallStack()
|
||||
updateCallStack();
|
||||
emit Bridge::getBridge()->updateDump();
|
||||
}
|
||||
|
||||
bool CallStackView::isSelectionValid()
|
||||
{
|
||||
return getCellContent(getInitialSelection(), ColThread).isEmpty();
|
||||
}
|
||||
|
||||
// For breakpoint/run to selection
|
||||
duint CallStackView::getSelectionVa()
|
||||
{
|
||||
if(isSelectionValid())
|
||||
return getCellUserdata(getInitialSelection(), ColFrom);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define CALLSTACKVIEW_H
|
||||
|
||||
#include "StdTable.h"
|
||||
class CommonActions;
|
||||
|
||||
class CallStackView : public StdTable
|
||||
{
|
||||
@ -9,6 +10,10 @@ class CallStackView : public StdTable
|
||||
public:
|
||||
explicit CallStackView(StdTable* parent = 0);
|
||||
void setupContextMenu();
|
||||
duint getSelectionVa();
|
||||
|
||||
protected:
|
||||
QString paintContent(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h) override;
|
||||
|
||||
protected slots:
|
||||
void updateCallStack();
|
||||
@ -19,7 +24,20 @@ protected slots:
|
||||
void showSuspectedCallStack();
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
ColThread = 0,
|
||||
ColAddress,
|
||||
ColTo,
|
||||
ColFrom,
|
||||
ColSize,
|
||||
ColComment,
|
||||
ColParty
|
||||
};
|
||||
|
||||
MenuBuilder* mMenuBuilder;
|
||||
CommonActions* mCommonActions;
|
||||
bool isSelectionValid();
|
||||
};
|
||||
|
||||
#endif // CALLSTACKVIEW_H
|
||||
|
@ -1546,7 +1546,15 @@ void MainWindow::setNameMenu(int hMenu, QString name)
|
||||
void MainWindow::runSelection()
|
||||
{
|
||||
if(DbgIsDebugging())
|
||||
DbgCmdExec(("run " + ToPtrString(mCpuWidget->getSelectionVa())));
|
||||
{
|
||||
duint addr = 0;
|
||||
if(mTabWidget->currentWidget() == mCpuWidget || (mCpuWidget->window() != this && mCpuWidget->isActiveWindow()))
|
||||
addr = mCpuWidget->getSelectionVa();
|
||||
else if(mTabWidget->currentWidget() == mCallStackView || (mCallStackView->window() != this && mCallStackView->isActiveWindow()))
|
||||
addr = mCallStackView->getSelectionVa();
|
||||
if(addr)
|
||||
DbgCmdExec("run " + ToPtrString(addr));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::runExpression()
|
||||
|
@ -313,7 +313,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "Watch1", 6);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "BreakpointsView", 7);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "MemoryMap", 8);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "CallStack", 6);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "CallStack", 7);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "SEH", 4);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "Script", 3);
|
||||
AbstractTableView::setupColumnConfigDefaultValue(guiUint, "Thread", 14);
|
||||
|
Loading…
x
Reference in New Issue
Block a user