1
0
Fork 0

Big refactor to improve the tracing experience

- "Trace record" has been renamed to "Trace coverage"
- "Run trace" has been renamed to "Trace recording"
- You can now start recording directly from the trace dialog
This commit is contained in:
Duncan Ogilvie 2022-07-20 01:42:48 +02:00
parent cb7e6ea892
commit 3361b2dfc2
37 changed files with 344 additions and 244 deletions

View File

@ -247,7 +247,7 @@ static void HandleZydisOperand(const Zydis & cp, int opindex, DISASM_ARGTYPE* ar
void TraceRecordManager::TraceExecuteRecord(const Zydis & newInstruction)
{
if(!isRunTraceEnabled())
if(!isTraceRecordingEnabled())
return;
unsigned char WriteBuffer[3072];
unsigned char* WriteBufferPtr = WriteBuffer;
@ -419,7 +419,7 @@ void TraceRecordManager::TraceExecuteRecord(const Zydis & newInstruction)
{
CloseHandle(rtFile);
String error = stringformatinline(StringUtils::sprintf("{winerror@%d}", GetLastError()));
dprintf(QT_TRANSLATE_NOOP("DBG", "Run trace has stopped unexpectedly because WriteFile() failed. GetLastError() = %s.\r\n"), error.c_str());
dprintf(QT_TRANSLATE_NOOP("DBG", "Trace recording has stopped unexpectedly because WriteFile() failed. GetLastError() = %s.\r\n"), error.c_str());
rtEnabled = false;
}
}
@ -486,12 +486,12 @@ void TraceRecordManager::increaseInstructionCounter()
InterlockedIncrement((volatile long*)&instructionCounter);
}
bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
bool TraceRecordManager::enableTraceRecording(bool enabled, const char* fileName)
{
if(enabled)
{
if(rtEnabled)
enableRunTrace(false, NULL); //re-enable run trace
enableTraceRecording(false, NULL); //re-enable run trace
if(!DbgIsDebugging())
return false;
rtFile = CreateFileW(StringUtils::Utf8ToUtf16(fileName).c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
@ -529,7 +529,7 @@ bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
CloseHandle(rtFile);
json_free(headerinfo);
json_decref(root);
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace failed to start because file header cannot be written."));
dputs(QT_TRANSLATE_NOOP("DBG", "Trace recording failed to start because the file header cannot be written."));
return false;
}
WriteFile(rtFile, headerinfo, (DWORD)headerinfosize, &written, nullptr);
@ -538,7 +538,7 @@ bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
if(written < headerinfosize) //disk-full?
{
CloseHandle(rtFile);
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace failed to start because file header cannot be written."));
dputs(QT_TRANSLATE_NOOP("DBG", "Trace recording failed to start because the file header cannot be written."));
return false;
}
}
@ -549,7 +549,7 @@ bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
rtNeedThreadId = true;
for(size_t i = 0; i < _countof(rtOldContextChanged); i++)
rtOldContextChanged[i] = true;
dprintf(QT_TRANSLATE_NOOP("DBG", "Run trace started. File: %s\r\n"), fileName);
dprintf(QT_TRANSLATE_NOOP("DBG", "Started trace recording to file: %s\r\n"), fileName);
Zydis cp;
unsigned char instr[MAX_DISASM_BUFFER];
auto cip = GetContextDataEx(hActiveThread, UE_CIP);
@ -564,7 +564,7 @@ bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
else
{
String error = stringformatinline(StringUtils::sprintf("{winerror@%d}", GetLastError()));
dprintf(QT_TRANSLATE_NOOP("DBG", "Cannot create run trace file. GetLastError() = %s.\r\n"), error.c_str());
dprintf(QT_TRANSLATE_NOOP("DBG", "Cannot create trace recording file. GetLastError() = %s.\r\n"), error.c_str());
return false;
}
}
@ -575,7 +575,7 @@ bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
CloseHandle(rtFile);
rtPrevInstAvailable = false;
rtEnabled = false;
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace stopped."));
dputs(QT_TRANSLATE_NOOP("DBG", "Trace recording stopped."));
}
return true;
}
@ -700,12 +700,12 @@ unsigned int TraceRecordManager::getModuleIndex(const String & moduleName)
}
}
bool TraceRecordManager::isRunTraceEnabled()
bool TraceRecordManager::isTraceRecordingEnabled()
{
return rtEnabled;
}
void _dbg_dbgtraceexecute(duint CIP)
void dbgtraceexecute(duint CIP)
{
if(TraceRecord.getTraceRecordType(CIP) != TraceRecordManager::TraceRecordType::TraceRecordNone)
{
@ -714,7 +714,7 @@ void _dbg_dbgtraceexecute(duint CIP)
if(MemRead(CIP, data, MAX_DISASM_BUFFER))
{
instruction.DisassembleSafe(CIP, data, MAX_DISASM_BUFFER);
if(TraceRecord.isRunTraceEnabled())
if(TraceRecord.isTraceRecordingEnabled())
{
TraceRecord.TraceExecute(CIP, instruction.Size());
TraceRecord.TraceExecuteRecord(instruction);
@ -727,7 +727,7 @@ void _dbg_dbgtraceexecute(duint CIP)
}
else
{
if(TraceRecord.isRunTraceEnabled())
if(TraceRecord.isTraceRecordingEnabled())
{
Zydis instruction;
unsigned char data[MAX_DISASM_BUFFER];
@ -740,34 +740,3 @@ void _dbg_dbgtraceexecute(duint CIP)
}
TraceRecord.increaseInstructionCounter();
}
unsigned int _dbg_dbggetTraceRecordHitCount(duint address)
{
return TraceRecord.getHitCount(address);
}
TRACERECORDBYTETYPE _dbg_dbggetTraceRecordByteType(duint address)
{
return (TRACERECORDBYTETYPE)TraceRecord.getByteType(address);
}
bool _dbg_dbgsetTraceRecordType(duint pageAddress, TRACERECORDTYPE type)
{
return TraceRecord.setTraceRecordType(pageAddress, (TraceRecordManager::TraceRecordType)type);
}
TRACERECORDTYPE _dbg_dbggetTraceRecordType(duint pageAddress)
{
return (TRACERECORDTYPE)TraceRecord.getTraceRecordType(pageAddress);
}
// When disabled, file name is not relevant and can be NULL
bool _dbg_dbgenableRunTrace(bool enabled, const char* fileName)
{
return TraceRecord.enableRunTrace(enabled, fileName);
}
bool _dbg_dbgisRunTraceEnabled()
{
return TraceRecord.isRunTraceEnabled();
}

View File

@ -60,8 +60,8 @@ public:
TraceRecordByteType getByteType(duint address);
void increaseInstructionCounter();
bool isRunTraceEnabled();
bool enableRunTrace(bool enabled, const char* fileName);
bool isTraceRecordingEnabled();
bool enableTraceRecording(bool enabled, const char* fileName);
void saveToDb(JSON root);
void loadFromDb(JSON root);
@ -113,14 +113,6 @@ private:
};
extern TraceRecordManager TraceRecord;
void _dbg_dbgtraceexecute(duint CIP);
//exported to bridge
unsigned int _dbg_dbggetTraceRecordHitCount(duint address);
TRACERECORDBYTETYPE _dbg_dbggetTraceRecordByteType(duint address);
bool _dbg_dbgsetTraceRecordType(duint pageAddress, TRACERECORDTYPE type);
TRACERECORDTYPE _dbg_dbggetTraceRecordType(duint pageAddress);
bool _dbg_dbgenableRunTrace(bool enabled, const char* fileName);
bool _dbg_dbgisRunTraceEnabled();
void dbgtraceexecute(duint CIP);
#endif // TRACERECORD_H

View File

@ -174,7 +174,7 @@ static bool _getjit(char* jit, bool jit64)
return true;
}
bool _getprocesslist(DBGPROCESSINFO** entries, int* count)
static bool _getprocesslist(DBGPROCESSINFO** entries, int* count)
{
std::vector<PROCESSENTRY32> infoList;
std::vector<std::string> commandList;
@ -440,7 +440,7 @@ static int SymAutoComplete(const char* Search, char** Buffer, int MaxSymbols)
return count;
}
MODULESYMBOLSTATUS _modsymbolstatus(duint base)
static MODULESYMBOLSTATUS _modsymbolstatus(duint base)
{
SHARED_ACQUIRE(LockModules);
auto modInfo = ModInfoFromAddr(base);
@ -463,6 +463,36 @@ static void _refreshmodulelist()
SymUpdateModuleList();
}
static unsigned int _getTraceRecordHitCount(duint address)
{
return TraceRecord.getHitCount(address);
}
static TRACERECORDBYTETYPE _getTraceRecordByteType(duint address)
{
return (TRACERECORDBYTETYPE)TraceRecord.getByteType(address);
}
static bool _setTraceRecordType(duint pageAddress, TRACERECORDTYPE type)
{
return TraceRecord.setTraceRecordType(pageAddress, (TraceRecordManager::TraceRecordType)type);
}
static TRACERECORDTYPE _getTraceRecordType(duint pageAddress)
{
return (TRACERECORDTYPE)TraceRecord.getTraceRecordType(pageAddress);
}
static bool _enableTraceRecording(bool enabled, const char* fileName)
{
return TraceRecord.enableTraceRecording(enabled, fileName);
}
static bool _isTraceRecordingEnabled()
{
return TraceRecord.isTraceRecordingEnabled();
}
void dbgfunctionsinit()
{
_dbgfunctions.AssembleAtEx = _assembleatex;
@ -508,10 +538,10 @@ void dbgfunctionsinit()
_dbgfunctions.GetBridgeBp = _getbridgebp;
_dbgfunctions.StringFormatInline = _stringformatinline;
_dbgfunctions.GetMnemonicBrief = _getmnemonicbrief;
_dbgfunctions.GetTraceRecordHitCount = _dbg_dbggetTraceRecordHitCount;
_dbgfunctions.GetTraceRecordByteType = _dbg_dbggetTraceRecordByteType;
_dbgfunctions.SetTraceRecordType = _dbg_dbgsetTraceRecordType;
_dbgfunctions.GetTraceRecordType = _dbg_dbggetTraceRecordType;
_dbgfunctions.GetTraceRecordHitCount = _getTraceRecordHitCount;
_dbgfunctions.GetTraceRecordByteType = _getTraceRecordByteType;
_dbgfunctions.SetTraceRecordType = _setTraceRecordType;
_dbgfunctions.GetTraceRecordType = _getTraceRecordType;
_dbgfunctions.EnumHandles = _enumhandles;
_dbgfunctions.GetHandleName = _gethandlename;
_dbgfunctions.EnumTcpConnections = _enumtcpconnections;

View File

@ -110,7 +110,7 @@ SCRIPT_EXPORT bool Script::Register::Set(Script::Register::RegisterEnum reg, dui
if(reg == ArchValue(EIP, RIP) || reg == CIP)
{
auto cip = GetContextDataEx(hActiveThread, UE_CIP);
_dbg_dbgtraceexecute(cip);
dbgtraceexecute(cip);
DebugUpdateGuiAsync(cip, false); //update disassembly + register view
}
else if(reg == ArchValue(ESP, RSP) || reg == SP || reg == CSP) //update stack

View File

@ -500,6 +500,6 @@ bool cbInstrTraceexecute(int argc, char* argv[])
duint addr;
if(!valfromstring(argv[1], &addr, false))
return false;
_dbg_dbgtraceexecute(addr);
dbgtraceexecute(addr);
return true;
}

View File

@ -480,7 +480,7 @@ bool cbDebugSkip(int argc, char* argv[])
{
disasmfast(cip, &basicinfo);
cip += basicinfo.size;
_dbg_dbgtraceexecute(cip);
dbgtraceexecute(cip);
}
SetContextDataEx(hActiveThread, UE_CIP, cip);
DebugUpdateGuiAsync(cip, false); //update GUI

View File

@ -171,14 +171,14 @@ bool cbDebugTraceSetLogFile(int argc, char* argv[])
return dbgsettracelogfile(fileName);
}
bool cbDebugStartRunTrace(int argc, char* argv[])
bool cbDebugStartTraceRecording(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
return _dbg_dbgenableRunTrace(true, argv[1]);
return TraceRecord.enableTraceRecording(true, argv[1]);
}
bool cbDebugStopRunTrace(int argc, char* argv[])
bool cbDebugStopTraceRecording(int argc, char* argv[])
{
return _dbg_dbgenableRunTrace(false, nullptr);
return TraceRecord.enableTraceRecording(false, nullptr);
}

View File

@ -14,5 +14,5 @@ bool cbDebugTraceSetLog(int argc, char* argv[]);
bool cbDebugTraceSetCommand(int argc, char* argv[]);
bool cbDebugTraceSetSwitchCondition(int argc, char* argv[]);
bool cbDebugTraceSetLogFile(int argc, char* argv[]);
bool cbDebugStartRunTrace(int argc, char* argv[]);
bool cbDebugStopRunTrace(int argc, char* argv[]);
bool cbDebugStartTraceRecording(int argc, char* argv[]);
bool cbDebugStopTraceRecording(int argc, char* argv[]);

View File

@ -746,7 +746,7 @@ void cbPauseBreakpoint()
DebugUpdateGuiSetStateAsync(CIP, true);
_dbg_animatestop(); // Stop animating when paused
// Trace record
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
//lock
lock(WAITID_RUN);
// Plugin callback
@ -925,7 +925,7 @@ static void cbGenericBreakpoint(BP_TYPE bptype, void* ExceptionAddress = nullptr
plugincbcall(CB_BREAKPOINT, &bpInfo);
// Trace record
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
// Watchdog
cbCheckWatchdog(0, nullptr);
@ -994,7 +994,7 @@ void cbRunToUserCodeBreakpoint(void* ExceptionAddress)
// lock
lock(WAITID_RUN);
// Trace record
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
// Update GUI
DebugUpdateGuiSetStateAsync(GetContextDataEx(hActiveThread, UE_CIP), true);
// Plugin callback
@ -1191,7 +1191,7 @@ void cbStep()
{
DebugUpdateGuiSetStateAsync(CIP, true);
// Trace record
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
// Plugin interaction
PLUG_CB_STEPPED stepInfo;
stepInfo.reserved = 0;
@ -1207,7 +1207,7 @@ void cbStep()
}
else
{
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
(bRepeatIn ? StepIntoWow64 : StepOverWrapper)((void*)cbStep);
}
}
@ -1220,7 +1220,7 @@ static void cbRtrFinalStep(bool checkRepeat = false)
hActiveThread = ThreadGetHandle(((DEBUG_EVENT*)GetDebugData())->dwThreadId);
duint CIP = GetContextDataEx(hActiveThread, UE_CIP);
// Trace record
_dbg_dbgtraceexecute(CIP);
dbgtraceexecute(CIP);
DebugUpdateGuiSetStateAsync(CIP, true);
//lock
lock(WAITID_RUN);
@ -1243,7 +1243,7 @@ void cbRtrStep()
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
duint csp = GetContextDataEx(hActiveThread, UE_CSP);
MemRead(cip, data, sizeof(data));
_dbg_dbgtraceexecute(cip);
dbgtraceexecute(cip);
if(mRtrPreviousCSP <= csp) //"Run until return" should break only if RSP is bigger than or equal to current value
{
if(data[0] == 0xC3 || data[0] == 0xC2) //retn instruction
@ -1342,7 +1342,7 @@ static void cbTraceUniversalConditionalStep(duint cip, bool bStepInto, void(*cal
}
else //continue tracing
{
_dbg_dbgtraceexecute(cip);
dbgtraceexecute(cip);
if(switchCondition) //switch (invert) the step type once
bStepInto = !bStepInto;
(bStepInto ? StepIntoWow64 : StepOverWrapper)((void*)callback);
@ -2903,7 +2903,7 @@ static void debugLoopFunction(INIT_STRUCT* init)
ThreadClear();
WatchClear();
TraceRecord.clear();
_dbg_dbgenableRunTrace(false, nullptr); //Stop run trace
TraceRecord.enableTraceRecording(false, nullptr); // Stop trace recording
GuiSetDebugState(stopped);
GuiUpdateAllViews();
dputs(QT_TRANSLATE_NOOP("DBG", "Debugging stopped!"));

View File

@ -116,7 +116,7 @@ void ExpressionFunctions::Init()
//Trace record
RegisterEasy("tr.enabled", trenabled);
RegisterEasy("tr.hitcount,tr.count", trhitcount);
RegisterEasy("tr.runtraceenabled", trisruntraceenabled);
RegisterEasy("tr.isrecording,tr.runtraceenabled", trisrecording);
//Byte/Word/Dword/Qword/Pointer
RegisterEasy("ReadByte,Byte,byte", readbyte);

View File

@ -436,9 +436,9 @@ namespace Exprfunc
return trenabled(addr) ? TraceRecord.getHitCount(addr) : 0;
}
duint trisruntraceenabled()
duint trisrecording()
{
return _dbg_dbgisRunTraceEnabled() ? 1 : 0;
return TraceRecord.isTraceRecordingEnabled() ? 1 : 0;
}
duint gettickcount()

View File

@ -57,7 +57,7 @@ namespace Exprfunc
duint trenabled(duint addr);
duint trhitcount(duint addr);
duint trisruntraceenabled();
duint trisrecording();
duint gettickcount();
duint readbyte(duint addr);

View File

@ -2619,7 +2619,7 @@ bool valtostring(const char* string, duint value, bool silent)
if(strstr(regName(), "ip"))
{
auto cip = GetContextDataEx(hActiveThread, UE_CIP);
_dbg_dbgtraceexecute(cip);
dbgtraceexecute(cip);
DebugUpdateGuiAsync(cip, false); //update disassembly + register view
}
else if(strstr(regName(), "sp")) //update stack

View File

@ -211,18 +211,18 @@ static void registercommands()
//tracing
dbgcmdnew("TraceIntoConditional,ticnd", cbDebugTraceIntoConditional, true); //Trace into conditional
dbgcmdnew("TraceOverConditional,tocnd", cbDebugTraceOverConditional, true); //Trace over conditional
dbgcmdnew("TraceIntoBeyondTraceRecord,tibt", cbDebugTraceIntoBeyondTraceRecord, true); //Trace into beyond trace record
dbgcmdnew("TraceOverBeyondTraceRecord,tobt", cbDebugTraceOverBeyondTraceRecord, true); //Trace over beyond trace record
dbgcmdnew("TraceIntoIntoTraceRecord,tiit", cbDebugTraceIntoIntoTraceRecord, true); //Trace into into trace record
dbgcmdnew("TraceOverIntoTraceRecord,toit", cbDebugTraceOverIntoTraceRecord, true); //Trace over into trace record
dbgcmdnew("TraceIntoBeyondTraceCoverage,TraceIntoBeyondTraceRecord,tibt", cbDebugTraceIntoBeyondTraceRecord, true); //Trace into beyond trace record
dbgcmdnew("TraceOverBeyondTraceCoverage,TraceOverBeyondTraceRecord,tobt", cbDebugTraceOverBeyondTraceRecord, true); //Trace over beyond trace record
dbgcmdnew("TraceIntoIntoTraceCoverage,TraceIntoIntoTraceRecord,tiit", cbDebugTraceIntoIntoTraceRecord, true); //Trace into into trace record
dbgcmdnew("TraceOverIntoTraceCoverage,TraceOverIntoTraceRecord,toit", cbDebugTraceOverIntoTraceRecord, true); //Trace over into trace record
dbgcmdnew("RunToParty", cbDebugRunToParty, true); //Run to code in a party
dbgcmdnew("RunToUserCode,rtu", cbDebugRunToUserCode, true); //Run to user code
dbgcmdnew("TraceSetLog,SetTraceLog", cbDebugTraceSetLog, true); //Set trace log text + condition
dbgcmdnew("TraceSetCommand,SetTraceCommand", cbDebugTraceSetCommand, true); //Set trace command text + condition
dbgcmdnew("TraceSetSwitchCondition,SetTraceSwitchCondition", cbDebugTraceSetSwitchCondition, true); //Set trace switch condition
dbgcmdnew("TraceSetLogFile,SetTraceLogFile", cbDebugTraceSetLogFile, true); //Set trace log file
dbgcmdnew("StartRunTrace,opentrace", cbDebugStartRunTrace, true); //start run trace (Ollyscript command "opentrace" "opens run trace window")
dbgcmdnew("StopRunTrace,tc", cbDebugStopRunTrace, true); //stop run trace (and Ollyscript command)
dbgcmdnew("StartTraceRecording,StartRunTrace,opentrace", cbDebugStartTraceRecording, true); //start run trace (Ollyscript command "opentrace" "opens run trace window")
dbgcmdnew("StopTraceRecording,StopRunTrace,tc", cbDebugStopTraceRecording, true); //stop run trace (and Ollyscript command)
//thread control
dbgcmdnew("createthread,threadcreate,newthread,threadnew", cbDebugCreatethread, true); //create thread

View File

@ -14,7 +14,17 @@ BrowseDialog::BrowseDialog(QWidget* parent, const QString & title, const QString
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::MSWindowsFixedSizeDialogHint);
setWindowTitle(title);
ui->label->setText(text);
ui->lineEdit->setText(defaultPath);
auto nativePath = QDir::toNativeSeparators(defaultPath);
ui->lineEdit->setText(nativePath);
// Select the filename when saving
auto lastSlashIdx = nativePath.lastIndexOf(QDir::separator());
if(save && !QFileInfo(nativePath).isDir() && lastSlashIdx != -1)
{
auto periodIdx = nativePath.lastIndexOf('.');
if(periodIdx == -1)
periodIdx = nativePath.length();
ui->lineEdit->setSelection(lastSlashIdx + 1, periodIdx - lastSlashIdx - 1);
}
QCompleter* completer = new QCompleter(ui->lineEdit);
completer->setModel(new QDirModel(completer));
ui->lineEdit->setCompleter(completer);

View File

@ -12,7 +12,7 @@ class BrowseDialog : public QDialog
Q_OBJECT
public:
explicit BrowseDialog(QWidget* parent, const QString & title, const QString & text, const QString & filter, const QString & defaultPath, bool save);
BrowseDialog(QWidget* parent, const QString & title, const QString & text, const QString & filter, const QString & defaultPath, bool save);
~BrowseDialog();
QString path;

View File

@ -24,6 +24,7 @@
#include "MemoryPage.h"
#include "CommonActions.h"
#include "BrowseDialog.h"
#include "Tracer/TraceBrowser.h"
CPUDisassembly::CPUDisassembly(QWidget* parent, bool isMain) : Disassembly(parent, isMain)
{
@ -389,27 +390,33 @@ void CPUDisassembly::setupRightClickContextMenu()
mMenuBuilder->addMenu(makeMenu(DIcon("label"), tr("Label")), labelMenu);
mCommonActions->build(mMenuBuilder, CommonActions::ActionComment | CommonActions::ActionBookmark);
QAction* traceRecordDisable = makeAction(DIcon("close-all-tabs"), tr("Disable"), SLOT(ActionTraceRecordDisableSlot()));
QAction* traceRecordEnableBit = makeAction(DIcon("bit"), tr("Bit"), SLOT(ActionTraceRecordBitSlot()));
QAction* traceRecordEnableByte = makeAction(DIcon("byte"), tr("Byte"), SLOT(ActionTraceRecordByteSlot()));
QAction* traceRecordEnableWord = makeAction(DIcon("word"), tr("Word"), SLOT(ActionTraceRecordWordSlot()));
QAction* traceRecordToggleRunTrace = makeShortcutAction(tr("Start Run Trace"), SLOT(ActionTraceRecordToggleRunTraceSlot()), "ActionToggleRunTrace");
mMenuBuilder->addMenu(makeMenu(DIcon("trace"), tr("Trace record")), [ = ](QMenu * menu)
QAction* traceCoverageDisable = makeAction(DIcon("close-all-tabs"), tr("Disable"), SLOT(traceCoverageDisableSlot()));
QAction* traceCoverageEnableBit = makeAction(DIcon("bit"), tr("Bit"), SLOT(traceCoverageBitSlot()));
QAction* traceCoverageEnableByte = makeAction(DIcon("byte"), tr("Byte"), SLOT(traceCoverageByteSlot()));
QAction* traceCoverageEnableWord = makeAction(DIcon("word"), tr("Word"), SLOT(traceCoverageWordSlot()));
QAction* traceCoverageToggleTraceRecording = makeShortcutAction(DIcon("control-record"), tr("Start trace recording"), SLOT(traceCoverageToggleTraceRecordingSlot()), "ActionToggleRunTrace");
mMenuBuilder->addMenu(makeMenu(DIcon("trace"), tr("Trace coverage")), [ = ](QMenu * menu)
{
if(DbgFunctions()->GetTraceRecordType(rvaToVa(getInitialSelection())) == TRACERECORDTYPE::TraceRecordNone)
{
menu->addAction(traceRecordEnableBit);
menu->addAction(traceRecordEnableByte);
menu->addAction(traceRecordEnableWord);
menu->addAction(traceCoverageEnableBit);
menu->addAction(traceCoverageEnableByte);
menu->addAction(traceCoverageEnableWord);
}
else
menu->addAction(traceRecordDisable);
menu->addAction(traceCoverageDisable);
menu->addSeparator();
if(DbgValFromString("tr.runtraceenabled()") == 1)
traceRecordToggleRunTrace->setText(tr("Stop Run Trace"));
if(TraceBrowser::isRecording())
{
traceCoverageToggleTraceRecording->setText(tr("Stop trace recording"));
traceCoverageToggleTraceRecording->setIcon(DIcon("control-stop"));
}
else
traceRecordToggleRunTrace->setText(tr("Start Run Trace"));
menu->addAction(traceRecordToggleRunTrace);
{
traceCoverageToggleTraceRecording->setText(tr("Start trace recording"));
traceCoverageToggleTraceRecording->setIcon(DIcon("control-record"));
}
menu->addAction(traceCoverageToggleTraceRecording);
return true;
});
@ -1876,7 +1883,7 @@ void CPUDisassembly::labelHelpSlot()
}
}
void CPUDisassembly::ActionTraceRecordBitSlot()
void CPUDisassembly::traceCoverageBitSlot()
{
if(!DbgIsDebugging())
return;
@ -1886,14 +1893,14 @@ void CPUDisassembly::ActionTraceRecordBitSlot()
{
if(!(DbgFunctions()->SetTraceRecordType(i, TRACERECORDTYPE::TraceRecordBitExec)))
{
GuiAddLogMessage(tr("Failed to set trace record.\n").toUtf8().constData());
GuiAddLogMessage(tr("Failed to enable trace coverage for page %1.\n").arg(ToPtrString(i)).toUtf8().constData());
break;
}
}
DbgCmdExec("traceexecute cip");
}
void CPUDisassembly::ActionTraceRecordByteSlot()
void CPUDisassembly::traceCoverageByteSlot()
{
if(!DbgIsDebugging())
return;
@ -1903,14 +1910,14 @@ void CPUDisassembly::ActionTraceRecordByteSlot()
{
if(!(DbgFunctions()->SetTraceRecordType(i, TRACERECORDTYPE::TraceRecordByteWithExecTypeAndCounter)))
{
GuiAddLogMessage(tr("Failed to set trace record.\n").toUtf8().constData());
GuiAddLogMessage(tr("Failed to enable trace coverage for page %1.\n").arg(ToPtrString(i)).toUtf8().constData());
break;
}
}
DbgCmdExec("traceexecute cip");
}
void CPUDisassembly::ActionTraceRecordWordSlot()
void CPUDisassembly::traceCoverageWordSlot()
{
if(!DbgIsDebugging())
return;
@ -1920,14 +1927,14 @@ void CPUDisassembly::ActionTraceRecordWordSlot()
{
if(!(DbgFunctions()->SetTraceRecordType(i, TRACERECORDTYPE::TraceRecordWordWithExecTypeAndCounter)))
{
GuiAddLogMessage(tr("Failed to set trace record.\n").toUtf8().constData());
GuiAddLogMessage(tr("Failed to enable trace coverage for page %1.\n").arg(ToPtrString(i)).toUtf8().constData());
break;
}
}
DbgCmdExec("traceexecute cip");
}
void CPUDisassembly::ActionTraceRecordDisableSlot()
void CPUDisassembly::traceCoverageDisableSlot()
{
if(!DbgIsDebugging())
return;
@ -1937,7 +1944,7 @@ void CPUDisassembly::ActionTraceRecordDisableSlot()
{
if(!(DbgFunctions()->SetTraceRecordType(i, TRACERECORDTYPE::TraceRecordNone)))
{
GuiAddLogMessage(tr("Failed to set trace record.\n").toUtf8().constData());
GuiAddLogMessage(tr("Failed to disable trace coverage for page %1.\n").arg(ToPtrString(i)).toUtf8().constData());
break;
}
}
@ -2045,31 +2052,7 @@ void CPUDisassembly::downloadCurrentSymbolsSlot()
DbgCmdExec(QString("symdownload \"%0\"").arg(module));
}
void CPUDisassembly::ActionTraceRecordToggleRunTraceSlot()
void CPUDisassembly::traceCoverageToggleTraceRecordingSlot()
{
if(!DbgIsDebugging())
return;
if(DbgValFromString("tr.runtraceenabled()") == 1)
DbgCmdExec("StopRunTrace");
else
{
QString defaultFileName;
char moduleName[MAX_MODULE_SIZE];
QDateTime currentTime = QDateTime::currentDateTime();
duint defaultModule = DbgValFromString("mod.main()");
if(DbgFunctions()->ModNameFromAddr(defaultModule, moduleName, false))
{
defaultFileName = QString::fromUtf8(moduleName);
}
defaultFileName += "-" + QLocale(QString(currentLocale)).toString(currentTime.date()) + " " + currentTime.time().toString("hh-mm-ss") + ArchValue(".trace32", ".trace64");
BrowseDialog browse(this, tr("Select stored file"), tr("Store run trace to the following file"),
tr("Run trace files (*.%1);;All files (*.*)").arg(ArchValue("trace32", "trace64")), QCoreApplication::applicationDirPath() + QDir::separator() + "db" + QDir::separator() + defaultFileName, true);
if(browse.exec() == QDialog::Accepted)
{
if(browse.path.contains(QChar('"')) || browse.path.contains(QChar('\'')))
SimpleErrorBox(this, tr("Error"), tr("File name contains invalid character."));
else
DbgCmdExec(QString("StartRunTrace \"%1\"").arg(browse.path));
}
}
TraceBrowser::toggleTraceRecording(this);
}

View File

@ -89,11 +89,11 @@ public slots:
void openSourceSlot();
void mnemonicHelpSlot();
void mnemonicBriefSlot();
void ActionTraceRecordBitSlot();
void ActionTraceRecordByteSlot();
void ActionTraceRecordWordSlot();
void ActionTraceRecordDisableSlot();
void ActionTraceRecordToggleRunTraceSlot();
void traceCoverageBitSlot();
void traceCoverageByteSlot();
void traceCoverageWordSlot();
void traceCoverageDisableSlot();
void traceCoverageToggleTraceRecordingSlot();
void displayWarningSlot(QString title, QString text);
void labelHelpSlot();
void analyzeSingleFunctionSlot();

View File

@ -320,7 +320,7 @@ MainWindow::MainWindow(QWidget* parent)
connect(ui->actionFunctions, SIGNAL(triggered()), this, SLOT(displayFunctions()));
connect(ui->actionCallStack, SIGNAL(triggered()), this, SLOT(displayCallstack()));
connect(ui->actionSEHChain, SIGNAL(triggered()), this, SLOT(displaySEHChain()));
connect(ui->actionTrace, SIGNAL(triggered()), this, SLOT(displayRunTrace()));
connect(ui->actionTrace, SIGNAL(triggered()), this, SLOT(displayTraceWidget()));
connect(ui->actionDonate, SIGNAL(triggered()), this, SLOT(donate()));
connect(ui->actionReportBug, SIGNAL(triggered()), this, SLOT(reportBug()));
connect(ui->actionBlog, SIGNAL(triggered()), this, SLOT(blog()));
@ -1104,22 +1104,22 @@ void MainWindow::setFocusToCommandBar()
void MainWindow::execTRBit()
{
mCpuWidget->getDisasmWidget()->ActionTraceRecordBitSlot();
mCpuWidget->getDisasmWidget()->traceCoverageBitSlot();
}
void MainWindow::execTRByte()
{
mCpuWidget->getDisasmWidget()->ActionTraceRecordByteSlot();
mCpuWidget->getDisasmWidget()->traceCoverageByteSlot();
}
void MainWindow::execTRWord()
{
mCpuWidget->getDisasmWidget()->ActionTraceRecordWordSlot();
mCpuWidget->getDisasmWidget()->traceCoverageWordSlot();
}
void MainWindow::execTRNone()
{
mCpuWidget->getDisasmWidget()->ActionTraceRecordDisableSlot();
mCpuWidget->getDisasmWidget()->traceCoverageDisableSlot();
}
void MainWindow::execTicnd()
@ -1813,7 +1813,7 @@ void MainWindow::displaySEHChain()
showQWidgetTab(mSEHChainView);
}
void MainWindow::displayRunTrace()
void MainWindow::displayTraceWidget()
{
showQWidgetTab(mTraceWidget);
}

View File

@ -90,7 +90,7 @@ public slots:
void displayThreadsWidget();
void displayVariables();
void displayGraphWidget();
void displayRunTrace();
void displayTraceWidget();
void openSettings();
void openAppearance();
void openCalculator();

View File

@ -204,7 +204,7 @@
</property>
<widget class="QMenu" name="menuTrace_record">
<property name="title">
<string>Trace record</string>
<string>Trace &amp;coverage</string>
</property>
<property name="icon">
<iconset theme="trace" resource="../../resource.qrc">
@ -1035,7 +1035,7 @@
<string>Bit</string>
</property>
<property name="statusTip">
<string>Enable trace record with 1 bit per byte to record whether the code has been executed.</string>
<string>Enable trace coverage with 1 bit (whether an instruction was executed or not)</string>
</property>
</action>
<action name="actionTRByte">
@ -1047,7 +1047,7 @@
<string>Byte</string>
</property>
<property name="statusTip">
<string>Enable trace record with 1 byte per byte to record how many times the code has been executed.</string>
<string>Enable trace coverage with 1 byte to record how many times an instruction has been executed.</string>
</property>
</action>
<action name="actionTRWord">
@ -1059,7 +1059,7 @@
<string>Word</string>
</property>
<property name="statusTip">
<string>Enable trace record with 2 bytes per byte to record how many times the code has been executed.</string>
<string>Enable trace coverage with 1 word to record how many times an instruction has been executed.</string>
</property>
</action>
<action name="actionTRTIBT">
@ -1068,10 +1068,10 @@
<normaloff>:/Default/icons/traceinto.png</normaloff>:/Default/icons/traceinto.png</iconset>
</property>
<property name="text">
<string>Trace into beyond trace record</string>
<string>Step into until reaching uncovered code</string>
</property>
<property name="statusTip">
<string>Trace into until the current instruction was not executed before. Equivalent command &quot;tibt&quot;</string>
<string>Step into until reaching an instruction that was not covered before. Equivalent command &quot;tibt&quot;</string>
</property>
</action>
<action name="actionTRTOBT">
@ -1080,10 +1080,10 @@
<normaloff>:/Default/icons/traceover.png</normaloff>:/Default/icons/traceover.png</iconset>
</property>
<property name="text">
<string>Trace over beyond trace record</string>
<string>Step over until reaching uncovered code</string>
</property>
<property name="statusTip">
<string>Trace over until the current instruction was not executed before. Equivalent command &quot;tobt&quot;</string>
<string>Step over until reaching an instruction that was not covered before. Equivalent command &quot;tobt&quot;</string>
</property>
</action>
<action name="actionTRTIIT">
@ -1092,10 +1092,10 @@
<normaloff>:/Default/icons/arrow-step-into.png</normaloff>:/Default/icons/arrow-step-into.png</iconset>
</property>
<property name="text">
<string>Trace into into trace record</string>
<string>Step into until reaching covered code</string>
</property>
<property name="statusTip">
<string>Trace into until the current instruction was executed before. Equivalent command &quot;tiit&quot;</string>
<string>Step into until reaching an instruction that has been covered before. Equivalent command &quot;tiit&quot;</string>
</property>
</action>
<action name="actionTRTOIT">
@ -1104,10 +1104,10 @@
<normaloff>:/Default/icons/arrow-step-over.png</normaloff>:/Default/icons/arrow-step-over.png</iconset>
</property>
<property name="text">
<string>Trace over into trace record</string>
<string>Step over until reaching covered code</string>
</property>
<property name="statusTip">
<string>Trace over until the current instruction was executed before. Equivalent command &quot;toit&quot;</string>
<string>Step over until reaching an instruction that has been covered before. Equivalent command &quot;toit&quot;</string>
</property>
</action>
<action name="actionTRNone">
@ -1116,10 +1116,10 @@
<normaloff>:/Default/icons/close-all-tabs.png</normaloff>:/Default/icons/close-all-tabs.png</iconset>
</property>
<property name="text">
<string>None</string>
<string>Disable</string>
</property>
<property name="statusTip">
<string>Disable trace record</string>
<string>Disable trace coverage</string>
</property>
</action>
<action name="actionRtu">

View File

@ -607,10 +607,10 @@ void MemoryMapView::dumpMemory()
end = base + size;
}
char modname[MAX_MODULE_SIZE] = "";
if(!DbgFunctions()->ModNameFromAddr(DbgEval("mod.main()"), modname, false))
*modname = '\0';
QString defaultFile = QString("%1/%2%3.bin").arg(QDir::currentPath(), *modname ? modname + QString("_") : "", getCellContent(getInitialSelection(), 0));
auto modname = mainModuleName();
if(!modname.isEmpty())
modname += '_';
QString defaultFile = QString("%1/%2%3.bin").arg(QDir::currentPath(), modname, getCellContent(getInitialSelection(), 0));
QString fileName = QFileDialog::getSaveFileName(this, tr("Save Memory Region"), defaultFile, tr("Binary files (*.bin);;All files (*.*)"));
if(fileName.length())
@ -622,11 +622,11 @@ void MemoryMapView::dumpMemory()
void MemoryMapView::loadMemory()
{
char modname[MAX_MODULE_SIZE] = "";
if(!DbgFunctions()->ModNameFromAddr(DbgEval("mod.main()"), modname, false))
*modname = '\0';
auto modname = mainModuleName();
if(!modname.isEmpty())
modname += '_';
auto addr = getCellContent(getInitialSelection(), 0);
QString defaultFile = QString("%1/%2%3.bin").arg(QDir::currentPath(), *modname ? modname + QString("_") : "", addr);
QString defaultFile = QString("%1/%2%3.bin").arg(QDir::currentPath(), modname, addr);
QString fileName = QFileDialog::getOpenFileName(this, tr("Load Memory Region"), defaultFile, tr("Binary files (*.bin);;All files (*.*)"));
if(fileName.length())

View File

@ -4,6 +4,7 @@
#include <QMessageBox>
#include "BrowseDialog.h"
#include "MiscUtil.h"
#include "Tracer/TraceBrowser.h"
SimpleTraceDialog::SimpleTraceDialog(QWidget* parent) :
QDialog(parent),
@ -52,6 +53,16 @@ void SimpleTraceDialog::on_btnOk_clicked()
if(msgyn.exec() == QMessageBox::No)
return;
}
if(ui->chkRecordTrace->isChecked())
{
if(!TraceBrowser::toggleTraceRecording(this))
{
ui->chkRecordTrace->setChecked(false);
SimpleWarningBox(this, tr("Error"), tr("Trace recording was requested, but not enabled."));
return;
}
ui->chkRecordTrace->setChecked(false);
}
auto logText = ui->editLogText->addHistoryClear();
auto logCondition = ui->editLogCondition->addHistoryClear();
if(!DbgCmdExecDirect(QString("TraceSetLog \"%1\", \"%2\"").arg(escapeText(logText), escapeText(logCondition)).toUtf8().constData()))
@ -89,9 +100,33 @@ void SimpleTraceDialog::on_btnOk_clicked()
void SimpleTraceDialog::on_btnLogFile_clicked()
{
BrowseDialog browse(this, tr("Trace log file"), tr("Enter the path to the log file."), tr("Log Files (*.txt *.log);;All Files (*.*)"), QCoreApplication::applicationDirPath(), true);
BrowseDialog browse(
this,
tr("Trace log file"),
tr("Enter the path to the log file."),
tr("Log Files (*.txt *.log);;All Files (*.*)"),
getDbPath(mainModuleName() + ".log", true),
true
);
if(browse.exec() == QDialog::Accepted)
mLogFile = browse.path;
else
mLogFile.clear();
}
int SimpleTraceDialog::exec()
{
if(TraceBrowser::isRecording())
{
ui->chkRecordTrace->setEnabled(false);
ui->chkRecordTrace->setChecked(true);
ui->chkRecordTrace->setToolTip(tr("Trace recording already started"));
}
else
{
ui->chkRecordTrace->setEnabled(true);
ui->chkRecordTrace->setChecked(false);
ui->chkRecordTrace->setToolTip("");
}
return QDialog::exec();
}

View File

@ -20,6 +20,9 @@ private slots:
void on_btnOk_clicked();
void on_btnLogFile_clicked();
public slots:
int exec() override;
private:
Ui::SimpleTraceDialog* ui;
QString mTraceCommand;

View File

@ -65,6 +65,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="chkRecordTrace">
<property name="text">
<string>&amp;Record trace</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnLogFile">
<property name="text">
@ -195,6 +202,7 @@
<tabstop>editCommandCondition</tabstop>
<tabstop>spinMaxTraceCount</tabstop>
<tabstop>editSwitchCondition</tabstop>
<tabstop>chkRecordTrace</tabstop>
<tabstop>btnLogFile</tabstop>
<tabstop>btnOk</tabstop>
<tabstop>btnCancel</tabstop>

View File

@ -2,6 +2,7 @@
#include "ui_SystemBreakpointScriptDialog.h"
#include "Bridge.h"
#include "Configuration.h"
#include "MiscUtil.h"
#include <QDirModel>
#include <QFile>
#include <QFileDialog>
@ -33,12 +34,7 @@ SystemBreakpointScriptDialog::SystemBreakpointScriptDialog(QWidget* parent) :
if(DbgIsDebugging())
{
char moduleName[MAX_MODULE_SIZE];
if(DbgFunctions()->ModNameFromAddr(DbgValFromString("mod.main()"), moduleName, true))
{
ui->groupBoxDebuggee->setTitle(tr("2. System breakpoint script for %1").arg(moduleName));
}
ui->groupBoxDebuggee->setTitle(tr("2. System breakpoint script for %1").arg(mainModuleName(true)));
ui->lineEditDebuggee->setText(DbgFunctions()->DbgGetDebuggeeInitScript());
}
else
@ -122,14 +118,7 @@ void SystemBreakpointScriptDialog::on_openDebuggee_clicked()
if(msgyn.exec() == QMessageBox::Yes)
{
// The new script is at db dir
QString defaultFileName;
char moduleName[MAX_MODULE_SIZE];
if(DbgFunctions()->ModNameFromAddr(DbgValFromString("mod.main()"), moduleName, false))
{
defaultFileName = QString::fromUtf8(moduleName);
}
defaultFileName = defaultFileName + ".autorun.txt";
defaultFileName = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + "db" + QDir::separator() + defaultFileName);
auto defaultFileName = getDbPath(mainModuleName() + ".autorun.txt");
// Create it
if(!QFile::exists(defaultFileName))
{

View File

@ -1,9 +1,9 @@
#include "Imports.h"
#include <QString>
void DbgCmdExec(const QString & cmd)
bool DbgCmdExec(const QString & cmd)
{
DbgCmdExec(cmd.toUtf8().constData());
return DbgCmdExec(cmd.toUtf8().constData());
}
bool DbgCmdExecDirect(const QString & cmd)

View File

@ -13,5 +13,5 @@
// Convenience overloads
class QString;
void DbgCmdExec(const QString & cmd);
bool DbgCmdExec(const QString & cmd);
bool DbgCmdExecDirect(const QString & cmd);

View File

@ -67,6 +67,41 @@ bool TraceBrowser::isFileOpened() const
return mTraceFile && mTraceFile->Progress() == 100 && mTraceFile->Length() > 0;
}
bool TraceBrowser::isRecording()
{
return DbgEval("tr.isrecording()") != 0;
}
bool TraceBrowser::toggleTraceRecording(QWidget* parent)
{
if(!DbgIsDebugging())
return false;
if(isRecording())
{
return DbgCmdExecDirect("StopTraceRecording");
}
else
{
auto extension = ArchValue(".trace32", ".trace64");
BrowseDialog browse(
parent,
tr("Start trace recording"),
tr("Trace recording file"),
tr("Trace recordings (*.%1);;All files (*.*)").arg(extension),
getDbPath(mainModuleName() + extension, true),
true
);
if(browse.exec() == QDialog::Accepted)
{
if(browse.path.contains(QChar('"')) || browse.path.contains(QChar('\'')))
SimpleErrorBox(parent, tr("Error"), tr("File name contains invalid character."));
else
return DbgCmdExecDirect(QString("StartTraceRecording \"%1\"").arg(browse.path));
}
}
return false;
}
QString TraceBrowser::getAddrText(dsint cur_addr, char label[MAX_LABEL_SIZE], bool getLabel)
{
QString addrText = "";
@ -784,8 +819,9 @@ void TraceBrowser::prepareData()
{
if(mTraceFile->Progress() == 100)
{
if(mTraceFile->Length() < getTableOffset() + viewables)
lines = mTraceFile->Length() - getTableOffset();
duint tableOffset = getTableOffset();
if(mTraceFile->Length() < tableOffset + viewables)
lines = mTraceFile->Length() - tableOffset;
else
lines = viewables;
}
@ -803,15 +839,21 @@ void TraceBrowser::setupRightClickContextMenu()
else
return mTraceFile->Registers(getInitialSelection()).regcontext.cip;
});
QAction* toggleRunTrace = makeShortcutAction(DIcon("trace"), tr("Start Run Trace"), SLOT(toggleRunTraceSlot()), "ActionToggleRunTrace");
mMenuBuilder->addAction(toggleRunTrace, [toggleRunTrace](QMenu*)
QAction* toggleTraceRecording = makeShortcutAction(DIcon("control-record"), tr("Start recording"), SLOT(toggleTraceRecordingSlot()), "ActionToggleRunTrace");
mMenuBuilder->addAction(toggleTraceRecording, [toggleTraceRecording](QMenu*)
{
if(!DbgIsDebugging())
return false;
if(DbgValFromString("tr.runtraceenabled()") == 1)
toggleRunTrace->setText(tr("Stop Run Trace"));
if(isRecording())
{
toggleTraceRecording->setText(tr("Stop recording"));
toggleTraceRecording->setIcon(DIcon("control-stop"));
}
else
toggleRunTrace->setText(tr("Start Run Trace"));
{
toggleTraceRecording->setText(tr("Start recording"));
toggleTraceRecording->setIcon(DIcon("control-record"));
}
return true;
});
auto mTraceFileIsNull = [this](QMenu*)
@ -830,11 +872,11 @@ void TraceBrowser::setupRightClickContextMenu()
else
return false;
});
mMenuBuilder->addAction(makeAction(DIcon("fatal-error"), tr("Close"), SLOT(closeFileSlot())), [this](QMenu*)
mMenuBuilder->addAction(makeAction(DIcon("close"), tr("Close recording"), SLOT(closeFileSlot())), [this](QMenu*)
{
return mTraceFile != nullptr;
});
mMenuBuilder->addAction(makeAction(DIcon("fatal-error"), tr("Close and delete"), SLOT(closeDeleteSlot())), [this](QMenu*)
mMenuBuilder->addAction(makeAction(DIcon("delete"), tr("Delete recording"), SLOT(closeDeleteSlot())), [this](QMenu*)
{
return mTraceFile != nullptr;
});
@ -1257,7 +1299,14 @@ void TraceBrowser::updateColors()
void TraceBrowser::openFileSlot()
{
BrowseDialog browse(this, tr("Open run trace file"), tr("Open trace file"), tr("Run trace files (*.%1);;All files (*.*)").arg(ArchValue("trace32", "trace64")), QApplication::applicationDirPath() + QDir::separator() + "db", false);
BrowseDialog browse(
this,
tr("Open trace recording"),
tr("Trace recording"),
tr("Trace recordings (*.%1);;All files (*.*)").arg(ArchValue("trace32", "trace64")),
getDbPath(),
false
);
if(browse.exec() != QDialog::Accepted)
return;
emit openSlot(browse.path);
@ -1276,39 +1325,15 @@ void TraceBrowser::openSlot(const QString & fileName)
mTraceFile->Open(fileName);
}
void TraceBrowser::toggleRunTraceSlot()
void TraceBrowser::toggleTraceRecordingSlot()
{
if(!DbgIsDebugging())
return;
if(DbgValFromString("tr.runtraceenabled()") == 1)
DbgCmdExec("StopRunTrace");
else
{
QString defaultFileName;
char moduleName[MAX_MODULE_SIZE];
QDateTime currentTime = QDateTime::currentDateTime();
duint defaultModule = DbgValFromString("mod.main()");
if(DbgFunctions()->ModNameFromAddr(defaultModule, moduleName, false))
{
defaultFileName = QString::fromUtf8(moduleName);
}
defaultFileName += "-" + QLocale(QString(currentLocale)).toString(currentTime.date()) + " " + currentTime.time().toString("hh-mm-ss") + ArchValue(".trace32", ".trace64");
BrowseDialog browse(this, tr("Select stored file"), tr("Store run trace to the following file"),
tr("Run trace files (*.%1);;All files (*.*)").arg(ArchValue("trace32", "trace64")), QCoreApplication::applicationDirPath() + QDir::separator() + "db" + QDir::separator() + defaultFileName, true);
if(browse.exec() == QDialog::Accepted)
{
if(browse.path.contains(QChar('"')) || browse.path.contains(QChar('\'')))
SimpleErrorBox(this, tr("Error"), tr("File name contains invalid character."));
else
DbgCmdExec(QString("StartRunTrace \"%1\"").arg(browse.path));
}
}
toggleTraceRecording(this);
}
void TraceBrowser::closeFileSlot()
{
if(DbgValFromString("tr.runtraceenabled()") == 1)
DbgCmdExec("StopRunTrace");
if(isRecording())
DbgCmdExecDirect("StopTraceRecording");
mTraceFile->Close();
delete mTraceFile;
mTraceFile = nullptr;
@ -1317,11 +1342,11 @@ void TraceBrowser::closeFileSlot()
void TraceBrowser::closeDeleteSlot()
{
QMessageBox msgbox(QMessageBox::Critical, tr("Close and delete"), tr("Are you really going to delete this file?"), QMessageBox::Yes | QMessageBox::Cancel, this);
QMessageBox msgbox(QMessageBox::Critical, tr("Delete recording"), tr("Are you sure you want to delete this recording?"), QMessageBox::Yes | QMessageBox::No, this);
if(msgbox.exec() == QMessageBox::Yes)
{
if(DbgValFromString("tr.runtraceenabled()") == 1)
DbgCmdExecDirect("StopRunTrace");
if(isRecording())
DbgCmdExecDirect("StopTraceRecording");
mTraceFile->Delete();
delete mTraceFile;
mTraceFile = nullptr;
@ -1333,7 +1358,7 @@ void TraceBrowser::parseFinishedSlot()
{
if(mTraceFile->isError())
{
SimpleErrorBox(this, tr("Error"), tr("Error when opening run trace file"));
SimpleErrorBox(this, tr("Error"), tr("Error when opening trace recording"));
delete mTraceFile;
mTraceFile = nullptr;
setRowCount(0);
@ -1829,7 +1854,7 @@ void TraceBrowser::updateSlot()
{
if(mTraceFile && mTraceFile->Progress() == 100) // && this->isVisible()
{
if(DbgValFromString("tr.runtraceenabled()") == 1)
if(isRecording())
{
mTraceFile->purgeLastPage();
setRowCount(mTraceFile->Length());

View File

@ -31,6 +31,9 @@ public:
bool isFileOpened() const;
TraceFileReader* getTraceFile() { return mTraceFile; }
static bool isRecording();
static bool toggleTraceRecording(QWidget* parent);
private:
enum TableColumnIndex
{
@ -151,7 +154,7 @@ signals:
public slots:
void openFileSlot();
void openSlot(const QString & fileName);
void toggleRunTraceSlot();
void toggleTraceRecordingSlot();
void closeFileSlot();
void closeDeleteSlot();
void parseFinishedSlot();

View File

@ -448,16 +448,16 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("DebugCommand", Shortcut({tr("Debug"), tr("Command")}, "Ctrl+Return", true));
defaultShortcuts.insert("DebugTraceIntoConditional", Shortcut({tr("Debug"), tr("Trace into...")}, "Ctrl+Alt+F7", true));
defaultShortcuts.insert("DebugTraceOverConditional", Shortcut({tr("Debug"), tr("Trace over...")}, "Ctrl+Alt+F8", true));
defaultShortcuts.insert("DebugEnableTraceRecordBit", Shortcut({tr("Debug"), tr("Trace Record"), tr("Bit")}, "", true));
defaultShortcuts.insert("DebugTraceRecordNone", Shortcut({tr("Debug"), tr("Trace Record"), tr("None")}, "", true));
defaultShortcuts.insert("DebugEnableTraceRecordBit", Shortcut({tr("Debug"), tr("Trace coverage"), tr("Bit")}, "", true));
defaultShortcuts.insert("DebugTraceRecordNone", Shortcut({tr("Debug"), tr("Trace coverage"), tr("None")}, "", true));
defaultShortcuts.insert("DebugInstrUndo", Shortcut({tr("Debug"), tr("Undo instruction")}, "Alt+U", true));
defaultShortcuts.insert("DebugAnimateInto", Shortcut({tr("Debug"), tr("Animate into")}, "Ctrl+F7", true));
defaultShortcuts.insert("DebugAnimateOver", Shortcut({tr("Debug"), tr("Animate over")}, "Ctrl+F8", true));
defaultShortcuts.insert("DebugAnimateCommand", Shortcut({tr("Debug"), tr("Animate command")}, "", true));
defaultShortcuts.insert("DebugTraceIntoIntoTracerecord", Shortcut({tr("Debug"), tr("Trace into into trace record")}, "", true));
defaultShortcuts.insert("DebugTraceOverIntoTracerecord", Shortcut({tr("Debug"), tr("Trace over into trace record")}, "", true));
defaultShortcuts.insert("DebugTraceIntoBeyondTracerecord", Shortcut({tr("Debug"), tr("Trace into beyond trace record")}, "", true));
defaultShortcuts.insert("DebugTraceOverBeyondTracerecord", Shortcut({tr("Debug"), tr("Trace over beyond trace record")}, "", true));
defaultShortcuts.insert("DebugTraceIntoIntoTracerecord", Shortcut({tr("Debug"), tr("Step into until reaching uncovered code")}, "", true));
defaultShortcuts.insert("DebugTraceOverIntoTracerecord", Shortcut({tr("Debug"), tr("Step over until reaching uncovered code")}, "", true));
defaultShortcuts.insert("DebugTraceIntoBeyondTracerecord", Shortcut({tr("Debug"), tr("Step into until reaching covered code")}, "", true));
defaultShortcuts.insert("DebugTraceOverBeyondTracerecord", Shortcut({tr("Debug"), tr("Step over until reaching covered code")}, "", true));
defaultShortcuts.insert("PluginsScylla", Shortcut({tr("Plugins"), tr("Scylla")}, "Ctrl+I", true));
@ -623,7 +623,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("ActionModifyValue", Shortcut({tr("Actions"), tr("Modify value")}, "Space"));
defaultShortcuts.insert("ActionWatchDwordQword", Shortcut({tr("Actions"), tr("Watch DWORD/QWORD")}));
defaultShortcuts.insert("ActionCopyFileOffset", Shortcut({tr("Actions"), tr("Copy File Offset")}));
defaultShortcuts.insert("ActionToggleRunTrace", Shortcut({tr("Actions"), tr("Start or Stop Run Trace")}));
defaultShortcuts.insert("ActionToggleRunTrace", Shortcut({tr("Actions"), tr("Start/Stop trace recording")}));
defaultShortcuts.insert("ActionCopyCroppedTable", Shortcut({tr("Actions"), tr("Copy -> Cropped Table")}));
defaultShortcuts.insert("ActionCopyTable", Shortcut({tr("Actions"), tr("Copy -> Table")}));

View File

@ -176,7 +176,14 @@ QIcon getFileIcon(QString file)
//Export table in CSV. TODO: Display a dialog where the user choose what column to export and in which encoding
bool ExportCSV(dsint rows, dsint columns, std::vector<QString> headers, std::function<QString(dsint, dsint)> getCellContent)
{
BrowseDialog browse(nullptr, QApplication::translate("ExportCSV", "Export data in CSV format"), QApplication::translate("ExportCSV", "Enter the CSV file name to export"), QApplication::translate("ExportCSV", "CSV files (*.csv);;All files (*.*)"), QApplication::applicationDirPath() + QDir::separator() + "db", true);
BrowseDialog browse(
nullptr,
QApplication::translate("ExportCSV", "Export data in CSV format"),
QApplication::translate("ExportCSV", "Enter the CSV file name to export"),
QApplication::translate("ExportCSV", "CSV files (*.csv);;All files (*.*)"),
getDbPath("export.csv", true),
true
);
browse.setWindowIcon(DIcon("database-export"));
if(browse.exec() == QDialog::Accepted)
{
@ -342,3 +349,44 @@ QIcon DIconHelper(QString name)
}
return QIcon::fromTheme(name);
}
QString getDbPath(const QString & filename, bool addDateTimeSuffix)
{
auto path = QString("%1/db").arg(QCoreApplication::applicationDirPath());
if(!filename.isEmpty())
{
path += '/';
path += filename;
// Add a date suffix before the extension
if(addDateTimeSuffix)
{
auto extensionIdx = path.lastIndexOf('.');
if(extensionIdx == -1)
{
extensionIdx = path.length();
}
auto now = QDateTime::currentDateTime();
auto suffix = QString().sprintf("-%04d%02d%02d-%02d%02d%02d",
now.date().year(),
now.date().month(),
now.date().day(),
now.time().hour(),
now.time().minute(),
now.time().second()
);
path.insert(extensionIdx, suffix);
}
}
return QDir::toNativeSeparators(path);
}
QString mainModuleName(bool extension)
{
auto base = DbgEval("mod.main()");
char name[MAX_MODULE_SIZE] = "";
if(base && DbgFunctions()->ModNameFromAddr(base, name, extension))
{
return name;
}
return QString();
}

View File

@ -22,5 +22,7 @@ bool isEaster();
bool isSeasonal();
QIcon getFileIcon(QString file);
QIcon DIconHelper(QString name);
QString getDbPath(const QString & filename = QString(), bool addDateTimeSuffix = false);
QString mainModuleName(bool extension = false);
#define DIcon(name) [](QString arg) { static QIcon icon(DIconHelper(std::move(arg))); return icon; }(name)

BIN
src/gui/icons/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

BIN
src/gui/icons/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

View File

@ -316,6 +316,9 @@
<file>icons/crash_dump.png</file>
<file>icons/exclamation.png</file>
<file>index.theme</file>
<file>icons/control-record.png</file>
<file>icons/delete.png</file>
<file>icons/close.png</file>
</qresource>
<qresource prefix="/css">
<file>default.css</file>