Refactor debugging into instance object
This commit is contained in:
parent
c5a31e85d8
commit
adb1b29c67
8 changed files with 655 additions and 408 deletions
|
@ -2,12 +2,9 @@
|
|||
#define _debug__hpp__included__
|
||||
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
|
||||
struct debug_handle
|
||||
{
|
||||
void* handle;
|
||||
};
|
||||
#include "library/dispatch.hpp"
|
||||
|
||||
enum debug_type
|
||||
{
|
||||
|
@ -18,27 +15,161 @@ enum debug_type
|
|||
DEBUG_FRAME,
|
||||
};
|
||||
|
||||
extern const uint64_t debug_all_addr;
|
||||
struct debug_callback_params_rwx
|
||||
{
|
||||
uint64_t addr; //Address.
|
||||
uint64_t value; //Value/CPU.
|
||||
};
|
||||
|
||||
debug_handle debug_add_callback(uint64_t addr, debug_type type,
|
||||
std::function<void(uint64_t addr, uint64_t value)> fn, std::function<void()> dtor);
|
||||
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str,
|
||||
bool true_insn)> fn, std::function<void()> dtor);
|
||||
debug_handle debug_add_frame_callback(std::function<void(uint64_t frame, bool loadstate)> fn,
|
||||
std::function<void()> dtor);
|
||||
void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle);
|
||||
void debug_fire_callback_read(uint64_t addr, uint64_t value);
|
||||
void debug_fire_callback_write(uint64_t addr, uint64_t value);
|
||||
void debug_fire_callback_exec(uint64_t addr, uint64_t value);
|
||||
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn = true);
|
||||
void debug_fire_callback_frame(uint64_t frame, bool loadstate);
|
||||
void debug_set_cheat(uint64_t addr, uint64_t value);
|
||||
void debug_clear_cheat(uint64_t addr);
|
||||
void debug_setxmask(uint64_t mask);
|
||||
void debug_tracelog(uint64_t proc, const std::string& filename);
|
||||
bool debug_tracelogging(uint64_t proc);
|
||||
void debug_set_tracelog_change_cb(std::function<void()> cb);
|
||||
void debug_core_change();
|
||||
void debug_request_break();
|
||||
struct debug_callback_params_trace
|
||||
{
|
||||
uint64_t cpu; //CPU number.
|
||||
const char* decoded_insn; //Decoded instruction
|
||||
bool true_insn; //True instruction flag.
|
||||
};
|
||||
|
||||
struct debug_callback_params_frame
|
||||
{
|
||||
uint64_t frame; //Frame number.
|
||||
bool loadstated; //Loadstate flag.
|
||||
};
|
||||
|
||||
struct debug_callback_params
|
||||
{
|
||||
debug_type type;
|
||||
union {
|
||||
debug_callback_params_rwx rwx; //READ/WRITE/EXECUTE
|
||||
debug_callback_params_trace trace; //TRACE.
|
||||
debug_callback_params_frame frame; //FRAME.
|
||||
};
|
||||
};
|
||||
|
||||
struct debug_callback_base
|
||||
{
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~debug_callback_base();
|
||||
/**
|
||||
* Do a callback.
|
||||
*/
|
||||
virtual void callback(const debug_callback_params& params) = 0;
|
||||
/**
|
||||
* Notify about killed callback.
|
||||
*/
|
||||
virtual void killed(uint64_t addr, debug_type type) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Debugging context.
|
||||
*/
|
||||
class debug_context
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Placeholder for all addresses.
|
||||
*/
|
||||
static const uint64_t all_addresses;
|
||||
/**
|
||||
* Add a callback.
|
||||
*/
|
||||
void add_callback(uint64_t addr, debug_type type, debug_callback_base& cb);
|
||||
/**
|
||||
* Remove a callback.
|
||||
*/
|
||||
void remove_callback(uint64_t addr, debug_type type, debug_callback_base& cb);
|
||||
/**
|
||||
* Fire a read callback.
|
||||
*/
|
||||
void do_callback_read(uint64_t addr, uint64_t value);
|
||||
/**
|
||||
* Fire a write callback.
|
||||
*/
|
||||
void do_callback_write(uint64_t addr, uint64_t value);
|
||||
/**
|
||||
* Fire a exec callback.
|
||||
*/
|
||||
void do_callback_exec(uint64_t addr, uint64_t value);
|
||||
/**
|
||||
* Fire a trace callback.
|
||||
*/
|
||||
void do_callback_trace(uint64_t cpu, const char* str, bool true_insn = true);
|
||||
/**
|
||||
* Fire a frame callback.
|
||||
*/
|
||||
void do_callback_frame(uint64_t frame, bool loadstate);
|
||||
/**
|
||||
* Set a cheat.
|
||||
*/
|
||||
void set_cheat(uint64_t addr, uint64_t value);
|
||||
/**
|
||||
* Clear a cheat.
|
||||
*/
|
||||
void clear_cheat(uint64_t addr);
|
||||
/**
|
||||
* Set execute callback mask.
|
||||
*/
|
||||
void setxmask(uint64_t mask);
|
||||
/**
|
||||
* Set tracelog file.
|
||||
*/
|
||||
void tracelog(uint64_t cpu, const std::string& filename);
|
||||
/**
|
||||
* Tracelogging on?
|
||||
*/
|
||||
bool is_tracelogging(uint64_t cpu);
|
||||
/**
|
||||
* Change tracelog change callback.
|
||||
*/
|
||||
void set_tracelog_change_cb(std::function<void()> cb);
|
||||
/**
|
||||
* Notify a core change.
|
||||
*/
|
||||
void core_change();
|
||||
/**
|
||||
* Request a break.
|
||||
*/
|
||||
void request_break();
|
||||
//These are public only for some debugging stuff.
|
||||
typedef std::list<debug_callback_base*> cb_list;
|
||||
std::map<uint64_t, cb_list> read_cb;
|
||||
std::map<uint64_t, cb_list> write_cb;
|
||||
std::map<uint64_t, cb_list> exec_cb;
|
||||
std::map<uint64_t, cb_list> trace_cb;
|
||||
std::map<uint64_t, cb_list> frame_cb;
|
||||
private:
|
||||
cb_list dummy_cb; //Always empty.
|
||||
uint64_t xmask = 1;
|
||||
std::function<void()> tracelog_change_cb;
|
||||
struct dispatch::target<> corechange;
|
||||
bool corechange_r = false;
|
||||
bool requesting_break = false;
|
||||
|
||||
struct tracelog_file : public debug_callback_base
|
||||
{
|
||||
std::ofstream stream;
|
||||
std::string full_filename;
|
||||
unsigned refcnt;
|
||||
tracelog_file(debug_context& parent);
|
||||
~tracelog_file();
|
||||
void callback(const debug_callback_params& p);
|
||||
void killed(uint64_t addr, debug_type type);
|
||||
private:
|
||||
debug_context& parent;
|
||||
};
|
||||
std::map<uint64_t, tracelog_file*> trace_outputs;
|
||||
|
||||
std::map<uint64_t, cb_list>& get_lists(debug_type type)
|
||||
{
|
||||
switch(type) {
|
||||
case DEBUG_READ: return read_cb;
|
||||
case DEBUG_WRITE: return write_cb;
|
||||
case DEBUG_EXEC: return exec_cb;
|
||||
case DEBUG_TRACE: return trace_cb;
|
||||
case DEBUG_FRAME: return frame_cb;
|
||||
default: throw std::runtime_error("Invalid debug callback type");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <deque>
|
||||
#include "core/command.hpp"
|
||||
#include "core/controllerframe.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/emustatus.hpp"
|
||||
#include "core/inthread.hpp"
|
||||
#include "core/movie.hpp"
|
||||
|
@ -95,6 +96,7 @@ struct emulator_instance
|
|||
cart_mappings_refresher cmapper;
|
||||
controller_state controls;
|
||||
project_state project;
|
||||
debug_context dbg;
|
||||
threads::id emu_thread;
|
||||
//Queue stuff.
|
||||
threads::lock queue_lock;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "core/command.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/instance.hpp"
|
||||
#include "core/mainloop.hpp"
|
||||
#include "core/moviedata.hpp"
|
||||
#include "library/directory.hpp"
|
||||
|
@ -12,55 +13,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct cb_rwx
|
||||
{
|
||||
std::function<void(uint64_t addr, uint64_t value)> cb;
|
||||
std::function<void()> dtor;
|
||||
};
|
||||
struct cb_trace
|
||||
{
|
||||
std::function<void(uint64_t proc, const char* str, bool true_insn)> cb;
|
||||
std::function<void()> dtor;
|
||||
};
|
||||
struct cb_frame
|
||||
{
|
||||
std::function<void(uint64_t frame, bool loadstate)> cb;
|
||||
std::function<void()> dtor;
|
||||
};
|
||||
typedef std::list<cb_rwx> cb_list;
|
||||
typedef std::list<cb_trace> cb2_list;
|
||||
typedef std::list<cb_frame> cb3_list;
|
||||
std::map<uint64_t, cb_list> read_cb;
|
||||
std::map<uint64_t, cb_list> write_cb;
|
||||
std::map<uint64_t, cb_list> exec_cb;
|
||||
std::map<uint64_t, cb2_list> trace_cb;
|
||||
std::map<uint64_t, cb3_list> frame_cb;
|
||||
cb_list dummy_cb; //Always empty.
|
||||
cb2_list dummy_cb2; //Always empty.
|
||||
cb3_list dummy_cb3; //Always empty.
|
||||
uint64_t xmask = 1;
|
||||
std::function<void()> tracelog_change_cb;
|
||||
struct dispatch::target<> corechange;
|
||||
bool corechange_r = false;
|
||||
bool requesting_break = false;
|
||||
|
||||
struct tracelog_file
|
||||
{
|
||||
std::ofstream stream;
|
||||
std::string full_filename;
|
||||
unsigned refcnt;
|
||||
};
|
||||
std::map<uint64_t, std::pair<tracelog_file*, debug_handle>> trace_outputs;
|
||||
|
||||
std::map<uint64_t, cb_list>& get_lists(debug_type type)
|
||||
{
|
||||
switch(type) {
|
||||
case DEBUG_READ: return read_cb;
|
||||
case DEBUG_WRITE: return write_cb;
|
||||
case DEBUG_EXEC: return exec_cb;
|
||||
default: throw std::runtime_error("Invalid debug callback type");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned debug_flag(debug_type type)
|
||||
{
|
||||
|
@ -75,179 +27,215 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
const uint64_t debug_all_addr = 0xFFFFFFFFFFFFFFFFULL;
|
||||
|
||||
namespace
|
||||
{
|
||||
template<class T> debug_handle _debug_add_callback(std::map<uint64_t, std::list<T>>& cb, uint64_t addr,
|
||||
debug_type type, T fn, std::function<void()> dtor)
|
||||
{
|
||||
if(!corechange_r) {
|
||||
corechange.set(notify_core_change, []() { debug_core_change(); });
|
||||
corechange_r = true;
|
||||
}
|
||||
if(!cb.count(addr) && type != DEBUG_FRAME)
|
||||
our_rom.rtype->set_debug_flags(addr, debug_flag(type), 0);
|
||||
|
||||
auto& lst = cb[addr];
|
||||
lst.push_back(fn);
|
||||
debug_handle h;
|
||||
h.handle = &*lst.rbegin();
|
||||
return h;
|
||||
}
|
||||
|
||||
template<class T> void _debug_remove_callback(T& cb, uint64_t addr, debug_type type, debug_handle handle)
|
||||
{
|
||||
if(!cb.count(addr)) return;
|
||||
auto& l = cb[addr];
|
||||
for(auto i = l.begin(); i != l.end(); i++) {
|
||||
if(&*i == handle.handle) {
|
||||
l.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(cb[addr].empty()) {
|
||||
cb.erase(addr);
|
||||
if(type != DEBUG_FRAME)
|
||||
our_rom.rtype->set_debug_flags(addr, 0, debug_flag(type));
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> void kill_hooks(T& cblist)
|
||||
template<class T> void kill_hooks(T& cblist, debug_type type)
|
||||
{
|
||||
while(!cblist.empty()) {
|
||||
if(cblist.begin()->second.empty()) {
|
||||
cblist.erase(cblist.begin()->first);
|
||||
continue;
|
||||
}
|
||||
auto key = cblist.begin()->first;
|
||||
auto tmp = cblist.begin()->second.begin();
|
||||
tmp->dtor();
|
||||
cblist.begin()->second.erase(cblist.begin()->second.begin());
|
||||
(*tmp)->killed(key, type);
|
||||
}
|
||||
cblist.clear();
|
||||
}
|
||||
}
|
||||
|
||||
debug_handle debug_add_callback(uint64_t addr, debug_type type, std::function<void(uint64_t addr, uint64_t value)> fn,
|
||||
std::function<void()> dtor)
|
||||
debug_callback_base::~debug_callback_base()
|
||||
{
|
||||
std::map<uint64_t, cb_list>& cb = get_lists(type);
|
||||
cb_rwx t;
|
||||
t.cb = fn;
|
||||
t.dtor = dtor;
|
||||
return _debug_add_callback(cb, addr, type, t, dtor);
|
||||
}
|
||||
|
||||
debug_handle debug_add_trace_callback(uint64_t proc, std::function<void(uint64_t proc, const char* str,
|
||||
bool true_insn)> fn, std::function<void()> dtor)
|
||||
const uint64_t debug_context::all_addresses = 0xFFFFFFFFFFFFFFFFULL;
|
||||
|
||||
void debug_context::add_callback(uint64_t addr, debug_type type, debug_callback_base& cb)
|
||||
{
|
||||
cb_trace t;
|
||||
t.cb = fn;
|
||||
t.dtor = dtor;
|
||||
return _debug_add_callback(trace_cb, proc, DEBUG_TRACE, t, dtor);
|
||||
std::map<uint64_t, cb_list>& xcb = get_lists(type);
|
||||
if(!corechange_r) {
|
||||
corechange.set(notify_core_change, [this]() { this->core_change(); });
|
||||
corechange_r = true;
|
||||
}
|
||||
if(!xcb.count(addr) && type != DEBUG_FRAME)
|
||||
our_rom.rtype->set_debug_flags(addr, debug_flag(type), 0);
|
||||
auto& lst = xcb[addr];
|
||||
lst.push_back(&cb);
|
||||
}
|
||||
|
||||
debug_handle debug_add_frame_callback(std::function<void(uint64_t frame, bool loadstate)> fn,
|
||||
std::function<void()> dtor)
|
||||
void debug_context::remove_callback(uint64_t addr, debug_type type, debug_callback_base& cb)
|
||||
{
|
||||
cb_frame t;
|
||||
t.cb = fn;
|
||||
t.dtor = dtor;
|
||||
return _debug_add_callback(frame_cb, 0, DEBUG_FRAME, t, dtor);
|
||||
}
|
||||
|
||||
void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle)
|
||||
{
|
||||
if(type == DEBUG_FRAME) {
|
||||
_debug_remove_callback(frame_cb, 0, DEBUG_FRAME, handle);
|
||||
} else if(type == DEBUG_TRACE) {
|
||||
_debug_remove_callback(trace_cb, addr, DEBUG_TRACE, handle);
|
||||
} else {
|
||||
_debug_remove_callback(get_lists(type), addr, type, handle);
|
||||
std::map<uint64_t, cb_list>& xcb = get_lists(type);
|
||||
if(type == DEBUG_FRAME) addr = 0;
|
||||
if(!xcb.count(addr)) return;
|
||||
auto& l = xcb[addr];
|
||||
for(auto i = l.begin(); i != l.end(); i++) {
|
||||
if(*i == &cb) {
|
||||
l.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(xcb[addr].empty()) {
|
||||
xcb.erase(addr);
|
||||
if(type != DEBUG_FRAME)
|
||||
our_rom.rtype->set_debug_flags(addr, 0, debug_flag(type));
|
||||
}
|
||||
}
|
||||
|
||||
void debug_fire_callback_read(uint64_t addr, uint64_t value)
|
||||
void debug_context::do_callback_read(uint64_t addr, uint64_t value)
|
||||
{
|
||||
debug_callback_params p;
|
||||
p.type = DEBUG_READ;
|
||||
p.rwx.addr = addr;
|
||||
p.rwx.value = value;
|
||||
|
||||
requesting_break = false;
|
||||
cb_list* cb1 = read_cb.count(debug_all_addr) ? &read_cb[debug_all_addr] : &dummy_cb;
|
||||
cb_list* cb1 = read_cb.count(all_addresses) ? &read_cb[all_addresses] : &dummy_cb;
|
||||
cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
|
||||
auto _cb1 = *cb1;
|
||||
auto _cb2 = *cb2;
|
||||
for(auto& i : _cb1) i.cb(addr, value);
|
||||
for(auto& i : _cb2) i.cb(addr, value);
|
||||
for(auto& i : _cb1) i->callback(p);
|
||||
for(auto& i : _cb2) i->callback(p);
|
||||
if(requesting_break)
|
||||
do_break_pause();
|
||||
}
|
||||
|
||||
void debug_fire_callback_write(uint64_t addr, uint64_t value)
|
||||
void debug_context::do_callback_write(uint64_t addr, uint64_t value)
|
||||
{
|
||||
debug_callback_params p;
|
||||
p.type = DEBUG_WRITE;
|
||||
p.rwx.addr = addr;
|
||||
p.rwx.value = value;
|
||||
|
||||
requesting_break = false;
|
||||
cb_list* cb1 = write_cb.count(debug_all_addr) ? &write_cb[debug_all_addr] : &dummy_cb;
|
||||
cb_list* cb1 = write_cb.count(all_addresses) ? &write_cb[all_addresses] : &dummy_cb;
|
||||
cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
|
||||
auto _cb1 = *cb1;
|
||||
auto _cb2 = *cb2;
|
||||
for(auto& i : _cb1) i.cb(addr, value);
|
||||
for(auto& i : _cb2) i.cb(addr, value);
|
||||
for(auto& i : _cb1) i->callback(p);
|
||||
for(auto& i : _cb2) i->callback(p);
|
||||
if(requesting_break)
|
||||
do_break_pause();
|
||||
}
|
||||
|
||||
void debug_fire_callback_exec(uint64_t addr, uint64_t value)
|
||||
void debug_context::do_callback_exec(uint64_t addr, uint64_t cpu)
|
||||
{
|
||||
debug_callback_params p;
|
||||
p.type = DEBUG_EXEC;
|
||||
p.rwx.addr = addr;
|
||||
p.rwx.value = cpu;
|
||||
|
||||
requesting_break = false;
|
||||
cb_list* cb1 = exec_cb.count(debug_all_addr) ? &exec_cb[debug_all_addr] : &dummy_cb;
|
||||
cb_list* cb1 = exec_cb.count(all_addresses) ? &exec_cb[all_addresses] : &dummy_cb;
|
||||
cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
|
||||
auto _cb1 = *cb1;
|
||||
auto _cb2 = *cb2;
|
||||
if(value & xmask)
|
||||
for(auto& i : _cb1) i.cb(addr, value);
|
||||
for(auto& i : _cb2) i.cb(addr, value);
|
||||
if((1ULL << cpu) & xmask)
|
||||
for(auto& i : _cb1) i->callback(p);
|
||||
for(auto& i : _cb2) i->callback(p);
|
||||
if(requesting_break)
|
||||
do_break_pause();
|
||||
}
|
||||
|
||||
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn)
|
||||
void debug_context::do_callback_trace(uint64_t cpu, const char* str, bool true_insn)
|
||||
{
|
||||
debug_callback_params p;
|
||||
p.type = DEBUG_TRACE;
|
||||
p.trace.cpu = cpu;
|
||||
p.trace.decoded_insn = str;
|
||||
p.trace.true_insn = true_insn;
|
||||
|
||||
requesting_break = false;
|
||||
cb2_list* cb = trace_cb.count(proc) ? &trace_cb[proc] : &dummy_cb2;
|
||||
cb_list* cb = trace_cb.count(cpu) ? &trace_cb[cpu] : &dummy_cb;
|
||||
auto _cb = *cb;
|
||||
for(auto& i : _cb) i.cb(proc, str, true_insn);
|
||||
for(auto& i : _cb) i->callback(p);
|
||||
if(requesting_break)
|
||||
do_break_pause();
|
||||
}
|
||||
|
||||
void debug_fire_callback_frame(uint64_t frame, bool loadstate)
|
||||
void debug_context::do_callback_frame(uint64_t frame, bool loadstate)
|
||||
{
|
||||
cb3_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb3;
|
||||
debug_callback_params p;
|
||||
p.type = DEBUG_FRAME;
|
||||
p.frame.frame = frame;
|
||||
p.frame.loadstated = loadstate;
|
||||
|
||||
cb_list* cb = frame_cb.count(0) ? &frame_cb[0] : &dummy_cb;
|
||||
auto _cb = *cb;
|
||||
for(auto& i : _cb) i.cb(frame, loadstate);
|
||||
for(auto& i : _cb) i->callback(p);
|
||||
}
|
||||
|
||||
void debug_set_cheat(uint64_t addr, uint64_t value)
|
||||
void debug_context::set_cheat(uint64_t addr, uint64_t value)
|
||||
{
|
||||
our_rom.rtype->set_cheat(addr, value, true);
|
||||
}
|
||||
|
||||
void debug_clear_cheat(uint64_t addr)
|
||||
void debug_context::clear_cheat(uint64_t addr)
|
||||
{
|
||||
our_rom.rtype->set_cheat(addr, 0, false);
|
||||
}
|
||||
|
||||
void debug_setxmask(uint64_t mask)
|
||||
void debug_context::setxmask(uint64_t mask)
|
||||
{
|
||||
xmask = mask;
|
||||
}
|
||||
|
||||
void debug_tracelog(uint64_t proc, const std::string& filename)
|
||||
bool debug_context::is_tracelogging(uint64_t cpu)
|
||||
{
|
||||
return (trace_outputs.count(cpu) != 0);
|
||||
}
|
||||
|
||||
void debug_context::set_tracelog_change_cb(std::function<void()> cb)
|
||||
{
|
||||
tracelog_change_cb = cb;
|
||||
}
|
||||
|
||||
void debug_context::core_change()
|
||||
{
|
||||
our_rom.rtype->debug_reset();
|
||||
kill_hooks(read_cb, DEBUG_READ);
|
||||
kill_hooks(write_cb, DEBUG_WRITE);
|
||||
kill_hooks(exec_cb, DEBUG_EXEC);
|
||||
kill_hooks(trace_cb, DEBUG_TRACE);
|
||||
}
|
||||
|
||||
void debug_context::request_break()
|
||||
{
|
||||
requesting_break = true;
|
||||
}
|
||||
|
||||
debug_context::tracelog_file::tracelog_file(debug_context& _parent)
|
||||
: parent(_parent)
|
||||
{
|
||||
}
|
||||
|
||||
debug_context::tracelog_file::~tracelog_file()
|
||||
{
|
||||
}
|
||||
|
||||
void debug_context::tracelog_file::callback(const debug_callback_params& p)
|
||||
{
|
||||
if(!parent.trace_outputs.count(p.trace.cpu)) return;
|
||||
parent.trace_outputs[p.trace.cpu]->stream << p.trace.decoded_insn << std::endl;
|
||||
}
|
||||
|
||||
void debug_context::tracelog_file::killed(uint64_t addr, debug_type type)
|
||||
{
|
||||
refcnt--;
|
||||
if(!refcnt)
|
||||
delete this;
|
||||
}
|
||||
|
||||
void debug_context::tracelog(uint64_t proc, const std::string& filename)
|
||||
{
|
||||
if(filename == "") {
|
||||
if(!trace_outputs.count(proc))
|
||||
return;
|
||||
debug_remove_callback(proc, DEBUG_TRACE, trace_outputs[proc].second);
|
||||
trace_outputs[proc].first->refcnt--;
|
||||
if(!trace_outputs[proc].first->refcnt) {
|
||||
delete trace_outputs[proc].first;
|
||||
}
|
||||
remove_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
|
||||
trace_outputs[proc]->refcnt--;
|
||||
if(!trace_outputs[proc]->refcnt)
|
||||
delete trace_outputs[proc];
|
||||
trace_outputs.erase(proc);
|
||||
messages << "Stopped tracelogging processor #" << proc << std::endl;
|
||||
if(tracelog_change_cb) tracelog_change_cb();
|
||||
|
@ -257,73 +245,55 @@ void debug_tracelog(uint64_t proc, const std::string& filename)
|
|||
std::string full_filename = directory::absolute_path(filename);
|
||||
bool found = false;
|
||||
for(auto i : trace_outputs) {
|
||||
if(i.second.first->full_filename == full_filename) {
|
||||
i.second.first->refcnt++;
|
||||
trace_outputs[proc].first = i.second.first;
|
||||
if(i.second->full_filename == full_filename) {
|
||||
i.second->refcnt++;
|
||||
trace_outputs[proc] = i.second;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
trace_outputs[proc].first = new tracelog_file;
|
||||
trace_outputs[proc].first->refcnt = 1;
|
||||
trace_outputs[proc].first->full_filename = full_filename;
|
||||
trace_outputs[proc].first->stream.open(full_filename);
|
||||
if(!trace_outputs[proc].first->stream) {
|
||||
delete trace_outputs[proc].first;
|
||||
trace_outputs[proc] = new tracelog_file(*this);
|
||||
trace_outputs[proc]->refcnt = 1;
|
||||
trace_outputs[proc]->full_filename = full_filename;
|
||||
trace_outputs[proc]->stream.open(full_filename);
|
||||
if(!trace_outputs[proc]->stream) {
|
||||
delete trace_outputs[proc];
|
||||
trace_outputs.erase(proc);
|
||||
throw std::runtime_error("Can't open '" + full_filename + "'");
|
||||
}
|
||||
}
|
||||
trace_outputs[proc].second = debug_add_trace_callback(proc, [](uint64_t proc, const char* str, bool dummy) {
|
||||
if(!trace_outputs.count(proc)) return;
|
||||
trace_outputs[proc].first->stream << str << std::endl;
|
||||
}, [proc]() { debug_tracelog(proc, ""); });
|
||||
try {
|
||||
add_callback(proc, DEBUG_TRACE, *trace_outputs[proc]);
|
||||
} catch(std::exception& e) {
|
||||
messages << "Error starting tracelogging: " << e.what() << std::endl;
|
||||
trace_outputs[proc]->refcnt--;
|
||||
if(!trace_outputs[proc]->refcnt)
|
||||
delete trace_outputs[proc];
|
||||
trace_outputs.erase(proc);
|
||||
throw;
|
||||
}
|
||||
messages << "Tracelogging processor #" << proc << " to '" << filename << "'" << std::endl;
|
||||
if(tracelog_change_cb) tracelog_change_cb();
|
||||
}
|
||||
|
||||
bool debug_tracelogging(uint64_t proc)
|
||||
{
|
||||
return (trace_outputs.count(proc) != 0);
|
||||
}
|
||||
|
||||
void debug_set_tracelog_change_cb(std::function<void()> cb)
|
||||
{
|
||||
tracelog_change_cb = cb;
|
||||
}
|
||||
|
||||
void debug_core_change()
|
||||
{
|
||||
our_rom.rtype->debug_reset();
|
||||
kill_hooks(read_cb);
|
||||
kill_hooks(write_cb);
|
||||
kill_hooks(exec_cb);
|
||||
kill_hooks(trace_cb);
|
||||
}
|
||||
|
||||
void debug_request_break()
|
||||
{
|
||||
requesting_break = true;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
command::fnptr<> callbacks_show(lsnes_cmds, "show-callbacks", "", "",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
for(auto& i : read_cb)
|
||||
for(auto& i : CORE().dbg.read_cb)
|
||||
for(auto& j : i.second)
|
||||
messages << "READ addr=" << i.first << " handle=" << &j << std::endl;
|
||||
for(auto& i : write_cb)
|
||||
for(auto& i : CORE().dbg.write_cb)
|
||||
for(auto& j : i.second)
|
||||
messages << "WRITE addr=" << i.first << " handle=" << &j << std::endl;
|
||||
for(auto& i : exec_cb)
|
||||
for(auto& i : CORE().dbg.exec_cb)
|
||||
for(auto& j : i.second)
|
||||
messages << "EXEC addr=" << i.first << " handle=" << &j << std::endl;
|
||||
for(auto& i : trace_cb)
|
||||
for(auto& i : CORE().dbg.trace_cb)
|
||||
for(auto& j : i.second)
|
||||
messages << "TRACE proc=" << i.first << " handle=" << &j << std::endl;
|
||||
for(auto& i : frame_cb)
|
||||
for(auto& i : CORE().dbg.frame_cb)
|
||||
for(auto& j : i.second)
|
||||
messages << "FRAME handle=" << &j << std::endl;
|
||||
});
|
||||
|
@ -335,19 +305,19 @@ namespace
|
|||
if(r[1] == "r") {
|
||||
uint64_t addr = parse_value<uint64_t>(r[2]);
|
||||
uint64_t val = parse_value<uint64_t>(r[3]);
|
||||
debug_fire_callback_read(addr, val);
|
||||
CORE().dbg.do_callback_read(addr, val);
|
||||
} else if(r[1] == "w") {
|
||||
uint64_t addr = parse_value<uint64_t>(r[2]);
|
||||
uint64_t val = parse_value<uint64_t>(r[3]);
|
||||
debug_fire_callback_write(addr, val);
|
||||
CORE().dbg.do_callback_write(addr, val);
|
||||
} else if(r[1] == "x") {
|
||||
uint64_t addr = parse_value<uint64_t>(r[2]);
|
||||
uint64_t val = parse_value<uint64_t>(r[3]);
|
||||
debug_fire_callback_exec(addr, val);
|
||||
CORE().dbg.do_callback_exec(addr, val);
|
||||
} else if(r[1] == "t") {
|
||||
uint64_t proc = parse_value<uint64_t>(r[2]);
|
||||
std::string str = r[3];
|
||||
debug_fire_callback_trace(proc, str.c_str());
|
||||
CORE().dbg.do_callback_trace(proc, str.c_str());
|
||||
} else
|
||||
throw std::runtime_error("Invalid operation");
|
||||
});
|
||||
|
@ -367,7 +337,7 @@ namespace
|
|||
}
|
||||
throw std::runtime_error("tracelog: Invalid CPU");
|
||||
out:
|
||||
debug_tracelog(_cpu, filename);
|
||||
CORE().dbg.tracelog(_cpu, filename);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -540,22 +540,22 @@ public:
|
|||
|
||||
void memory_read(uint64_t addr, uint64_t value)
|
||||
{
|
||||
debug_fire_callback_read(addr, value);
|
||||
CORE().dbg.do_callback_read(addr, value);
|
||||
}
|
||||
|
||||
void memory_write(uint64_t addr, uint64_t value)
|
||||
{
|
||||
debug_fire_callback_write(addr, value);
|
||||
CORE().dbg.do_callback_write(addr, value);
|
||||
}
|
||||
|
||||
void memory_execute(uint64_t addr, uint64_t proc)
|
||||
{
|
||||
debug_fire_callback_exec(addr, proc);
|
||||
CORE().dbg.do_callback_exec(addr, proc);
|
||||
}
|
||||
|
||||
void memory_trace(uint64_t proc, const char* str, bool insn)
|
||||
{
|
||||
debug_fire_callback_trace(proc, str, insn);
|
||||
CORE().dbg.do_callback_trace(proc, str, insn);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1334,8 +1334,7 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
|
|||
stop_at_frame_active = false;
|
||||
just_did_loadstate = first_round;
|
||||
CORE().controls.reset_framehold();
|
||||
debug_fire_callback_frame(CORE().mlogic.get_movie().get_current_frame(),
|
||||
true);
|
||||
CORE().dbg.do_callback_frame(CORE().mlogic.get_movie().get_current_frame(), true);
|
||||
continue;
|
||||
} else if(r < 0) {
|
||||
//Not exactly desriable, but this at least won't desync.
|
||||
|
@ -1357,7 +1356,7 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
|
|||
just_did_loadstate = false;
|
||||
}
|
||||
frame_irq_time = get_utime() - time_x;
|
||||
debug_fire_callback_frame(CORE().mlogic.get_movie().get_current_frame(), false);
|
||||
CORE().dbg.do_callback_frame(CORE().mlogic.get_movie().get_current_frame(), false);
|
||||
our_rom.rtype->emulate();
|
||||
random_mix_timing_entropy();
|
||||
time_x = get_utime();
|
||||
|
@ -1372,7 +1371,7 @@ out:
|
|||
CORE().commentary.kill();
|
||||
CORE().system_thread_available = false;
|
||||
//Kill some things to avoid crashes.
|
||||
debug_core_change();
|
||||
CORE().dbg.core_change();
|
||||
CORE().project.set(NULL, true);
|
||||
CORE().mwatch.clear_multi(CORE().mwatch.enumerate());
|
||||
}
|
||||
|
|
|
@ -200,40 +200,6 @@ namespace
|
|||
L.setmetatable(-2);
|
||||
}
|
||||
|
||||
struct lua_debug_callback
|
||||
{
|
||||
uint64_t addr;
|
||||
debug_type type;
|
||||
debug_handle h;
|
||||
bool dead;
|
||||
const void* lua_fn;
|
||||
static int dtor(lua_State* L)
|
||||
{
|
||||
lua_debug_callback* D = (lua_debug_callback*)lua_touserdata(L, 1);
|
||||
return D->_dtor(L);
|
||||
}
|
||||
int _dtor(lua_State* L);
|
||||
};
|
||||
std::map<uint64_t, std::list<lua_debug_callback*>> cbs;
|
||||
|
||||
int lua_debug_callback::_dtor(lua_State* L)
|
||||
{
|
||||
if(dead) return 0;
|
||||
dead = true;
|
||||
lua_pushlightuserdata(L, &type);
|
||||
lua_pushnil(L);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
debug_remove_callback(addr, type, h);
|
||||
for(auto j = cbs[addr].begin(); j != cbs[addr].end(); j++)
|
||||
if(*j == this) {
|
||||
cbs[addr].erase(j);
|
||||
break;
|
||||
}
|
||||
if(cbs[addr].empty())
|
||||
cbs.erase(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void do_lua_error(lua::state& L, int ret)
|
||||
{
|
||||
if(!ret) return;
|
||||
|
@ -253,81 +219,236 @@ namespace
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char lua_cb_list_key = 0;
|
||||
|
||||
struct lua_debug_callback2 : public debug_callback_base
|
||||
{
|
||||
lua::state* L;
|
||||
uint64_t addr;
|
||||
debug_type type;
|
||||
bool dead;
|
||||
const void* lua_fn;
|
||||
~lua_debug_callback2();
|
||||
void link_to_list();
|
||||
void set_lua_fn(int slot);
|
||||
void unregister();
|
||||
void callback(const debug_callback_params& p);
|
||||
void killed(uint64_t addr, debug_type type);
|
||||
static int on_lua_gc(lua_State* L);
|
||||
lua_debug_callback2* prev;
|
||||
lua_debug_callback2* next;
|
||||
};
|
||||
|
||||
struct lua_debug_callback_dict
|
||||
{
|
||||
~lua_debug_callback_dict();
|
||||
std::map<std::pair<debug_type, uint64_t>, lua_debug_callback2*> cblist;
|
||||
static int on_lua_gc(lua_State* L);
|
||||
};
|
||||
|
||||
lua_debug_callback2::~lua_debug_callback2()
|
||||
{
|
||||
if(!prev) {
|
||||
L->pushlightuserdata(&lua_cb_list_key);
|
||||
L->rawget(LUA_REGISTRYINDEX);
|
||||
if(!L->isnil(-1)) {
|
||||
lua_debug_callback_dict* dc = (lua_debug_callback_dict*)L->touserdata(-1);
|
||||
std::pair<debug_type, uint64_t> key = std::make_pair(type, addr);
|
||||
if(dc->cblist.count(key)) {
|
||||
dc->cblist[key] = next;
|
||||
if(!next)
|
||||
dc->cblist.erase(key);
|
||||
}
|
||||
}
|
||||
L->pop(1);
|
||||
}
|
||||
//Unlink from list.
|
||||
if(prev) prev->next = next;
|
||||
if(next) next->prev = prev;
|
||||
prev = next = NULL;
|
||||
}
|
||||
|
||||
void lua_debug_callback2::link_to_list()
|
||||
{
|
||||
prev = NULL;
|
||||
L->pushlightuserdata(&lua_cb_list_key);
|
||||
L->rawget(LUA_REGISTRYINDEX);
|
||||
if(L->isnil(-1)) {
|
||||
//No existing dict, create one.
|
||||
L->pop(1);
|
||||
L->pushlightuserdata(&lua_cb_list_key);
|
||||
lua_debug_callback_dict* D = (lua_debug_callback_dict*)
|
||||
L->newuserdata(sizeof(lua_debug_callback_dict));
|
||||
new(D) lua_debug_callback_dict;
|
||||
L->newtable();
|
||||
L->pushstring("__gc");
|
||||
L->pushcclosure(&lua_debug_callback_dict::on_lua_gc, 0);
|
||||
L->rawset(-3);
|
||||
L->setmetatable(-2);
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
}
|
||||
L->pushlightuserdata(&lua_cb_list_key);
|
||||
L->rawget(LUA_REGISTRYINDEX);
|
||||
lua_debug_callback2* was = NULL;
|
||||
lua_debug_callback_dict* dc = (lua_debug_callback_dict*)L->touserdata(-1);
|
||||
std::pair<debug_type, uint64_t> key = std::make_pair(type, addr);
|
||||
if(dc->cblist.count(key))
|
||||
was = dc->cblist[key];
|
||||
dc->cblist[key] = this;
|
||||
next = was;
|
||||
L->pop(1);
|
||||
|
||||
}
|
||||
|
||||
void lua_debug_callback2::set_lua_fn(int slot)
|
||||
{
|
||||
//Convert to absolute slot.
|
||||
if(slot < 0)
|
||||
slot = L->gettop() + slot;
|
||||
//Write the function.
|
||||
L->pushlightuserdata((char*)this + 1);
|
||||
L->pushvalue(slot);
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
lua_fn = L->topointer(slot);
|
||||
}
|
||||
|
||||
void lua_debug_callback2::unregister()
|
||||
{
|
||||
//Unregister.
|
||||
CORE().dbg.remove_callback(addr, type, *this);
|
||||
dead = true;
|
||||
//Delink from Lua, prompting Lua to GC this.
|
||||
L->pushlightuserdata(this);
|
||||
L->pushnil();
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
L->pushlightuserdata((char*)this + 1);
|
||||
L->pushnil();
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
void lua_debug_callback2::callback(const debug_callback_params& p)
|
||||
{
|
||||
L->pushlightuserdata((char*)this + 1);
|
||||
L->rawget(LUA_REGISTRYINDEX);
|
||||
switch(p.type) {
|
||||
case DEBUG_READ:
|
||||
case DEBUG_WRITE:
|
||||
case DEBUG_EXEC:
|
||||
L->pushnumber(p.rwx.addr);
|
||||
L->pushnumber(p.rwx.value);
|
||||
do_lua_error(*L, L->pcall(2, 0, 0));
|
||||
break;
|
||||
case DEBUG_TRACE:
|
||||
L->pushnumber(p.trace.cpu);
|
||||
L->pushstring(p.trace.decoded_insn);
|
||||
L->pushboolean(p.trace.true_insn);
|
||||
do_lua_error(*L, L->pcall(3, 0, 0));
|
||||
break;
|
||||
case DEBUG_FRAME:
|
||||
L->pushnumber(p.frame.frame);
|
||||
L->pushboolean(p.frame.loadstated);
|
||||
do_lua_error(*L, L->pcall(2, 0, 0));
|
||||
break;
|
||||
default:
|
||||
//Remove the junk from stack.
|
||||
L->pop(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lua_debug_callback2::killed(uint64_t addr, debug_type type)
|
||||
{
|
||||
//Assume this has been unregistered.
|
||||
dead = true;
|
||||
//Delink from Lua, lua will GC this.
|
||||
L->pushlightuserdata(this);
|
||||
L->pushnil();
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
L->pushlightuserdata((char*)this + 1);
|
||||
L->pushnil();
|
||||
L->rawset(LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
int lua_debug_callback2::on_lua_gc(lua_State* _L)
|
||||
{
|
||||
//We need to destroy the object.
|
||||
lua_debug_callback2* D = (lua_debug_callback2*)lua_touserdata(_L, 1);
|
||||
if(!D->dead) {
|
||||
//Unregister this!
|
||||
CORE().dbg.remove_callback(D->addr, D->type, *D);
|
||||
D->dead = true;
|
||||
}
|
||||
D->~lua_debug_callback2();
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_debug_callback_dict::~lua_debug_callback_dict()
|
||||
{
|
||||
}
|
||||
|
||||
int lua_debug_callback_dict::on_lua_gc(lua_State* _L)
|
||||
{
|
||||
lua_pushlightuserdata(_L, &lua_cb_list_key);
|
||||
lua_pushnil(_L);
|
||||
lua_rawset(_L, LUA_REGISTRYINDEX);
|
||||
lua_debug_callback_dict* D = (lua_debug_callback_dict*)lua_touserdata(_L, 1);
|
||||
D->~lua_debug_callback_dict();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<debug_type type>
|
||||
void handle_registerX(lua::state& L, uint64_t addr, int lfn)
|
||||
{
|
||||
auto& cbl = cbs[addr];
|
||||
|
||||
//Put the context in userdata so it can be gc'd when Lua context is terminated.
|
||||
lua_debug_callback* D = (lua_debug_callback*)L.newuserdata(sizeof(lua_debug_callback));
|
||||
lua_debug_callback2* D = (lua_debug_callback2*)L.newuserdata(sizeof(lua_debug_callback2));
|
||||
new(D) lua_debug_callback2;
|
||||
L.newtable();
|
||||
L.pushstring("__gc");
|
||||
L.pushcclosure(&lua_debug_callback::dtor, 0);
|
||||
L.pushcclosure(&lua_debug_callback2::on_lua_gc, 0);
|
||||
L.rawset(-3);
|
||||
L.setmetatable(-2);
|
||||
L.pushlightuserdata(&D->addr);
|
||||
L.pushlightuserdata(D);
|
||||
L.pushvalue(-2);
|
||||
L.rawset(LUA_REGISTRYINDEX);
|
||||
L.pop(1); //Pop the copy of object.
|
||||
|
||||
cbl.push_back(D);
|
||||
|
||||
D->dead = false;
|
||||
D->L = &L.get_master();
|
||||
D->addr = addr;
|
||||
D->type = type;
|
||||
D->lua_fn = L.topointer(lfn);
|
||||
lua::state* LL = &L.get_master();
|
||||
void* D2 = &D->type;
|
||||
if(type != DEBUG_TRACE)
|
||||
D->h = debug_add_callback(addr, type, [LL, D2](uint64_t addr, uint64_t value) {
|
||||
LL->pushlightuserdata(D2);
|
||||
LL->rawget(LUA_REGISTRYINDEX);
|
||||
LL->pushnumber(addr);
|
||||
LL->pushnumber(value);
|
||||
do_lua_error(*LL, LL->pcall(2, 0, 0));
|
||||
}, [LL, D]() {
|
||||
LL->pushlightuserdata(&D->addr);
|
||||
LL->pushnil();
|
||||
LL->rawset(LUA_REGISTRYINDEX);
|
||||
D->_dtor(LL->handle());
|
||||
});
|
||||
else
|
||||
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str, bool true_insn) {
|
||||
LL->pushlightuserdata(D2);
|
||||
LL->rawget(LUA_REGISTRYINDEX);
|
||||
LL->pushnumber(proc);
|
||||
LL->pushstring(str);
|
||||
LL->pushboolean(true_insn);
|
||||
do_lua_error(*LL, LL->pcall(3, 0, 0));
|
||||
}, [LL, D]() {
|
||||
LL->pushlightuserdata(&D->addr);
|
||||
LL->pushnil();
|
||||
LL->rawset(LUA_REGISTRYINDEX);
|
||||
D->_dtor(LL->handle());
|
||||
});
|
||||
L.pushlightuserdata(D2);
|
||||
L.pushvalue(lfn);
|
||||
L.rawset(LUA_REGISTRYINDEX);
|
||||
D->dead = false;
|
||||
D->set_lua_fn(lfn);
|
||||
D->link_to_list();
|
||||
|
||||
CORE().dbg.add_callback(addr, type, *D);
|
||||
}
|
||||
|
||||
template<debug_type type>
|
||||
void handle_unregisterX(lua::state& L, uint64_t addr, int lfn)
|
||||
{
|
||||
if(!cbs.count(addr))
|
||||
return;
|
||||
auto& cbl = cbs[addr];
|
||||
for(auto i = cbl.begin(); i != cbl.end(); i++) {
|
||||
if((*i)->type != type) continue;
|
||||
if(L.topointer(lfn) != (*i)->lua_fn) continue;
|
||||
L.pushlightuserdata(&(*i)->type);
|
||||
L.pushnil();
|
||||
L.rawset(LUA_REGISTRYINDEX);
|
||||
(*i)->_dtor(L.handle());
|
||||
//Lua will GC the object.
|
||||
break;
|
||||
}
|
||||
lua_debug_callback_dict* Dx;
|
||||
lua_debug_callback2* D = NULL;
|
||||
L.pushlightuserdata(&lua_cb_list_key);
|
||||
L.rawget(LUA_REGISTRYINDEX);
|
||||
if(!L.isnil(-1)) {
|
||||
Dx = (lua_debug_callback_dict*)L.touserdata(-1);
|
||||
auto key = std::make_pair(type, addr);
|
||||
if(Dx->cblist.count(key))
|
||||
D = Dx->cblist[key];
|
||||
L.pop(1);
|
||||
while(D) {
|
||||
if(D->dead || D->type != type || D->addr != addr || L.topointer(lfn) != D->lua_fn) {
|
||||
D = D->next;
|
||||
continue;
|
||||
}
|
||||
//Remove this.
|
||||
auto Dold = D;
|
||||
D = D->next;
|
||||
Dold->unregister();
|
||||
}
|
||||
} else
|
||||
L.pop(1);
|
||||
}
|
||||
|
||||
typedef void(*dummy1_t)(lua::state& L, uint64_t addr, int lfn);
|
||||
|
@ -368,11 +489,24 @@ namespace
|
|||
|
||||
command::fnptr<> callbacks_show_lua(lsnes_cmds, "show-lua-callbacks", "", "",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
for(auto& i : cbs)
|
||||
for(auto& j : i.second)
|
||||
messages << "addr=" << j->addr << " type=" << j->type << " handle="
|
||||
<< j->h.handle << " dead=" << j->dead << " lua_fn="
|
||||
<< j->lua_fn << std::endl;
|
||||
lua::state& L = CORE().lua;
|
||||
lua_debug_callback2* D;
|
||||
lua_debug_callback_dict* Dx;
|
||||
L.pushlightuserdata(&lua_cb_list_key);
|
||||
L.rawget(LUA_REGISTRYINDEX);
|
||||
if(!L.isnil(-1)) {
|
||||
Dx = (lua_debug_callback_dict*)L.touserdata(-1);
|
||||
for(auto Dy : Dx->cblist) {
|
||||
D = Dy.second;
|
||||
while(D) {
|
||||
messages << "addr=" << D->addr << " type=" << D->type << " handle="
|
||||
<< D << " dead=" << D->dead << " lua_fn="
|
||||
<< D->lua_fn << std::endl;
|
||||
D = D->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
L.pop(1);
|
||||
});
|
||||
|
||||
template<typename T, T (memory_space::*rfun)(uint64_t addr), bool (memory_space::*wfun)(uint64_t addr,
|
||||
|
@ -477,10 +611,10 @@ namespace
|
|||
addr = lua_get_read_address(P);
|
||||
|
||||
if(P.is_novalue()) {
|
||||
debug_clear_cheat(addr);
|
||||
CORE().dbg.clear_cheat(addr);
|
||||
} else {
|
||||
P(value);
|
||||
debug_set_cheat(addr, value);
|
||||
CORE().dbg.set_cheat(addr, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -488,7 +622,7 @@ namespace
|
|||
int setxmask(lua::state& L, lua::parameters& P)
|
||||
{
|
||||
auto value = P.arg<uint64_t>();
|
||||
debug_setxmask(value);
|
||||
CORE().dbg.setxmask(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -435,10 +435,10 @@ namespace
|
|||
if(addr >= vmasize)
|
||||
throw std::runtime_error("Address out of range");
|
||||
if(P.is_novalue()) {
|
||||
debug_clear_cheat(vmabase + addr);
|
||||
CORE().dbg.clear_cheat(vmabase + addr);
|
||||
} else {
|
||||
P(value);
|
||||
debug_set_cheat(vmabase + addr, value);
|
||||
CORE().dbg.set_cheat(vmabase + addr, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -505,7 +505,7 @@ namespace
|
|||
std::vector<std::pair<uint64_t, debug_type>> listsyms;
|
||||
};
|
||||
|
||||
class wxwin_tracelog : public wxFrame
|
||||
class wxwin_tracelog : public wxFrame, public debug_callback_base
|
||||
{
|
||||
public:
|
||||
wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname);
|
||||
|
@ -544,8 +544,8 @@ namespace
|
|||
void scroll_pane(uint64_t line);
|
||||
int cpuid;
|
||||
volatile bool trace_active;
|
||||
debug_handle trace_handle;
|
||||
debug_handle trace_handle_frame;
|
||||
void callback(const debug_callback_params& params);
|
||||
void killed(uint64_t addr, debug_type type);
|
||||
void do_rwx_break(uint64_t addr, uint64_t value, debug_type type);
|
||||
void kill_debug_hooks();
|
||||
scroll_bar* scroll;
|
||||
|
@ -562,7 +562,7 @@ namespace
|
|||
std::string find_string;
|
||||
bool dirty;
|
||||
bool singlestepping;
|
||||
std::map<std::pair<uint64_t, debug_type>, debug_handle> rwx_breakpoints;
|
||||
std::map<std::pair<uint64_t, debug_type>, bool> rwx_breakpoints;
|
||||
wxMenuItem* m_singlestep;
|
||||
};
|
||||
|
||||
|
@ -587,15 +587,14 @@ namespace
|
|||
|
||||
void wxwin_tracelog::kill_debug_hooks()
|
||||
{
|
||||
debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle);
|
||||
debug_remove_callback(cpuid, DEBUG_FRAME, trace_handle_frame);
|
||||
CORE().dbg.remove_callback(cpuid, DEBUG_TRACE, *this);
|
||||
CORE().dbg.remove_callback(cpuid, DEBUG_FRAME, *this);
|
||||
threads::alock h(buffer_mutex);
|
||||
for(auto& i : rwx_breakpoints) {
|
||||
if(!i.second.handle)
|
||||
if(!i.second)
|
||||
continue;
|
||||
debug_remove_callback(i.first.first, i.first.second, i.second);
|
||||
//Dirty hack.
|
||||
i.second.handle = NULL;
|
||||
CORE().dbg.remove_callback(i.first.first, i.first.second, *this);
|
||||
i.second = false;
|
||||
}
|
||||
trace_active = false;
|
||||
convert_break_to_pause();
|
||||
|
@ -708,7 +707,80 @@ namespace
|
|||
|
||||
void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_type type)
|
||||
{
|
||||
debug_request_break();
|
||||
lsnes_instance.dbg.request_break();
|
||||
}
|
||||
|
||||
void wxwin_tracelog::callback(const debug_callback_params& p)
|
||||
{
|
||||
switch(p.type) {
|
||||
case DEBUG_READ:
|
||||
case DEBUG_WRITE:
|
||||
case DEBUG_EXEC:
|
||||
do_rwx_break(p.rwx.addr, p.rwx.value, p.type);
|
||||
break;
|
||||
case DEBUG_TRACE: {
|
||||
if(!trace_active)
|
||||
return;
|
||||
//Got tracelog line, send it.
|
||||
threads::alock h(buffer_mutex);
|
||||
lines_waiting.push_back(p.trace.decoded_insn);
|
||||
if(!unprocessed_lines) {
|
||||
unprocessed_lines = true;
|
||||
runuifun([this]() { this->process_lines(); });
|
||||
}
|
||||
if(singlestepping && p.trace.true_insn) {
|
||||
lsnes_instance.dbg.request_break();
|
||||
singlestepping = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DEBUG_FRAME: {
|
||||
std::ostringstream xstr;
|
||||
xstr << "------------ ";
|
||||
xstr << "Frame " << p.frame.frame;
|
||||
if(p.frame.loadstated) xstr << " (loadstated)";
|
||||
xstr << " ------------";
|
||||
std::string str = xstr.str();
|
||||
threads::alock h(buffer_mutex);
|
||||
lines_waiting.push_back(str);
|
||||
if(!unprocessed_lines) {
|
||||
unprocessed_lines = true;
|
||||
runuifun([this]() { this->process_lines(); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wxwin_tracelog::killed(uint64_t addr, debug_type type)
|
||||
{
|
||||
switch(type) {
|
||||
case DEBUG_READ:
|
||||
case DEBUG_WRITE:
|
||||
case DEBUG_EXEC: {
|
||||
//We need to kill this hook if still active.
|
||||
auto i2 = std::make_pair(addr, type);
|
||||
auto& h = rwx_breakpoints[i2];
|
||||
if(h)
|
||||
lsnes_instance.dbg.remove_callback(addr, type, *this);
|
||||
h = false;
|
||||
break;
|
||||
}
|
||||
case DEBUG_TRACE:
|
||||
//Dtor!
|
||||
if(!trace_active)
|
||||
return;
|
||||
kill_debug_hooks();
|
||||
runuifun([this]() {
|
||||
this->enabled->SetValue(false);
|
||||
this->enabled->Enable(false);
|
||||
this->m_singlestep->Enable(false);
|
||||
});
|
||||
break;
|
||||
case DEBUG_FRAME:
|
||||
//Do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wxwin_tracelog::on_enabled(wxCommandEvent& e)
|
||||
|
@ -721,64 +793,11 @@ namespace
|
|||
broken2 = true;
|
||||
for(auto& i : rwx_breakpoints) {
|
||||
auto i2 = i.first;
|
||||
i.second = debug_add_callback(i.first.first, i.first.second,
|
||||
[this, i2](uint64_t addr, uint64_t value) {
|
||||
this->do_rwx_break(addr, value, i2.second);
|
||||
}, [this, i2] {
|
||||
//We need to kill this hook if still active.
|
||||
auto& h = rwx_breakpoints[i2];
|
||||
if(h.handle)
|
||||
debug_remove_callback(i2.first, i2.second, h);
|
||||
h.handle = NULL;
|
||||
});
|
||||
lsnes_instance.dbg.add_callback(i2.first, i2.second, *this);
|
||||
i.second = true;
|
||||
}
|
||||
this->trace_handle = debug_add_trace_callback(cpuid, [this](uint64_t proc,
|
||||
const char* str, bool true_instruction) {
|
||||
if(!this->trace_active)
|
||||
return;
|
||||
//Got tracelog line, send it.
|
||||
threads::alock h(this->buffer_mutex);
|
||||
lines_waiting.push_back(str);
|
||||
if(!this->unprocessed_lines) {
|
||||
this->unprocessed_lines = true;
|
||||
runuifun([this]() { this->process_lines(); });
|
||||
}
|
||||
if(this->singlestepping && true_instruction) {
|
||||
debug_request_break();
|
||||
this->singlestepping = false;
|
||||
}
|
||||
}, [this]() {
|
||||
//Dtor!
|
||||
auto tmp = this;
|
||||
if(!tmp->trace_active)
|
||||
return;
|
||||
tmp->kill_debug_hooks();
|
||||
//We can't use this anymore.
|
||||
runuifun([tmp]() {
|
||||
tmp->enabled->SetValue(false);
|
||||
tmp->m_singlestep->Enable(false);
|
||||
});
|
||||
});
|
||||
this->trace_handle_frame = debug_add_frame_callback([this](uint64_t frame,
|
||||
bool loadstate) {
|
||||
std::ostringstream xstr;
|
||||
xstr << "------------ ";
|
||||
xstr << "Frame " << frame;
|
||||
if(loadstate) xstr << " (loadstated)";
|
||||
xstr << " ------------";
|
||||
std::string str = xstr.str();
|
||||
threads::alock h(this->buffer_mutex);
|
||||
lines_waiting.push_back(str);
|
||||
if(!this->unprocessed_lines) {
|
||||
this->unprocessed_lines = true;
|
||||
runuifun([this]() { this->process_lines(); });
|
||||
}
|
||||
}, [this]() {
|
||||
auto tmp = this;
|
||||
if(!tmp->trace_active)
|
||||
return;
|
||||
debug_remove_callback(0, DEBUG_TRACE, trace_handle_frame);
|
||||
});
|
||||
lsnes_instance.dbg.add_callback(cpuid, DEBUG_TRACE, *this);
|
||||
lsnes_instance.dbg.add_callback(0, DEBUG_FRAME, *this);
|
||||
this->trace_active = true;
|
||||
} else if(trace_active) {
|
||||
this->trace_active = false;
|
||||
|
@ -1051,27 +1070,19 @@ back:
|
|||
std::pair<uint64_t, debug_type> i2 = std::make_pair(addr, dtype);
|
||||
if(!trace_active) {
|
||||
//We'll register this later.
|
||||
rwx_breakpoints[i2] = debug_handle();
|
||||
rwx_breakpoints[i2] = false;
|
||||
return;
|
||||
}
|
||||
rwx_breakpoints[i2] = debug_add_callback(i2.first, i2.second,
|
||||
[this, i2](uint64_t addr, uint64_t value) {
|
||||
this->do_rwx_break(addr, value, i2.second);
|
||||
}, [this, i2] {
|
||||
//We need to kill this hook if still active.
|
||||
auto& h = rwx_breakpoints[i2];
|
||||
if(h.handle)
|
||||
debug_remove_callback(i2.first, i2.second, h);
|
||||
h.handle = NULL;
|
||||
});
|
||||
lsnes_instance.dbg.add_callback(i2.first, i2.second, *this);
|
||||
rwx_breakpoints[i2] = true;
|
||||
}
|
||||
|
||||
void wxwin_tracelog::remove_breakpoint(uint64_t addr, debug_type dtype)
|
||||
{
|
||||
std::pair<uint64_t, debug_type> i2 = std::make_pair(addr, dtype);
|
||||
auto& h = rwx_breakpoints[i2];
|
||||
if(h.handle)
|
||||
debug_remove_callback(i2.first, i2.second, h);
|
||||
if(h)
|
||||
lsnes_instance.dbg.remove_callback(i2.first, i2.second, *this);
|
||||
rwx_breakpoints.erase(i2);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ tracelog_menu::tracelog_menu(wxWindow* win, int wxid_low, int wxid_high)
|
|||
wxid_range_high = wxid_high;
|
||||
win->Connect(wxid_low, wxid_high, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(tracelog_menu::on_select), NULL, this);
|
||||
debug_set_tracelog_change_cb([this]() { runuifun([this]() { this->update(); }); });
|
||||
lsnes_instance.dbg.set_tracelog_change_cb([this]() { runuifun([this]() { this->update(); }); });
|
||||
corechange.set(notify_core_change, [this]() { runuifun([this]() { this->update(); }); });
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,11 @@ void tracelog_menu::on_select(wxCommandEvent& e)
|
|||
try {
|
||||
std::string filename = choose_file_save(pwin, "Save " + cpunames[rid] + " Trace",
|
||||
lsnes_instance.project.moviepath(), filetype_trace, "");
|
||||
debug_tracelog(rid, filename);
|
||||
lsnes_instance.dbg.tracelog(rid, filename);
|
||||
} catch(canceled_exception& e) {
|
||||
}
|
||||
} else {
|
||||
debug_tracelog(rid, "");
|
||||
lsnes_instance.dbg.tracelog(rid, "");
|
||||
}
|
||||
}
|
||||
update();
|
||||
|
@ -56,7 +56,7 @@ void tracelog_menu::update()
|
|||
for(auto i : _items) {
|
||||
items.push_back(AppendCheckItem(wxid_range_low + 2 * id, towxstring(i + " (to file)...")));
|
||||
cpunames[id] = i;
|
||||
items[id]->Check(debug_tracelogging(id));
|
||||
items[id]->Check(lsnes_instance.dbg.is_tracelogging(id));
|
||||
id++;
|
||||
}
|
||||
items.push_back(AppendSeparator());
|
||||
|
|
Loading…
Add table
Reference in a new issue