Merge pull request #2556 from torusrxxx/patch000000b2

Run to selection+breakpoint in call stack view
This commit is contained in:
Duncan Ogilvie 2021-01-08 02:33:32 +01:00 committed by GitHub
commit 3a339698a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 256 additions and 40 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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()

View File

@ -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);