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"
11 #include "library/memoryspace.hpp"
23 unsigned debug_flag(debug_context::etype type
)
26 case debug_context::DEBUG_READ
: return 1;
27 case debug_context::DEBUG_WRITE
: return 2;
28 case debug_context::DEBUG_EXEC
: return 4;
29 case debug_context::DEBUG_TRACE
: return 8;
30 case debug_context::DEBUG_FRAME
: return 0;
31 default: throw std::runtime_error("Invalid debug callback type");
38 template<class T
> void kill_hooks(T
& cblist
, debug_context::etype type
)
40 while(!cblist
.empty()) {
41 if(cblist
.begin()->second
.empty()) {
42 cblist
.erase(cblist
.begin()->first
);
45 auto key
= cblist
.begin()->first
;
46 auto tmp
= cblist
.begin()->second
.begin();
47 (*tmp
)->killed(key
, type
);
48 cblist
.begin()->second
.erase(cblist
.begin()->second
.begin());
54 debug_context::debug_context(emulator_dispatch
& _dispatch
, loaded_rom
& _rom
, memory_space
& _mspace
,
56 : edispatch(_dispatch
), rom(_rom
), mspace(_mspace
), cmd(_cmd
),
57 showhooks(cmd
, CDEBUG::scb
, [this]() { this->do_showhooks(); }),
58 genevent(cmd
, CDEBUG::genevt
, [this](const std::string
& a
) { this->do_genevent(a
); }),
59 tracecmd(cmd
, CDEBUG::tr
, [this](const std::string
& a
) { this->do_tracecmd(a
); })
63 debug_context::callback_base::~callback_base()
67 const uint64_t debug_context::all_addresses
= 0xFFFFFFFFFFFFFFFFULL
;
69 void debug_context::add_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
72 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
74 corechange
.set(edispatch
.core_change
, [this]() { this->core_change(); });
77 if(!xcb
.count(addr
) && type
!= DEBUG_FRAME
)
78 core
.rom
->set_debug_flags(addr
, debug_flag(type
), 0);
79 auto& lst
= xcb
[addr
];
83 void debug_context::remove_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
85 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
86 if(type
== DEBUG_FRAME
) addr
= 0;
87 if(!xcb
.count(addr
)) return;
89 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
95 if(xcb
[addr
].empty()) {
97 if(type
!= DEBUG_FRAME
)
98 rom
.set_debug_flags(addr
, 0, debug_flag(type
));
102 void debug_context::do_callback_read(uint64_t addr
, uint64_t value
)
109 requesting_break
= false;
110 cb_list
* cb1
= read_cb
.count(all_addresses
) ? &read_cb
[all_addresses
] : &dummy_cb
;
111 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
114 for(auto& i
: _cb1
) i
->callback(p
);
115 for(auto& i
: _cb2
) i
->callback(p
);
120 void debug_context::do_callback_write(uint64_t addr
, uint64_t value
)
123 p
.type
= DEBUG_WRITE
;
127 requesting_break
= false;
128 cb_list
* cb1
= write_cb
.count(all_addresses
) ? &write_cb
[all_addresses
] : &dummy_cb
;
129 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
132 for(auto& i
: _cb1
) i
->callback(p
);
133 for(auto& i
: _cb2
) i
->callback(p
);
138 void debug_context::do_callback_exec(uint64_t addr
, uint64_t cpu
)
145 requesting_break
= false;
146 cb_list
* cb1
= exec_cb
.count(all_addresses
) ? &exec_cb
[all_addresses
] : &dummy_cb
;
147 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
150 if((1ULL << cpu
) & xmask
)
151 for(auto& i
: _cb1
) i
->callback(p
);
152 for(auto& i
: _cb2
) i
->callback(p
);
157 void debug_context::do_callback_trace(uint64_t cpu
, const char* str
, bool true_insn
)
160 p
.type
= DEBUG_TRACE
;
162 p
.trace
.decoded_insn
= str
;
163 p
.trace
.true_insn
= true_insn
;
165 requesting_break
= false;
166 cb_list
* cb
= trace_cb
.count(cpu
) ? &trace_cb
[cpu
] : &dummy_cb
;
168 for(auto& i
: _cb
) i
->callback(p
);
173 void debug_context::do_callback_frame(uint64_t frame
, bool loadstate
)
176 p
.type
= DEBUG_FRAME
;
177 p
.frame
.frame
= frame
;
178 p
.frame
.loadstated
= loadstate
;
180 cb_list
* cb
= frame_cb
.count(0) ? &frame_cb
[0] : &dummy_cb
;
182 for(auto& i
: _cb
) i
->callback(p
);
185 void debug_context::set_cheat(uint64_t addr
, uint64_t value
)
187 rom
.set_cheat(addr
, value
, true);
190 void debug_context::clear_cheat(uint64_t addr
)
192 rom
.set_cheat(addr
, 0, false);
195 void debug_context::setxmask(uint64_t mask
)
200 bool debug_context::is_tracelogging(uint64_t cpu
)
202 return (trace_outputs
.count(cpu
) != 0);
205 void debug_context::set_tracelog_change_cb(std::function
<void()> cb
)
207 tracelog_change_cb
= cb
;
210 void debug_context::core_change()
213 kill_hooks(read_cb
, DEBUG_READ
);
214 kill_hooks(write_cb
, DEBUG_WRITE
);
215 kill_hooks(exec_cb
, DEBUG_EXEC
);
216 kill_hooks(trace_cb
, DEBUG_TRACE
);
219 void debug_context::request_break()
221 requesting_break
= true;
224 debug_context::tracelog_file::tracelog_file(debug_context
& _parent
)
229 debug_context::tracelog_file::~tracelog_file()
233 void debug_context::tracelog_file::callback(const debug_context::params
& p
)
235 if(!parent
.trace_outputs
.count(p
.trace
.cpu
)) return;
236 parent
.trace_outputs
[p
.trace
.cpu
]->stream
<< p
.trace
.decoded_insn
<< std::endl
;
239 void debug_context::tracelog_file::killed(uint64_t addr
, debug_context::etype type
)
246 void debug_context::tracelog(uint64_t proc
, const std::string
& filename
)
249 if(!trace_outputs
.count(proc
))
251 remove_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
252 trace_outputs
[proc
]->refcnt
--;
253 if(!trace_outputs
[proc
]->refcnt
)
254 delete trace_outputs
[proc
];
255 trace_outputs
.erase(proc
);
256 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
257 if(tracelog_change_cb
) tracelog_change_cb();
260 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
261 std::string full_filename
= directory::absolute_path(filename
);
263 for(auto i
: trace_outputs
) {
264 if(i
.second
->full_filename
== full_filename
) {
266 trace_outputs
[proc
] = i
.second
;
272 trace_outputs
[proc
] = new tracelog_file(*this);
273 trace_outputs
[proc
]->refcnt
= 1;
274 trace_outputs
[proc
]->full_filename
= full_filename
;
275 trace_outputs
[proc
]->stream
.open(full_filename
);
276 if(!trace_outputs
[proc
]->stream
) {
277 delete trace_outputs
[proc
];
278 trace_outputs
.erase(proc
);
279 throw std::runtime_error("Can't open '" + full_filename
+ "'");
283 add_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
284 } catch(std::exception
& e
) {
285 messages
<< "Error starting tracelogging: " << e
.what() << std::endl
;
286 trace_outputs
[proc
]->refcnt
--;
287 if(!trace_outputs
[proc
]->refcnt
)
288 delete trace_outputs
[proc
];
289 trace_outputs
.erase(proc
);
292 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
293 if(tracelog_change_cb
) tracelog_change_cb();
296 void debug_context::do_showhooks()
298 for(auto& i
: read_cb
)
299 for(auto& j
: i
.second
)
300 messages
<< "READ addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
302 for(auto& i
: write_cb
)
303 for(auto& j
: i
.second
)
304 messages
<< "WRITE addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
306 for(auto& i
: exec_cb
)
307 for(auto& j
: i
.second
)
308 messages
<< "EXEC addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
310 for(auto& i
: trace_cb
)
311 for(auto& j
: i
.second
)
312 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
313 for(auto& i
: frame_cb
)
314 for(auto& j
: i
.second
)
315 messages
<< "FRAME handle=" << &j
<< std::endl
;
318 void debug_context::do_genevent(const std::string
& args
)
320 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
321 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
323 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
324 uint64_t val
= parse_value
<uint64_t>(r
[3]);
325 do_callback_read(addr
, val
);
326 } else if(r
[1] == "w") {
327 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
328 uint64_t val
= parse_value
<uint64_t>(r
[3]);
329 do_callback_write(addr
, val
);
330 } else if(r
[1] == "x") {
331 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
332 uint64_t val
= parse_value
<uint64_t>(r
[3]);
333 do_callback_exec(addr
, val
);
334 } else if(r
[1] == "t") {
335 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
336 std::string str
= r
[3];
337 do_callback_trace(proc
, str
.c_str());
339 throw std::runtime_error("Invalid operation");
342 void debug_context::do_tracecmd(const std::string
& args
)
344 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
345 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
346 std::string cpu
= r
[1];
347 std::string filename
= r
[3];
349 for(auto i
: rom
.get_trace_cpus()) {
354 throw std::runtime_error("tracelog: Invalid CPU");
356 tracelog(_cpu
, filename
);