1 #include "core/command.hpp"
2 #include "core/debug.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/moviedata.hpp"
5 #include "library/directory.hpp"
16 std::function
<void(uint64_t addr
, uint64_t value
)> cb
;
17 std::function
<void()> dtor
;
21 std::function
<void(uint64_t proc
, const char* str
)> cb
;
22 std::function
<void()> dtor
;
24 typedef std::list
<cb_rwx
> cb_list
;
25 typedef std::list
<cb_trace
> cb2_list
;
26 std::map
<uint64_t, cb_list
> read_cb
;
27 std::map
<uint64_t, cb_list
> write_cb
;
28 std::map
<uint64_t, cb_list
> exec_cb
;
29 std::map
<uint64_t, cb2_list
> trace_cb
;
30 cb_list dummy_cb
; //Always empty.
31 cb2_list dummy_cb2
; //Always empty.
33 std::function
<void()> tracelog_change_cb
;
34 struct dispatch::target
<> corechange
;
35 bool corechange_r
= false;
40 std::string full_filename
;
43 std::map
<uint64_t, std::pair
<tracelog_file
*, debug_handle
>> trace_outputs
;
45 std::map
<uint64_t, cb_list
>& get_lists(debug_type type
)
48 case DEBUG_READ
: return read_cb
;
49 case DEBUG_WRITE
: return write_cb
;
50 case DEBUG_EXEC
: return exec_cb
;
51 default: throw std::runtime_error("Invalid debug callback type");
55 unsigned debug_flag(debug_type type
)
58 case DEBUG_READ
: return 1;
59 case DEBUG_WRITE
: return 2;
60 case DEBUG_EXEC
: return 4;
61 case DEBUG_TRACE
: return 8;
62 default: throw std::runtime_error("Invalid debug callback type");
67 const uint64_t debug_all_addr
= 0xFFFFFFFFFFFFFFFFULL
;
71 template<class T
> debug_handle
_debug_add_callback(std::map
<uint64_t, std::list
<T
>>& cb
, uint64_t addr
,
72 debug_type type
, T fn
, std::function
<void()> dtor
)
75 corechange
.set(notify_core_change
, []() { debug_core_change(); });
79 our_rom
.rtype
->set_debug_flags(addr
, debug_flag(type
), 0);
84 h
.handle
= &*lst
.rbegin();
88 template<class T
> void _debug_remove_callback(T
& cb
, uint64_t addr
, debug_type type
, debug_handle handle
)
90 if(!cb
.count(addr
)) return;
92 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
93 if(&*i
== handle
.handle
) {
98 if(cb
[addr
].empty()) {
100 our_rom
.rtype
->set_debug_flags(addr
, 0, debug_flag(type
));
104 template<class T
> void kill_hooks(T
& cblist
)
106 while(!cblist
.empty()) {
107 if(cblist
.begin()->second
.empty()) {
108 cblist
.erase(cblist
.begin()->first
);
111 auto tmp
= cblist
.begin()->second
.begin();
118 debug_handle
debug_add_callback(uint64_t addr
, debug_type type
, std::function
<void(uint64_t addr
, uint64_t value
)> fn
,
119 std::function
<void()> dtor
)
121 std::map
<uint64_t, cb_list
>& cb
= get_lists(type
);
125 return _debug_add_callback(cb
, addr
, type
, t
, dtor
);
128 debug_handle
debug_add_trace_callback(uint64_t proc
, std::function
<void(uint64_t proc
, const char* str
)> fn
,
129 std::function
<void()> dtor
)
134 return _debug_add_callback(trace_cb
, proc
, DEBUG_TRACE
, t
, dtor
);
137 void debug_remove_callback(uint64_t addr
, debug_type type
, debug_handle handle
)
139 if(type
== DEBUG_TRACE
) {
140 _debug_remove_callback(trace_cb
, addr
, DEBUG_TRACE
, handle
);
142 _debug_remove_callback(get_lists(type
), addr
, type
, handle
);
146 void debug_fire_callback_read(uint64_t addr
, uint64_t value
)
148 cb_list
* cb1
= read_cb
.count(debug_all_addr
) ? &read_cb
[debug_all_addr
] : &dummy_cb
;
149 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
150 for(auto& i
: *cb1
) i
.cb(addr
, value
);
151 for(auto& i
: *cb2
) i
.cb(addr
, value
);
154 void debug_fire_callback_write(uint64_t addr
, uint64_t value
)
156 cb_list
* cb1
= write_cb
.count(debug_all_addr
) ? &write_cb
[debug_all_addr
] : &dummy_cb
;
157 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
158 for(auto& i
: *cb1
) i
.cb(addr
, value
);
159 for(auto& i
: *cb2
) i
.cb(addr
, value
);
162 void debug_fire_callback_exec(uint64_t addr
, uint64_t value
)
164 cb_list
* cb1
= exec_cb
.count(debug_all_addr
) ? &exec_cb
[debug_all_addr
] : &dummy_cb
;
165 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
167 for(auto& i
: *cb1
) i
.cb(addr
, value
);
168 for(auto& i
: *cb2
) i
.cb(addr
, value
);
171 void debug_fire_callback_trace(uint64_t proc
, const char* str
)
173 cb2_list
* cb
= trace_cb
.count(proc
) ? &trace_cb
[proc
] : &dummy_cb2
;
174 for(auto& i
: *cb
) i
.cb(proc
, str
);
177 void debug_set_cheat(uint64_t addr
, uint64_t value
)
179 our_rom
.rtype
->set_cheat(addr
, value
, true);
182 void debug_clear_cheat(uint64_t addr
)
184 our_rom
.rtype
->set_cheat(addr
, 0, false);
187 void debug_setxmask(uint64_t mask
)
192 void debug_tracelog(uint64_t proc
, const std::string
& filename
)
195 if(!trace_outputs
.count(proc
))
197 debug_remove_callback(proc
, DEBUG_TRACE
, trace_outputs
[proc
].second
);
198 trace_outputs
[proc
].first
->refcnt
--;
199 if(!trace_outputs
[proc
].first
->refcnt
) {
200 delete trace_outputs
[proc
].first
;
202 trace_outputs
.erase(proc
);
203 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
204 if(tracelog_change_cb
) tracelog_change_cb();
207 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
208 std::string full_filename
= get_absolute_path(filename
);
210 for(auto i
: trace_outputs
) {
211 if(i
.second
.first
->full_filename
== full_filename
) {
212 i
.second
.first
->refcnt
++;
213 trace_outputs
[proc
].first
= i
.second
.first
;
219 trace_outputs
[proc
].first
= new tracelog_file
;
220 trace_outputs
[proc
].first
->refcnt
= 1;
221 trace_outputs
[proc
].first
->full_filename
= full_filename
;
222 trace_outputs
[proc
].first
->stream
.open(full_filename
);
223 if(!trace_outputs
[proc
].first
->stream
) {
224 delete trace_outputs
[proc
].first
;
225 trace_outputs
.erase(proc
);
226 throw std::runtime_error("Can't open '" + full_filename
+ "'");
229 trace_outputs
[proc
].second
= debug_add_trace_callback(proc
, [](uint64_t proc
, const char* str
) {
230 if(!trace_outputs
.count(proc
)) return;
231 trace_outputs
[proc
].first
->stream
<< str
<< std::endl
;
232 }, [proc
]() { debug_tracelog(proc
, ""); });
233 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
234 if(tracelog_change_cb
) tracelog_change_cb();
237 bool debug_tracelogging(uint64_t proc
)
239 return (trace_outputs
.count(proc
) != 0);
242 void debug_set_tracelog_change_cb(std::function
<void()> cb
)
244 tracelog_change_cb
= cb
;
247 void debug_core_change()
249 our_rom
.rtype
->debug_reset();
251 kill_hooks(write_cb
);
253 kill_hooks(trace_cb
);
258 command::fnptr
<> callbacks_show(lsnes_cmd
, "show-callbacks", "", "",
259 []() throw(std::bad_alloc
, std::runtime_error
) {
260 for(auto& i
: read_cb
)
261 for(auto& j
: i
.second
)
262 messages
<< "READ addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
263 for(auto& i
: write_cb
)
264 for(auto& j
: i
.second
)
265 messages
<< "WRITE addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
266 for(auto& i
: exec_cb
)
267 for(auto& j
: i
.second
)
268 messages
<< "EXEC addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
269 for(auto& i
: trace_cb
)
270 for(auto& j
: i
.second
)
271 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
274 command::fnptr
<const std::string
&> generate_event(lsnes_cmd
, "generate-memory-event", "", "",
275 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
276 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
277 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
279 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
280 uint64_t val
= parse_value
<uint64_t>(r
[3]);
281 debug_fire_callback_read(addr
, val
);
282 } else if(r
[1] == "w") {
283 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
284 uint64_t val
= parse_value
<uint64_t>(r
[3]);
285 debug_fire_callback_write(addr
, val
);
286 } else if(r
[1] == "x") {
287 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
288 uint64_t val
= parse_value
<uint64_t>(r
[3]);
289 debug_fire_callback_exec(addr
, val
);
290 } else if(r
[1] == "t") {
291 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
292 std::string str
= r
[3];
293 debug_fire_callback_trace(proc
, str
.c_str());
295 throw std::runtime_error("Invalid operation");
298 command::fnptr
<const std::string
&> tracelog(lsnes_cmd
, "tracelog", "Trace log control",
299 "Trace log control\nSyntax: tracelog <cpuid> <file> Start tracing\nSyntax: tracelog <cpuid> "
300 "End tracing", [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
301 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
302 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
303 std::string cpu
= r
[1];
304 std::string filename
= r
[3];
306 for(auto i
: our_rom
.rtype
->get_trace_cpus()) {
311 throw std::runtime_error("tracelog: Invalid CPU");
313 debug_tracelog(_cpu
, filename
);