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"
22 unsigned debug_flag(debug_context::etype type
)
25 case debug_context::DEBUG_READ
: return 1;
26 case debug_context::DEBUG_WRITE
: return 2;
27 case debug_context::DEBUG_EXEC
: return 4;
28 case debug_context::DEBUG_TRACE
: return 8;
29 case debug_context::DEBUG_FRAME
: return 0;
30 default: throw std::runtime_error("Invalid debug callback type");
37 template<class T
> void kill_hooks(T
& cblist
, debug_context::etype type
)
39 while(!cblist
.empty()) {
40 if(cblist
.begin()->second
.empty()) {
41 cblist
.erase(cblist
.begin()->first
);
44 auto key
= cblist
.begin()->first
;
45 auto tmp
= cblist
.begin()->second
.begin();
46 (*tmp
)->killed(key
, type
);
47 cblist
.begin()->second
.erase(cblist
.begin()->second
.begin());
53 debug_context::debug_context(emulator_dispatch
& _dispatch
, loaded_rom
& _rom
, memory_space
& _mspace
,
55 : edispatch(_dispatch
), rom(_rom
), mspace(_mspace
), cmd(_cmd
),
56 showhooks(cmd
, CDEBUG::scb
, [this]() { this->do_showhooks(); }),
57 genevent(cmd
, CDEBUG::genevt
, [this](const std::string
& a
) { this->do_genevent(a
); }),
58 tracecmd(cmd
, CDEBUG::tr
, [this](const std::string
& a
) { this->do_tracecmd(a
); })
62 debug_context::callback_base::~callback_base()
66 const uint64_t debug_context::all_addresses
= 0xFFFFFFFFFFFFFFFFULL
;
68 void debug_context::add_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
71 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
73 corechange
.set(edispatch
.core_change
, [this]() { this->core_change(); });
76 if(!xcb
.count(addr
) && type
!= DEBUG_FRAME
)
77 core
.rom
->set_debug_flags(addr
, debug_flag(type
), 0);
78 auto& lst
= xcb
[addr
];
82 void debug_context::remove_callback(uint64_t addr
, debug_context::etype type
, debug_context::callback_base
& cb
)
84 std::map
<uint64_t, cb_list
>& xcb
= get_lists(type
);
85 if(type
== DEBUG_FRAME
) addr
= 0;
86 if(!xcb
.count(addr
)) return;
88 for(auto i
= l
.begin(); i
!= l
.end(); i
++) {
94 if(xcb
[addr
].empty()) {
96 if(type
!= DEBUG_FRAME
)
97 rom
.set_debug_flags(addr
, 0, debug_flag(type
));
101 void debug_context::do_callback_read(uint64_t addr
, uint64_t value
)
108 requesting_break
= false;
109 cb_list
* cb1
= read_cb
.count(all_addresses
) ? &read_cb
[all_addresses
] : &dummy_cb
;
110 cb_list
* cb2
= read_cb
.count(addr
) ? &read_cb
[addr
] : &dummy_cb
;
113 for(auto& i
: _cb1
) i
->callback(p
);
114 for(auto& i
: _cb2
) i
->callback(p
);
119 void debug_context::do_callback_write(uint64_t addr
, uint64_t value
)
122 p
.type
= DEBUG_WRITE
;
126 requesting_break
= false;
127 cb_list
* cb1
= write_cb
.count(all_addresses
) ? &write_cb
[all_addresses
] : &dummy_cb
;
128 cb_list
* cb2
= write_cb
.count(addr
) ? &write_cb
[addr
] : &dummy_cb
;
131 for(auto& i
: _cb1
) i
->callback(p
);
132 for(auto& i
: _cb2
) i
->callback(p
);
137 void debug_context::do_callback_exec(uint64_t addr
, uint64_t cpu
)
144 requesting_break
= false;
145 cb_list
* cb1
= exec_cb
.count(all_addresses
) ? &exec_cb
[all_addresses
] : &dummy_cb
;
146 cb_list
* cb2
= exec_cb
.count(addr
) ? &exec_cb
[addr
] : &dummy_cb
;
149 if((1ULL << cpu
) & xmask
)
150 for(auto& i
: _cb1
) i
->callback(p
);
151 for(auto& i
: _cb2
) i
->callback(p
);
156 void debug_context::do_callback_trace(uint64_t cpu
, const char* str
, bool true_insn
)
159 p
.type
= DEBUG_TRACE
;
161 p
.trace
.decoded_insn
= str
;
162 p
.trace
.true_insn
= true_insn
;
164 requesting_break
= false;
165 cb_list
* cb
= trace_cb
.count(cpu
) ? &trace_cb
[cpu
] : &dummy_cb
;
167 for(auto& i
: _cb
) i
->callback(p
);
172 void debug_context::do_callback_frame(uint64_t frame
, bool loadstate
)
175 p
.type
= DEBUG_FRAME
;
176 p
.frame
.frame
= frame
;
177 p
.frame
.loadstated
= loadstate
;
179 cb_list
* cb
= frame_cb
.count(0) ? &frame_cb
[0] : &dummy_cb
;
181 for(auto& i
: _cb
) i
->callback(p
);
184 void debug_context::set_cheat(uint64_t addr
, uint64_t value
)
186 rom
.set_cheat(addr
, value
, true);
189 void debug_context::clear_cheat(uint64_t addr
)
191 rom
.set_cheat(addr
, 0, false);
194 void debug_context::setxmask(uint64_t mask
)
199 bool debug_context::is_tracelogging(uint64_t cpu
)
201 return (trace_outputs
.count(cpu
) != 0);
204 void debug_context::set_tracelog_change_cb(std::function
<void()> cb
)
206 tracelog_change_cb
= cb
;
209 void debug_context::core_change()
212 kill_hooks(read_cb
, DEBUG_READ
);
213 kill_hooks(write_cb
, DEBUG_WRITE
);
214 kill_hooks(exec_cb
, DEBUG_EXEC
);
215 kill_hooks(trace_cb
, DEBUG_TRACE
);
218 void debug_context::request_break()
220 requesting_break
= true;
223 debug_context::tracelog_file::tracelog_file(debug_context
& _parent
)
228 debug_context::tracelog_file::~tracelog_file()
232 void debug_context::tracelog_file::callback(const debug_context::params
& p
)
234 if(!parent
.trace_outputs
.count(p
.trace
.cpu
)) return;
235 parent
.trace_outputs
[p
.trace
.cpu
]->stream
<< p
.trace
.decoded_insn
<< std::endl
;
238 void debug_context::tracelog_file::killed(uint64_t addr
, debug_context::etype type
)
245 void debug_context::tracelog(uint64_t proc
, const std::string
& filename
)
248 if(!trace_outputs
.count(proc
))
250 remove_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
251 trace_outputs
[proc
]->refcnt
--;
252 if(!trace_outputs
[proc
]->refcnt
)
253 delete trace_outputs
[proc
];
254 trace_outputs
.erase(proc
);
255 messages
<< "Stopped tracelogging processor #" << proc
<< std::endl
;
256 if(tracelog_change_cb
) tracelog_change_cb();
259 if(trace_outputs
.count(proc
)) throw std::runtime_error("Already tracelogging");
260 std::string full_filename
= directory::absolute_path(filename
);
262 for(auto i
: trace_outputs
) {
263 if(i
.second
->full_filename
== full_filename
) {
265 trace_outputs
[proc
] = i
.second
;
271 trace_outputs
[proc
] = new tracelog_file(*this);
272 trace_outputs
[proc
]->refcnt
= 1;
273 trace_outputs
[proc
]->full_filename
= full_filename
;
274 trace_outputs
[proc
]->stream
.open(full_filename
);
275 if(!trace_outputs
[proc
]->stream
) {
276 delete trace_outputs
[proc
];
277 trace_outputs
.erase(proc
);
278 throw std::runtime_error("Can't open '" + full_filename
+ "'");
282 add_callback(proc
, DEBUG_TRACE
, *trace_outputs
[proc
]);
283 } catch(std::exception
& e
) {
284 messages
<< "Error starting tracelogging: " << e
.what() << std::endl
;
285 trace_outputs
[proc
]->refcnt
--;
286 if(!trace_outputs
[proc
]->refcnt
)
287 delete trace_outputs
[proc
];
288 trace_outputs
.erase(proc
);
291 messages
<< "Tracelogging processor #" << proc
<< " to '" << filename
<< "'" << std::endl
;
292 if(tracelog_change_cb
) tracelog_change_cb();
295 void debug_context::do_showhooks()
297 for(auto& i
: read_cb
)
298 for(auto& j
: i
.second
)
299 messages
<< "READ addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
301 for(auto& i
: write_cb
)
302 for(auto& j
: i
.second
)
303 messages
<< "WRITE addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
305 for(auto& i
: exec_cb
)
306 for(auto& j
: i
.second
)
307 messages
<< "EXEC addr=" << mspace
.address_to_textual(i
.first
) << " handle=" << &j
309 for(auto& i
: trace_cb
)
310 for(auto& j
: i
.second
)
311 messages
<< "TRACE proc=" << i
.first
<< " handle=" << &j
<< std::endl
;
312 for(auto& i
: frame_cb
)
313 for(auto& j
: i
.second
)
314 messages
<< "FRAME handle=" << &j
<< std::endl
;
317 void debug_context::do_genevent(const std::string
& args
)
319 regex_results r
= regex("([^ \t]+) ([^ \t]+) (.+)", args
);
320 if(!r
) throw std::runtime_error("generate-memory-event: Bad arguments");
322 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
323 uint64_t val
= parse_value
<uint64_t>(r
[3]);
324 do_callback_read(addr
, val
);
325 } else if(r
[1] == "w") {
326 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
327 uint64_t val
= parse_value
<uint64_t>(r
[3]);
328 do_callback_write(addr
, val
);
329 } else if(r
[1] == "x") {
330 uint64_t addr
= parse_value
<uint64_t>(r
[2]);
331 uint64_t val
= parse_value
<uint64_t>(r
[3]);
332 do_callback_exec(addr
, val
);
333 } else if(r
[1] == "t") {
334 uint64_t proc
= parse_value
<uint64_t>(r
[2]);
335 std::string str
= r
[3];
336 do_callback_trace(proc
, str
.c_str());
338 throw std::runtime_error("Invalid operation");
341 void debug_context::do_tracecmd(const std::string
& args
)
343 regex_results r
= regex("([^ \t]+)([ \t]+(.+))?", args
);
344 if(!r
) throw std::runtime_error("tracelog: Bad arguments");
345 std::string cpu
= r
[1];
346 std::string filename
= r
[3];
348 for(auto i
: rom
.get_trace_cpus()) {
353 throw std::runtime_error("tracelog: Invalid CPU");
355 tracelog(_cpu
, filename
);