Don't try to unregister killed debug CBs
[lsnes.git] / src / core / debug.cpp
blobb1e3bba5c36434a54aada6315a30b8b20eb2d6e5
1 #include "cmdhelp/debug.hpp"
2 #include "core/command.hpp"
3 #include "core/debug.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/instance.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/messages.hpp"
8 #include "core/moviedata.hpp"
9 #include "core/rom.hpp"
10 #include "library/directory.hpp"
11 #include "library/memoryspace.hpp"
13 #include <stdexcept>
14 #include <list>
15 #include <map>
16 #include <fstream>
19 namespace
22 unsigned debug_flag(debug_context::etype type)
24 switch(type) {
25 case debug_context::DEBUG_READ: return 1;
26 case debug_context::DEBUG_WRITE: return 2;
27 case debug_context::DEBUG_EXEC: return 4;
28 case debug_context::DEBUG_TRACE: return 8;
29 case debug_context::DEBUG_FRAME: return 0;
30 default: throw std::runtime_error("Invalid debug callback type");
35 namespace
37 template<class T> void kill_hooks(T& cblist, debug_context::etype type)
39 while(!cblist.empty()) {
40 if(cblist.begin()->second.empty()) {
41 cblist.erase(cblist.begin()->first);
42 continue;
44 auto key = cblist.begin()->first;
45 auto tmp = cblist.begin()->second.begin();
46 (*tmp)->killed(key, type);
47 cblist.begin()->second.erase(cblist.begin()->second.begin());
49 cblist.clear();
53 debug_context::debug_context(emulator_dispatch& _dispatch, loaded_rom& _rom, memory_space& _mspace,
54 command::group& _cmd)
55 : edispatch(_dispatch), rom(_rom), mspace(_mspace), cmd(_cmd),
56 showhooks(cmd, CDEBUG::scb, [this]() { this->do_showhooks(); }),
57 genevent(cmd, CDEBUG::genevt, [this](const std::string& a) { this->do_genevent(a); }),
58 tracecmd(cmd, CDEBUG::tr, [this](const std::string& a) { this->do_tracecmd(a); })
62 debug_context::callback_base::~callback_base()
66 const uint64_t debug_context::all_addresses = 0xFFFFFFFFFFFFFFFFULL;
68 void debug_context::add_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
70 auto& core = CORE();
71 std::map<uint64_t, cb_list>& xcb = get_lists(type);
72 if(!corechange_r) {
73 corechange.set(edispatch.core_change, [this]() { this->core_change(); });
74 corechange_r = true;
76 if(!xcb.count(addr) && type != DEBUG_FRAME)
77 core.rom->set_debug_flags(addr, debug_flag(type), 0);
78 auto& lst = xcb[addr];
79 lst.push_back(&cb);
82 void debug_context::remove_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
84 std::map<uint64_t, cb_list>& xcb = get_lists(type);
85 if(type == DEBUG_FRAME) addr = 0;
86 if(!xcb.count(addr)) return;
87 auto& l = xcb[addr];
88 for(auto i = l.begin(); i != l.end(); i++) {
89 if(*i == &cb) {
90 l.erase(i);
91 break;
94 if(xcb[addr].empty()) {
95 xcb.erase(addr);
96 if(type != DEBUG_FRAME)
97 rom.set_debug_flags(addr, 0, debug_flag(type));
101 void debug_context::do_callback_read(uint64_t addr, uint64_t value)
103 params p;
104 p.type = DEBUG_READ;
105 p.rwx.addr = addr;
106 p.rwx.value = value;
108 requesting_break = false;
109 cb_list* cb1 = read_cb.count(all_addresses) ? &read_cb[all_addresses] : &dummy_cb;
110 cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
111 auto _cb1 = *cb1;
112 auto _cb2 = *cb2;
113 for(auto& i : _cb1) i->callback(p);
114 for(auto& i : _cb2) i->callback(p);
115 if(requesting_break)
116 do_break_pause();
119 void debug_context::do_callback_write(uint64_t addr, uint64_t value)
121 params p;
122 p.type = DEBUG_WRITE;
123 p.rwx.addr = addr;
124 p.rwx.value = value;
126 requesting_break = false;
127 cb_list* cb1 = write_cb.count(all_addresses) ? &write_cb[all_addresses] : &dummy_cb;
128 cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
129 auto _cb1 = *cb1;
130 auto _cb2 = *cb2;
131 for(auto& i : _cb1) i->callback(p);
132 for(auto& i : _cb2) i->callback(p);
133 if(requesting_break)
134 do_break_pause();
137 void debug_context::do_callback_exec(uint64_t addr, uint64_t cpu)
139 params p;
140 p.type = DEBUG_EXEC;
141 p.rwx.addr = addr;
142 p.rwx.value = cpu;
144 requesting_break = false;
145 cb_list* cb1 = exec_cb.count(all_addresses) ? &exec_cb[all_addresses] : &dummy_cb;
146 cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
147 auto _cb1 = *cb1;
148 auto _cb2 = *cb2;
149 if((1ULL << cpu) & xmask)
150 for(auto& i : _cb1) i->callback(p);
151 for(auto& i : _cb2) i->callback(p);
152 if(requesting_break)
153 do_break_pause();
156 void debug_context::do_callback_trace(uint64_t cpu, const char* str, bool true_insn)
158 params p;
159 p.type = DEBUG_TRACE;
160 p.trace.cpu = cpu;
161 p.trace.decoded_insn = str;
162 p.trace.true_insn = true_insn;
164 requesting_break = false;
165 cb_list* cb = trace_cb.count(cpu) ? &trace_cb[cpu] : &dummy_cb;
166 auto _cb = *cb;
167 for(auto& i : _cb) i->callback(p);
168 if(requesting_break)
169 do_break_pause();
172 void debug_context::do_callback_frame(uint64_t frame, bool loadstate)
174 params p;
175 p.type = DEBUG_FRAME;
176 p.frame.frame = frame;
177 p.frame.loadstated = loadstate;
179 cb_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb;
180 auto _cb = *cb;
181 for(auto& i : _cb) i->callback(p);
184 void debug_context::set_cheat(uint64_t addr, uint64_t value)
186 rom.set_cheat(addr, value, true);
189 void debug_context::clear_cheat(uint64_t addr)
191 rom.set_cheat(addr, 0, false);
194 void debug_context::setxmask(uint64_t mask)
196 xmask = mask;
199 bool debug_context::is_tracelogging(uint64_t cpu)
201 return (trace_outputs.count(cpu) != 0);
204 void debug_context::set_tracelog_change_cb(std::function<void()> cb)
206 tracelog_change_cb = cb;
209 void debug_context::core_change()
211 rom.debug_reset();
212 kill_hooks(read_cb, DEBUG_READ);
213 kill_hooks(write_cb, DEBUG_WRITE);
214 kill_hooks(exec_cb, DEBUG_EXEC);
215 kill_hooks(trace_cb, DEBUG_TRACE);
218 void debug_context::request_break()
220 requesting_break = true;
223 debug_context::tracelog_file::tracelog_file(debug_context& _parent)
224 : parent(_parent)
228 debug_context::tracelog_file::~tracelog_file()
232 void debug_context::tracelog_file::callback(const debug_context::params& p)
234 if(!parent.trace_outputs.count(p.trace.cpu)) return;
235 parent.trace_outputs[p.trace.cpu]->stream << p.trace.decoded_insn << std::endl;
238 void debug_context::tracelog_file::killed(uint64_t addr, debug_context::etype type)
240 refcnt--;
241 if(!refcnt)
242 delete this;
245 void debug_context::tracelog(uint64_t proc, const std::string& filename)
247 if(filename == "") {
248 if(!trace_outputs.count(proc))
249 return;
250 remove_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
251 trace_outputs[proc]->refcnt--;
252 if(!trace_outputs[proc]->refcnt)
253 delete trace_outputs[proc];
254 trace_outputs.erase(proc);
255 messages << "Stopped tracelogging processor #" << proc << std::endl;
256 if(tracelog_change_cb) tracelog_change_cb();
257 return;
259 if(trace_outputs.count(proc)) throw std::runtime_error("Already tracelogging");
260 std::string full_filename = directory::absolute_path(filename);
261 bool found = false;
262 for(auto i : trace_outputs) {
263 if(i.second->full_filename == full_filename) {
264 i.second->refcnt++;
265 trace_outputs[proc] = i.second;
266 found = true;
267 break;
270 if(!found) {
271 trace_outputs[proc] = new tracelog_file(*this);
272 trace_outputs[proc]->refcnt = 1;
273 trace_outputs[proc]->full_filename = full_filename;
274 trace_outputs[proc]->stream.open(full_filename);
275 if(!trace_outputs[proc]->stream) {
276 delete trace_outputs[proc];
277 trace_outputs.erase(proc);
278 throw std::runtime_error("Can't open '" + full_filename + "'");
281 try {
282 add_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
283 } catch(std::exception& e) {
284 messages << "Error starting tracelogging: " << e.what() << std::endl;
285 trace_outputs[proc]->refcnt--;
286 if(!trace_outputs[proc]->refcnt)
287 delete trace_outputs[proc];
288 trace_outputs.erase(proc);
289 throw;
291 messages << "Tracelogging processor #" << proc << " to '" << filename << "'" << std::endl;
292 if(tracelog_change_cb) tracelog_change_cb();
295 void debug_context::do_showhooks()
297 for(auto& i : read_cb)
298 for(auto& j : i.second)
299 messages << "READ addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
300 << std::endl;
301 for(auto& i : write_cb)
302 for(auto& j : i.second)
303 messages << "WRITE addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
304 << std::endl;
305 for(auto& i : exec_cb)
306 for(auto& j : i.second)
307 messages << "EXEC addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
308 << std::endl;
309 for(auto& i : trace_cb)
310 for(auto& j : i.second)
311 messages << "TRACE proc=" << i.first << " handle=" << &j << std::endl;
312 for(auto& i : frame_cb)
313 for(auto& j : i.second)
314 messages << "FRAME handle=" << &j << std::endl;
317 void debug_context::do_genevent(const std::string& args)
319 regex_results r = regex("([^ \t]+) ([^ \t]+) (.+)", args);
320 if(!r) throw std::runtime_error("generate-memory-event: Bad arguments");
321 if(r[1] == "r") {
322 uint64_t addr = parse_value<uint64_t>(r[2]);
323 uint64_t val = parse_value<uint64_t>(r[3]);
324 do_callback_read(addr, val);
325 } else if(r[1] == "w") {
326 uint64_t addr = parse_value<uint64_t>(r[2]);
327 uint64_t val = parse_value<uint64_t>(r[3]);
328 do_callback_write(addr, val);
329 } else if(r[1] == "x") {
330 uint64_t addr = parse_value<uint64_t>(r[2]);
331 uint64_t val = parse_value<uint64_t>(r[3]);
332 do_callback_exec(addr, val);
333 } else if(r[1] == "t") {
334 uint64_t proc = parse_value<uint64_t>(r[2]);
335 std::string str = r[3];
336 do_callback_trace(proc, str.c_str());
337 } else
338 throw std::runtime_error("Invalid operation");
341 void debug_context::do_tracecmd(const std::string& args)
343 regex_results r = regex("([^ \t]+)([ \t]+(.+))?", args);
344 if(!r) throw std::runtime_error("tracelog: Bad arguments");
345 std::string cpu = r[1];
346 std::string filename = r[3];
347 uint64_t _cpu = 0;
348 for(auto i : rom.get_trace_cpus()) {
349 if(cpu == i)
350 goto out;
351 _cpu++;
353 throw std::runtime_error("tracelog: Invalid CPU");
354 out:
355 tracelog(_cpu, filename);