1
0
Fork 0

Implement basic support for per-breakpoint log files

This commit is contained in:
Duncan Ogilvie 2024-03-02 01:31:23 +01:00
parent 1342a9f449
commit db264d18d5
6 changed files with 146 additions and 1 deletions

View File

@ -510,6 +510,21 @@ bool BpSetCommandCondition(duint Address, BP_TYPE Type, const char* Condition)
return true;
}
bool BpSetLogFile(duint Address, BP_TYPE Type, const char* LogFile)
{
ASSERT_DEBUGGING("Command function call");
EXCLUSIVE_ACQUIRE(LockBreakpoints);
// Set breakpoint hit command
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
if(!bpInfo)
return false;
bpInfo->logFile = LogFile;
return true;
}
bool BpSetFastResume(duint Address, BP_TYPE Type, bool fastResume)
{
ASSERT_DEBUGGING("Command function call");
@ -843,6 +858,7 @@ void BpCacheSave(JSON Root)
json_object_set_new(jsonObj, "logCondition", json_string(breakpoint.logCondition));
json_object_set_new(jsonObj, "commandText", json_string(breakpoint.commandText));
json_object_set_new(jsonObj, "commandCondition", json_string(breakpoint.commandCondition));
json_object_set_new(jsonObj, "logFile", json_string(breakpoint.logFile));
json_object_set_new(jsonObj, "fastResume", json_boolean(breakpoint.fastResume));
json_object_set_new(jsonObj, "silent", json_boolean(breakpoint.silent));
json_array_append_new(jsonBreakpoints, jsonObj);
@ -908,6 +924,7 @@ void BpCacheLoad(JSON Root, bool migrateCommandCondition)
loadStringValue(value, breakpoint.logCondition, "logCondition");
loadStringValue(value, breakpoint.commandText, "commandText");
loadStringValue(value, breakpoint.commandCondition, "commandCondition");
loadStringValue(value, breakpoint.logFile, "logFile");
// On 2023-06-10 the default of the command condition was changed from $breakpointcondition to 1
// If we detect an older database, try to preserve the old behavior.

View File

@ -38,6 +38,7 @@ struct BREAKPOINT
std::string logCondition; // condition to log
std::string commandText; // script command to execute.
std::string commandCondition; // condition to execute the command
std::string logFile; // file path to log to
uint32 hitcount; // hit counter
bool fastResume; // if true, debugger resumes without any GUI/Script/Plugin interaction.
duint memsize; // memory breakpoint size (not implemented)
@ -62,6 +63,7 @@ bool BpSetLogText(duint Address, BP_TYPE Type, const char* Log);
bool BpSetLogCondition(duint Address, BP_TYPE Type, const char* Condition);
bool BpSetCommandText(duint Address, BP_TYPE Type, const char* Cmd);
bool BpSetCommandCondition(duint Address, BP_TYPE Type, const char* Condition);
bool BpSetLogFile(duint Address, BP_TYPE Type, const char* LogFile);
bool BpSetFastResume(duint Address, BP_TYPE Type, bool fastResume);
bool BpSetSingleshoot(duint Address, BP_TYPE Type, bool singleshoot);
bool BpEnumAll(BPENUMCALLBACK EnumCallback, const char* Module, duint base = 0);

View File

@ -59,6 +59,11 @@ static bool cbDebugSetBPXCommandConditionCommon(BP_TYPE Type, int argc, char* ar
return cbDebugSetBPXTextCommon(Type, argc, argv, String(GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "command condition"))), BpSetCommandCondition);
}
static bool cbDebugSetBPXLogFileCommon(BP_TYPE Type, int argc, char* argv[])
{
return cbDebugSetBPXTextCommon(Type, argc, argv, String(GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "log file"))), BpSetLogFile);
}
static bool cbDebugSetBPXFastResumeCommon(BP_TYPE Type, int argc, char* argv[])
{
BREAKPOINT bp;
@ -208,6 +213,11 @@ bool cbDebugSetBPXCommandCondition(int argc, char* argv[])
return cbDebugSetBPXCommandConditionCommon(BPNORMAL, argc, argv);
}
bool cbDebugSetBPXLogFile(int argc, char* argv[])
{
return cbDebugSetBPXLogFileCommon(BPNORMAL, argc, argv);
}
bool cbDebugSetBPXFastResume(int argc, char* argv[])
{
return cbDebugSetBPXFastResumeCommon(BPNORMAL, argc, argv);
@ -263,6 +273,11 @@ bool cbDebugSetBPXHardwareCommandCondition(int argc, char* argv[])
return cbDebugSetBPXCommandConditionCommon(BPHARDWARE, argc, argv);
}
bool cbDebugSetBPXHardwareLogFile(int argc, char* argv[])
{
return cbDebugSetBPXLogFileCommon(BPHARDWARE, argc, argv);
}
bool cbDebugSetBPXHardwareFastResume(int argc, char* argv[])
{
return cbDebugSetBPXFastResumeCommon(BPHARDWARE, argc, argv);
@ -318,6 +333,11 @@ bool cbDebugSetBPXMemoryCommandCondition(int argc, char* argv[])
return cbDebugSetBPXCommandConditionCommon(BPMEMORY, argc, argv);
}
bool cbDebugSetBPXMemoryLogFile(int argc, char* argv[])
{
return cbDebugSetBPXLogFileCommon(BPMEMORY, argc, argv);
}
bool cbDebugSetBPXMemoryFastResume(int argc, char* argv[])
{
return cbDebugSetBPXFastResumeCommon(BPMEMORY, argc, argv);
@ -373,6 +393,11 @@ bool cbDebugSetBPXDLLCommandCondition(int argc, char* argv[])
return cbDebugSetBPXCommandConditionCommon(BPDLL, argc, argv);
}
bool cbDebugSetBPXDLLLogFile(int argc, char* argv[])
{
return cbDebugSetBPXLogFileCommon(BPDLL, argc, argv);
}
bool cbDebugSetBPXDLLFastResume(int argc, char* argv[])
{
return cbDebugSetBPXFastResumeCommon(BPDLL, argc, argv);
@ -428,6 +453,11 @@ bool cbDebugSetBPXExceptionCommandCondition(int argc, char* argv[])
return cbDebugSetBPXCommandConditionCommon(BPEXCEPTION, argc, argv);
}
bool cbDebugSetBPXExceptionLogFile(int argc, char* argv[])
{
return cbDebugSetBPXLogFileCommon(BPEXCEPTION, argc, argv);
}
bool cbDebugSetBPXExceptionFastResume(int argc, char* argv[])
{
return cbDebugSetBPXFastResumeCommon(BPEXCEPTION, argc, argv);

View File

@ -8,6 +8,7 @@ bool cbDebugSetBPXLog(int argc, char* argv[]);
bool cbDebugSetBPXLogCondition(int argc, char* argv[]);
bool cbDebugSetBPXCommand(int argc, char* argv[]);
bool cbDebugSetBPXCommandCondition(int argc, char* argv[]);
bool cbDebugSetBPXLogFile(int argc, char* argv[]);
bool cbDebugSetBPXFastResume(int argc, char* argv[]);
bool cbDebugSetBPXSingleshoot(int argc, char* argv[]);
bool cbDebugSetBPXSilent(int argc, char* argv[]);
@ -20,6 +21,7 @@ bool cbDebugSetBPXHardwareLog(int argc, char* argv[]);
bool cbDebugSetBPXHardwareLogCondition(int argc, char* argv[]);
bool cbDebugSetBPXHardwareCommand(int argc, char* argv[]);
bool cbDebugSetBPXHardwareCommandCondition(int argc, char* argv[]);
bool cbDebugSetBPXHardwareLogFile(int argc, char* argv[]);
bool cbDebugSetBPXHardwareFastResume(int argc, char* argv[]);
bool cbDebugSetBPXHardwareSingleshoot(int argc, char* argv[]);
bool cbDebugSetBPXHardwareSilent(int argc, char* argv[]);
@ -32,6 +34,7 @@ bool cbDebugSetBPXMemoryLog(int argc, char* argv[]);
bool cbDebugSetBPXMemoryLogCondition(int argc, char* argv[]);
bool cbDebugSetBPXMemoryCommand(int argc, char* argv[]);
bool cbDebugSetBPXMemoryCommandCondition(int argc, char* argv[]);
bool cbDebugSetBPXMemoryLogFile(int argc, char* argv[]);
bool cbDebugSetBPXMemoryFastResume(int argc, char* argv[]);
bool cbDebugSetBPXMemorySingleshoot(int argc, char* argv[]);
bool cbDebugSetBPXMemorySilent(int argc, char* argv[]);
@ -44,6 +47,7 @@ bool cbDebugSetBPXDLLLog(int argc, char* argv[]);
bool cbDebugSetBPXDLLLogCondition(int argc, char* argv[]);
bool cbDebugSetBPXDLLCommand(int argc, char* argv[]);
bool cbDebugSetBPXDLLCommandCondition(int argc, char* argv[]);
bool cbDebugSetBPXDLLLogFile(int argc, char* argv[]);
bool cbDebugSetBPXDLLFastResume(int argc, char* argv[]);
bool cbDebugSetBPXDLLSingleshoot(int argc, char* argv[]);
bool cbDebugSetBPXDLLSilent(int argc, char* argv[]);
@ -56,6 +60,7 @@ bool cbDebugSetBPXExceptionLog(int argc, char* argv[]);
bool cbDebugSetBPXExceptionLogCondition(int argc, char* argv[]);
bool cbDebugSetBPXExceptionCommand(int argc, char* argv[]);
bool cbDebugSetBPXExceptionCommandCondition(int argc, char* argv[]);
bool cbDebugSetBPXExceptionLogFile(int argc, char* argv[]);
bool cbDebugSetBPXExceptionFastResume(int argc, char* argv[]);
bool cbDebugSetBPXExceptionSingleshoot(int argc, char* argv[]);
bool cbDebugSetBPXExceptionSilent(int argc, char* argv[]);

View File

@ -102,6 +102,44 @@ static TITANCBSTEP gStepIntoPartyCallback;
HANDLE hDebugLoopThread = nullptr;
DWORD dwDebugFlags = 0;
struct BreakpointLogFile
{
HANDLE hFile = INVALID_HANDLE_VALUE;
bool needsFlush = false;
};
static std::unordered_map<std::string, BreakpointLogFile> gBreakpointLogFiles;
static BreakpointLogFile & dbgOpenBreakpointLogFile(const std::string & logFile)
{
// TODO: convert filename to lower case?
auto itr = gBreakpointLogFiles.find(logFile);
if(itr != gBreakpointLogFiles.end())
return itr->second;
auto hFile = CreateFileW(
StringUtils::Utf8ToUtf16(logFile).c_str(),
FILE_APPEND_DATA | FILE_GENERIC_READ, FILE_SHARE_READ,
nullptr,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
nullptr
);
if(hFile != INVALID_HANDLE_VALUE)
{
SetFilePointer(hFile, 0, nullptr, FILE_END);
BreakpointLogFile newFile;
newFile.hFile = hFile;
return gBreakpointLogFiles.emplace(logFile, newFile).first->second;
}
else
{
static BreakpointLogFile invalidLogFile;
return invalidLogFile;
}
}
static duint dbgcleartracestate()
{
auto steps = traceState.StepCount();
@ -279,6 +317,15 @@ void cbDebuggerPaused()
}
// Watchdog
cbCheckWatchdog(0, nullptr);
// Flush breakpoint logs
for(auto & itr : gBreakpointLogFiles)
{
if(itr.second.needsFlush)
{
FlushFileBuffers(itr.second.hFile);
itr.second.needsFlush = false;
}
}
}
void dbginit()
@ -949,9 +996,34 @@ static void cbGenericBreakpoint(BP_TYPE bptype, const void* ExceptionAddress = n
// Update breakpoint view
DebugUpdateBreakpointsViewAsync();
DWORD logFileError = ERROR_SUCCESS;
if(!bp.logText.empty() && logCondition == 1) //log
{
dprintf_untranslated("%s\n", stringformatinline(bp.logText).c_str());
auto formattedText = stringformatinline(bp.logText);
if(!bp.logFile.empty())
{
auto logFile = dbgOpenBreakpointLogFile(bp.logFile);
if(logFile.hFile == INVALID_HANDLE_VALUE)
{
// Pause and display the error
breakCondition = 1;
logFileError = GetLastError();
// Show the log in the regular log tab
dprintf_untranslated("%s\n", formattedText.c_str());
}
else
{
formattedText += "\n";
DWORD written = 0;
WriteFile(logFile.hFile, formattedText.c_str(), (DWORD)formattedText.length(), &written, nullptr);
logFile.needsFlush = true;
}
}
else
{
dprintf_untranslated("%s\n", formattedText.c_str());
}
}
if(!bp.commandText.empty() && commandCondition) //command
{
@ -980,6 +1052,13 @@ static void cbGenericBreakpoint(BP_TYPE bptype, const void* ExceptionAddress = n
else //resume immediately
unlock(WAITID_RUN);
// Make sure the log file error is displayed last
if(logFileError != ERROR_SUCCESS)
{
String error = stringformatinline(StringUtils::sprintf("{winerror@%x}", GetLastError()));
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to open breakpoint log: %s (%s)\n"), bp.logFile.c_str(), error.c_str());
}
//wait until the user resumes
wait(WAITID_RUN);
}
@ -2955,6 +3034,13 @@ static void debugLoopFunction(INIT_STRUCT* init)
DeleteFileW(gDllLoader.c_str());
gDllLoader.clear();
}
// Close the breakpoint log files
for(const auto & itr : gBreakpointLogFiles)
{
CloseHandle(itr.second.hFile);
}
gBreakpointLogFiles.clear();
}
void dbgsetdebuggeeinitscript(const char* fileName)

View File

@ -182,6 +182,7 @@ static void registercommands()
dbgcmdnew("SetBreakpointLogCondition,bplogcondition", cbDebugSetBPXLogCondition, true); //set breakpoint logCondition
dbgcmdnew("SetBreakpointCommand", cbDebugSetBPXCommand, true); //set breakpoint command on hit
dbgcmdnew("SetBreakpointCommandCondition", cbDebugSetBPXCommandCondition, true); //set breakpoint commandCondition
dbgcmdnew("SetBreakpointLogFile", cbDebugSetBPXLogFile, true); //set breakpoint logFile
dbgcmdnew("SetBreakpointFastResume", cbDebugSetBPXFastResume, true); //set breakpoint fast resume
dbgcmdnew("SetBreakpointSingleshoot", cbDebugSetBPXSingleshoot, true); //set breakpoint singleshoot
dbgcmdnew("SetBreakpointSilent", cbDebugSetBPXSilent, true); //set breakpoint fast resume
@ -194,6 +195,7 @@ static void registercommands()
dbgcmdnew("SetHardwareBreakpointLogCondition,bphwlogcondition", cbDebugSetBPXHardwareLogCondition, true); //set breakpoint logText
dbgcmdnew("SetHardwareBreakpointCommand", cbDebugSetBPXHardwareCommand, true); //set breakpoint command on hit
dbgcmdnew("SetHardwareBreakpointCommandCondition", cbDebugSetBPXHardwareCommandCondition, true); //set breakpoint commandCondition
dbgcmdnew("SetHardwareBreakpointLogFile", cbDebugSetBPXHardwareLogFile, true); //set breakpoint logFile
dbgcmdnew("SetHardwareBreakpointFastResume", cbDebugSetBPXHardwareFastResume, true); //set breakpoint fast resume
dbgcmdnew("SetHardwareBreakpointSingleshoot", cbDebugSetBPXHardwareSingleshoot, true); //set breakpoint singleshoot
dbgcmdnew("SetHardwareBreakpointSilent", cbDebugSetBPXHardwareSilent, true); //set breakpoint fast resume
@ -206,6 +208,7 @@ static void registercommands()
dbgcmdnew("SetMemoryBreakpointLogCondition,bpmlogcondition", cbDebugSetBPXMemoryLogCondition, true); //set breakpoint logCondition
dbgcmdnew("SetMemoryBreakpointCommand", cbDebugSetBPXMemoryCommand, true); //set breakpoint command on hit
dbgcmdnew("SetMemoryBreakpointCommandCondition", cbDebugSetBPXMemoryCommandCondition, true); //set breakpoint commandCondition
dbgcmdnew("SetMemoryBreakpointLogFile", cbDebugSetBPXMemoryLogFile, true); //set breakpoint logFile
dbgcmdnew("SetMemoryBreakpointFastResume", cbDebugSetBPXMemoryFastResume, true); //set breakpoint fast resume
dbgcmdnew("SetMemoryBreakpointSingleshoot", cbDebugSetBPXMemorySingleshoot, true); //set breakpoint singleshoot
dbgcmdnew("SetMemoryBreakpointSilent", cbDebugSetBPXMemorySilent, true); //set breakpoint fast resume
@ -218,6 +221,7 @@ static void registercommands()
dbgcmdnew("SetLibrarianBreakpointLogCondition", cbDebugSetBPXDLLLogCondition, true); //set breakpoint logCondition
dbgcmdnew("SetLibrarianBreakpointCommand", cbDebugSetBPXDLLCommand, true); //set breakpoint command on hit
dbgcmdnew("SetLibrarianBreakpointCommandCondition", cbDebugSetBPXDLLCommandCondition, true); //set breakpoint commandCondition
dbgcmdnew("SetLibrarianBreakpointLogFile", cbDebugSetBPXDLLLogFile, true); //set breakpoint logFile
dbgcmdnew("SetLibrarianBreakpointFastResume", cbDebugSetBPXDLLFastResume, true); //set breakpoint fast resume
dbgcmdnew("SetLibrarianBreakpointSingleshoot", cbDebugSetBPXDLLSingleshoot, true); //set breakpoint singleshoot
dbgcmdnew("SetLibrarianBreakpointSilent", cbDebugSetBPXDLLSilent, true); //set breakpoint fast resume
@ -230,6 +234,7 @@ static void registercommands()
dbgcmdnew("SetExceptionBreakpointLogCondition", cbDebugSetBPXExceptionLogCondition, true); //set breakpoint logCondition
dbgcmdnew("SetExceptionBreakpointCommand", cbDebugSetBPXExceptionCommand, true); //set breakpoint command on hit
dbgcmdnew("SetExceptionBreakpointCommandCondition", cbDebugSetBPXExceptionCommandCondition, true); //set breakpoint commandCondition
dbgcmdnew("SetExceptionBreakpointLogFile", cbDebugSetBPXExceptionLogFile, true); //set breakpoint logFile
dbgcmdnew("SetExceptionBreakpointFastResume", cbDebugSetBPXExceptionFastResume, true); //set breakpoint fast resume
dbgcmdnew("SetExceptionBreakpointSingleshoot", cbDebugSetBPXExceptionSingleshoot, true); //set breakpoint singleshoot
dbgcmdnew("SetExceptionBreakpointSilent", cbDebugSetBPXExceptionSilent, true); //set breakpoint fast resume