Some non-instance variables cleanup
[lsnes.git] / src / core / debug.cpp
blob4485cd44ef75597a67a91c0b3b9d4a6d05eba116
1 #include "core/command.hpp"
2 #include "core/debug.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/instance.hpp"
5 #include "core/mainloop.hpp"
6 #include "core/messages.hpp"
7 #include "core/moviedata.hpp"
8 #include "library/directory.hpp"
10 #include <stdexcept>
11 #include <list>
12 #include <map>
13 #include <fstream>
16 namespace
19 unsigned debug_flag(debug_context::etype type)
21 switch(type) {
22 case debug_context::DEBUG_READ: return 1;
23 case debug_context::DEBUG_WRITE: return 2;
24 case debug_context::DEBUG_EXEC: return 4;
25 case debug_context::DEBUG_TRACE: return 8;
26 case debug_context::DEBUG_FRAME: return 0;
27 default: throw std::runtime_error("Invalid debug callback type");
32 namespace
34 template<class T> void kill_hooks(T& cblist, debug_context::etype type)
36 while(!cblist.empty()) {
37 if(cblist.begin()->second.empty()) {
38 cblist.erase(cblist.begin()->first);
39 continue;
41 auto key = cblist.begin()->first;
42 auto tmp = cblist.begin()->second.begin();
43 cblist.begin()->second.erase(cblist.begin()->second.begin());
44 (*tmp)->killed(key, type);
46 cblist.clear();
50 debug_context::debug_context(emulator_dispatch& _dispatch)
51 : edispatch(_dispatch)
55 debug_context::callback_base::~callback_base()
59 const uint64_t debug_context::all_addresses = 0xFFFFFFFFFFFFFFFFULL;
61 void debug_context::add_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
63 std::map<uint64_t, cb_list>& xcb = get_lists(type);
64 if(!corechange_r) {
65 corechange.set(edispatch.core_change, [this]() { this->core_change(); });
66 corechange_r = true;
68 if(!xcb.count(addr) && type != DEBUG_FRAME)
69 our_rom.rtype->set_debug_flags(addr, debug_flag(type), 0);
70 auto& lst = xcb[addr];
71 lst.push_back(&cb);
74 void debug_context::remove_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
76 std::map<uint64_t, cb_list>& xcb = get_lists(type);
77 if(type == DEBUG_FRAME) addr = 0;
78 if(!xcb.count(addr)) return;
79 auto& l = xcb[addr];
80 for(auto i = l.begin(); i != l.end(); i++) {
81 if(*i == &cb) {
82 l.erase(i);
83 break;
86 if(xcb[addr].empty()) {
87 xcb.erase(addr);
88 if(type != DEBUG_FRAME)
89 our_rom.rtype->set_debug_flags(addr, 0, debug_flag(type));
93 void debug_context::do_callback_read(uint64_t addr, uint64_t value)
95 params p;
96 p.type = DEBUG_READ;
97 p.rwx.addr = addr;
98 p.rwx.value = value;
100 requesting_break = false;
101 cb_list* cb1 = read_cb.count(all_addresses) ? &read_cb[all_addresses] : &dummy_cb;
102 cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
103 auto _cb1 = *cb1;
104 auto _cb2 = *cb2;
105 for(auto& i : _cb1) i->callback(p);
106 for(auto& i : _cb2) i->callback(p);
107 if(requesting_break)
108 do_break_pause();
111 void debug_context::do_callback_write(uint64_t addr, uint64_t value)
113 params p;
114 p.type = DEBUG_WRITE;
115 p.rwx.addr = addr;
116 p.rwx.value = value;
118 requesting_break = false;
119 cb_list* cb1 = write_cb.count(all_addresses) ? &write_cb[all_addresses] : &dummy_cb;
120 cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
121 auto _cb1 = *cb1;
122 auto _cb2 = *cb2;
123 for(auto& i : _cb1) i->callback(p);
124 for(auto& i : _cb2) i->callback(p);
125 if(requesting_break)
126 do_break_pause();
129 void debug_context::do_callback_exec(uint64_t addr, uint64_t cpu)
131 params p;
132 p.type = DEBUG_EXEC;
133 p.rwx.addr = addr;
134 p.rwx.value = cpu;
136 requesting_break = false;
137 cb_list* cb1 = exec_cb.count(all_addresses) ? &exec_cb[all_addresses] : &dummy_cb;
138 cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
139 auto _cb1 = *cb1;
140 auto _cb2 = *cb2;
141 if((1ULL << cpu) & xmask)
142 for(auto& i : _cb1) i->callback(p);
143 for(auto& i : _cb2) i->callback(p);
144 if(requesting_break)
145 do_break_pause();
148 void debug_context::do_callback_trace(uint64_t cpu, const char* str, bool true_insn)
150 params p;
151 p.type = DEBUG_TRACE;
152 p.trace.cpu = cpu;
153 p.trace.decoded_insn = str;
154 p.trace.true_insn = true_insn;
156 requesting_break = false;
157 cb_list* cb = trace_cb.count(cpu) ? &trace_cb[cpu] : &dummy_cb;
158 auto _cb = *cb;
159 for(auto& i : _cb) i->callback(p);
160 if(requesting_break)
161 do_break_pause();
164 void debug_context::do_callback_frame(uint64_t frame, bool loadstate)
166 params p;
167 p.type = DEBUG_FRAME;
168 p.frame.frame = frame;
169 p.frame.loadstated = loadstate;
171 cb_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb;
172 auto _cb = *cb;
173 for(auto& i : _cb) i->callback(p);
176 void debug_context::set_cheat(uint64_t addr, uint64_t value)
178 our_rom.rtype->set_cheat(addr, value, true);
181 void debug_context::clear_cheat(uint64_t addr)
183 our_rom.rtype->set_cheat(addr, 0, false);
186 void debug_context::setxmask(uint64_t mask)
188 xmask = mask;
191 bool debug_context::is_tracelogging(uint64_t cpu)
193 return (trace_outputs.count(cpu) != 0);
196 void debug_context::set_tracelog_change_cb(std::function<void()> cb)
198 tracelog_change_cb = cb;
201 void debug_context::core_change()
203 our_rom.rtype->debug_reset();
204 kill_hooks(read_cb, DEBUG_READ);
205 kill_hooks(write_cb, DEBUG_WRITE);
206 kill_hooks(exec_cb, DEBUG_EXEC);
207 kill_hooks(trace_cb, DEBUG_TRACE);
210 void debug_context::request_break()
212 requesting_break = true;
215 debug_context::tracelog_file::tracelog_file(debug_context& _parent)
216 : parent(_parent)
220 debug_context::tracelog_file::~tracelog_file()
224 void debug_context::tracelog_file::callback(const debug_context::params& p)
226 if(!parent.trace_outputs.count(p.trace.cpu)) return;
227 parent.trace_outputs[p.trace.cpu]->stream << p.trace.decoded_insn << std::endl;
230 void debug_context::tracelog_file::killed(uint64_t addr, debug_context::etype type)
232 refcnt--;
233 if(!refcnt)
234 delete this;
237 void debug_context::tracelog(uint64_t proc, const std::string& filename)
239 if(filename == "") {
240 if(!trace_outputs.count(proc))
241 return;
242 remove_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
243 trace_outputs[proc]->refcnt--;
244 if(!trace_outputs[proc]->refcnt)
245 delete trace_outputs[proc];
246 trace_outputs.erase(proc);
247 messages << "Stopped tracelogging processor #" << proc << std::endl;
248 if(tracelog_change_cb) tracelog_change_cb();
249 return;
251 if(trace_outputs.count(proc)) throw std::runtime_error("Already tracelogging");
252 std::string full_filename = directory::absolute_path(filename);
253 bool found = false;
254 for(auto i : trace_outputs) {
255 if(i.second->full_filename == full_filename) {
256 i.second->refcnt++;
257 trace_outputs[proc] = i.second;
258 found = true;
259 break;
262 if(!found) {
263 trace_outputs[proc] = new tracelog_file(*this);
264 trace_outputs[proc]->refcnt = 1;
265 trace_outputs[proc]->full_filename = full_filename;
266 trace_outputs[proc]->stream.open(full_filename);
267 if(!trace_outputs[proc]->stream) {
268 delete trace_outputs[proc];
269 trace_outputs.erase(proc);
270 throw std::runtime_error("Can't open '" + full_filename + "'");
273 try {
274 add_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
275 } catch(std::exception& e) {
276 messages << "Error starting tracelogging: " << e.what() << std::endl;
277 trace_outputs[proc]->refcnt--;
278 if(!trace_outputs[proc]->refcnt)
279 delete trace_outputs[proc];
280 trace_outputs.erase(proc);
281 throw;
283 messages << "Tracelogging processor #" << proc << " to '" << filename << "'" << std::endl;
284 if(tracelog_change_cb) tracelog_change_cb();
287 namespace
289 command::fnptr<> CMD_callbacks_show(lsnes_cmds, "show-callbacks", "", "",
290 []() throw(std::bad_alloc, std::runtime_error) {
291 auto& core = CORE();
292 for(auto& i : core.dbg->read_cb)
293 for(auto& j : i.second)
294 messages << "READ addr=" << i.first << " handle=" << &j << std::endl;
295 for(auto& i : core.dbg->write_cb)
296 for(auto& j : i.second)
297 messages << "WRITE addr=" << i.first << " handle=" << &j << std::endl;
298 for(auto& i : core.dbg->exec_cb)
299 for(auto& j : i.second)
300 messages << "EXEC addr=" << i.first << " handle=" << &j << std::endl;
301 for(auto& i : core.dbg->trace_cb)
302 for(auto& j : i.second)
303 messages << "TRACE proc=" << i.first << " handle=" << &j << std::endl;
304 for(auto& i : core.dbg->frame_cb)
305 for(auto& j : i.second)
306 messages << "FRAME handle=" << &j << std::endl;
309 command::fnptr<const std::string&> CMD_generate_event(lsnes_cmds, "generate-memory-event", "", "",
310 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
311 regex_results r = regex("([^ \t]+) ([^ \t]+) (.+)", args);
312 if(!r) throw std::runtime_error("generate-memory-event: Bad arguments");
313 if(r[1] == "r") {
314 uint64_t addr = parse_value<uint64_t>(r[2]);
315 uint64_t val = parse_value<uint64_t>(r[3]);
316 CORE().dbg->do_callback_read(addr, val);
317 } else if(r[1] == "w") {
318 uint64_t addr = parse_value<uint64_t>(r[2]);
319 uint64_t val = parse_value<uint64_t>(r[3]);
320 CORE().dbg->do_callback_write(addr, val);
321 } else if(r[1] == "x") {
322 uint64_t addr = parse_value<uint64_t>(r[2]);
323 uint64_t val = parse_value<uint64_t>(r[3]);
324 CORE().dbg->do_callback_exec(addr, val);
325 } else if(r[1] == "t") {
326 uint64_t proc = parse_value<uint64_t>(r[2]);
327 std::string str = r[3];
328 CORE().dbg->do_callback_trace(proc, str.c_str());
329 } else
330 throw std::runtime_error("Invalid operation");
333 command::fnptr<const std::string&> CMD_tracelog(lsnes_cmds, "tracelog", "Trace log control",
334 "Trace log control\nSyntax: tracelog <cpuid> <file> Start tracing\nSyntax: tracelog <cpuid> "
335 "End tracing", [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
336 regex_results r = regex("([^ \t]+)([ \t]+(.+))?", args);
337 if(!r) throw std::runtime_error("tracelog: Bad arguments");
338 std::string cpu = r[1];
339 std::string filename = r[3];
340 uint64_t _cpu = 0;
341 for(auto i : our_rom.rtype->get_trace_cpus()) {
342 if(cpu == i)
343 goto out;
344 _cpu++;
346 throw std::runtime_error("tracelog: Invalid CPU");
347 out:
348 CORE().dbg->tracelog(_cpu, filename);