Allow binding commands to class instance
[lsnes.git] / src / core / debug.cpp
blob3d38b32d07a285e7fe6cca6548b8a92316728c7f
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"
12 #include <stdexcept>
13 #include <list>
14 #include <map>
15 #include <fstream>
18 namespace
21 unsigned debug_flag(debug_context::etype type)
23 switch(type) {
24 case debug_context::DEBUG_READ: return 1;
25 case debug_context::DEBUG_WRITE: return 2;
26 case debug_context::DEBUG_EXEC: return 4;
27 case debug_context::DEBUG_TRACE: return 8;
28 case debug_context::DEBUG_FRAME: return 0;
29 default: throw std::runtime_error("Invalid debug callback type");
34 namespace
36 template<class T> void kill_hooks(T& cblist, debug_context::etype type)
38 while(!cblist.empty()) {
39 if(cblist.begin()->second.empty()) {
40 cblist.erase(cblist.begin()->first);
41 continue;
43 auto key = cblist.begin()->first;
44 auto tmp = cblist.begin()->second.begin();
45 cblist.begin()->second.erase(cblist.begin()->second.begin());
46 (*tmp)->killed(key, type);
48 cblist.clear();
52 debug_context::debug_context(emulator_dispatch& _dispatch, loaded_rom& _rom, command::group& _cmd)
53 : edispatch(_dispatch), rom(_rom), cmd(_cmd),
54 showhooks(cmd, STUBS::showhooks, [this]() { this->do_showhooks(); }),
55 genevent(cmd, STUBS::genevent, [this](const std::string& a) { this->do_genevent(a); }),
56 tracecmd(cmd, STUBS::trace, [this](const std::string& a) { this->do_tracecmd(a); })
60 debug_context::callback_base::~callback_base()
64 const uint64_t debug_context::all_addresses = 0xFFFFFFFFFFFFFFFFULL;
66 void debug_context::add_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
68 auto& core = CORE();
69 std::map<uint64_t, cb_list>& xcb = get_lists(type);
70 if(!corechange_r) {
71 corechange.set(edispatch.core_change, [this]() { this->core_change(); });
72 corechange_r = true;
74 if(!xcb.count(addr) && type != DEBUG_FRAME)
75 core.rom->set_debug_flags(addr, debug_flag(type), 0);
76 auto& lst = xcb[addr];
77 lst.push_back(&cb);
80 void debug_context::remove_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
82 std::map<uint64_t, cb_list>& xcb = get_lists(type);
83 if(type == DEBUG_FRAME) addr = 0;
84 if(!xcb.count(addr)) return;
85 auto& l = xcb[addr];
86 for(auto i = l.begin(); i != l.end(); i++) {
87 if(*i == &cb) {
88 l.erase(i);
89 break;
92 if(xcb[addr].empty()) {
93 xcb.erase(addr);
94 if(type != DEBUG_FRAME)
95 rom.set_debug_flags(addr, 0, debug_flag(type));
99 void debug_context::do_callback_read(uint64_t addr, uint64_t value)
101 params p;
102 p.type = DEBUG_READ;
103 p.rwx.addr = addr;
104 p.rwx.value = value;
106 requesting_break = false;
107 cb_list* cb1 = read_cb.count(all_addresses) ? &read_cb[all_addresses] : &dummy_cb;
108 cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
109 auto _cb1 = *cb1;
110 auto _cb2 = *cb2;
111 for(auto& i : _cb1) i->callback(p);
112 for(auto& i : _cb2) i->callback(p);
113 if(requesting_break)
114 do_break_pause();
117 void debug_context::do_callback_write(uint64_t addr, uint64_t value)
119 params p;
120 p.type = DEBUG_WRITE;
121 p.rwx.addr = addr;
122 p.rwx.value = value;
124 requesting_break = false;
125 cb_list* cb1 = write_cb.count(all_addresses) ? &write_cb[all_addresses] : &dummy_cb;
126 cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
127 auto _cb1 = *cb1;
128 auto _cb2 = *cb2;
129 for(auto& i : _cb1) i->callback(p);
130 for(auto& i : _cb2) i->callback(p);
131 if(requesting_break)
132 do_break_pause();
135 void debug_context::do_callback_exec(uint64_t addr, uint64_t cpu)
137 params p;
138 p.type = DEBUG_EXEC;
139 p.rwx.addr = addr;
140 p.rwx.value = cpu;
142 requesting_break = false;
143 cb_list* cb1 = exec_cb.count(all_addresses) ? &exec_cb[all_addresses] : &dummy_cb;
144 cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
145 auto _cb1 = *cb1;
146 auto _cb2 = *cb2;
147 if((1ULL << cpu) & xmask)
148 for(auto& i : _cb1) i->callback(p);
149 for(auto& i : _cb2) i->callback(p);
150 if(requesting_break)
151 do_break_pause();
154 void debug_context::do_callback_trace(uint64_t cpu, const char* str, bool true_insn)
156 params p;
157 p.type = DEBUG_TRACE;
158 p.trace.cpu = cpu;
159 p.trace.decoded_insn = str;
160 p.trace.true_insn = true_insn;
162 requesting_break = false;
163 cb_list* cb = trace_cb.count(cpu) ? &trace_cb[cpu] : &dummy_cb;
164 auto _cb = *cb;
165 for(auto& i : _cb) i->callback(p);
166 if(requesting_break)
167 do_break_pause();
170 void debug_context::do_callback_frame(uint64_t frame, bool loadstate)
172 params p;
173 p.type = DEBUG_FRAME;
174 p.frame.frame = frame;
175 p.frame.loadstated = loadstate;
177 cb_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb;
178 auto _cb = *cb;
179 for(auto& i : _cb) i->callback(p);
182 void debug_context::set_cheat(uint64_t addr, uint64_t value)
184 rom.set_cheat(addr, value, true);
187 void debug_context::clear_cheat(uint64_t addr)
189 rom.set_cheat(addr, 0, false);
192 void debug_context::setxmask(uint64_t mask)
194 xmask = mask;
197 bool debug_context::is_tracelogging(uint64_t cpu)
199 return (trace_outputs.count(cpu) != 0);
202 void debug_context::set_tracelog_change_cb(std::function<void()> cb)
204 tracelog_change_cb = cb;
207 void debug_context::core_change()
209 rom.debug_reset();
210 kill_hooks(read_cb, DEBUG_READ);
211 kill_hooks(write_cb, DEBUG_WRITE);
212 kill_hooks(exec_cb, DEBUG_EXEC);
213 kill_hooks(trace_cb, DEBUG_TRACE);
216 void debug_context::request_break()
218 requesting_break = true;
221 debug_context::tracelog_file::tracelog_file(debug_context& _parent)
222 : parent(_parent)
226 debug_context::tracelog_file::~tracelog_file()
230 void debug_context::tracelog_file::callback(const debug_context::params& p)
232 if(!parent.trace_outputs.count(p.trace.cpu)) return;
233 parent.trace_outputs[p.trace.cpu]->stream << p.trace.decoded_insn << std::endl;
236 void debug_context::tracelog_file::killed(uint64_t addr, debug_context::etype type)
238 refcnt--;
239 if(!refcnt)
240 delete this;
243 void debug_context::tracelog(uint64_t proc, const std::string& filename)
245 if(filename == "") {
246 if(!trace_outputs.count(proc))
247 return;
248 remove_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
249 trace_outputs[proc]->refcnt--;
250 if(!trace_outputs[proc]->refcnt)
251 delete trace_outputs[proc];
252 trace_outputs.erase(proc);
253 messages << "Stopped tracelogging processor #" << proc << std::endl;
254 if(tracelog_change_cb) tracelog_change_cb();
255 return;
257 if(trace_outputs.count(proc)) throw std::runtime_error("Already tracelogging");
258 std::string full_filename = directory::absolute_path(filename);
259 bool found = false;
260 for(auto i : trace_outputs) {
261 if(i.second->full_filename == full_filename) {
262 i.second->refcnt++;
263 trace_outputs[proc] = i.second;
264 found = true;
265 break;
268 if(!found) {
269 trace_outputs[proc] = new tracelog_file(*this);
270 trace_outputs[proc]->refcnt = 1;
271 trace_outputs[proc]->full_filename = full_filename;
272 trace_outputs[proc]->stream.open(full_filename);
273 if(!trace_outputs[proc]->stream) {
274 delete trace_outputs[proc];
275 trace_outputs.erase(proc);
276 throw std::runtime_error("Can't open '" + full_filename + "'");
279 try {
280 add_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
281 } catch(std::exception& e) {
282 messages << "Error starting tracelogging: " << e.what() << std::endl;
283 trace_outputs[proc]->refcnt--;
284 if(!trace_outputs[proc]->refcnt)
285 delete trace_outputs[proc];
286 trace_outputs.erase(proc);
287 throw;
289 messages << "Tracelogging processor #" << proc << " to '" << filename << "'" << std::endl;
290 if(tracelog_change_cb) tracelog_change_cb();
293 void debug_context::do_showhooks()
295 for(auto& i : read_cb)
296 for(auto& j : i.second)
297 messages << "READ addr=" << i.first << " handle=" << &j << std::endl;
298 for(auto& i : write_cb)
299 for(auto& j : i.second)
300 messages << "WRITE addr=" << i.first << " handle=" << &j << std::endl;
301 for(auto& i : exec_cb)
302 for(auto& j : i.second)
303 messages << "EXEC addr=" << i.first << " handle=" << &j << std::endl;
304 for(auto& i : trace_cb)
305 for(auto& j : i.second)
306 messages << "TRACE proc=" << i.first << " handle=" << &j << std::endl;
307 for(auto& i : frame_cb)
308 for(auto& j : i.second)
309 messages << "FRAME handle=" << &j << std::endl;
312 void debug_context::do_genevent(const std::string& args)
314 regex_results r = regex("([^ \t]+) ([^ \t]+) (.+)", args);
315 if(!r) throw std::runtime_error("generate-memory-event: Bad arguments");
316 if(r[1] == "r") {
317 uint64_t addr = parse_value<uint64_t>(r[2]);
318 uint64_t val = parse_value<uint64_t>(r[3]);
319 do_callback_read(addr, val);
320 } else if(r[1] == "w") {
321 uint64_t addr = parse_value<uint64_t>(r[2]);
322 uint64_t val = parse_value<uint64_t>(r[3]);
323 do_callback_write(addr, val);
324 } else if(r[1] == "x") {
325 uint64_t addr = parse_value<uint64_t>(r[2]);
326 uint64_t val = parse_value<uint64_t>(r[3]);
327 do_callback_exec(addr, val);
328 } else if(r[1] == "t") {
329 uint64_t proc = parse_value<uint64_t>(r[2]);
330 std::string str = r[3];
331 do_callback_trace(proc, str.c_str());
332 } else
333 throw std::runtime_error("Invalid operation");
336 void debug_context::do_tracecmd(const std::string& args)
338 regex_results r = regex("([^ \t]+)([ \t]+(.+))?", args);
339 if(!r) throw std::runtime_error("tracelog: Bad arguments");
340 std::string cpu = r[1];
341 std::string filename = r[3];
342 uint64_t _cpu = 0;
343 for(auto i : rom.get_trace_cpus()) {
344 if(cpu == i)
345 goto out;
346 _cpu++;
348 throw std::runtime_error("tracelog: Invalid CPU");
349 out:
350 tracelog(_cpu, filename);