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