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"
19 unsigned debug_flag(debug_context::etype 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");
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
);
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
);
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
);
65 corechange
.set(edispatch
.core_change
, [this]() { this->core_change(); });
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
];
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;
80 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
86 if(xcb
[addr
].empty()) {
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
)
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
;
105 for(auto& i
: _cb1
) i
->callback(p
);
106 for(auto& i
: _cb2
) i
->callback(p
);
111 void debug_context::do_callback_write(uint64_t addr
, uint64_t value
)
114 p
.type
= DEBUG_WRITE
;
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
;
123 for(auto& i
: _cb1
) i
->callback(p
);
124 for(auto& i
: _cb2
) i
->callback(p
);
129 void debug_context::do_callback_exec(uint64_t addr
, uint64_t 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
;
141 if((1ULL << cpu
) & xmask
)
142 for(auto& i
: _cb1
) i
->callback(p
);
143 for(auto& i
: _cb2
) i
->callback(p
);
148 void debug_context::do_callback_trace(uint64_t cpu
, const char* str
, bool true_insn
)
151 p
.type
= DEBUG_TRACE
;
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
;
159 for(auto& i
: _cb
) i
->callback(p
);
164 void debug_context::do_callback_frame(uint64_t frame
, bool loadstate
)
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
;
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
)
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
)
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
)
237 void debug_context::tracelog(uint64_t proc
, const std::string
& filename
)
240 if(!trace_outputs
.count(proc
))
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();
251 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
252 std::string full_filename
= directory::absolute_path(filename
);
254 for(auto i
: trace_outputs
) {
255 if(i
.second
->full_filename
== full_filename
) {
257 trace_outputs
[proc
] = i
.second
;
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
+ "'");
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
);
283 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
284 if(tracelog_change_cb
) tracelog_change_cb();
289 command::fnptr
<> CMD_callbacks_show(lsnes_cmds
, "show-callbacks", "", "",
290 []() throw(std::bad_alloc
, std::runtime_error
) {
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");
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());
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];
341 for(auto i
: our_rom
.rtype
->get_trace_cpus()) {
346 throw std::runtime_error("tracelog: Invalid CPU");
348 CORE().dbg
->tracelog(_cpu
, filename
);