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
;
25 typedef std::list
<cb_rwx
> cb_list
;
26 typedef std::list
<cb_trace
> cb2_list
;
27 std::map
<uint64_t, cb_list
> read_cb
;
28 std::map
<uint64_t, cb_list
> write_cb
;
29 std::map
<uint64_t, cb_list
> exec_cb
;
30 std::map
<uint64_t, cb2_list
> trace_cb
;
31 cb_list dummy_cb
; //Always empty.
32 cb2_list dummy_cb2
; //Always empty.
34 std::function
<void()> tracelog_change_cb
;
35 struct dispatch::target
<> corechange
;
36 bool corechange_r
= false;
37 bool requesting_break
= false;
42 std::string full_filename
;
45 std::map
<uint64_t, std::pair
<tracelog_file
*, debug_handle
>> trace_outputs
;
47 std::map
<uint64_t, cb_list
>& get_lists(debug_type type
)
50 case DEBUG_READ
: return read_cb
;
51 case DEBUG_WRITE
: return write_cb
;
52 case DEBUG_EXEC
: return exec_cb
;
53 default: throw std::runtime_error("Invalid debug callback type");
57 unsigned debug_flag(debug_type type
)
60 case DEBUG_READ
: return 1;
61 case DEBUG_WRITE
: return 2;
62 case DEBUG_EXEC
: return 4;
63 case DEBUG_TRACE
: return 8;
64 default: throw std::runtime_error("Invalid debug callback type");
69 const uint64_t debug_all_addr
= 0xFFFFFFFFFFFFFFFFULL
;
73 template<class T
> debug_handle
_debug_add_callback(std::map
<uint64_t, std::list
<T
>>& cb
, uint64_t addr
,
74 debug_type type
, T fn
, std::function
<void()> dtor
)
77 corechange
.set(notify_core_change
, []() { debug_core_change(); });
81 our_rom
.rtype
->set_debug_flags(addr
, debug_flag(type
), 0);
86 h
.handle
= &*lst
.rbegin();
90 template<class T
> void _debug_remove_callback(T
& cb
, uint64_t addr
, debug_type type
, debug_handle handle
)
92 if(!cb
.count(addr
)) return;
94 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
95 if(&*i
== handle
.handle
) {
100 if(cb
[addr
].empty()) {
102 our_rom
.rtype
->set_debug_flags(addr
, 0, debug_flag(type
));
106 template<class T
> void kill_hooks(T
& cblist
)
108 while(!cblist
.empty()) {
109 if(cblist
.begin()->second
.empty()) {
110 cblist
.erase(cblist
.begin()->first
);
113 auto tmp
= cblist
.begin()->second
.begin();
120 debug_handle
debug_add_callback(uint64_t addr
, debug_type type
, std::function
<void(uint64_t addr
, uint64_t value
)> fn
,
121 std::function
<void()> dtor
)
123 std::map
<uint64_t, cb_list
>& cb
= get_lists(type
);
127 return _debug_add_callback(cb
, addr
, type
, t
, dtor
);
130 debug_handle
debug_add_trace_callback(uint64_t proc
, std::function
<void(uint64_t proc
, const char* str
,
131 bool true_insn
)> fn
, std::function
<void()> dtor
)
136 return _debug_add_callback(trace_cb
, proc
, DEBUG_TRACE
, t
, dtor
);
139 void debug_remove_callback(uint64_t addr
, debug_type type
, debug_handle handle
)
141 if(type
== DEBUG_TRACE
) {
142 _debug_remove_callback(trace_cb
, addr
, DEBUG_TRACE
, handle
);
144 _debug_remove_callback(get_lists(type
), addr
, type
, handle
);
148 void debug_fire_callback_read(uint64_t addr
, uint64_t value
)
150 requesting_break
= false;
151 cb_list
* cb1
= read_cb
.count(debug_all_addr
) ? &read_cb
[debug_all_addr
] : &dummy_cb
;
152 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
155 for(auto& i
: _cb1
) i
.cb(addr
, value
);
156 for(auto& i
: _cb2
) i
.cb(addr
, value
);
161 void debug_fire_callback_write(uint64_t addr
, uint64_t value
)
163 requesting_break
= false;
164 cb_list
* cb1
= write_cb
.count(debug_all_addr
) ? &write_cb
[debug_all_addr
] : &dummy_cb
;
165 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
168 for(auto& i
: _cb1
) i
.cb(addr
, value
);
169 for(auto& i
: _cb2
) i
.cb(addr
, value
);
174 void debug_fire_callback_exec(uint64_t addr
, uint64_t value
)
176 requesting_break
= false;
177 cb_list
* cb1
= exec_cb
.count(debug_all_addr
) ? &exec_cb
[debug_all_addr
] : &dummy_cb
;
178 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
182 for(auto& i
: _cb1
) i
.cb(addr
, value
);
183 for(auto& i
: _cb2
) i
.cb(addr
, value
);
188 void debug_fire_callback_trace(uint64_t proc
, const char* str
, bool true_insn
)
190 requesting_break
= false;
191 cb2_list
* cb
= trace_cb
.count(proc
) ? &trace_cb
[proc
] : &dummy_cb2
;
193 for(auto& i
: _cb
) i
.cb(proc
, str
, true_insn
);
198 void debug_set_cheat(uint64_t addr
, uint64_t value
)
200 our_rom
.rtype
->set_cheat(addr
, value
, true);
203 void debug_clear_cheat(uint64_t addr
)
205 our_rom
.rtype
->set_cheat(addr
, 0, false);
208 void debug_setxmask(uint64_t mask
)
213 void debug_tracelog(uint64_t proc
, const std::string
& filename
)
216 if(!trace_outputs
.count(proc
))
218 debug_remove_callback(proc
, DEBUG_TRACE
, trace_outputs
[proc
].second
);
219 trace_outputs
[proc
].first
->refcnt
--;
220 if(!trace_outputs
[proc
].first
->refcnt
) {
221 delete trace_outputs
[proc
].first
;
223 trace_outputs
.erase(proc
);
224 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
225 if(tracelog_change_cb
) tracelog_change_cb();
228 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
229 std::string full_filename
= get_absolute_path(filename
);
231 for(auto i
: trace_outputs
) {
232 if(i
.second
.first
->full_filename
== full_filename
) {
233 i
.second
.first
->refcnt
++;
234 trace_outputs
[proc
].first
= i
.second
.first
;
240 trace_outputs
[proc
].first
= new tracelog_file
;
241 trace_outputs
[proc
].first
->refcnt
= 1;
242 trace_outputs
[proc
].first
->full_filename
= full_filename
;
243 trace_outputs
[proc
].first
->stream
.open(full_filename
);
244 if(!trace_outputs
[proc
].first
->stream
) {
245 delete trace_outputs
[proc
].first
;
246 trace_outputs
.erase(proc
);
247 throw std::runtime_error("Can't open '" + full_filename
+ "'");
250 trace_outputs
[proc
].second
= debug_add_trace_callback(proc
, [](uint64_t proc
, const char* str
, bool dummy
) {
251 if(!trace_outputs
.count(proc
)) return;
252 trace_outputs
[proc
].first
->stream
<< str
<< std::endl
;
253 }, [proc
]() { debug_tracelog(proc
, ""); });
254 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
255 if(tracelog_change_cb
) tracelog_change_cb();
258 bool debug_tracelogging(uint64_t proc
)
260 return (trace_outputs
.count(proc
) != 0);
263 void debug_set_tracelog_change_cb(std::function
<void()> cb
)
265 tracelog_change_cb
= cb
;
268 void debug_core_change()
270 our_rom
.rtype
->debug_reset();
272 kill_hooks(write_cb
);
274 kill_hooks(trace_cb
);
277 void debug_request_break()
279 requesting_break
= true;
284 command::fnptr
<> callbacks_show(lsnes_cmd
, "show-callbacks", "", "",
285 []() throw(std::bad_alloc
, std::runtime_error
) {
286 for(auto& i
: read_cb
)
287 for(auto& j
: i
.second
)
288 messages
<< "READ addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
289 for(auto& i
: write_cb
)
290 for(auto& j
: i
.second
)
291 messages
<< "WRITE addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
292 for(auto& i
: exec_cb
)
293 for(auto& j
: i
.second
)
294 messages
<< "EXEC addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
295 for(auto& i
: trace_cb
)
296 for(auto& j
: i
.second
)
297 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
300 command::fnptr
<const std::string
&> generate_event(lsnes_cmd
, "generate-memory-event", "", "",
301 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
302 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
303 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
305 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
306 uint64_t val
= parse_value
<uint64_t>(r
[3]);
307 debug_fire_callback_read(addr
, val
);
308 } else if(r
[1] == "w") {
309 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
310 uint64_t val
= parse_value
<uint64_t>(r
[3]);
311 debug_fire_callback_write(addr
, val
);
312 } else if(r
[1] == "x") {
313 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
314 uint64_t val
= parse_value
<uint64_t>(r
[3]);
315 debug_fire_callback_exec(addr
, val
);
316 } else if(r
[1] == "t") {
317 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
318 std::string str
= r
[3];
319 debug_fire_callback_trace(proc
, str
.c_str());
321 throw std::runtime_error("Invalid operation");
324 command::fnptr
<const std::string
&> tracelog(lsnes_cmd
, "tracelog", "Trace log control",
325 "Trace log control\nSyntax: tracelog <cpuid> <file> Start tracing\nSyntax: tracelog <cpuid> "
326 "End tracing", [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
327 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
328 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
329 std::string cpu
= r
[1];
330 std::string filename
= r
[3];
332 for(auto i
: our_rom
.rtype
->get_trace_cpus()) {
337 throw std::runtime_error("tracelog: Invalid CPU");
339 debug_tracelog(_cpu
, filename
);