1
0
Fork 0

Merge pull request #2573 from torusrxxx/patch000000b9

Go to function return in trace view
This commit is contained in:
Duncan Ogilvie 2021-01-14 13:03:55 +01:00 committed by GitHub
commit 5a59ce27c7
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 125 additions and 38 deletions

View File

@ -265,6 +265,8 @@ void TraceRecordManager::TraceExecuteRecord(const Zydis & newInstruction)
// Don't try to resolve memory values for invalid/lea/nop instructions // Don't try to resolve memory values for invalid/lea/nop instructions
if(newInstruction.Success() && !newInstruction.IsNop() && newInstruction.GetId() != ZYDIS_MNEMONIC_LEA) if(newInstruction.Success() && !newInstruction.IsNop() && newInstruction.GetId() != ZYDIS_MNEMONIC_LEA)
{ {
// This can happen when trace execute instruction is used, we need to fix that or something would go wrong with the GUI.
newContext.registers.regcontext.cip = newInstruction.Address();
DISASM_ARGTYPE argType; DISASM_ARGTYPE argType;
duint value; duint value;
unsigned char memoryContent[memoryContentSize]; unsigned char memoryContent[memoryContentSize];

View File

@ -67,10 +67,10 @@ void ExceptionDirectoryAnalysis::Analyse()
EnumerateFunctionRuntimeEntries64([&](PRUNTIME_FUNCTION Function) EnumerateFunctionRuntimeEntries64([&](PRUNTIME_FUNCTION Function)
{ {
auto funcAddr = mModuleBase + Function->BeginAddress; auto funcAddr = mModuleBase + Function->BeginAddress;
auto funcEnd = mModuleBase + Function->EndAddress; auto funcEnd = mModuleBase + Function->EndAddress - 1;
// If within limits... // If within limits...
if(inRange(funcAddr) && inRange(funcEnd)) if(inRange(funcAddr) && inRange(funcEnd) && funcAddr <= funcEnd)
mFunctions.push_back({ funcAddr, funcEnd }); mFunctions.push_back({ funcAddr, funcEnd });
return true; return true;

View File

@ -507,13 +507,36 @@ bool BpSetSingleshoot(duint Address, BP_TYPE Type, bool singleshoot)
ASSERT_DEBUGGING("Command function call"); ASSERT_DEBUGGING("Command function call");
EXCLUSIVE_ACQUIRE(LockBreakpoints); EXCLUSIVE_ACQUIRE(LockBreakpoints);
// Set breakpoint fast resume // Set breakpoint singleshoot
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address); BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
if(!bpInfo) if(!bpInfo)
return false; return false;
bpInfo->singleshoot = singleshoot; bpInfo->singleshoot = singleshoot;
// Update singleshoot information in TitanEngine
switch(Type)
{
case BPNORMAL:
bpInfo->titantype = (bpInfo->titantype & ~UE_SINGLESHOOT) | (singleshoot ? UE_SINGLESHOOT : 0);
if(IsBPXEnabled(Address) && bpInfo->enabled)
{
if(!DeleteBPX(Address))
dprintf(QT_TRANSLATE_NOOP("DBG", "Delete breakpoint failed (DeleteBPX): %p\n"), Address);
if(!SetBPX(Address, bpInfo->titantype, (void*)cbUserBreakpoint))
dprintf(QT_TRANSLATE_NOOP("DBG", "Error setting breakpoint at %p! (SetBPX)\n"), Address);
}
break;
case BPMEMORY:
if(bpInfo->enabled)
{
if(!RemoveMemoryBPX(Address, bpInfo->memsize))
dprintf(QT_TRANSLATE_NOOP("DBG", "Delete memory breakpoint failed (RemoveMemoryBPX): %p\n"), Address);
if(!SetMemoryBPXEx(Address, bpInfo->memsize, bpInfo->titantype, !singleshoot, (void*)cbMemoryBreakpoint))
dprintf(QT_TRANSLATE_NOOP("DBG", "Could not enable memory breakpoint %p (SetMemoryBPXEx)\n"), Address);
}
break;
}
return true; return true;
} }

View File

@ -61,6 +61,7 @@ static bool cbDisableAllBreakpoints(const BREAKPOINT* bp)
return true; return true;
} }
// Software breakpoints
bool cbDebugSetBPX(int argc, char* argv[]) //bp addr [,name [,type]] bool cbDebugSetBPX(int argc, char* argv[]) //bp addr [,name [,type]]
{ {
if(IsArgumentsLessThan(argc, 2)) if(IsArgumentsLessThan(argc, 2))
@ -325,6 +326,7 @@ bool cbDebugDisableBPX(int argc, char* argv[])
return true; return true;
} }
// Hardware breakpoints
static bool cbDeleteAllHardwareBreakpoints(const BREAKPOINT* bp) static bool cbDeleteAllHardwareBreakpoints(const BREAKPOINT* bp)
{ {
if(bp->type != BPHARDWARE) if(bp->type != BPHARDWARE)
@ -627,6 +629,7 @@ bool cbDebugDisableHardwareBreakpoint(int argc, char* argv[])
return true; return true;
} }
// Memory breakpoints
static bool cbDeleteAllMemoryBreakpoints(const BREAKPOINT* bp) static bool cbDeleteAllMemoryBreakpoints(const BREAKPOINT* bp)
{ {
if(bp->type != BPMEMORY) if(bp->type != BPMEMORY)
@ -897,6 +900,7 @@ bool cbDebugDisableMemoryBreakpoint(int argc, char* argv[])
return true; return true;
} }
// DLL breakpoints
static bool cbDeleteAllDllBreakpoints(const BREAKPOINT* bp) static bool cbDeleteAllDllBreakpoints(const BREAKPOINT* bp)
{ {
if(bp->type != BPDLL || !bp->enabled) if(bp->type != BPDLL || !bp->enabled)
@ -1111,6 +1115,7 @@ bool cbDebugBpDllDisable(int argc, char* argv[])
return true; return true;
} }
// Exception breakpoints
static bool cbDeleteAllExceptionBreakpoints(const BREAKPOINT* bp) static bool cbDeleteAllExceptionBreakpoints(const BREAKPOINT* bp)
{ {
if(bp->type != BPEXCEPTION) if(bp->type != BPEXCEPTION)

View File

@ -760,7 +760,14 @@ static void handleBreakCondition(const BREAKPOINT & bp, const void* ExceptionAdd
if(doBreak) if(doBreak)
{ {
if(bp.singleshoot) if(bp.singleshoot)
{
BpDelete(bp.addr, bp.type); BpDelete(bp.addr, bp.type);
if(bp.type == BPHARDWARE) // Remove this singleshoot hardware breakpoint
{
if(TITANDRXVALID(bp.titantype) && !DeleteHardwareBreakPoint(TITANGETDRX(bp.titantype)))
dprintf(QT_TRANSLATE_NOOP("DBG", "Delete hardware breakpoint failed: %p (DeleteHardwareBreakPoint)\n"), bp.addr);
}
}
if(!bp.silent) if(!bp.silent)
{ {
switch(bp.type) switch(bp.type)
@ -1208,26 +1215,24 @@ static void cbRtrFinalStep(bool checkRepeat = false)
void cbRtrStep() void cbRtrStep()
{ {
hActiveThread = ThreadGetHandle(((DEBUG_EVENT*)GetDebugData())->dwThreadId); hActiveThread = ThreadGetHandle(((DEBUG_EVENT*)GetDebugData())->dwThreadId);
unsigned char ch = 0x90; unsigned char data[MAX_DISASM_BUFFER];
memset(data, 0x90, sizeof(data));
duint cip = GetContextDataEx(hActiveThread, UE_CIP); duint cip = GetContextDataEx(hActiveThread, UE_CIP);
duint csp = GetContextDataEx(hActiveThread, UE_CSP); duint csp = GetContextDataEx(hActiveThread, UE_CSP);
MemRead(cip, &ch, 1); MemRead(cip, data, sizeof(data));
if(bTraceRecordEnabledDuringTrace) if(bTraceRecordEnabledDuringTrace)
_dbg_dbgtraceexecute(cip); _dbg_dbgtraceexecute(cip);
if(mRtrPreviousCSP <= csp) //"Run until return" should break only if RSP is bigger than or equal to current value if(mRtrPreviousCSP <= csp) //"Run until return" should break only if RSP is bigger than or equal to current value
{ {
if(ch == 0xC3 || ch == 0xC2) //retn instruction if(data[0] == 0xC3 || data[0] == 0xC2) //retn instruction
cbRtrFinalStep(true); cbRtrFinalStep(true);
else if(ch == 0x26 || ch == 0x36 || ch == 0x2e || ch == 0x3e || (ch >= 0x64 && ch <= 0x67) || ch == 0xf2 || ch == 0xf3 //instruction prefixes else if(data[0] == 0x26 || data[0] == 0x36 || data[0] == 0x2e || data[0] == 0x3e || (data[0] >= 0x64 && data[0] <= 0x67) || data[0] == 0xf2 || data[0] == 0xf3 //instruction prefixes
#ifdef _WIN64 #ifdef _WIN64
|| (ch >= 0x40 && ch <= 0x4f) || (data[0] >= 0x40 && data[0] <= 0x4f)
#endif //_WIN64 #endif //_WIN64
) )
{ {
Zydis cp; Zydis cp;
unsigned char data[MAX_DISASM_BUFFER];
memset(data, 0, sizeof(data));
MemRead(cip, data, MAX_DISASM_BUFFER);
if(cp.Disassemble(cip, data) && cp.IsRet()) if(cp.Disassemble(cip, data) && cp.IsRet())
cbRtrFinalStep(true); cbRtrFinalStep(true);
else else

View File

@ -46,6 +46,7 @@ CPURegistersView::CPURegistersView(CPUWidget* parent) : RegistersView(parent), m
wCM_FollowInDump = new QAction(DIcon("dump.png"), tr("Follow in Dump"), this); wCM_FollowInDump = new QAction(DIcon("dump.png"), tr("Follow in Dump"), this);
wCM_FollowInStack = new QAction(DIcon("stack.png"), tr("Follow in Stack"), this); wCM_FollowInStack = new QAction(DIcon("stack.png"), tr("Follow in Stack"), this);
wCM_FollowInMemoryMap = new QAction(DIcon("memmap_find_address_page"), tr("Follow in Memory Map"), this); wCM_FollowInMemoryMap = new QAction(DIcon("memmap_find_address_page"), tr("Follow in Memory Map"), this);
wCM_RemoveHardware = new QAction(DIcon("breakpoint_remove.png"), tr("&Remove hardware breakpoint"), this);
wCM_Incrementx87Stack = setupAction(DIcon("arrow-small-down.png"), tr("Increment x87 Stack"), this); wCM_Incrementx87Stack = setupAction(DIcon("arrow-small-down.png"), tr("Increment x87 Stack"), this);
wCM_Decrementx87Stack = setupAction(DIcon("arrow-small-up.png"), tr("Decrement x87 Stack"), this); wCM_Decrementx87Stack = setupAction(DIcon("arrow-small-up.png"), tr("Decrement x87 Stack"), this);
wCM_ChangeFPUView = new QAction(DIcon("change-view.png"), tr("Change view"), this); wCM_ChangeFPUView = new QAction(DIcon("change-view.png"), tr("Change view"), this);
@ -163,6 +164,7 @@ CPURegistersView::CPURegistersView(CPUWidget* parent) : RegistersView(parent), m
connect(wCM_FollowInDump, SIGNAL(triggered()), this, SLOT(onFollowInDump())); connect(wCM_FollowInDump, SIGNAL(triggered()), this, SLOT(onFollowInDump()));
connect(wCM_FollowInStack, SIGNAL(triggered()), this, SLOT(onFollowInStack())); connect(wCM_FollowInStack, SIGNAL(triggered()), this, SLOT(onFollowInStack()));
connect(wCM_FollowInMemoryMap, SIGNAL(triggered()), this, SLOT(onFollowInMemoryMap())); connect(wCM_FollowInMemoryMap, SIGNAL(triggered()), this, SLOT(onFollowInMemoryMap()));
connect(wCM_RemoveHardware, SIGNAL(triggered()), this, SLOT(onRemoveHardware()));
connect(wCM_IncrementPtrSize, SIGNAL(triggered()), this, SLOT(onIncrementPtrSize())); connect(wCM_IncrementPtrSize, SIGNAL(triggered()), this, SLOT(onIncrementPtrSize()));
connect(wCM_DecrementPtrSize, SIGNAL(triggered()), this, SLOT(onDecrementPtrSize())); connect(wCM_DecrementPtrSize, SIGNAL(triggered()), this, SLOT(onDecrementPtrSize()));
connect(wCM_Push, SIGNAL(triggered()), this, SLOT(onPushAction())); connect(wCM_Push, SIGNAL(triggered()), this, SLOT(onPushAction()));
@ -770,6 +772,15 @@ void CPURegistersView::onFollowInMemoryMap()
} }
} }
void CPURegistersView::onRemoveHardware()
{
if(mSelected == DR0 || mSelected == DR1 || mSelected == DR2 || mSelected == DR3)
{
QString addr = QString("%1").arg((* ((duint*) registerValue(&wRegDumpStruct, mSelected))), mRegisterPlaces[mSelected].valuesize, 16, QChar('0')).toUpper();
DbgCmdExec(QString().sprintf("bphc \"%s\"", addr.toUtf8().constData()));
}
}
void CPURegistersView::displayCustomContextMenuSlot(QPoint pos) void CPURegistersView::displayCustomContextMenuSlot(QPoint pos)
{ {
if(!isActive) if(!isActive)
@ -854,6 +865,12 @@ void CPURegistersView::displayCustomContextMenuSlot(QPoint pos)
} }
} }
if(mSelected == DR0 || mSelected == DR1 || mSelected == DR2 || mSelected == DR3)
{
if(* ((duint*) registerValue(&wRegDumpStruct, mSelected)) != 0)
wMenu.addAction(wCM_RemoveHardware);
}
wMenu.addAction(wCM_CopyToClipboard); wMenu.addAction(wCM_CopyToClipboard);
if(mFPUx87_80BITSDISPLAY.contains(mSelected)) if(mFPUx87_80BITSDISPLAY.contains(mSelected))
{ {

View File

@ -37,6 +37,7 @@ protected slots:
void onFollowInDumpN(); void onFollowInDumpN();
void onFollowInStack(); void onFollowInStack();
void onFollowInMemoryMap(); void onFollowInMemoryMap();
void onRemoveHardware();
void onIncrementPtrSize(); void onIncrementPtrSize();
void onDecrementPtrSize(); void onDecrementPtrSize();
void onPushAction(); void onPushAction();
@ -78,6 +79,7 @@ private:
QAction* wCM_FollowInDump; QAction* wCM_FollowInDump;
QAction* wCM_FollowInStack; QAction* wCM_FollowInStack;
QAction* wCM_FollowInMemoryMap; QAction* wCM_FollowInMemoryMap;
QAction* wCM_RemoveHardware;
QAction* wCM_Incrementx87Stack; QAction* wCM_Incrementx87Stack;
QAction* wCM_Decrementx87Stack; QAction* wCM_Decrementx87Stack;
QAction* wCM_ChangeFPUView; QAction* wCM_ChangeFPUView;

View File

@ -876,6 +876,7 @@ void TraceBrowser::setupRightClickContextMenu()
}); });
MenuBuilder* gotoMenu = new MenuBuilder(this, isValid); MenuBuilder* gotoMenu = new MenuBuilder(this, isValid);
gotoMenu->addAction(makeShortcutAction(DIcon("goto.png"), tr("Expression"), SLOT(gotoSlot()), "ActionGotoExpression"), isValid); gotoMenu->addAction(makeShortcutAction(DIcon("goto.png"), tr("Expression"), SLOT(gotoSlot()), "ActionGotoExpression"), isValid);
gotoMenu->addAction(makeAction(DIcon("arrow-step-rtr.png"), tr("Function return"), SLOT(rtrSlot())), isValid);
gotoMenu->addAction(makeShortcutAction(DIcon("previous.png"), tr("Previous"), SLOT(gotoPreviousSlot()), "ActionGotoPrevious"), [this](QMenu*) gotoMenu->addAction(makeShortcutAction(DIcon("previous.png"), tr("Previous"), SLOT(gotoPreviousSlot()), "ActionGotoPrevious"), [this](QMenu*)
{ {
return mHistory.historyHasPrev(); return mHistory.historyHasPrev();
@ -1330,7 +1331,7 @@ void TraceBrowser::parseFinishedSlot()
{ {
if(mTraceFile->isError()) if(mTraceFile->isError())
{ {
SimpleErrorBox(this, tr("Error"), "Error when opening run trace file"); SimpleErrorBox(this, tr("Error"), tr("Error when opening run trace file"));
delete mTraceFile; delete mTraceFile;
mTraceFile = nullptr; mTraceFile = nullptr;
setRowCount(0); setRowCount(0);
@ -1370,6 +1371,16 @@ void TraceBrowser::mnemonicHelpSlot()
emit displayLogWidget(); emit displayLogWidget();
} }
void TraceBrowser::disasm(unsigned long long index, bool history)
{
setSingleSelection(index);
makeVisible(index);
if(history)
mHistory.addVaToHistory(index);
updateViewport();
emit selectionChanged(getInitialSelection());
}
void TraceBrowser::gotoSlot() void TraceBrowser::gotoSlot()
{ {
if(mTraceFile == nullptr || mTraceFile->Progress() < 100) if(mTraceFile == nullptr || mTraceFile->Progress() < 100)
@ -1379,37 +1390,26 @@ void TraceBrowser::gotoSlot()
{ {
auto val = DbgValFromString(gotoDlg.expressionText.toUtf8().constData()); auto val = DbgValFromString(gotoDlg.expressionText.toUtf8().constData());
if(val >= 0 && val < mTraceFile->Length()) if(val >= 0 && val < mTraceFile->Length())
{ disasm(val);
setSingleSelection(val);
makeVisible(val);
mHistory.addVaToHistory(val);
updateViewport();
}
} }
} }
void TraceBrowser::rtrSlot()
{
// Let's hope this search will be fast...
disasm(TraceFileSearchFuncReturn(mTraceFile, getInitialSelection()));
}
void TraceBrowser::gotoNextSlot() void TraceBrowser::gotoNextSlot()
{ {
if(mHistory.historyHasNext()) if(mHistory.historyHasNext())
{ disasm(mHistory.historyNext(), false);
auto index = mHistory.historyNext();
setSingleSelection(index);
makeVisible(index);
updateViewport();
emit selectionChanged(getInitialSelection());
}
} }
void TraceBrowser::gotoPreviousSlot() void TraceBrowser::gotoPreviousSlot()
{ {
if(mHistory.historyHasPrev()) if(mHistory.historyHasPrev())
{ disasm(mHistory.historyPrev(), false);
auto index = mHistory.historyPrev();
setSingleSelection(index);
makeVisible(index);
updateViewport();
emit selectionChanged(getInitialSelection());
}
} }
void TraceBrowser::copyCipSlot() void TraceBrowser::copyCipSlot()

View File

@ -160,6 +160,7 @@ public slots:
void onSelectionChanged(unsigned long long selection); void onSelectionChanged(unsigned long long selection);
void gotoSlot(); void gotoSlot();
void rtrSlot();
void gotoPreviousSlot(); void gotoPreviousSlot();
void gotoNextSlot(); void gotoNextSlot();
void enableHighlightingModeSlot(); void enableHighlightingModeSlot();
@ -182,6 +183,9 @@ public slots:
void updateSlot(); void updateSlot();
void toggleAutoDisassemblyFollowSelectionSlot(); void toggleAutoDisassemblyFollowSelectionSlot();
protected:
void disasm(unsigned long long index, bool history = true);
}; };
#endif //TRACEBROWSER_H #endif //TRACEBROWSER_H

View File

@ -12,7 +12,7 @@ static bool inRange(duint value, duint start, duint end)
int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end) int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end)
{ {
int count = 0; int count = 0;
Zydis cp; Zydis zy;
QString title; QString title;
if(start == end) if(start == end)
title = QCoreApplication::translate("TraceFileSearch", "Constant: %1").arg(ToPtrString(start)); title = QCoreApplication::translate("TraceFileSearch", "Constant: %1").arg(ToPtrString(start));
@ -77,8 +77,8 @@ int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end)
unsigned char opcode[16]; unsigned char opcode[16];
int opcodeSize = 0; int opcodeSize = 0;
file->OpCode(index, opcode, &opcodeSize); file->OpCode(index, opcode, &opcodeSize);
cp.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize); zy.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize);
GuiReferenceSetCellContent(count, 2, cp.InstructionText(true).c_str()); GuiReferenceSetCellContent(count, 2, zy.InstructionText(true).c_str());
//GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress //GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress
count++; count++;
} }
@ -89,7 +89,7 @@ int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end)
int TraceFileSearchMemReference(TraceFileReader* file, duint address) int TraceFileSearchMemReference(TraceFileReader* file, duint address)
{ {
int count = 0; int count = 0;
Zydis cp; Zydis zy;
GuiReferenceInitialize(QCoreApplication::translate("TraceFileSearch", "Reference").toUtf8().constData()); GuiReferenceInitialize(QCoreApplication::translate("TraceFileSearch", "Reference").toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Address").toUtf8().constData()); GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Address").toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Index").toUtf8().constData()); GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Index").toUtf8().constData());
@ -122,8 +122,8 @@ int TraceFileSearchMemReference(TraceFileReader* file, duint address)
unsigned char opcode[16]; unsigned char opcode[16];
int opcodeSize = 0; int opcodeSize = 0;
file->OpCode(index, opcode, &opcodeSize); file->OpCode(index, opcode, &opcodeSize);
cp.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize); zy.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize);
GuiReferenceSetCellContent(count, 2, cp.InstructionText(true).c_str()); GuiReferenceSetCellContent(count, 2, zy.InstructionText(true).c_str());
//GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress //GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress
count++; count++;
} }
@ -131,3 +131,31 @@ int TraceFileSearchMemReference(TraceFileReader* file, duint address)
} }
return count; return count;
} }
unsigned long long TraceFileSearchFuncReturn(TraceFileReader* file, unsigned long long start)
{
auto mCsp = file->Registers(start).regcontext.csp;
auto TID = file->ThreadId(start);
Zydis zy;
for(unsigned long long index = start; index < file->Length(); index++)
{
if(mCsp <= file->Registers(index).regcontext.csp && file->ThreadId(index) == TID) //"Run until return" should break only if RSP is bigger than or equal to current value
{
unsigned char data[16];
int opcodeSize = 0;
file->OpCode(index, data, &opcodeSize);
if(data[0] == 0xC3 || data[0] == 0xC2) //retn instruction
return index;
else if(data[0] == 0x26 || data[0] == 0x36 || data[0] == 0x2e || data[0] == 0x3e || (data[0] >= 0x64 && data[0] <= 0x67) || data[0] == 0xf2 || data[0] == 0xf3 //instruction prefixes
#ifdef _WIN64
|| (data[0] >= 0x40 && data[0] <= 0x4f)
#endif //_WIN64
)
{
if(zy.Disassemble(file->Registers(index).regcontext.cip, data, opcodeSize) && zy.IsRet())
return index;
}
}
}
return start; //Nothing found, so just stay here
}

View File

@ -5,4 +5,5 @@ class TraceFileReader;
int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end); int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end);
int TraceFileSearchMemReference(TraceFileReader* file, duint address); int TraceFileSearchMemReference(TraceFileReader* file, duint address);
unsigned long long TraceFileSearchFuncReturn(TraceFileReader* file, unsigned long long start);
#endif //TRACEFILESEARCH_H #endif //TRACEFILESEARCH_H