diff --git a/api/debuggerapi.h b/api/debuggerapi.h index ebfdde5a..68b85905 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -519,6 +519,23 @@ namespace BinaryNinjaDebuggerAPI { TTDMemoryEvent() : threadId(0), uniqueThreadId(0), accessType(TTDMemoryRead), address(0), size(0), memoryAddress(0), instructionAddress(0), value(0) {} }; + struct TTDPositionRangeIndexedMemoryEvent{ + TTDPosition position; // Position of the memory event + uint32_t threadId; // Thread ID that performed the access + uint32_t uniqueThreadId; // Unique thread ID that performed the access + uint64_t address; // Memory address accessed + uint64_t instructionAddress; // Instruction pointer at time of access + uint64_t size; // Size of memory access + TTDMemoryAccessType accessType; // Type of memory access (parsed from object) + uint64_t value; // Value that was read/written/executed + uint8_t data[8]; // The next 8 bytes of data at the memory address + + TTDPositionRangeIndexedMemoryEvent() : threadId(0), uniqueThreadId(0), address(0), size(0), accessType(TTDMemoryRead), value(0) + { + memset(data, 0, sizeof(data)); + } + }; + struct TTDCallEvent { std::string eventType; // Event type (always "Call" for TTD.Calls objects) @@ -799,6 +816,7 @@ namespace BinaryNinjaDebuggerAPI { // TTD Memory Analysis Methods std::vector GetTTDMemoryAccessForAddress(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead); + std::vector GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime); std::vector GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0); std::vector GetTTDEvents(TTDEventType eventType); std::vector GetAllTTDEvents(); @@ -807,7 +825,7 @@ namespace BinaryNinjaDebuggerAPI { // TTD Code Coverage Analysis Methods bool IsInstructionExecuted(uint64_t address); - bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress); + bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime); size_t GetExecutedInstructionCount() const; bool SaveCodeCoverageToFile(const std::string& filePath) const; bool LoadCodeCoverageFromFile(const std::string& filePath); diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index d4c5173b..5b708896 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -1078,6 +1078,43 @@ bool DebuggerController::IsTTD() return BNDebuggerIsTTD(m_object); } +std::vector DebuggerController::GetTTDMemoryAccessForPositionRange(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime) +{ + std::vector result; + + BNDebuggerTTDMemoryAccessType type = static_cast(accessType); + BNDebuggerTTDPosition bnStartTime = {startTime.sequence, startTime.step}; + BNDebuggerTTDPosition bnEndTime = {endTime.sequence, endTime.step}; + + size_t count = 0; + BNDebuggerTTDPositionRangeIndexedMemoryEvent* events = BNDebuggerGetTTDMemoryAccessForPositionRange(m_object, address, endAddress, type, bnStartTime, bnEndTime, &count); + + if (events && count > 0) + { + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + TTDPositionRangeIndexedMemoryEvent event; + event.threadId = events[i].threadId; + event.uniqueThreadId = events[i].uniqueThreadId; + event.position.sequence = events[i].position.sequence; + event.position.step = events[i].position.step; + event.accessType = static_cast(events[i].accessType); + event.address = events[i].address; + event.size = events[i].size; + event.instructionAddress = events[i].instructionAddress; + event.value = events[i].value; + for (size_t j = 0; j < 8; j++) + { + event.data[j] = events[i].data[j]; + } + result.push_back(event); + } + BNDebuggerFreeTTDPositionRangeIndexedMemoryEvents(events, count); + } + + return result; +} std::vector DebuggerController::GetTTDMemoryAccessForAddress(uint64_t address, uint64_t endAddress, TTDMemoryAccessType accessType) { @@ -1325,9 +1362,14 @@ bool DebuggerController::IsInstructionExecuted(uint64_t address) } -bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress) +bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime) { - return BNDebuggerRunCodeCoverageAnalysisRange(m_object, startAddress, endAddress); + BNDebuggerTTDPosition startPos, endPos; + startPos.sequence = startTime.sequence; + startPos.step = startTime.step; + endPos.sequence = endTime.sequence; + endPos.step = endTime.step; + return BNDebuggerRunCodeCoverageAnalysisRange(m_object, startAddress, endAddress, startPos, endPos); } diff --git a/api/ffi.h b/api/ffi.h index ebc23813..f467d256 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -335,6 +335,19 @@ extern "C" BNDebuggerTTDMemoryAccessType accessType; } BNDebuggerTTDMemoryEvent; + typedef struct BNDebuggerTTDPositionRangeIndexedMemoryEvent + { + BNDebuggerTTDPosition position; + uint32_t threadId; + uint32_t uniqueThreadId; + uint64_t address; + uint64_t instructionAddress; + uint64_t size; + BNDebuggerTTDMemoryAccessType accessType; + uint64_t value; + uint8_t data[8]; + } BNDebuggerTTDPositionRangeIndexedMemoryEvent; + typedef struct BNDebuggerTTDCallEvent { char* eventType; // Event type (always "Call" for TTD.Calls objects) @@ -665,6 +678,9 @@ extern "C" // TTD Memory Analysis Functions DEBUGGER_FFI_API BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForAddress(BNDebuggerController* controller, uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType, size_t* count); + DEBUGGER_FFI_API BNDebuggerTTDPositionRangeIndexedMemoryEvent* BNDebuggerGetTTDMemoryAccessForPositionRange(BNDebuggerController* controller, + uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType ,BNDebuggerTTDPosition startPosition, BNDebuggerTTDPosition endPosition, + size_t* count); DEBUGGER_FFI_API BNDebuggerTTDCallEvent* BNDebuggerGetTTDCallsForSymbols(BNDebuggerController* controller, const char* symbols, uint64_t startReturnAddress, uint64_t endReturnAddress, size_t* count); DEBUGGER_FFI_API BNDebuggerTTDEvent* BNDebuggerGetTTDEvents(BNDebuggerController* controller, @@ -673,12 +689,13 @@ extern "C" DEBUGGER_FFI_API BNDebuggerTTDPosition BNDebuggerGetCurrentTTDPosition(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerSetTTDPosition(BNDebuggerController* controller, BNDebuggerTTDPosition position); DEBUGGER_FFI_API void BNDebuggerFreeTTDMemoryEvents(BNDebuggerTTDMemoryEvent* events, size_t count); + DEBUGGER_FFI_API void BNDebuggerFreeTTDPositionRangeIndexedMemoryEvents(BNDebuggerTTDPositionRangeIndexedMemoryEvent* events, size_t count); DEBUGGER_FFI_API void BNDebuggerFreeTTDCallEvents(BNDebuggerTTDCallEvent* events, size_t count); DEBUGGER_FFI_API void BNDebuggerFreeTTDEvents(BNDebuggerTTDEvent* events, size_t count); // TTD Code Coverage Analysis Functions DEBUGGER_FFI_API bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t address); - DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress); + DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime); DEBUGGER_FFI_API size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerSaveCodeCoverageToFile(BNDebuggerController* controller, const char* filePath); DEBUGGER_FFI_API bool BNDebuggerLoadCodeCoverageFromFile(BNDebuggerController* controller, const char* filePath); diff --git a/core/adapters/dbgengttdadapter.cpp b/core/adapters/dbgengttdadapter.cpp index 317855cf..08f80cc5 100644 --- a/core/adapters/dbgengttdadapter.cpp +++ b/core/adapters/dbgengttdadapter.cpp @@ -403,6 +403,18 @@ std::vector DbgEngTTDAdapter::GetTTDMemoryAccessForAddress(uint6 return events; } +std::vector DbgEngTTDAdapter::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime) +{ + std::vector events; + + if (!QueryMemoryAccessByAddressAndPositionRange(startAddress, endAddress, accessType, startTime, endTime, events)) + { + LogError("Failed to query TTD memory access events for address range 0x%llx-0x%llx", startAddress, endAddress); + } + + return events; +} + TTDPosition DbgEngTTDAdapter::GetCurrentTTDPosition() { TTDPosition position; @@ -563,6 +575,50 @@ bool DbgEngTTDAdapter::QueryMemoryAccessByAddress(uint64_t startAddress, uint64_ } } +bool DbgEngTTDAdapter::QueryMemoryAccessByAddressAndPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime, std::vector& events) +{ + if (!m_debugControl) + { + LogError("Debug control interface not available"); + return false; + } + + try + { + // Build the access type string for TTD memory queries - combine flags as needed + std::string accessTypeStr; + if (accessType & TTDMemoryRead) accessTypeStr += "r"; + if (accessType & TTDMemoryWrite) accessTypeStr += "w"; + if (accessType & TTDMemoryExecute) accessTypeStr += "e"; + + if (accessTypeStr.empty()) + { + LogError("Invalid access type specified"); + return false; + } + + // Create the actual TTD memory query expression + std::string expression = fmt::format("@$cursession.TTD.MemoryForPositionRange(0x{:x},0x{:x},\"{}\",\"{:x}:{:x}\",\"{:x}:{:x}\")", startAddress, endAddress, accessTypeStr, startTime.sequence,startTime.step, endTime.sequence, endTime.step); + + LogInfo("Executing TTD memory query: %s", expression.c_str()); + + // Execute the query and parse results + if (!ParseTTDPositionRangeIndexedMemoryObjects(expression, accessType, events)) + { + LogError("Failed to parse TTD memory objects from query"); + return false; + } + + LogInfo("Successfully retrieved %zu TTD memory events", events.size()); + return true; + } + catch (const std::exception& e) + { + LogError("Exception in QueryMemoryAccessByAddressAndPositionRange: %s", e.what()); + return false; + } +} + void DbgEngTTDAdapter::GenerateDefaultAdapterSettings(BinaryView* data) { @@ -965,6 +1021,256 @@ bool DbgEngTTDAdapter::ParseTTDMemoryObjects(const std::string& expression, TTDM } } +// `MemoryForPositionRange` does not truncate the value field to match the actual size of the memory access, but contains all the parts to piece it together. +bool DbgEngTTDAdapter::ParseTTDPositionRangeIndexedMemoryObjects(const std::string& expression, TTDMemoryAccessType accessType, std::vector& events) +{ + if (!m_hostEvaluator) + { + LogError("Data model evaluator not available"); + return false; + } + + try + { + // Convert expression to wide string + std::wstring wExpression(expression.begin(), expression.end()); + + // Create context for evaluation + ComPtr hostContext; + if (FAILED(m_debugHost->GetCurrentContext(hostContext.GetAddressOf()))) + { + LogError("Failed to get current debug host context"); + return false; + } + + // Evaluate the TTD memory collection expression + ComPtr result; + ComPtr metadata; + HRESULT hr = m_hostEvaluator->EvaluateExtendedExpression( + hostContext.Get(), + wExpression.c_str(), + nullptr, // No binding context + result.GetAddressOf(), + metadata.GetAddressOf() + ); + + if (FAILED(hr)) + { + LogError("Failed to evaluate TTD memory expression '%s': 0x%08x", expression.c_str(), hr); + return false; + } + + // Check if result is iterable (collection) + ComPtr iterableConcept; + if (FAILED(result->GetConcept(__uuidof(IIterableConcept), &iterableConcept, nullptr))) + { + LogError("TTD memory result is not iterable"); + return false; + } + + // Get iterator + ComPtr iterator; + if (FAILED(iterableConcept->GetIterator(result.Get(), &iterator))) + { + LogError("Failed to get iterator for TTD memory objects"); + return false; + } + + // Iterate through memory objects + ComPtr memoryObject; + ComPtr metadataKeyStore; + + // Get the max results setting + auto adapterSettings = GetAdapterSettings(); + BNSettingsScope scope = SettingsResourceScope; + auto maxResults = adapterSettings->Get("ttd.maxMemoryQueryResults", GetData(), &scope); + + uint64_t resultCounter = 0; + bool wasLimited = false; + + while (SUCCEEDED(iterator->GetNext(&memoryObject, 0, nullptr, &metadataKeyStore))) + { + if (!memoryObject) + break; + + // Check if we've reached the limit (0 means no limit) + if (maxResults > 0 && resultCounter >= maxResults) + { + wasLimited = true; + break; + } + + TTDPositionRangeIndexedMemoryEvent event; + + // Extract all fields from the memory object based on Microsoft documentation + + // Get ThreadId + ComPtr threadIdObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"ThreadId", &threadIdObj, nullptr))) + { + VARIANT vtThreadId; + VariantInit(&vtThreadId); + if (SUCCEEDED(threadIdObj->GetIntrinsicValueAs(VT_UI4, &vtThreadId))) + { + event.threadId = vtThreadId.ulVal; + } + VariantClear(&vtThreadId); + } + + // Get UniqueThreadId + ComPtr uniqueThreadIdObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"UniqueThreadId", &uniqueThreadIdObj, nullptr))) + { + VARIANT vtUniqueThreadId; + VariantInit(&vtUniqueThreadId); + if (SUCCEEDED(uniqueThreadIdObj->GetIntrinsicValueAs(VT_UI4, &vtUniqueThreadId))) + { + event.uniqueThreadId = vtUniqueThreadId.ulVal; + } + VariantClear(&vtUniqueThreadId); + } + + // Get TimeStart for position + ComPtr positionObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"Position", &positionObj, nullptr))) + { + // TimeStart is typically a TTD position object with Sequence and Steps + ComPtr sequenceObj, stepsObj; + if (SUCCEEDED(positionObj->GetKeyValue(L"Sequence", &sequenceObj, nullptr))) + { + VARIANT vtSequence; + VariantInit(&vtSequence); + if (SUCCEEDED(sequenceObj->GetIntrinsicValueAs(VT_UI8, &vtSequence))) + { + event.position.sequence = vtSequence.ullVal; + } + VariantClear(&vtSequence); + } + + if (SUCCEEDED(positionObj->GetKeyValue(L"Steps", &stepsObj, nullptr))) + { + VARIANT vtSteps; + VariantInit(&vtSteps); + if (SUCCEEDED(stepsObj->GetIntrinsicValueAs(VT_UI8, &vtSteps))) + { + event.position.step = vtSteps.ullVal; + } + VariantClear(&vtSteps); + } + } + + // Get Address + ComPtr addressObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"Address", &addressObj, nullptr))) + { + VARIANT vtAddress; + VariantInit(&vtAddress); + if (SUCCEEDED(addressObj->GetIntrinsicValueAs(VT_UI8, &vtAddress))) + { + event.address = vtAddress.ullVal; + } + VariantClear(&vtAddress); + } + + // Get Size + ComPtr sizeObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"Size", &sizeObj, nullptr))) + { + VARIANT vtSize; + VariantInit(&vtSize); + if (SUCCEEDED(sizeObj->GetIntrinsicValueAs(VT_UI8, &vtSize))) + { + event.size = vtSize.ullVal; + } + VariantClear(&vtSize); + } + + // Get IP (Instruction Pointer) + ComPtr ipObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"IP", &ipObj, nullptr))) + { + VARIANT vtIP; + VariantInit(&vtIP); + if (SUCCEEDED(ipObj->GetIntrinsicValueAs(VT_UI8, &vtIP))) + { + event.instructionAddress = vtIP.ullVal; + } + VariantClear(&vtIP); + } + + // Get Value (the value that was read/written/executed) + ComPtr valueObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"Value", &valueObj, nullptr))) + { + VARIANT vtValue; + VariantInit(&vtValue); + if (SUCCEEDED(valueObj->GetIntrinsicValueAs(VT_UI8, &vtValue))) + { + event.value = vtValue.ullVal; + } + VariantClear(&vtValue); + } + + // Get AccessType from the object itself + ComPtr accessTypeObj; + if (SUCCEEDED(memoryObject->GetKeyValue(L"AccessType", &accessTypeObj, nullptr))) + { + VARIANT vtAccessType; + VariantInit(&vtAccessType); + if (SUCCEEDED(accessTypeObj->GetIntrinsicValueAs(VT_BSTR, &vtAccessType))) + { + _bstr_t bstr(vtAccessType.bstrVal); + std::string accessTypeStr = std::string(bstr); + + // Parse access type string to bitfield + TTDMemoryAccessType parsedAccessType = static_cast(0); + if (accessTypeStr.find("Read") != std::string::npos) + parsedAccessType = static_cast(parsedAccessType | TTDMemoryRead); + if (accessTypeStr.find("Write") != std::string::npos) + parsedAccessType = static_cast(parsedAccessType | TTDMemoryWrite); + if (accessTypeStr.find("Execute") != std::string::npos) + parsedAccessType = static_cast(parsedAccessType | TTDMemoryExecute); + + event.accessType = parsedAccessType; + } + else + { + // Fallback to query parameter if parsing fails + event.accessType = accessType; + } + VariantClear(&vtAccessType); + } + else + { + // Fallback to query parameter if field is not available + event.accessType = accessType; + } + + events.push_back(event); + resultCounter++; + + // Reset objects for next iteration + memoryObject.Reset(); + metadataKeyStore.Reset(); + } + + if (wasLimited) + { + LogWarnF("Successfully parsed {} TTD memory events from data model (limited by max results setting of {})", events.size(), maxResults); + } + else + { + LogInfo("Successfully parsed %zu TTD memory events from data model", events.size()); + } + return true; + } + catch (const std::exception& e) + { + LogError("Exception in ParseTTDPositionRangeIndexedMemoryObjects: %s", e.what()); + return false; + } +} + std::vector DbgEngTTDAdapter::GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress) { diff --git a/core/adapters/dbgengttdadapter.h b/core/adapters/dbgengttdadapter.h index c740d473..b8b57e42 100644 --- a/core/adapters/dbgengttdadapter.h +++ b/core/adapters/dbgengttdadapter.h @@ -48,6 +48,9 @@ namespace BinaryNinjaDebugger { // TTD Memory Analysis Methods std::vector GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead) override; + std::vector GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime) override; + + // TTD Position Methods TTDPosition GetCurrentTTDPosition() override; bool SetTTDPosition(const TTDPosition& position) override; @@ -64,6 +67,7 @@ namespace BinaryNinjaDebugger { private: // Helper methods for TTD memory analysis bool QueryMemoryAccessByAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, std::vector& events); + bool QueryMemoryAccessByAddressAndPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, TTDPosition startTime, TTDPosition endTime, std::vector& events); // Helper methods for TTD calls analysis bool QueryCallsForSymbols(const std::vector& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress, std::vector& events); @@ -87,6 +91,7 @@ namespace BinaryNinjaDebugger { // Data model helper methods std::string EvaluateDataModelExpression(const std::string& expression); bool ParseTTDMemoryObjects(const std::string& expression, TTDMemoryAccessType accessType, std::vector& events); + bool ParseTTDPositionRangeIndexedMemoryObjects(const std::string& expression, TTDMemoryAccessType accessType, std::vector& events); // Data model interfaces for TTD IHostDataModelAccess* m_dataModelManager; diff --git a/core/debugadapter.cpp b/core/debugadapter.cpp index 048e402d..3e8b5392 100644 --- a/core/debugadapter.cpp +++ b/core/debugadapter.cpp @@ -219,6 +219,12 @@ std::vector DebugAdapter::GetTTDMemoryAccessForAddress(uint64_t return {}; } +std::vector DebugAdapter::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime) +{ + // Default implementation returns empty results for adapters that don't support TTD + return {}; +} + std::vector DebugAdapter::GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress) { diff --git a/core/debugadapter.h b/core/debugadapter.h index f733014f..92932eca 100644 --- a/core/debugadapter.h +++ b/core/debugadapter.h @@ -390,6 +390,7 @@ namespace BinaryNinjaDebugger { // TTD (Time Travel Debugging) methods - default implementations return empty results virtual std::vector GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead); + virtual std::vector GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime); virtual std::vector GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0); virtual std::vector GetTTDEvents(TTDEventType eventType); virtual std::vector GetAllTTDEvents(); diff --git a/core/debuggercommon.h b/core/debuggercommon.h index 9c3b2e53..276c7945 100644 --- a/core/debuggercommon.h +++ b/core/debuggercommon.h @@ -137,6 +137,23 @@ namespace BinaryNinjaDebugger { TTDMemoryEvent() : threadId(0), uniqueThreadId(0), address(0), size(0), memoryAddress(0), instructionAddress(0), value(0), accessType(TTDMemoryRead) {} }; + struct TTDPositionRangeIndexedMemoryEvent{ + TTDPosition position; // Position of the memory event + uint32_t threadId; // Thread ID that performed the access + uint32_t uniqueThreadId; // Unique thread ID that performed the access + uint64_t address; // Memory address accessed + uint64_t instructionAddress; // Instruction pointer at time of access + uint64_t size; // Size of memory access + TTDMemoryAccessType accessType; // Type of memory access (parsed from object) + uint64_t value; // Value that was read/written/executed + uint8_t data[8]; // The next 8 bytes of data at the memory address + + TTDPositionRangeIndexedMemoryEvent() : threadId(0), uniqueThreadId(0), address(0), size(0), accessType(TTDMemoryRead), value(0) + { + memset(data, 0, sizeof(data)); + } + }; + // TTD Call Event - complete set of fields from Microsoft documentation for TTD.Calls struct TTDCallEvent { diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index a51e0d3a..a8bdc390 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -3134,6 +3134,24 @@ std::vector DebuggerController::GetTTDMemoryAccessForAddress(uin return events; } +std::vector DebuggerController::GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime) +{ + std::vector events; + + if (!m_state->IsConnected() || !IsTTD()) + { + LogError("Current adapter does not support TTD"); + return events; + } + + if (m_adapter) + { + events = m_adapter->GetTTDMemoryAccessForPositionRange(startAddress, endAddress, accessType, startTime, endTime); + } + + return events; +} + std::vector DebuggerController::GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress) { std::vector events; @@ -3227,7 +3245,7 @@ bool DebuggerController::IsInstructionExecuted(uint64_t address) } -bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress) +bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime) { if (!m_state->IsConnected() || !IsTTD()) { @@ -3245,11 +3263,22 @@ bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t m_executedInstructions.clear(); m_codeCoverageAnalysisRun = false; - LogInfo("Starting TTD code coverage analysis for range 0x%" PRIX64 " - 0x%" PRIX64 "...", startAddress, endAddress); + LogInfo("Starting TTD code coverage analysis."); + LogInfo("\tAddress range: 0x%" PRIX64 " - 0x%" PRIX64, startAddress, endAddress); + //log time range + bool endTimeIsMax = endTime.sequence== std::numeric_limits::max() && endTime.step == std::numeric_limits::max(); + if(endTimeIsMax) + { + LogInfo("\tTime range: %" PRIX64 ":%" PRIX64 " - end of trace", startTime.sequence, startTime.step); + } + else{ + LogInfo("\tTime range: %" PRIX64 ":%" PRIX64 " - %" PRIX64 ":%" PRIX64, startTime.sequence, startTime.step, + endTime.sequence, endTime.step); + } // Query TTD for execute access covering the specified range - auto events = GetTTDMemoryAccessForAddress(startAddress, endAddress, TTDMemoryExecute); - + auto events = GetTTDMemoryAccessForPositionRange(startAddress, endAddress, TTDMemoryExecute, startTime, endTime); + for (const auto& event : events) { if (event.accessType == TTDMemoryExecute) @@ -3257,13 +3286,14 @@ bool DebuggerController::RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t // Add all executed instruction addresses within the range if (event.instructionAddress >= startAddress && event.instructionAddress <= endAddress) { - m_executedInstructions.insert(event.instructionAddress); + // Check if the event is within the specified time range + m_executedInstructions.insert(event.address); } } } m_codeCoverageAnalysisRun = true; - LogInfo("TTD code coverage analysis completed for range. Found 0x%" PRIu64 "executed instructions.", + LogInfo("TTD code coverage analysis completed for ranges. Found %" PRIu64 " executed instructions.", (uint64_t)m_executedInstructions.size()); return true; @@ -3309,7 +3339,7 @@ bool DebuggerController::SaveCodeCoverageToFile(const std::string& filePath) con } file.close(); - LogError("%s", fmt::format("Saved {} executed instruction addresses to {}", count, filePath.c_str()).c_str()); + LogInfo("%s", fmt::format("Saved {} executed instruction addresses to {}", count, filePath.c_str()).c_str()); return true; } diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index 1014bb97..067dbf47 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -389,6 +389,7 @@ namespace BinaryNinjaDebugger { // TTD Memory Analysis Methods std::vector GetTTDMemoryAccessForAddress(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType = TTDMemoryRead); + std::vector GetTTDMemoryAccessForPositionRange(uint64_t startAddress, uint64_t endAddress, TTDMemoryAccessType accessType, const TTDPosition startTime, const TTDPosition endTime); std::vector GetTTDCallsForSymbols(const std::string& symbols, uint64_t startReturnAddress = 0, uint64_t endReturnAddress = 0); std::vector GetTTDEvents(TTDEventType eventType); std::vector GetAllTTDEvents(); @@ -397,7 +398,7 @@ namespace BinaryNinjaDebugger { // TTD Code Coverage Analysis Methods bool IsInstructionExecuted(uint64_t address); - bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress); + bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime); size_t GetExecutedInstructionCount() const; bool SaveCodeCoverageToFile(const std::string& filePath) const; bool LoadCodeCoverageFromFile(const std::string& filePath); diff --git a/core/ffi.cpp b/core/ffi.cpp index 5f51c8cb..63262457 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -1226,6 +1226,44 @@ BNDebuggerTTDMemoryEvent* BNDebuggerGetTTDMemoryAccessForAddress(BNDebuggerContr return result; } +BNDebuggerTTDPositionRangeIndexedMemoryEvent* BNDebuggerGetTTDMemoryAccessForPositionRange(BNDebuggerController* controller, + uint64_t address, uint64_t endAddress, BNDebuggerTTDMemoryAccessType accessType, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime, size_t* count) +{ + if (!count) + return nullptr; + + *count = 0; + + TTDMemoryAccessType type = static_cast(accessType); + TTDPosition startPos(startTime.sequence, startTime.step); + TTDPosition endPos(endTime.sequence, endTime.step); + auto events = controller->object->GetTTDMemoryAccessForPositionRange(address, endAddress, type, startPos, endPos); + if (events.empty()) + return nullptr; + + *count = events.size(); + auto result = new BNDebuggerTTDPositionRangeIndexedMemoryEvent[events.size()]; + + for (size_t i = 0; i < events.size(); i++) + { + result[i].threadId = events[i].threadId; + result[i].uniqueThreadId = events[i].uniqueThreadId; + result[i].position.sequence = events[i].position.sequence; + result[i].position.step = events[i].position.step; + result[i].address = events[i].address; + result[i].size = events[i].size; + result[i].instructionAddress = events[i].instructionAddress; + result[i].value = events[i].value; + result[i].accessType = static_cast(events[i].accessType); + for (size_t j = 0; j < 8; j++) + { + result[i].data[j]=events[i].data[j]; + } + } + + return result; +} + BNDebuggerTTDPosition BNDebuggerGetCurrentTTDPosition(BNDebuggerController* controller) { auto position = controller->object->GetCurrentTTDPosition(); @@ -1246,9 +1284,11 @@ bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t return controller->object->IsInstructionExecuted(address); } -bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress) +bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime) { - return controller->object->RunCodeCoverageAnalysis(startAddress, endAddress); + TTDPosition startPos(startTime.sequence, startTime.step); + TTDPosition endPos(endTime.sequence, endTime.step); + return controller->object->RunCodeCoverageAnalysis(startAddress, endAddress, startPos, endPos); } size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller) @@ -1282,6 +1322,14 @@ void BNDebuggerFreeTTDMemoryEvents(BNDebuggerTTDMemoryEvent* events, size_t coun } } +void BNDebuggerFreeTTDPositionRangeIndexedMemoryEvents(BNDebuggerTTDPositionRangeIndexedMemoryEvent* events, size_t count) +{ + if (events && count > 0) + { + delete[] events; + } +} + BNDebuggerTTDCallEvent* BNDebuggerGetTTDCallsForSymbols(BNDebuggerController* controller, const char* symbols, uint64_t startReturnAddress, uint64_t endReturnAddress, size_t* count) diff --git a/ui/ttdanalysisdialog.cpp b/ui/ttdanalysisdialog.cpp index 2aa84b44..1d3ba1b2 100644 --- a/ui/ttdanalysisdialog.cpp +++ b/ui/ttdanalysisdialog.cpp @@ -27,8 +27,8 @@ TTDAnalysisWorker::TTDAnalysisWorker(DbgRef controller, TTDA { } -TTDAnalysisWorker::TTDAnalysisWorker(DbgRef controller, TTDAnalysisType type, uint64_t startAddress, uint64_t endAddress, QObject* parent) - : QThread(parent), m_controller(controller), m_analysisType(type), m_useRange(true), m_startAddress(startAddress), m_endAddress(endAddress) +TTDAnalysisWorker::TTDAnalysisWorker(DbgRef controller, TTDAnalysisType type, uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime, QObject* parent) + : QThread(parent), m_controller(controller), m_analysisType(type), m_useRange(true), m_startAddress(startAddress), m_endAddress(endAddress), m_startTime(startTime), m_endTime(endTime) { } @@ -58,14 +58,16 @@ void TTDAnalysisWorker::run() emit analysisProgress(30, "Running code coverage analysis..."); if (m_useRange) { - success = m_controller->RunCodeCoverageAnalysis(m_startAddress, m_endAddress); + success = m_controller->RunCodeCoverageAnalysis(m_startAddress, m_endAddress, m_startTime, m_endTime); if (success) { resultCount = m_controller->GetExecutedInstructionCount(); - message = QString("Range-based code coverage analysis completed successfully. Found %1 executed instructions in range 0x%2 - 0x%3.") + message = QString("Range-based code coverage analysis completed successfully. Found %1 executed instructions in address range 0x%2 - 0x%3 and time range (%4,%5) - (%6,%7).") .arg(resultCount) .arg(m_startAddress, 0, 16) - .arg(m_endAddress, 0, 16); + .arg(m_endAddress, 0, 16) + .arg(m_startTime.sequence).arg(m_startTime.step) + .arg(m_endTime.sequence).arg(m_endTime.step); } else { @@ -197,9 +199,26 @@ void TTDAnalysisDialog::setupUI() m_endAddressEdit->setText(QString("0x") + QString::number(m_data->GetImageBase() + 0x7000, 16)); m_endAddressEdit->setEnabled(true); rangeControlsLayout->addWidget(m_endAddressEdit); - rangeLayout->addLayout(rangeControlsLayout); + // Time settings + QGroupBox* timeGroup = new QGroupBox("Time Range (Optional)"); + QVBoxLayout* timeLayout = new QVBoxLayout(timeGroup); + + QHBoxLayout* timeControlsLayout = new QHBoxLayout(); + timeControlsLayout->addWidget(new QLabel("Start Time:")); + m_startTimeEdit = new QLineEdit(); + //set text to starting position + m_startTimeEdit->setEnabled(true); + timeControlsLayout->addWidget(m_startTimeEdit); + + timeControlsLayout->addWidget(new QLabel("End Time:")); + m_endTimeEdit = new QLineEdit(); + //set text to ending position + m_endTimeEdit->setEnabled(true); + timeControlsLayout->addWidget(m_endTimeEdit); + timeLayout->addLayout(timeControlsLayout); + // Connect range checkbox to enable/disable range controls connect(m_useRangeCheckBox, &QCheckBox::toggled, [this](bool checked) { m_startAddressEdit->setEnabled(checked); @@ -207,6 +226,7 @@ void TTDAnalysisDialog::setupUI() }); mainLayout->addWidget(rangeGroup); + mainLayout->addWidget(timeGroup); // Cache settings QGroupBox* cacheGroup = new QGroupBox("Cache Settings"); @@ -382,6 +402,11 @@ void TTDAnalysisDialog::onRunAnalysis() bool startOk, endOk; QString startText = m_startAddressEdit->text().trimmed(); QString endText = m_endAddressEdit->text().trimmed(); + endText=endText.replace("`", "").replace("0x", ""); // remove extra address formatting + startText=startText.replace("`", "").replace("0x", ""); // windbg format looks like: 000000dd`7e7fed80 + QString startTimeText = m_startTimeEdit->text().trimmed(); + QString endTimeText = m_endTimeEdit->text().trimmed(); + TTDPosition startTime, endTime; if (startText.isEmpty() || endText.isEmpty()) { @@ -389,8 +414,52 @@ void TTDAnalysisDialog::onRunAnalysis() return; } - uint64_t startAddress = startText.toULongLong(&startOk, 0); // Auto-detect base (0x for hex) - uint64_t endAddress = endText.toULongLong(&endOk, 0); + if (startTimeText.isEmpty()) + { + startTime = TTDPosition(0, 0); + }else{ + QStringList startTimeParts = startTimeText.split(u':'); + if (startTimeParts.size() != 2) + { + QMessageBox::warning(this, "Invalid Time", "Start time must be in the format 'sequence:step'"); + return; + } + bool seqOk, stepOk; + uint64_t sequence = startTimeParts[0].toULongLong(&seqOk, 16); + uint64_t step = startTimeParts[1].toULongLong(&stepOk, 16); + if (!seqOk || !stepOk) + { + QMessageBox::warning(this, "Invalid Time", "Start time contains invalid numbers"); + return; + } + startTime = TTDPosition(sequence, step); + } + + if (endTimeText.isEmpty()) + { + endTime = TTDPosition(std::numeric_limits::max(),std::numeric_limits::max()); + } + else + { + QStringList endTimeParts = endTimeText.split(u':'); + if (endTimeParts.size() != 2) + { + QMessageBox::warning(this, "Invalid Time", "End time must be in the format 'sequence:step'"); + return; + } + bool seqOk, stepOk; + uint64_t sequence = endTimeParts[0].toULongLong(&seqOk, 16); + uint64_t step = endTimeParts[1].toULongLong(&stepOk, 16); + if (!seqOk || !stepOk) + { + QMessageBox::warning(this, "Invalid Time", "End time contains invalid numbers"); + return; + } + endTime = TTDPosition(sequence, step); + } + + uint64_t startAddress = startText.toULongLong(&startOk, 16); + uint64_t endAddress = endText.toULongLong(&endOk, 16); if (!startOk || !endOk) { @@ -405,7 +474,7 @@ void TTDAnalysisDialog::onRunAnalysis() } // Start range-based analysis in worker thread - m_currentWorker = new TTDAnalysisWorker(m_controller, analysisType, startAddress, endAddress, this); + m_currentWorker = new TTDAnalysisWorker(m_controller, analysisType, startAddress, endAddress, startTime, endTime, this); } else { diff --git a/ui/ttdanalysisdialog.h b/ui/ttdanalysisdialog.h index ff43e052..2d6347dc 100644 --- a/ui/ttdanalysisdialog.h +++ b/ui/ttdanalysisdialog.h @@ -70,7 +70,7 @@ class TTDAnalysisWorker : public QThread public: TTDAnalysisWorker(DbgRef controller, TTDAnalysisType type, QObject* parent = nullptr); - TTDAnalysisWorker(DbgRef controller, TTDAnalysisType type, uint64_t startAddress, uint64_t endAddress, QObject* parent = nullptr); + TTDAnalysisWorker(DbgRef controller, TTDAnalysisType type, uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime, QObject* parent = nullptr); protected: void run() override; @@ -85,6 +85,8 @@ class TTDAnalysisWorker : public QThread bool m_useRange; uint64_t m_startAddress; uint64_t m_endAddress; + TTDPosition m_startTime; + TTDPosition m_endTime; }; class TTDAnalysisDialog : public QDialog @@ -136,6 +138,10 @@ private slots: QLineEdit* m_startAddressEdit; QLineEdit* m_endAddressEdit; + // Time controls + QLineEdit* m_startTimeEdit; + QLineEdit* m_endTimeEdit; + // Analysis data QList m_analysisResults; TTDAnalysisWorker* m_currentWorker; diff --git a/ui/ttdcoveragerenderlayer.cpp b/ui/ttdcoveragerenderlayer.cpp index ad45fef1..6566b727 100644 --- a/ui/ttdcoveragerenderlayer.cpp +++ b/ui/ttdcoveragerenderlayer.cpp @@ -56,7 +56,7 @@ void TTDCoverageRenderLayer::ApplyToBlock(Ref block, std::vector function, std: line.highlight.r = 0; line.highlight.g = 0; line.highlight.b = 0; - line.highlight.alpha = 64; // Light highlight + line.highlight.alpha = 255; } } } \ No newline at end of file diff --git a/ui/ttdmemorywidget.cpp b/ui/ttdmemorywidget.cpp index 65a640a3..13573b42 100644 --- a/ui/ttdmemorywidget.cpp +++ b/ui/ttdmemorywidget.cpp @@ -63,9 +63,7 @@ ColumnVisibilityDialog::ColumnVisibilityDialog(QWidget* parent, const QStringLis // Reset to default visibility (hide Event Type, Time End, Unique Thread ID) QList defaultVisibility; defaultVisibility << true // Index - << false // Event Type (hidden by default) - << true // Time Start - << false // Time End (hidden by default) + << true // Position << true // Access Type << true // Address << true // Size @@ -102,14 +100,12 @@ TTDMemoryQueryWidget::TTDMemoryQueryWidget(QWidget* parent, BinaryViewRef data) m_controller = DebuggerController::GetController(m_data); // Initialize column names and visibility - m_columnNames << "Index" << "Event Type" << "Time Start" << "Time End" << "Access Type" + m_columnNames << "Index" << "Position" << "Access Type" << "Address" << "Size" << "Value" << "Thread ID" << "Unique Thread ID" << "IP"; // Set default visibility (hide Event Type, Time End, Unique Thread ID) m_columnVisibility << true // Index - << false // Event Type (hidden by default) - << true // Time Start - << false // Time End (hidden by default) + << true // Position << true // Access Type << true // Address << true // Size @@ -159,8 +155,30 @@ void TTDMemoryQueryWidget::setupUI() m_endAddressEdit->setPlaceholderText("0xFFFFFFFF"); } - inputLayout->addRow("Start Address:", m_startAddressEdit); - inputLayout->addRow("End Address:", m_endAddressEdit); + // Put both address fields on the same line + QHBoxLayout* addressLayout = new QHBoxLayout(); + addressLayout->addWidget(new QLabel("Start:")); + addressLayout->addWidget(m_startAddressEdit); + addressLayout->addWidget(new QLabel("End:")); + addressLayout->addWidget(m_endAddressEdit); + + inputLayout->addRow("Address Range:", addressLayout); + + m_startTimeEdit = new QLineEdit(); + m_startTimeEdit->setToolTip("Start time in format 'sequence:step' (hexadecimal), leave blank for start of recording"); + m_startTimeEdit->setPlaceholderText("e.g. 0:0"); + + m_endTimeEdit = new QLineEdit(); + m_endTimeEdit->setToolTip("End time in format 'sequence:step' (hexadecimal), leave blank for end of recording"); + m_endTimeEdit->setPlaceholderText("e.g. 23f:a7"); + + QHBoxLayout* timeLayout = new QHBoxLayout(); + timeLayout->addWidget(new QLabel("Start Time:")); + timeLayout->addWidget(m_startTimeEdit); + timeLayout->addWidget(new QLabel("End Time:")); + timeLayout->addWidget(m_endTimeEdit); + + inputLayout->addRow("Time Range (Optional):", timeLayout); // Memory access type checkboxes QHBoxLayout* accessLayout = new QHBoxLayout(); @@ -245,15 +263,13 @@ void TTDMemoryQueryWidget::setupTable() QHeaderView* header = m_resultsTable->horizontalHeader(); header->setStretchLastSection(true); m_resultsTable->setColumnWidth(0, 80); // Index - m_resultsTable->setColumnWidth(1, 100); // Event Type - m_resultsTable->setColumnWidth(2, 120); // Time Start - m_resultsTable->setColumnWidth(3, 120); // Time End - m_resultsTable->setColumnWidth(4, 100); // Access Type - m_resultsTable->setColumnWidth(5, 120); // Address - m_resultsTable->setColumnWidth(6, 80); // Size - m_resultsTable->setColumnWidth(7, 120); // Value - m_resultsTable->setColumnWidth(8, 80); // Thread ID - m_resultsTable->setColumnWidth(9, 100); // Unique Thread ID + m_resultsTable->setColumnWidth(1, 100); // Position + m_resultsTable->setColumnWidth(2, 100); // Access Type + m_resultsTable->setColumnWidth(3, 120); // Address + m_resultsTable->setColumnWidth(4, 80); // Size + m_resultsTable->setColumnWidth(5, 120); // Value + m_resultsTable->setColumnWidth(6, 80); // Thread ID + m_resultsTable->setColumnWidth(7, 100); // Unique Thread ID // IP column will stretch // Apply initial column visibility @@ -317,7 +333,43 @@ void TTDMemoryQueryWidget::performQuery() // Parse input parameters uint64_t startAddress = parseAddress(m_startAddressEdit->text()); uint64_t endAddress = parseAddress(m_endAddressEdit->text()); - + TTDPosition startTime ; + TTDPosition endTime ; + if (m_startTimeEdit->text().isEmpty()) + { + startTime = TTDPosition(0, 0); + } + else + { + try + { + startTime = parseTimePosition(m_startTimeEdit->text()); + } + catch (const std::invalid_argument&) + { + QMessageBox::warning(this, "Invalid Start Time", + "Start time must be in the format 'sequence:step' with valid hexadecimal numbers."); + return; + } + } + + if (m_endTimeEdit->text().isEmpty()) + { + endTime = TTDPosition(std::numeric_limits::max(), std::numeric_limits::max()); + } + else + { + try{ + endTime = parseTimePosition(m_endTimeEdit->text()); + } + catch (const std::invalid_argument&) + { + QMessageBox::warning(this, "Invalid End Time", + "End time must be in the format 'sequence:step' with valid hexadecimal numbers."); + return; + } + } + if (endAddress <= startAddress) { QMessageBox::warning(this, "Invalid Address Range", @@ -332,6 +384,13 @@ void TTDMemoryQueryWidget::performQuery() "Please select at least one memory access type (Read, Write, or Execute)."); return; } + + if (endTime < startTime) + { + QMessageBox::warning(this, "Invalid Time Range", + "End time must be greater than or equal to start time."); + return; + } // Clear previous results clearResults(); @@ -344,7 +403,7 @@ void TTDMemoryQueryWidget::performQuery() try { // Execute the TTD memory query - auto events = m_controller->GetTTDMemoryAccessForAddress(startAddress, endAddress, accessType); + auto events = m_controller->GetTTDMemoryAccessForPositionRange(startAddress, endAddress, accessType, startTime, endTime); // Populate the results table m_resultsTable->setRowCount((int)events.size()); @@ -356,50 +415,40 @@ void TTDMemoryQueryWidget::performQuery() // Index m_resultsTable->setItem(i, 0, new NumericalTableWidgetItem(QString("0x%1").arg(i, 0, 16), i)); - // Event Type - m_resultsTable->setItem(i, 1, new QTableWidgetItem(QString::fromStdString(event.eventType))); - - // Time Start - QString timeStartStr = QString("%1:%2") - .arg(event.timeStart.sequence, 0, 16) - .arg(event.timeStart.step, 0, 16); - uint64_t timeStartSortValue = (event.timeStart.sequence << 32) | (event.timeStart.step & 0xFFFFFFFF); - m_resultsTable->setItem(i, 2, new NumericalTableWidgetItem(timeStartStr, timeStartSortValue)); - - // Time End - QString timeEndStr = QString("%1:%2") - .arg(event.timeEnd.sequence, 0, 16) - .arg(event.timeEnd.step, 0, 16); - uint64_t timeEndSortValue = (event.timeEnd.sequence << 32) | (event.timeEnd.step & 0xFFFFFFFF); - m_resultsTable->setItem(i, 3, new NumericalTableWidgetItem(timeEndStr, timeEndSortValue)); + // Position + QString PositionStr = QString("%1:%2") + .arg(event.position.sequence, 0, 16) + .arg(event.position.step, 0, 16); + uint64_t positionSortValue = (event.position.sequence << 32) | (event.position.step & 0xFFFFFFFF); + m_resultsTable->setItem(i, 1, new NumericalTableWidgetItem(PositionStr, positionSortValue)); // Access Type QString accessTypeStr; if (event.accessType & TTDMemoryRead) accessTypeStr += "R"; if (event.accessType & TTDMemoryWrite) accessTypeStr += "W"; if (event.accessType & TTDMemoryExecute) accessTypeStr += "E"; - m_resultsTable->setItem(i, 4, new QTableWidgetItem(accessTypeStr)); + m_resultsTable->setItem(i, 2, new QTableWidgetItem(accessTypeStr)); // Address QString addressStr = QString("0x%1").arg(event.address, 0, 16); - m_resultsTable->setItem(i, 5, new NumericalTableWidgetItem(addressStr, event.address)); + m_resultsTable->setItem(i, 3, new NumericalTableWidgetItem(addressStr, event.address)); // Size - m_resultsTable->setItem(i, 6, new NumericalTableWidgetItem(QString::number(event.size), event.size)); + m_resultsTable->setItem(i, 4, new NumericalTableWidgetItem(QString::number(event.size), event.size)); - // Value - QString valueStr = QString("0x%1").arg(event.value, 0, 16); - m_resultsTable->setItem(i, 7, new NumericalTableWidgetItem(valueStr, event.value)); + // Value truncated to the number of bytes specified by size + QString valueStr = QString("0x%1").arg(event.value & ((1ULL << (event.size * 8)) - 1), 0, 16); + m_resultsTable->setItem(i, 5, new NumericalTableWidgetItem(valueStr, event.value)); // Thread ID - m_resultsTable->setItem(i, 8, new NumericalTableWidgetItem(QString::number(event.threadId), event.threadId)); + m_resultsTable->setItem(i, 6, new NumericalTableWidgetItem(QString::number(event.threadId), event.threadId)); // Unique Thread ID - m_resultsTable->setItem(i, 9, new NumericalTableWidgetItem(QString::number(event.uniqueThreadId), event.uniqueThreadId)); + m_resultsTable->setItem(i, 7, new NumericalTableWidgetItem(QString::number(event.uniqueThreadId), event.uniqueThreadId)); // IP (Instruction Address) QString instrAddrStr = QString("0x%1").arg(event.instructionAddress, 0, 16); - m_resultsTable->setItem(i, 10, new NumericalTableWidgetItem(instrAddrStr, event.instructionAddress)); + m_resultsTable->setItem(i, 8, new NumericalTableWidgetItem(instrAddrStr, event.instructionAddress)); } updateStatus(QString("Found %1 memory access events").arg(events.size())); @@ -427,7 +476,7 @@ void TTDMemoryQueryWidget::onCellDoubleClicked(int row, int column) if (row < 0 || row >= m_resultsTable->rowCount()) return; - if (column == TimeStartColumn || column == TimeEndColumn) + if (column == PositionColumn) { // Parse position and navigate to it QTableWidgetItem* posItem = m_resultsTable->item(row, column); @@ -603,9 +652,7 @@ void TTDMemoryQueryWidget::resetColumnsToDefault() // Reset to default visibility (hide Event Type, Time End, Unique Thread ID) m_columnVisibility.clear(); m_columnVisibility << true // Index - << false // Event Type (hidden by default) - << true // Time Start - << false // Time End (hidden by default) + << true // Position << true // Access Type << true // Address << true // Size @@ -629,14 +676,40 @@ uint64_t TTDMemoryQueryWidget::parseAddress(const QString& text) return 0; // Remove 0x prefix if present - if (cleanText.startsWith("0x", Qt::CaseInsensitive)) + if (cleanText.startsWith("0x", Qt::CaseInsensitive)){ cleanText = cleanText.mid(2); + } + // Remove ' character if present (default address display format in windbg console) + else if (cleanText.contains("`")){ + cleanText = cleanText.replace("`", ""); + } + bool ok; uint64_t address = cleanText.toULongLong(&ok, 16); return ok ? address : 0; } +TTDPosition TTDMemoryQueryWidget::parseTimePosition(const QString& text) +{ + QString cleanText = text.trimmed(); + if (cleanText.isEmpty()) + return TTDPosition(0, 0); // Default to start + + QStringList parts = cleanText.split(':'); + if (parts.size() != 2) + throw std::invalid_argument("Invalid time position format"); + + bool ok1, ok2; + uint64_t sequence = parts[0].toULongLong(&ok1, 16); + uint64_t step = parts[1].toULongLong(&ok2, 16); + + if (ok1 && ok2) + return TTDPosition(sequence, step); + else + throw std::invalid_argument("Invalid time position format"); +} + TTDMemoryAccessType TTDMemoryQueryWidget::getSelectedAccessTypes() { TTDMemoryAccessType accessType = static_cast(0); diff --git a/ui/ttdmemorywidget.h b/ui/ttdmemorywidget.h index 4c5f3dfa..e8644dee 100644 --- a/ui/ttdmemorywidget.h +++ b/ui/ttdmemorywidget.h @@ -70,9 +70,7 @@ class TTDMemoryQueryWidget : public QWidget // Enum for logical column identification enum LogicalColumn { IndexColumn = 0, - EventTypeColumn, - TimeStartColumn, - TimeEndColumn, + PositionColumn, AccessTypeColumn, AddressColumn, SizeColumn, @@ -89,6 +87,8 @@ class TTDMemoryQueryWidget : public QWidget // Input controls QLineEdit* m_startAddressEdit; QLineEdit* m_endAddressEdit; + QLineEdit* m_startTimeEdit; + QLineEdit* m_endTimeEdit; QCheckBox* m_readAccessCheck; QCheckBox* m_writeAccessCheck; QCheckBox* m_executeAccessCheck; @@ -114,6 +114,7 @@ class TTDMemoryQueryWidget : public QWidget void setupTable(); void updateStatus(const QString& message); uint64_t parseAddress(const QString& text); + TTDPosition parseTimePosition(const QString& text); TTDMemoryAccessType getSelectedAccessTypes(); void setupContextMenu(); void setupUIActions();