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"
21 unsigned debug_flag(debug_context::etype type
)
24 case debug_context::DEBUG_READ
: return 1;
25 case debug_context::DEBUG_WRITE
: return 2;
26 case debug_context::DEBUG_EXEC
: return 4;
27 case debug_context::DEBUG_TRACE
: return 8;
28 case debug_context::DEBUG_FRAME
: return 0;
29 default: throw std::runtime_error("Invalid debug callback type");
36 template<class T
> void kill_hooks(T
& cblist
, debug_context::etype type
)
38 while(!cblist
.empty()) {
39 if(cblist
.begin()->second
.empty()) {
40 cblist
.erase(cblist
.begin()->first
);
43 auto key
= cblist
.begin()->first
;
44 auto tmp
= cblist
.begin()->second
.begin();
45 cblist
.begin()->second
.erase(cblist
.begin()->second
.begin());
46 (*tmp
)->killed(key
, type
);
52 debug_context::debug_context(emulator_dispatch
& _dispatch
, loaded_rom
& _rom
, command::group
& _cmd
)
53 : edispatch(_dispatch
), rom(_rom
), cmd(_cmd
),
54 showhooks(cmd
, STUBS::showhooks
, [this]() { this->do_showhooks(); }),
55 genevent(cmd
, STUBS::genevent
, [this](const std::string
& a
) { this->do_genevent(a
); }),
56 tracecmd(cmd
, STUBS::trace
, [this](const std::string
& a
) { this->do_tracecmd(a
); })
60 debug_context::callback_base::~callback_base()
64 const uint64_t debug_context::all_addresses
= 0xFFFFFFFFFFFFFFFFULL
;
66 void debug_context::add_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
69 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
71 corechange
.set(edispatch
.core_change
, [this]() { this->core_change(); });
74 if(!xcb
.count(addr
) && type
!= DEBUG_FRAME
)
75 core
.rom
->set_debug_flags(addr
, debug_flag(type
), 0);
76 auto& lst
= xcb
[addr
];
80 void debug_context::remove_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
82 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
83 if(type
== DEBUG_FRAME
) addr
= 0;
84 if(!xcb
.count(addr
)) return;
86 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
92 if(xcb
[addr
].empty()) {
94 if(type
!= DEBUG_FRAME
)
95 rom
.set_debug_flags(addr
, 0, debug_flag(type
));
99 void debug_context::do_callback_read(uint64_t addr
, uint64_t value
)
106 requesting_break
= false;
107 cb_list
* cb1
= read_cb
.count(all_addresses
) ? &read_cb
[all_addresses
] : &dummy_cb
;
108 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
111 for(auto& i
: _cb1
) i
->callback(p
);
112 for(auto& i
: _cb2
) i
->callback(p
);
117 void debug_context::do_callback_write(uint64_t addr
, uint64_t value
)
120 p
.type
= DEBUG_WRITE
;
124 requesting_break
= false;
125 cb_list
* cb1
= write_cb
.count(all_addresses
) ? &write_cb
[all_addresses
] : &dummy_cb
;
126 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
129 for(auto& i
: _cb1
) i
->callback(p
);
130 for(auto& i
: _cb2
) i
->callback(p
);
135 void debug_context::do_callback_exec(uint64_t addr
, uint64_t cpu
)
142 requesting_break
= false;
143 cb_list
* cb1
= exec_cb
.count(all_addresses
) ? &exec_cb
[all_addresses
] : &dummy_cb
;
144 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
147 if((1ULL << cpu
) & xmask
)
148 for(auto& i
: _cb1
) i
->callback(p
);
149 for(auto& i
: _cb2
) i
->callback(p
);
154 void debug_context::do_callback_trace(uint64_t cpu
, const char* str
, bool true_insn
)
157 p
.type
= DEBUG_TRACE
;
159 p
.trace
.decoded_insn
= str
;
160 p
.trace
.true_insn
= true_insn
;
162 requesting_break
= false;
163 cb_list
* cb
= trace_cb
.count(cpu
) ? &trace_cb
[cpu
] : &dummy_cb
;
165 for(auto& i
: _cb
) i
->callback(p
);
170 void debug_context::do_callback_frame(uint64_t frame
, bool loadstate
)
173 p
.type
= DEBUG_FRAME
;
174 p
.frame
.frame
= frame
;
175 p
.frame
.loadstated
= loadstate
;
177 cb_list
* cb
= frame_cb
.count(0) ? &frame_cb
[0] : &dummy_cb
;
179 for(auto& i
: _cb
) i
->callback(p
);
182 void debug_context::set_cheat(uint64_t addr
, uint64_t value
)
184 rom
.set_cheat(addr
, value
, true);
187 void debug_context::clear_cheat(uint64_t addr
)
189 rom
.set_cheat(addr
, 0, false);
192 void debug_context::setxmask(uint64_t mask
)
197 bool debug_context::is_tracelogging(uint64_t cpu
)
199 return (trace_outputs
.count(cpu
) != 0);
202 void debug_context::set_tracelog_change_cb(std::function
<void()> cb
)
204 tracelog_change_cb
= cb
;
207 void debug_context::core_change()
210 kill_hooks(read_cb
, DEBUG_READ
);
211 kill_hooks(write_cb
, DEBUG_WRITE
);
212 kill_hooks(exec_cb
, DEBUG_EXEC
);
213 kill_hooks(trace_cb
, DEBUG_TRACE
);
216 void debug_context::request_break()
218 requesting_break
= true;
221 debug_context::tracelog_file::tracelog_file(debug_context
& _parent
)
226 debug_context::tracelog_file::~tracelog_file()
230 void debug_context::tracelog_file::callback(const debug_context::params
& p
)
232 if(!parent
.trace_outputs
.count(p
.trace
.cpu
)) return;
233 parent
.trace_outputs
[p
.trace
.cpu
]->stream
<< p
.trace
.decoded_insn
<< std::endl
;
236 void debug_context::tracelog_file::killed(uint64_t addr
, debug_context::etype type
)
243 void debug_context::tracelog(uint64_t proc
, const std::string
& filename
)
246 if(!trace_outputs
.count(proc
))
248 remove_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
249 trace_outputs
[proc
]->refcnt
--;
250 if(!trace_outputs
[proc
]->refcnt
)
251 delete trace_outputs
[proc
];
252 trace_outputs
.erase(proc
);
253 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
254 if(tracelog_change_cb
) tracelog_change_cb();
257 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
258 std::string full_filename
= directory::absolute_path(filename
);
260 for(auto i
: trace_outputs
) {
261 if(i
.second
->full_filename
== full_filename
) {
263 trace_outputs
[proc
] = i
.second
;
269 trace_outputs
[proc
] = new tracelog_file(*this);
270 trace_outputs
[proc
]->refcnt
= 1;
271 trace_outputs
[proc
]->full_filename
= full_filename
;
272 trace_outputs
[proc
]->stream
.open(full_filename
);
273 if(!trace_outputs
[proc
]->stream
) {
274 delete trace_outputs
[proc
];
275 trace_outputs
.erase(proc
);
276 throw std::runtime_error("Can't open '" + full_filename
+ "'");
280 add_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
281 } catch(std::exception
& e
) {
282 messages
<< "Error starting tracelogging: " << e
.what() << std::endl
;
283 trace_outputs
[proc
]->refcnt
--;
284 if(!trace_outputs
[proc
]->refcnt
)
285 delete trace_outputs
[proc
];
286 trace_outputs
.erase(proc
);
289 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
290 if(tracelog_change_cb
) tracelog_change_cb();
293 void debug_context::do_showhooks()
295 for(auto& i
: read_cb
)
296 for(auto& j
: i
.second
)
297 messages
<< "READ addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
298 for(auto& i
: write_cb
)
299 for(auto& j
: i
.second
)
300 messages
<< "WRITE addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
301 for(auto& i
: exec_cb
)
302 for(auto& j
: i
.second
)
303 messages
<< "EXEC addr=" << i
.first
<< " handle=" << &j
<< std::endl
;
304 for(auto& i
: trace_cb
)
305 for(auto& j
: i
.second
)
306 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
307 for(auto& i
: frame_cb
)
308 for(auto& j
: i
.second
)
309 messages
<< "FRAME handle=" << &j
<< std::endl
;
312 void debug_context::do_genevent(const std::string
& args
)
314 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
315 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
317 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
318 uint64_t val
= parse_value
<uint64_t>(r
[3]);
319 do_callback_read(addr
, val
);
320 } else if(r
[1] == "w") {
321 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
322 uint64_t val
= parse_value
<uint64_t>(r
[3]);
323 do_callback_write(addr
, val
);
324 } else if(r
[1] == "x") {
325 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
326 uint64_t val
= parse_value
<uint64_t>(r
[3]);
327 do_callback_exec(addr
, val
);
328 } else if(r
[1] == "t") {
329 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
330 std::string str
= r
[3];
331 do_callback_trace(proc
, str
.c_str());
333 throw std::runtime_error("Invalid operation");
336 void debug_context::do_tracecmd(const std::string
& args
)
338 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
339 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
340 std::string cpu
= r
[1];
341 std::string filename
= r
[3];
343 for(auto i
: rom
.get_trace_cpus()) {
348 throw std::runtime_error("tracelog: Invalid CPU");
350 tracelog(_cpu
, filename
);