yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
watchpoint_manager.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <algorithm>
5#include "absl/strings/str_format.h"
6#include "util/log.h"
7
8namespace yaze {
9namespace emu {
10
11uint32_t WatchpointManager::AddWatchpoint(uint32_t start_address, uint32_t end_address,
12 bool track_reads, bool track_writes,
13 bool break_on_access,
14 const std::string& description) {
15 Watchpoint wp;
16 wp.id = next_id_++;
17 wp.start_address = start_address;
18 wp.end_address = end_address;
19 wp.track_reads = track_reads;
20 wp.track_writes = track_writes;
21 wp.break_on_access = break_on_access;
22 wp.enabled = true;
23 wp.description = description.empty()
24 ? absl::StrFormat("Watch $%06X-$%06X", start_address, end_address)
25 : description;
26
27 watchpoints_[wp.id] = wp;
28
29 LOG_INFO("Watchpoint", "Added watchpoint #%d: %s (R=%d, W=%d, Break=%d)",
30 wp.id, wp.description.c_str(), track_reads, track_writes, break_on_access);
31
32 return wp.id;
33}
34
36 auto it = watchpoints_.find(id);
37 if (it != watchpoints_.end()) {
38 LOG_INFO("Watchpoint", "Removed watchpoint #%d", id);
39 watchpoints_.erase(it);
40 }
41}
42
43void WatchpointManager::SetEnabled(uint32_t id, bool enabled) {
44 auto it = watchpoints_.find(id);
45 if (it != watchpoints_.end()) {
46 it->second.enabled = enabled;
47 LOG_INFO("Watchpoint", "Watchpoint #%d %s", id, enabled ? "enabled" : "disabled");
48 }
49}
50
51bool WatchpointManager::OnMemoryAccess(uint32_t pc, uint32_t address, bool is_write,
52 uint8_t old_value, uint8_t new_value,
53 uint64_t cycle_count) {
54 bool should_break = false;
55
56 for (auto& [id, wp] : watchpoints_) {
57 if (!wp.enabled || !IsInRange(wp, address)) {
58 continue;
59 }
60
61 // Check if this access type is tracked
62 bool should_log = (is_write && wp.track_writes) || (!is_write && wp.track_reads);
63 if (!should_log) {
64 continue;
65 }
66
67 // Log the access
68 AccessLog log;
69 log.pc = pc;
70 log.address = address;
71 log.old_value = old_value;
72 log.new_value = new_value;
73 log.is_write = is_write;
74 log.cycle_count = cycle_count;
75 log.description = absl::StrFormat("%s at $%06X: $%02X -> $%02X (PC=$%06X)",
76 is_write ? "WRITE" : "READ",
77 address, old_value, new_value, pc);
78
79 wp.history.push_back(log);
80
81 // Limit history size
82 if (wp.history.size() > Watchpoint::kMaxHistorySize) {
83 wp.history.pop_front();
84 }
85
86 // Check if should break
87 if (wp.break_on_access) {
88 should_break = true;
89 LOG_INFO("Watchpoint", "Hit watchpoint #%d: %s", id, log.description.c_str());
90 }
91 }
92
93 return should_break;
94}
95
96std::vector<WatchpointManager::Watchpoint> WatchpointManager::GetAllWatchpoints() const {
97 std::vector<Watchpoint> result;
98 result.reserve(watchpoints_.size());
99 for (const auto& [id, wp] : watchpoints_) {
100 result.push_back(wp);
101 }
102 std::sort(result.begin(), result.end(),
103 [](const Watchpoint& a, const Watchpoint& b) { return a.id < b.id; });
104 return result;
105}
106
107std::vector<WatchpointManager::AccessLog> WatchpointManager::GetHistory(
108 uint32_t address, int max_entries) const {
109 std::vector<AccessLog> result;
110
111 for (const auto& [id, wp] : watchpoints_) {
112 if (IsInRange(wp, address)) {
113 for (const auto& log : wp.history) {
114 if (log.address == address) {
115 result.push_back(log);
116 if (result.size() >= static_cast<size_t>(max_entries)) {
117 break;
118 }
119 }
120 }
121 }
122 }
123
124 return result;
125}
126
128 LOG_INFO("Watchpoint", "Cleared all watchpoints (%zu total)", watchpoints_.size());
129 watchpoints_.clear();
130}
131
133 for (auto& [id, wp] : watchpoints_) {
134 wp.history.clear();
135 }
136 LOG_INFO("Watchpoint", "Cleared all watchpoint history");
137}
138
139bool WatchpointManager::ExportHistoryToCSV(const std::string& filepath) const {
140 std::ofstream out(filepath);
141 if (!out.is_open()) {
142 return false;
143 }
144
145 // CSV Header
146 out << "Watchpoint,PC,Address,Type,OldValue,NewValue,Cycle,Description\n";
147
148 for (const auto& [id, wp] : watchpoints_) {
149 for (const auto& log : wp.history) {
150 out << absl::StrFormat("%d,$%06X,$%06X,%s,$%02X,$%02X,%llu,\"%s\"\n",
151 id, log.pc, log.address,
152 log.is_write ? "WRITE" : "READ",
153 log.old_value, log.new_value, log.cycle_count,
154 log.description);
155 }
156 }
157
158 out.close();
159 LOG_INFO("Watchpoint", "Exported watchpoint history to %s", filepath.c_str());
160 return true;
161}
162
163} // namespace emu
164} // namespace yaze
165
uint32_t AddWatchpoint(uint32_t start_address, uint32_t end_address, bool track_reads, bool track_writes, bool break_on_access=false, const std::string &description="")
Add a memory watchpoint.
bool ExportHistoryToCSV(const std::string &filepath) const
Export access history to CSV.
std::vector< Watchpoint > GetAllWatchpoints() const
Get all watchpoints.
std::unordered_map< uint32_t, Watchpoint > watchpoints_
std::vector< AccessLog > GetHistory(uint32_t address, int max_entries=100) const
Get access history for a specific address.
bool IsInRange(const Watchpoint &wp, uint32_t address) const
void ClearAll()
Clear all watchpoints.
bool OnMemoryAccess(uint32_t pc, uint32_t address, bool is_write, uint8_t old_value, uint8_t new_value, uint64_t cycle_count)
Check if memory access should break/log.
void ClearHistory()
Clear history for all watchpoints.
void SetEnabled(uint32_t id, bool enabled)
Enable or disable a watchpoint.
void RemoveWatchpoint(uint32_t id)
Remove a watchpoint.
#define LOG_INFO(category, format,...)
Definition log.h:106
Main namespace for the application.