Skip to content

Commit 8b0eedb

Browse files
Copilotxusheng6
andcommitted
Add caching functionality and improve TTD code coverage analysis
Co-authored-by: xusheng6 <[email protected]>
1 parent c711bd9 commit 8b0eedb

File tree

7 files changed

+214
-14
lines changed

7 files changed

+214
-14
lines changed

api/debuggerapi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@ namespace BinaryNinjaDebuggerAPI {
671671
// TTD Code Coverage Analysis Methods
672672
bool IsInstructionExecuted(uint64_t address);
673673
bool RunCodeCoverageAnalysis();
674+
size_t GetExecutedInstructionCount() const;
675+
bool SaveCodeCoverageToFile(const std::string& filePath) const;
676+
bool LoadCodeCoverageFromFile(const std::string& filePath);
674677

675678
void PostDebuggerEvent(const DebuggerEvent& event);
676679

api/debuggercontroller.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,24 @@ bool DebuggerController::RunCodeCoverageAnalysis()
999999
}
10001000

10011001

1002+
size_t DebuggerController::GetExecutedInstructionCount() const
1003+
{
1004+
return BNDebuggerGetExecutedInstructionCount(m_object);
1005+
}
1006+
1007+
1008+
bool DebuggerController::SaveCodeCoverageToFile(const std::string& filePath) const
1009+
{
1010+
return BNDebuggerSaveCodeCoverageToFile(m_object, filePath.c_str());
1011+
}
1012+
1013+
1014+
bool DebuggerController::LoadCodeCoverageFromFile(const std::string& filePath)
1015+
{
1016+
return BNDebuggerLoadCodeCoverageFromFile(m_object, filePath.c_str());
1017+
}
1018+
1019+
10021020
void DebuggerController::PostDebuggerEvent(const DebuggerEvent &event)
10031021
{
10041022
BNDebuggerEvent* evt = new BNDebuggerEvent;

api/ffi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,9 @@ extern "C"
540540
// TTD Code Coverage Analysis Functions
541541
DEBUGGER_FFI_API bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t address);
542542
DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysis(BNDebuggerController* controller);
543+
DEBUGGER_FFI_API size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller);
544+
DEBUGGER_FFI_API bool BNDebuggerSaveCodeCoverageToFile(BNDebuggerController* controller, const char* filePath);
545+
DEBUGGER_FFI_API bool BNDebuggerLoadCodeCoverageFromFile(BNDebuggerController* controller, const char* filePath);
543546

544547
DEBUGGER_FFI_API void BNDebuggerPostDebuggerEvent(BNDebuggerController* controller, BNDebuggerEvent* event);
545548

core/debuggercontroller.cpp

Lines changed: 138 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
#include "debuggercontroller.h"
1818
#include <thread>
19+
#include <fstream>
1920
#include "lowlevelilinstruction.h"
2021
#include "mediumlevelilinstruction.h"
2122
#include "highlevelilinstruction.h"
@@ -2918,31 +2919,158 @@ bool DebuggerController::RunCodeCoverageAnalysis()
29182919

29192920
LogInfo("Starting TTD code coverage analysis...");
29202921

2921-
// Iterate through all functions and their instruction addresses
2922+
// Get all instruction addresses from all functions
2923+
std::set<uint64_t> allInstructionAddresses;
29222924
for (auto func : bv->GetAnalysisFunctionList())
29232925
{
2924-
uint64_t funcStart = func->GetStart();
2925-
uint64_t funcEnd = func->GetHighestAddress();
2926-
2927-
// Query TTD for execute access at each instruction address
2928-
auto events = GetTTDMemoryAccessForAddress(funcStart, funcEnd, TTDMemoryExecute);
2929-
2930-
for (const auto& event : events)
2926+
for (auto block : func->GetBasicBlocks())
2927+
{
2928+
uint64_t addr = block->GetStart();
2929+
uint64_t end = block->GetEnd();
2930+
while (addr < end)
2931+
{
2932+
allInstructionAddresses.insert(addr);
2933+
size_t instrLen = func->GetArchitecture()->GetInstructionLength(bv, addr);
2934+
if (instrLen == 0)
2935+
instrLen = 1; // Fallback to avoid infinite loop
2936+
addr += instrLen;
2937+
}
2938+
}
2939+
}
2940+
2941+
LogInfo("Analyzing {} instruction addresses for execution traces...", allInstructionAddresses.size());
2942+
2943+
// Query TTD for execute access covering the entire executable range
2944+
uint64_t minAddr = *allInstructionAddresses.begin();
2945+
uint64_t maxAddr = *allInstructionAddresses.rbegin();
2946+
2947+
auto events = GetTTDMemoryAccessForAddress(minAddr, maxAddr - minAddr + 16, TTDMemoryExecute);
2948+
2949+
for (const auto& event : events)
2950+
{
2951+
if (event.accessType == TTDMemoryExecute)
29312952
{
2932-
if (event.accessType == TTDMemoryExecute)
2953+
// Only count instruction addresses that we actually care about
2954+
if (allInstructionAddresses.find(event.instructionAddress) != allInstructionAddresses.end())
29332955
{
29342956
m_executedInstructions.insert(event.instructionAddress);
29352957
}
29362958
}
29372959
}
29382960

29392961
m_codeCoverageAnalysisRun = true;
2940-
LogInfo("TTD code coverage analysis completed. Found {} executed instructions.", m_executedInstructions.size());
2962+
LogInfo("TTD code coverage analysis completed. Found {} executed instructions out of {} total.",
2963+
m_executedInstructions.size(), allInstructionAddresses.size());
29412964

29422965
return true;
29432966
}
29442967

29452968

2969+
size_t DebuggerController::GetExecutedInstructionCount() const
2970+
{
2971+
return m_executedInstructions.size();
2972+
}
2973+
2974+
2975+
bool DebuggerController::SaveCodeCoverageToFile(const std::string& filePath) const
2976+
{
2977+
if (!m_codeCoverageAnalysisRun)
2978+
{
2979+
LogError("No code coverage analysis has been run");
2980+
return false;
2981+
}
2982+
2983+
try
2984+
{
2985+
std::ofstream file(filePath, std::ios::binary);
2986+
if (!file.is_open())
2987+
{
2988+
LogError("Failed to open file for writing: {}", filePath);
2989+
return false;
2990+
}
2991+
2992+
// Write header
2993+
uint32_t magic = 0x54544443; // "TTDC" - TTD Coverage
2994+
uint32_t version = 1;
2995+
size_t count = m_executedInstructions.size();
2996+
2997+
file.write(reinterpret_cast<const char*>(&magic), sizeof(magic));
2998+
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
2999+
file.write(reinterpret_cast<const char*>(&count), sizeof(count));
3000+
3001+
// Write addresses
3002+
for (uint64_t addr : m_executedInstructions)
3003+
{
3004+
file.write(reinterpret_cast<const char*>(&addr), sizeof(addr));
3005+
}
3006+
3007+
file.close();
3008+
LogInfo("Saved {} executed instruction addresses to {}", count, filePath);
3009+
return true;
3010+
}
3011+
catch (const std::exception& e)
3012+
{
3013+
LogError("Error saving code coverage: {}", e.what());
3014+
return false;
3015+
}
3016+
}
3017+
3018+
3019+
bool DebuggerController::LoadCodeCoverageFromFile(const std::string& filePath)
3020+
{
3021+
try
3022+
{
3023+
std::ifstream file(filePath, std::ios::binary);
3024+
if (!file.is_open())
3025+
{
3026+
LogError("Failed to open file for reading: {}", filePath);
3027+
return false;
3028+
}
3029+
3030+
// Read header
3031+
uint32_t magic, version;
3032+
size_t count;
3033+
3034+
file.read(reinterpret_cast<char*>(&magic), sizeof(magic));
3035+
if (magic != 0x54544443)
3036+
{
3037+
LogError("Invalid file format (magic number mismatch)");
3038+
return false;
3039+
}
3040+
3041+
file.read(reinterpret_cast<char*>(&version), sizeof(version));
3042+
if (version != 1)
3043+
{
3044+
LogError("Unsupported file version: {}", version);
3045+
return false;
3046+
}
3047+
3048+
file.read(reinterpret_cast<char*>(&count), sizeof(count));
3049+
3050+
// Clear existing data and read addresses
3051+
m_executedInstructions.clear();
3052+
3053+
for (size_t i = 0; i < count; i++)
3054+
{
3055+
uint64_t addr;
3056+
file.read(reinterpret_cast<char*>(&addr), sizeof(addr));
3057+
m_executedInstructions.insert(addr);
3058+
}
3059+
3060+
file.close();
3061+
m_codeCoverageAnalysisRun = true;
3062+
3063+
LogInfo("Loaded {} executed instruction addresses from {}", count, filePath);
3064+
return true;
3065+
}
3066+
catch (const std::exception& e)
3067+
{
3068+
LogError("Error loading code coverage: {}", e.what());
3069+
return false;
3070+
}
3071+
}
3072+
3073+
29463074
void DebuggerController::OnRebased(BinaryView* oldView, BinaryView* newView)
29473075
{
29483076
m_data = newView;

core/debuggercontroller.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ namespace BinaryNinjaDebugger {
366366
// TTD Code Coverage Analysis Methods
367367
bool IsInstructionExecuted(uint64_t address);
368368
bool RunCodeCoverageAnalysis();
369+
size_t GetExecutedInstructionCount() const;
370+
bool SaveCodeCoverageToFile(const std::string& filePath) const;
371+
bool LoadCodeCoverageFromFile(const std::string& filePath);
369372

370373
void OnRebased(BinaryView* oldView, BinaryView* newView);
371374

core/ffi.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,21 @@ bool BNDebuggerRunCodeCoverageAnalysis(BNDebuggerController* controller)
11171117
return controller->object->RunCodeCoverageAnalysis();
11181118
}
11191119

1120+
size_t BNDebuggerGetExecutedInstructionCount(BNDebuggerController* controller)
1121+
{
1122+
return controller->object->GetExecutedInstructionCount();
1123+
}
1124+
1125+
bool BNDebuggerSaveCodeCoverageToFile(BNDebuggerController* controller, const char* filePath)
1126+
{
1127+
return controller->object->SaveCodeCoverageToFile(std::string(filePath));
1128+
}
1129+
1130+
bool BNDebuggerLoadCodeCoverageFromFile(BNDebuggerController* controller, const char* filePath)
1131+
{
1132+
return controller->object->LoadCodeCoverageFromFile(std::string(filePath));
1133+
}
1134+
11201135
void BNDebuggerFreeTTDMemoryEvents(BNDebuggerTTDMemoryEvent* events)
11211136
{
11221137
if (events)

ui/ttdanalysisdialog.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ void TTDAnalysisWorker::run()
5454
success = m_controller->RunCodeCoverageAnalysis();
5555
if (success)
5656
{
57-
// The result count would need to be exposed from the controller
58-
// For now, we'll use a placeholder
59-
resultCount = 1; // Placeholder - would need API to get actual count
60-
message = "Code coverage analysis completed successfully";
57+
resultCount = m_controller->GetExecutedInstructionCount();
58+
message = QString("Code coverage analysis completed successfully. Found %1 executed instructions.").arg(resultCount);
6159
}
6260
else
6361
{
@@ -232,6 +230,15 @@ void TTDAnalysisDialog::populateAnalysisList()
232230
codeCoverage.cachePath = getDefaultCachePath(TTDAnalysisType::CodeCoverage);
233231
codeCoverage.resultCount = 0;
234232

233+
// Check if cache file exists
234+
QFileInfo cacheFile(codeCoverage.cachePath);
235+
QFileInfo dataFile(codeCoverage.cachePath + ".data");
236+
if (cacheFile.exists() && dataFile.exists())
237+
{
238+
codeCoverage.description += "\n\nCached results available from: " + cacheFile.lastModified().toString();
239+
codeCoverage.status = TTDAnalysisStatus::LoadedFromCache;
240+
}
241+
235242
m_analysisResults.append(codeCoverage);
236243

237244
// Update list widget
@@ -532,6 +539,7 @@ bool TTDAnalysisDialog::saveAnalysisResults(const TTDAnalysisResult& result)
532539
cacheDir.mkpath(".");
533540
}
534541

542+
// Save metadata as JSON
535543
QJsonObject json;
536544
json["type"] = static_cast<int>(result.type);
537545
json["name"] = result.name;
@@ -547,6 +555,15 @@ bool TTDAnalysisDialog::saveAnalysisResults(const TTDAnalysisResult& result)
547555
return false;
548556

549557
file.write(doc.toJson());
558+
file.close();
559+
560+
// Save actual analysis data using controller
561+
if (result.type == TTDAnalysisType::CodeCoverage && m_controller)
562+
{
563+
QString dataPath = cachePath + ".data";
564+
return m_controller->SaveCodeCoverageToFile(dataPath.toStdString());
565+
}
566+
550567
return true;
551568
}
552569

@@ -570,6 +587,19 @@ bool TTDAnalysisDialog::loadAnalysisResults(TTDAnalysisResult& result)
570587
result.resultCount = json["resultCount"].toVariant().toULongLong();
571588
result.lastRun = QDateTime::fromString(json["lastRun"].toString(), Qt::ISODate);
572589

590+
file.close();
591+
592+
// Load actual analysis data using controller
593+
if (result.type == TTDAnalysisType::CodeCoverage && m_controller)
594+
{
595+
QString dataPath = cachePath + ".data";
596+
QFileInfo dataFile(dataPath);
597+
if (dataFile.exists())
598+
{
599+
return m_controller->LoadCodeCoverageFromFile(dataPath.toStdString());
600+
}
601+
}
602+
573603
return true;
574604
}
575605

0 commit comments

Comments
 (0)