mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-21 04:33:56 +00:00
Finish tenet tracer refactoring
This commit is contained in:
@@ -13,11 +13,6 @@
|
||||
#include <utils/finally.hpp>
|
||||
#include <utils/interupt_handler.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
#ifdef OS_EMSCRIPTEN
|
||||
#include <event_handler.hpp>
|
||||
#endif
|
||||
@@ -301,20 +296,11 @@ namespace
|
||||
|
||||
win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str());
|
||||
|
||||
// Enable TenetTracer and assign it to the context.
|
||||
std::unique_ptr<TenetTracer> tenet_tracer;
|
||||
std::optional<tenet_tracer> tenet_tracer{};
|
||||
if (options.tenet_trace)
|
||||
{
|
||||
win_emu->log.log("Tenet Tracer enabled. Output: tenet_trace.log\n");
|
||||
tenet_tracer = std::make_unique<TenetTracer>(*win_emu, "tenet_trace.log");
|
||||
|
||||
// Set up the hook to call the tracer for each instruction.
|
||||
win_emu->emu().hook_memory_execution([&](uint64_t address) {
|
||||
if (tenet_tracer)
|
||||
{
|
||||
tenet_tracer->process_instruction(address);
|
||||
}
|
||||
});
|
||||
tenet_tracer.emplace(*win_emu, "tenet_trace.log");
|
||||
}
|
||||
|
||||
register_analysis_callbacks(context);
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <ranges>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <unordered_set>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
#include <platform/platform.hpp>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "std_include.hpp"
|
||||
#include "tenet_tracer.hpp"
|
||||
#include "scoped_hook.hpp"
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -47,68 +48,72 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
TenetTracer::TenetTracer(windows_emulator& win_emu, const std::string& log_filename)
|
||||
: m_win_emu(win_emu),
|
||||
m_log_file(log_filename)
|
||||
tenet_tracer::tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename)
|
||||
: win_emu_(win_emu),
|
||||
log_file_(log_filename)
|
||||
{
|
||||
if (!m_log_file)
|
||||
if (!log_file_)
|
||||
{
|
||||
throw std::runtime_error("TenetTracer: Failed to open log file -> " + log_filename);
|
||||
throw std::runtime_error("TenetTracer: Failed to open log file -> " + log_filename.string());
|
||||
}
|
||||
|
||||
auto& emu = m_win_emu.emu();
|
||||
auto& emu = win_emu_.emu();
|
||||
|
||||
auto* read_hook = emu.hook_memory_read(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
|
||||
this->log_memory_read(a, d, s); //
|
||||
});
|
||||
|
||||
m_read_hook = scoped_hook(emu, read_hook);
|
||||
read_hook_ = scoped_hook(emu, read_hook);
|
||||
|
||||
auto* write_hook = emu.hook_memory_write(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
|
||||
this->log_memory_write(a, d, s); //
|
||||
});
|
||||
write_hook_ = scoped_hook(emu, write_hook);
|
||||
|
||||
m_write_hook = scoped_hook(emu, write_hook);
|
||||
auto* execute_hook = emu.hook_memory_execution([&](uint64_t address) {
|
||||
this->process_instruction(address); //
|
||||
});
|
||||
execute_hook_ = scoped_hook(emu, execute_hook);
|
||||
}
|
||||
|
||||
TenetTracer::~TenetTracer()
|
||||
tenet_tracer::~tenet_tracer()
|
||||
{
|
||||
filter_and_write_buffer();
|
||||
|
||||
if (m_log_file.is_open())
|
||||
if (log_file_.is_open())
|
||||
{
|
||||
m_log_file.close();
|
||||
log_file_.close();
|
||||
}
|
||||
}
|
||||
|
||||
void TenetTracer::filter_and_write_buffer()
|
||||
void tenet_tracer::filter_and_write_buffer()
|
||||
{
|
||||
if (m_raw_log_buffer.empty())
|
||||
if (raw_log_buffer_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* exe_module = m_win_emu.mod_manager.executable;
|
||||
const auto* exe_module = win_emu_.mod_manager.executable;
|
||||
if (!exe_module)
|
||||
{
|
||||
for (const auto& line : m_raw_log_buffer)
|
||||
for (const auto& line : raw_log_buffer_)
|
||||
{
|
||||
m_log_file << line << '\n';
|
||||
log_file_ << line << '\n';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_raw_log_buffer.empty())
|
||||
if (!raw_log_buffer_.empty())
|
||||
{
|
||||
m_log_file << m_raw_log_buffer.front() << '\n';
|
||||
log_file_ << raw_log_buffer_.front() << '\n';
|
||||
}
|
||||
|
||||
bool currently_outside = false;
|
||||
std::map<std::string, std::string> accumulated_changes;
|
||||
|
||||
for (size_t i = 1; i < m_raw_log_buffer.size(); ++i)
|
||||
for (size_t i = 1; i < raw_log_buffer_.size(); ++i)
|
||||
{
|
||||
const auto& line = m_raw_log_buffer[i];
|
||||
const auto& line = raw_log_buffer_[i];
|
||||
|
||||
size_t rip_pos = line.find("rip=0x");
|
||||
if (rip_pos == std::string::npos)
|
||||
@@ -120,92 +125,85 @@ void TenetTracer::filter_and_write_buffer()
|
||||
uint64_t address = std::strtoull(line.c_str() + rip_pos + 6, &end_ptr, 16);
|
||||
|
||||
bool is_line_inside = exe_module->is_within(address);
|
||||
const auto _1 = utils::finally([&] {
|
||||
currently_outside = !is_line_inside; //
|
||||
});
|
||||
|
||||
if (is_line_inside)
|
||||
if (!is_line_inside)
|
||||
{
|
||||
// We are inside the main module.
|
||||
if (currently_outside)
|
||||
{
|
||||
// JUST ENTERED FROM OUTSIDE (moment of return from API)
|
||||
// 1. Create a synthetic log line from the accumulated changes.
|
||||
if (!accumulated_changes.empty())
|
||||
{
|
||||
std::stringstream summary_line;
|
||||
bool first = true;
|
||||
|
||||
// Separate rip from the map as it will be added at the end.
|
||||
auto rip_it = accumulated_changes.find("rip");
|
||||
std::string last_rip;
|
||||
if (rip_it != accumulated_changes.end())
|
||||
{
|
||||
last_rip = rip_it->second;
|
||||
accumulated_changes.erase(rip_it);
|
||||
}
|
||||
|
||||
for (const auto& pair : accumulated_changes)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << pair.first << "=" << pair.second;
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Add the last known rip at the end.
|
||||
if (!last_rip.empty())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << "rip=" << last_rip;
|
||||
}
|
||||
|
||||
m_log_file << summary_line.str() << '\n';
|
||||
}
|
||||
accumulated_changes.clear();
|
||||
}
|
||||
|
||||
// 2. Write the current line within the main module.
|
||||
m_log_file << line << '\n';
|
||||
currently_outside = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are outside the main module.
|
||||
// 1. Accumulate the changes.
|
||||
parse_and_accumulate_changes(line, accumulated_changes);
|
||||
currently_outside = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto _2 = utils::finally([&] {
|
||||
log_file_ << line << '\n'; //
|
||||
});
|
||||
|
||||
if (!currently_outside || accumulated_changes.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream summary_line;
|
||||
bool first = true;
|
||||
|
||||
auto rip_it = accumulated_changes.find("rip");
|
||||
std::string last_rip;
|
||||
if (rip_it != accumulated_changes.end())
|
||||
{
|
||||
last_rip = rip_it->second;
|
||||
accumulated_changes.erase(rip_it);
|
||||
}
|
||||
|
||||
for (const auto& pair : accumulated_changes)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << pair.first << "=" << pair.second;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!last_rip.empty())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << "rip=" << last_rip;
|
||||
}
|
||||
|
||||
log_file_ << summary_line.str() << '\n';
|
||||
accumulated_changes.clear();
|
||||
}
|
||||
|
||||
m_raw_log_buffer.clear();
|
||||
raw_log_buffer_.clear();
|
||||
}
|
||||
|
||||
void TenetTracer::log_memory_read(uint64_t address, const void* data, size_t size)
|
||||
void tenet_tracer::log_memory_read(uint64_t address, const void* data, size_t size)
|
||||
{
|
||||
if (!m_mem_read_log.str().empty())
|
||||
if (!mem_read_log_.str().empty())
|
||||
{
|
||||
m_mem_read_log << ";";
|
||||
mem_read_log_ << ";";
|
||||
}
|
||||
|
||||
m_mem_read_log << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
mem_read_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
void TenetTracer::log_memory_write(uint64_t address, const void* data, size_t size)
|
||||
void tenet_tracer::log_memory_write(uint64_t address, const void* data, size_t size)
|
||||
{
|
||||
if (!m_mem_write_log.str().empty())
|
||||
if (!mem_write_log_.str().empty())
|
||||
{
|
||||
m_mem_write_log << ";";
|
||||
mem_write_log_ << ";";
|
||||
}
|
||||
|
||||
m_mem_write_log << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
mem_write_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
void TenetTracer::process_instruction(uint64_t address)
|
||||
void tenet_tracer::process_instruction(uint64_t address)
|
||||
{
|
||||
auto& emu = m_win_emu.emu();
|
||||
auto& emu = win_emu_.emu();
|
||||
std::stringstream trace_line;
|
||||
|
||||
std::array<uint64_t, GPRs_TO_TRACE.size()> current_regs;
|
||||
@@ -215,7 +213,7 @@ void TenetTracer::process_instruction(uint64_t address)
|
||||
}
|
||||
|
||||
bool first_entry = true;
|
||||
auto append_separator = [&]() {
|
||||
auto append_separator = [&] {
|
||||
if (!first_entry)
|
||||
{
|
||||
trace_line << ",";
|
||||
@@ -223,20 +221,20 @@ void TenetTracer::process_instruction(uint64_t address)
|
||||
first_entry = false;
|
||||
};
|
||||
|
||||
if (m_is_first_instruction)
|
||||
if (is_first_instruction_)
|
||||
{
|
||||
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
|
||||
{
|
||||
append_separator();
|
||||
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
|
||||
}
|
||||
m_is_first_instruction = false;
|
||||
is_first_instruction_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
|
||||
{
|
||||
if (m_previous_regs[i] != current_regs[i])
|
||||
if (previous_registers_[i] != current_regs[i])
|
||||
{
|
||||
append_separator();
|
||||
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
|
||||
@@ -247,26 +245,25 @@ void TenetTracer::process_instruction(uint64_t address)
|
||||
append_separator();
|
||||
trace_line << "rip=" << format_hex(address);
|
||||
|
||||
std::string mem_reads = m_mem_read_log.str();
|
||||
const auto mem_reads = mem_read_log_.str();
|
||||
if (!mem_reads.empty())
|
||||
{
|
||||
append_separator();
|
||||
trace_line << "mr=" << mem_reads;
|
||||
}
|
||||
std::string mem_writes = m_mem_write_log.str();
|
||||
|
||||
const auto mem_writes = mem_write_log_.str();
|
||||
if (!mem_writes.empty())
|
||||
{
|
||||
append_separator();
|
||||
trace_line << "mw=" << mem_writes;
|
||||
}
|
||||
|
||||
// Add the data to the buffer instead of writing directly to the file.
|
||||
m_raw_log_buffer.push_back(trace_line.str());
|
||||
raw_log_buffer_.push_back(trace_line.str());
|
||||
previous_registers_ = current_regs;
|
||||
|
||||
m_previous_regs = current_regs;
|
||||
|
||||
m_mem_read_log.str("");
|
||||
m_mem_read_log.clear();
|
||||
m_mem_write_log.str("");
|
||||
m_mem_write_log.clear();
|
||||
mem_read_log_.str("");
|
||||
mem_read_log_.clear();
|
||||
mem_write_log_.str("");
|
||||
mem_write_log_.clear();
|
||||
}
|
||||
@@ -3,14 +3,7 @@
|
||||
#include <windows_emulator.hpp>
|
||||
#include <emulator/x86_register.hpp>
|
||||
#include <emulator/scoped_hook.hpp>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
|
||||
// List of registers to trace for the x64 architecture.
|
||||
constexpr std::array<std::pair<x86_register, std::string_view>, 16> GPRs_TO_TRACE = {
|
||||
{
|
||||
{x86_register::rax, "rax"},
|
||||
@@ -32,39 +25,34 @@ constexpr std::array<std::pair<x86_register, std::string_view>, 16> GPRs_TO_TRAC
|
||||
},
|
||||
};
|
||||
|
||||
class TenetTracer
|
||||
class tenet_tracer
|
||||
{
|
||||
public:
|
||||
TenetTracer(windows_emulator& win_emu, const std::string& log_filename);
|
||||
~TenetTracer();
|
||||
tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename);
|
||||
~tenet_tracer();
|
||||
|
||||
TenetTracer(TenetTracer&) = delete;
|
||||
TenetTracer(const TenetTracer&) = delete;
|
||||
TenetTracer& operator=(TenetTracer&) = delete;
|
||||
TenetTracer& operator=(const TenetTracer&) = delete;
|
||||
|
||||
void process_instruction(uint64_t address);
|
||||
tenet_tracer(tenet_tracer&) = delete;
|
||||
tenet_tracer(const tenet_tracer&) = delete;
|
||||
tenet_tracer& operator=(tenet_tracer&) = delete;
|
||||
tenet_tracer& operator=(const tenet_tracer&) = delete;
|
||||
|
||||
private:
|
||||
void filter_and_write_buffer();
|
||||
void log_memory_read(uint64_t address, const void* data, size_t size);
|
||||
void log_memory_write(uint64_t address, const void* data, size_t size);
|
||||
void process_instruction(uint64_t address);
|
||||
|
||||
windows_emulator& m_win_emu;
|
||||
std::ofstream m_log_file;
|
||||
windows_emulator& win_emu_;
|
||||
std::ofstream log_file_;
|
||||
|
||||
// In-memory buffering for performance.
|
||||
std::vector<std::string> m_raw_log_buffer;
|
||||
std::vector<std::string> raw_log_buffer_;
|
||||
std::array<uint64_t, GPRs_TO_TRACE.size()> previous_registers_{};
|
||||
bool is_first_instruction_ = true;
|
||||
|
||||
// Use an array instead of a map to store the register state of the previous instruction.
|
||||
std::array<uint64_t, GPRs_TO_TRACE.size()> m_previous_regs{};
|
||||
bool m_is_first_instruction = true;
|
||||
std::stringstream mem_read_log_;
|
||||
std::stringstream mem_write_log_;
|
||||
|
||||
// To temporarily store memory operations.
|
||||
std::stringstream m_mem_read_log;
|
||||
std::stringstream m_mem_write_log;
|
||||
|
||||
// To manage memory hooks.
|
||||
scoped_hook m_read_hook;
|
||||
scoped_hook m_write_hook;
|
||||
scoped_hook read_hook_;
|
||||
scoped_hook write_hook_;
|
||||
scoped_hook execute_hook_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user