Add <functional> to files that use std::function
[lsnes.git] / src / core / debug.cpp
blob5714f3da47a87df30cd195e9b8bd7fcfb487c6ba
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 <functional>
14 #include <stdexcept>
15 #include <list>
16 #include <map>
17 #include <fstream>
20 namespace
23 unsigned debug_flag(debug_context::etype type)
25 switch(type) {
26 case debug_context::DEBUG_READ: return 1;
27 case debug_context::DEBUG_WRITE: return 2;
28 case debug_context::DEBUG_EXEC: return 4;
29 case debug_context::DEBUG_TRACE: return 8;
30 case debug_context::DEBUG_FRAME: return 0;
31 default: throw std::runtime_error("Invalid debug callback type");
36 namespace
38 template<class T> void kill_hooks(T& cblist, debug_context::etype type)
40 while(!cblist.empty()) {
41 if(cblist.begin()->second.empty()) {
42 cblist.erase(cblist.begin()->first);
43 continue;
45 auto key = cblist.begin()->first;
46 auto tmp = cblist.begin()->second.begin();
47 (*tmp)->killed(key, type);
48 cblist.begin()->second.erase(cblist.begin()->second.begin());
50 cblist.clear();
54 debug_context::debug_context(emulator_dispatch& _dispatch, loaded_rom& _rom, memory_space& _mspace,
55 command::group& _cmd)
56 : edispatch(_dispatch), rom(_rom), mspace(_mspace), cmd(_cmd),
57 showhooks(cmd, CDEBUG::scb, [this]() { this->do_showhooks(); }),
58 genevent(cmd, CDEBUG::genevt, [this](const std::string& a) { this->do_genevent(a); }),
59 tracecmd(cmd, CDEBUG::tr, [this](const std::string& a) { this->do_tracecmd(a); })
63 debug_context::callback_base::~callback_base()
67 const uint64_t debug_context::all_addresses = 0xFFFFFFFFFFFFFFFFULL;
69 void debug_context::add_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
71 auto& core = CORE();
72 std::map<uint64_t, cb_list>& xcb = get_lists(type);
73 if(!corechange_r) {
74 corechange.set(edispatch.core_change, [this]() { this->core_change(); });
75 corechange_r = true;
77 if(!xcb.count(addr) && type != DEBUG_FRAME)
78 core.rom->set_debug_flags(addr, debug_flag(type), 0);
79 auto& lst = xcb[addr];
80 lst.push_back(&cb);
83 void debug_context::remove_callback(uint64_t addr, debug_context::etype type, debug_context::callback_base& cb)
85 std::map<uint64_t, cb_list>& xcb = get_lists(type);
86 if(type == DEBUG_FRAME) addr = 0;
87 if(!xcb.count(addr)) return;
88 auto& l = xcb[addr];
89 for(auto i = l.begin(); i != l.end(); i++) {
90 if(*i == &cb) {
91 l.erase(i);
92 break;
95 if(xcb[addr].empty()) {
96 xcb.erase(addr);
97 if(type != DEBUG_FRAME)
98 rom.set_debug_flags(addr, 0, debug_flag(type));
102 void debug_context::do_callback_read(uint64_t addr, uint64_t value)
104 params p;
105 p.type = DEBUG_READ;
106 p.rwx.addr = addr;
107 p.rwx.value = value;
109 requesting_break = false;
110 cb_list* cb1 = read_cb.count(all_addresses) ? &read_cb[all_addresses] : &dummy_cb;
111 cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
112 auto _cb1 = *cb1;
113 auto _cb2 = *cb2;
114 for(auto& i : _cb1) i->callback(p);
115 for(auto& i : _cb2) i->callback(p);
116 if(requesting_break)
117 do_break_pause();
120 void debug_context::do_callback_write(uint64_t addr, uint64_t value)
122 params p;
123 p.type = DEBUG_WRITE;
124 p.rwx.addr = addr;
125 p.rwx.value = value;
127 requesting_break = false;
128 cb_list* cb1 = write_cb.count(all_addresses) ? &write_cb[all_addresses] : &dummy_cb;
129 cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
130 auto _cb1 = *cb1;
131 auto _cb2 = *cb2;
132 for(auto& i : _cb1) i->callback(p);
133 for(auto& i : _cb2) i->callback(p);
134 if(requesting_break)
135 do_break_pause();
138 void debug_context::do_callback_exec(uint64_t addr, uint64_t cpu)
140 params p;
141 p.type = DEBUG_EXEC;
142 p.rwx.addr = addr;
143 p.rwx.value = cpu;
145 requesting_break = false;
146 cb_list* cb1 = exec_cb.count(all_addresses) ? &exec_cb[all_addresses] : &dummy_cb;
147 cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
148 auto _cb1 = *cb1;
149 auto _cb2 = *cb2;
150 if((1ULL << cpu) & xmask)
151 for(auto& i : _cb1) i->callback(p);
152 for(auto& i : _cb2) i->callback(p);
153 if(requesting_break)
154 do_break_pause();
157 void debug_context::do_callback_trace(uint64_t cpu, const char* str, bool true_insn)
159 params p;
160 p.type = DEBUG_TRACE;
161 p.trace.cpu = cpu;
162 p.trace.decoded_insn = str;
163 p.trace.true_insn = true_insn;
165 requesting_break = false;
166 cb_list* cb = trace_cb.count(cpu) ? &trace_cb[cpu] : &dummy_cb;
167 auto _cb = *cb;
168 for(auto& i : _cb) i->callback(p);
169 if(requesting_break)
170 do_break_pause();
173 void debug_context::do_callback_frame(uint64_t frame, bool loadstate)
175 params p;
176 p.type = DEBUG_FRAME;
177 p.frame.frame = frame;
178 p.frame.loadstated = loadstate;
180 cb_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb;
181 auto _cb = *cb;
182 for(auto& i : _cb) i->callback(p);
185 void debug_context::set_cheat(uint64_t addr, uint64_t value)
187 rom.set_cheat(addr, value, true);
190 void debug_context::clear_cheat(uint64_t addr)
192 rom.set_cheat(addr, 0, false);
195 void debug_context::setxmask(uint64_t mask)
197 xmask = mask;
200 bool debug_context::is_tracelogging(uint64_t cpu)
202 return (trace_outputs.count(cpu) != 0);
205 void debug_context::set_tracelog_change_cb(std::function<void()> cb)
207 tracelog_change_cb = cb;
210 void debug_context::core_change()
212 rom.debug_reset();
213 kill_hooks(read_cb, DEBUG_READ);
214 kill_hooks(write_cb, DEBUG_WRITE);
215 kill_hooks(exec_cb, DEBUG_EXEC);
216 kill_hooks(trace_cb, DEBUG_TRACE);
219 void debug_context::request_break()
221 requesting_break = true;
224 debug_context::tracelog_file::tracelog_file(debug_context& _parent)
225 : parent(_parent)
229 debug_context::tracelog_file::~tracelog_file()
233 void debug_context::tracelog_file::callback(const debug_context::params& p)
235 if(!parent.trace_outputs.count(p.trace.cpu)) return;
236 parent.trace_outputs[p.trace.cpu]->stream << p.trace.decoded_insn << std::endl;
239 void debug_context::tracelog_file::killed(uint64_t addr, debug_context::etype type)
241 refcnt--;
242 if(!refcnt)
243 delete this;
246 void debug_context::tracelog(uint64_t proc, const std::string& filename)
248 if(filename == "") {
249 if(!trace_outputs.count(proc))
250 return;
251 remove_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
252 trace_outputs[proc]->refcnt--;
253 if(!trace_outputs[proc]->refcnt)
254 delete trace_outputs[proc];
255 trace_outputs.erase(proc);
256 messages << "Stopped tracelogging processor #" << proc << std::endl;
257 if(tracelog_change_cb) tracelog_change_cb();
258 return;
260 if(trace_outputs.count(proc)) throw std::runtime_error("Already tracelogging");
261 std::string full_filename = directory::absolute_path(filename);
262 bool found = false;
263 for(auto i : trace_outputs) {
264 if(i.second->full_filename == full_filename) {
265 i.second->refcnt++;
266 trace_outputs[proc] = i.second;
267 found = true;
268 break;
271 if(!found) {
272 trace_outputs[proc] = new tracelog_file(*this);
273 trace_outputs[proc]->refcnt = 1;
274 trace_outputs[proc]->full_filename = full_filename;
275 trace_outputs[proc]->stream.open(full_filename);
276 if(!trace_outputs[proc]->stream) {
277 delete trace_outputs[proc];
278 trace_outputs.erase(proc);
279 throw std::runtime_error("Can't open '" + full_filename + "'");
282 try {
283 add_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
284 } catch(std::exception& e) {
285 messages << "Error starting tracelogging: " << e.what() << std::endl;
286 trace_outputs[proc]->refcnt--;
287 if(!trace_outputs[proc]->refcnt)
288 delete trace_outputs[proc];
289 trace_outputs.erase(proc);
290 throw;
292 messages << "Tracelogging processor #" << proc << " to '" << filename << "'" << std::endl;
293 if(tracelog_change_cb) tracelog_change_cb();
296 void debug_context::do_showhooks()
298 for(auto& i : read_cb)
299 for(auto& j : i.second)
300 messages << "READ addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
301 << std::endl;
302 for(auto& i : write_cb)
303 for(auto& j : i.second)
304 messages << "WRITE addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
305 << std::endl;
306 for(auto& i : exec_cb)
307 for(auto& j : i.second)
308 messages << "EXEC addr=" << mspace.address_to_textual(i.first) << " handle=" << &j
309 << std::endl;
310 for(auto& i : trace_cb)
311 for(auto& j : i.second)
312 messages << "TRACE proc=" << i.first << " handle=" << &j << std::endl;
313 for(auto& i : frame_cb)
314 for(auto& j : i.second)
315 messages << "FRAME handle=" << &j << std::endl;
318 void debug_context::do_genevent(const std::string& args)
320 regex_results r = regex("([^ \t]+) ([^ \t]+) (.+)", args);
321 if(!r) throw std::runtime_error("generate-memory-event: Bad arguments");
322 if(r[1] == "r") {
323 uint64_t addr = parse_value<uint64_t>(r[2]);
324 uint64_t val = parse_value<uint64_t>(r[3]);
325 do_callback_read(addr, val);
326 } else if(r[1] == "w") {
327 uint64_t addr = parse_value<uint64_t>(r[2]);
328 uint64_t val = parse_value<uint64_t>(r[3]);
329 do_callback_write(addr, val);
330 } else if(r[1] == "x") {
331 uint64_t addr = parse_value<uint64_t>(r[2]);
332 uint64_t val = parse_value<uint64_t>(r[3]);
333 do_callback_exec(addr, val);
334 } else if(r[1] == "t") {
335 uint64_t proc = parse_value<uint64_t>(r[2]);
336 std::string str = r[3];
337 do_callback_trace(proc, str.c_str());
338 } else
339 throw std::runtime_error("Invalid operation");
342 void debug_context::do_tracecmd(const std::string& args)
344 regex_results r = regex("([^ \t]+)([ \t]+(.+))?", args);
345 if(!r) throw std::runtime_error("tracelog: Bad arguments");
346 std::string cpu = r[1];
347 std::string filename = r[3];
348 uint64_t _cpu = 0;
349 for(auto i : rom.get_trace_cpus()) {
350 if(cpu == i)
351 goto out;
352 _cpu++;
354 throw std::runtime_error("tracelog: Invalid CPU");
355 out:
356 tracelog(_cpu, filename);