1 #include "core/command.hpp"
2 #include "core/debug.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/mainloop.hpp"
5 #include "core/moviedata.hpp"
6 #include "library/directory.hpp"
17 std::function
<void(uint64_t addr
, uint64_t value
)> cb
;
18 std::function
<void()> dtor
;
22 std::function
<void(uint64_t proc
, const char* str
, bool true_insn
)> cb
;
23 std::function
<void()> dtor
;
27 std::function
<void(uint64_t frame
, bool loadstate
)> cb
;
28 std::function
<void()> dtor
;
30 typedef std::list
<cb_rwx
> cb_list
;
31 typedef std::list
<cb_trace
> cb2_list
;
32 typedef std::list
<cb_frame
> cb3_list
;
33 std::map
<uint64_t, cb_list
> read_cb
;
34 std::map
<uint64_t, cb_list
> write_cb
;
35 std::map
<uint64_t, cb_list
> exec_cb
;
36 std::map
<uint64_t, cb2_list
> trace_cb
;
37 std::map
<uint64_t, cb3_list
> frame_cb
;
38 cb_list dummy_cb
; //Always empty.
39 cb2_list dummy_cb2
; //Always empty.
40 cb3_list dummy_cb3
; //Always empty.
42 std::function
<void()> tracelog_change_cb
;
43 struct dispatch::target
<> corechange
;
44 bool corechange_r
= false;
45 bool requesting_break
= false;
50 std::string full_filename
;
53 std::map
<uint64_t, std::pair
<tracelog_file
*, debug_handle
>> trace_outputs
;
55 std::map
<uint64_t, cb_list
>& get_lists(debug_type type
)
58 case DEBUG_READ
: return read_cb
;
59 case DEBUG_WRITE
: return write_cb
;
60 case DEBUG_EXEC
: return exec_cb
;
61 default: throw std::runtime_error("Invalid debug callback type");
65 unsigned debug_flag(debug_type type
)
68 case DEBUG_READ
: return 1;
69 case DEBUG_WRITE
: return 2;
70 case DEBUG_EXEC
: return 4;
71 case DEBUG_TRACE
: return 8;
72 case DEBUG_FRAME
: return 0;
73 default: throw std::runtime_error("Invalid debug callback type");
78 const uint64_t debug_all_addr
= 0xFFFFFFFFFFFFFFFFULL
;
82 template<class T
> debug_handle
_debug_add_callback(std::map
<uint64_t, std::list
<T
>>& cb
, uint64_t addr
,
83 debug_type type
, T fn
, std::function
<void()> dtor
)
86 corechange
.set(notify_core_change
, []() { debug_core_change(); });
89 if(!cb
.count(addr
) && type
!= DEBUG_FRAME
)
90 our_rom
.rtype
->set_debug_flags(addr
, debug_flag(type
), 0);
95 h
.handle
= &*lst
.rbegin();
99 template<class T
> void _debug_remove_callback(T
& cb
, uint64_t addr
, debug_type type
, debug_handle handle
)
101 if(!cb
.count(addr
)) return;
103 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
104 if(&*i
== handle
.handle
) {
109 if(cb
[addr
].empty()) {
111 if(type
!= DEBUG_FRAME
)
112 our_rom
.rtype
->set_debug_flags(addr
, 0, debug_flag(type
));
116 template<class T
> void kill_hooks(T
& cblist
)
118 while(!cblist
.empty()) {
119 if(cblist
.begin()->second
.empty()) {
120 cblist
.erase(cblist
.begin()->first
);
123 auto tmp
= cblist
.begin()->second
.begin();
130 debug_handle
debug_add_callback(uint64_t addr
, debug_type type
, std::function
<void(uint64_t addr
, uint64_t value
)> fn
,
131 std::function
<void()> dtor
)
133 std::map
<uint64_t, cb_list
>& cb
= get_lists(type
);
137 return _debug_add_callback(cb
, addr
, type
, t
, dtor
);
140 debug_handle
debug_add_trace_callback(uint64_t proc
, std::function
<void(uint64_t proc
, const char* str
,
141 bool true_insn
)> fn
, std::function
<void()> dtor
)
146 return _debug_add_callback(trace_cb
, proc
, DEBUG_TRACE
, t
, dtor
);
149 debug_handle
debug_add_frame_callback(std::function
<void(uint64_t frame
, bool loadstate
)> fn
,
150 std::function
<void()> dtor
)
155 return _debug_add_callback(frame_cb
, 0, DEBUG_FRAME
, t
, dtor
);
158 void debug_remove_callback(uint64_t addr
, debug_type type
, debug_handle handle
)
160 if(type
== DEBUG_FRAME
) {
161 _debug_remove_callback(frame_cb
, 0, DEBUG_FRAME
, handle
);
162 } else if(type
== DEBUG_TRACE
) {
163 _debug_remove_callback(trace_cb
, addr
, DEBUG_TRACE
, handle
);
165 _debug_remove_callback(get_lists(type
), addr
, type
, handle
);
169 void debug_fire_callback_read(uint64_t addr
, uint64_t value
)
171 requesting_break
= false;
172 cb_list
* cb1
= read_cb
.count(debug_all_addr
) ? &read_cb
[debug_all_addr
] : &dummy_cb
;
173 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
176 for(auto& i
: _cb1
) i
.cb(addr
, value
);
177 for(auto& i
: _cb2
) i
.cb(addr
, value
);
182 void debug_fire_callback_write(uint64_t addr
, uint64_t value
)
184 requesting_break
= false;
185 cb_list
* cb1
= write_cb
.count(debug_all_addr
) ? &write_cb
[debug_all_addr
] : &dummy_cb
;
186 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
189 for(auto& i
: _cb1
) i
.cb(addr
, value
);
190 for(auto& i
: _cb2
) i
.cb(addr
, value
);
195 void debug_fire_callback_exec(uint64_t addr
, uint64_t value
)
197 requesting_break
= false;
198 cb_list
* cb1
= exec_cb
.count(debug_all_addr
) ? &exec_cb
[debug_all_addr
] : &dummy_cb
;
199 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
203 for(auto& i
: _cb1
) i
.cb(addr
, value
);
204 for(auto& i
: _cb2
) i
.cb(addr
, value
);
209 void debug_fire_callback_trace(uint64_t proc
, const char* str
, bool true_insn
)
211 requesting_break
= false;
212 cb2_list
* cb
= trace_cb
.count(proc
) ? &trace_cb
[proc
] : &dummy_cb2
;
214 for(auto& i
: _cb
) i
.cb(proc
, str
, true_insn
);
219 void debug_fire_callback_frame(uint64_t frame
, bool loadstate
)
221 cb3_list
* cb
= frame_cb
.count(0) ? &frame_cb
[0] : &dummy_cb3
;
223 for(auto& i
: _cb
) i
.cb(frame
, loadstate
);
226 void debug_set_cheat(uint64_t addr
, uint64_t value
)
228 our_rom
.rtype
->set_cheat(addr
, value
, true);
231 void debug_clear_cheat(uint64_t addr
)
233 our_rom
.rtype
->set_cheat(addr
, 0, false);
236 void debug_setxmask(uint64_t mask
)
241 void debug_tracelog(uint64_t proc
, const std::string
& filename
)
244 if(!trace_outputs
.count(proc
))
246 debug_remove_callback(proc
, DEBUG_TRACE
, trace_outputs
[proc
].second
);
247 trace_outputs
[proc
].first
->refcnt
--;
248 if(!trace_outputs
[proc
].first
->refcnt
) {
249 delete trace_outputs
[proc
].first
;
251 trace_outputs
.erase(proc
);
252 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
253 if(tracelog_change_cb
) tracelog_change_cb();
256 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
257 std::string full_filename
= get_absolute_path(filename
);
259 for(auto i
: trace_outputs
) {
260 if(i
.second
.first
->full_filename
== full_filename
) {
261 i
.second
.first
->refcnt
++;
262 trace_outputs
[proc
].first
= i
.second
.first
;
268 trace_outputs
[proc
].first
= new tracelog_file
;
269 trace_outputs
[proc
].first
->refcnt
= 1;
270 trace_outputs
[proc
].first
->full_filename
= full_filename
;
271 trace_outputs
[proc
].first
->stream
.open(full_filename
);
272 if(!trace_outputs
[proc
].first
->stream
) {
273 delete trace_outputs
[proc
].first
;
274 trace_outputs
.erase(proc
);
275 throw std::runtime_error("Can't open '" + full_filename
+ "'");
278 trace_outputs
[proc
].second
= debug_add_trace_callback(proc
, [](uint64_t proc
, const char* str
, bool dummy
) {
279 if(!trace_outputs
.count(proc
)) return;
280 trace_outputs
[proc
].first
->stream
<< str
<< std::endl
;
281 }, [proc
]() { debug_tracelog(proc
, ""); });
282 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
283 if(tracelog_change_cb
) tracelog_change_cb();
286 bool debug_tracelogging(uint64_t proc
)
288 return (trace_outputs
.count(proc
) != 0);
291 void debug_set_tracelog_change_cb(std::function
<void()> cb
)
293 tracelog_change_cb
= cb
;
296 void debug_core_change()
298 our_rom
.rtype
->debug_reset();
300 kill_hooks(write_cb
);
302 kill_hooks(trace_cb
);
305 void debug_request_break()
307 requesting_break
= true;
312 command::fnptr
<> callbacks_show(lsnes_cmd
, "show-callbacks", "", "",
313 []() throw(std::bad_alloc
, std::runtime_error
) {
314 for(auto& i
: read_cb
)
315 for(auto& j
: i
.second
)
316 messages
<< "READ addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
317 for(auto& i
: write_cb
)
318 for(auto& j
: i
.second
)
319 messages
<< "WRITE addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
320 for(auto& i
: exec_cb
)
321 for(auto& j
: i
.second
)
322 messages
<< "EXEC addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
323 for(auto& i
: trace_cb
)
324 for(auto& j
: i
.second
)
325 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
326 for(auto& i
: frame_cb
)
327 for(auto& j
: i
.second
)
328 messages
<< "FRAME handle=" << &j
<< std::endl
;
331 command::fnptr
<const std::string
&> generate_event(lsnes_cmd
, "generate-memory-event", "", "",
332 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
333 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
334 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
336 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
337 uint64_t val
= parse_value
<uint64_t>(r
[3]);
338 debug_fire_callback_read(addr
, val
);
339 } else if(r
[1] == "w") {
340 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
341 uint64_t val
= parse_value
<uint64_t>(r
[3]);
342 debug_fire_callback_write(addr
, val
);
343 } else if(r
[1] == "x") {
344 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
345 uint64_t val
= parse_value
<uint64_t>(r
[3]);
346 debug_fire_callback_exec(addr
, val
);
347 } else if(r
[1] == "t") {
348 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
349 std::string str
= r
[3];
350 debug_fire_callback_trace(proc
, str
.c_str());
352 throw std::runtime_error("Invalid operation");
355 command::fnptr
<const std::string
&> tracelog(lsnes_cmd
, "tracelog", "Trace log control",
356 "Trace log control\nSyntax: tracelog <cpuid> <file> Start tracing\nSyntax: tracelog <cpuid> "
357 "End tracing", [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
358 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
359 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
360 std::string cpu
= r
[1];
361 std::string filename
= r
[3];
363 for(auto i
: our_rom
.rtype
->get_trace_cpus()) {
368 throw std::runtime_error("tracelog: Invalid CPU");
370 debug_tracelog(_cpu
, filename
);