Tracelogger: Breakpoints

This commit is contained in:
Ilari Liusvaara 2014-02-22 21:31:13 +02:00
parent bad1141c65
commit 1994af1d09
7 changed files with 460 additions and 38 deletions

View file

@ -21,13 +21,13 @@ extern const uint64_t debug_all_addr;
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)> 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);
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);
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn = true);
void debug_set_cheat(uint64_t addr, uint64_t value);
void debug_clear_cheat(uint64_t addr);
void debug_setxmask(uint64_t mask);
@ -35,5 +35,6 @@ 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();
#endif

View file

@ -30,6 +30,8 @@ void switch_projects(const std::string& newproj);
void close_rom();
void load_new_rom(const romload_request& req);
void reload_current_rom();
void do_break_pause();
void convert_break_to_pause();
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary;
extern settingvar::variable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary;

View file

@ -1,6 +1,7 @@
#include "core/command.hpp"
#include "core/debug.hpp"
#include "core/dispatch.hpp"
#include "core/mainloop.hpp"
#include "core/moviedata.hpp"
#include "library/directory.hpp"
#include <stdexcept>
@ -18,7 +19,7 @@ namespace
};
struct cb_trace
{
std::function<void(uint64_t proc, const char* str)> cb;
std::function<void(uint64_t proc, const char* str, bool true_insn)> cb;
std::function<void()> dtor;
};
typedef std::list<cb_rwx> cb_list;
@ -33,6 +34,7 @@ namespace
std::function<void()> tracelog_change_cb;
struct dispatch::target<> corechange;
bool corechange_r = false;
bool requesting_break = false;
struct tracelog_file
{
@ -125,8 +127,8 @@ debug_handle debug_add_callback(uint64_t addr, debug_type type, std::function<vo
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)> 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)
{
cb_trace t;
t.cb = fn;
@ -145,33 +147,45 @@ void debug_remove_callback(uint64_t addr, debug_type type, debug_handle handle)
void debug_fire_callback_read(uint64_t addr, uint64_t value)
{
requesting_break = false;
cb_list* cb1 = read_cb.count(debug_all_addr) ? &read_cb[debug_all_addr] : &dummy_cb;
cb_list* cb2 = read_cb.count(addr) ? &read_cb[addr] : &dummy_cb;
for(auto& i : *cb1) i.cb(addr, value);
for(auto& i : *cb2) i.cb(addr, value);
if(requesting_break)
do_break_pause();
}
void debug_fire_callback_write(uint64_t addr, uint64_t value)
{
requesting_break = false;
cb_list* cb1 = write_cb.count(debug_all_addr) ? &write_cb[debug_all_addr] : &dummy_cb;
cb_list* cb2 = write_cb.count(addr) ? &write_cb[addr] : &dummy_cb;
for(auto& i : *cb1) i.cb(addr, value);
for(auto& i : *cb2) i.cb(addr, value);
if(requesting_break)
do_break_pause();
}
void debug_fire_callback_exec(uint64_t addr, uint64_t value)
{
requesting_break = false;
cb_list* cb1 = exec_cb.count(debug_all_addr) ? &exec_cb[debug_all_addr] : &dummy_cb;
cb_list* cb2 = exec_cb.count(addr) ? &exec_cb[addr] : &dummy_cb;
if(value & xmask)
for(auto& i : *cb1) i.cb(addr, value);
for(auto& i : *cb2) i.cb(addr, value);
if(requesting_break)
do_break_pause();
}
void debug_fire_callback_trace(uint64_t proc, const char* str)
void debug_fire_callback_trace(uint64_t proc, const char* str, bool true_insn)
{
requesting_break = false;
cb2_list* cb = trace_cb.count(proc) ? &trace_cb[proc] : &dummy_cb2;
for(auto& i : *cb) i.cb(proc, str);
for(auto& i : *cb) i.cb(proc, str, true_insn);
if(requesting_break)
do_break_pause();
}
void debug_set_cheat(uint64_t addr, uint64_t value)
@ -226,7 +240,7 @@ void debug_tracelog(uint64_t proc, const std::string& filename)
throw std::runtime_error("Can't open '" + full_filename + "'");
}
}
trace_outputs[proc].second = debug_add_trace_callback(proc, [](uint64_t proc, const char* str) {
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, ""); });
@ -253,6 +267,11 @@ void debug_core_change()
kill_hooks(trace_cb);
}
void debug_request_break()
{
requesting_break = true;
}
namespace
{
command::fnptr<> callbacks_show(lsnes_cmd, "show-callbacks", "", "",

View file

@ -81,6 +81,7 @@ namespace
bool cancel_advance;
//Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
enum advance_mode amode;
enum advance_mode old_mode;
//Mode and filename of pending load, one of LOAD_* constants.
bool load_paused;
int loadmode;
@ -104,8 +105,9 @@ namespace
//Macro hold.
bool macro_hold_1;
bool macro_hold_2;
//In break pause.
bool break_pause = false;
enum advance_mode old_mode;
std::string save_jukebox_name(size_t i)
{
@ -252,6 +254,8 @@ namespace
//Do pending load (automatically unpauses).
void mark_pending_load(std::string filename, int lmode)
{
if(break_pause)
break_pause = false;
loadmode = lmode;
pending_load = filename;
old_mode = amode;
@ -327,6 +331,12 @@ void update_movie_state()
_status.set("!subframe", "N/A");
}
_status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
if(break_pause)
_status.set("!pause", "B");
else if(amode == ADVANCE_PAUSE)
_status.set("!pause", "P");
else
_status.set("!pause", "");
if(movb) {
auto& mo = movb.get_movie();
readonly = mo.readonly_mode();
@ -552,26 +562,25 @@ namespace
command::fnptr<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
"Syntax: unpause-emulator\nUnpauses the emulator.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
break_pause = false;
amode = ADVANCE_AUTO;
platform::set_paused(false);
platform::cancel_wait();
messages << "Unpaused" << std::endl;
});
command::fnptr<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
"Syntax: pause-emulator\n(Un)pauses the emulator.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(amode != ADVANCE_AUTO) {
if(amode != ADVANCE_AUTO || break_pause) {
break_pause = false;
amode = ADVANCE_AUTO;
platform::set_paused(false);
platform::cancel_wait();
messages << "Unpaused" << std::endl;
} else {
platform::cancel_wait();
cancel_advance = false;
stop_at_frame_active = false;
amode = ADVANCE_PAUSE;
messages << "Paused" << std::endl;
}
});
@ -666,6 +675,8 @@ namespace
command::fnptr<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
"Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(break_pause)
break_pause = false;
amode = ADVANCE_FRAME;
cancel_advance = false;
advanced_once = false;
@ -684,6 +695,8 @@ namespace
command::fnptr<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
"Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(break_pause)
break_pause = false;
amode = ADVANCE_SUBFRAME;
cancel_advance = false;
advanced_once = false;
@ -694,6 +707,8 @@ namespace
command::fnptr<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
"No help available\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(break_pause)
break_pause = false;
cancel_advance = true;
platform::cancel_wait();
platform::set_paused(false);
@ -702,6 +717,8 @@ namespace
command::fnptr<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
"Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(break_pause)
break_pause = false;
amode = ADVANCE_SKIPLAG_PENDING;
platform::cancel_wait();
platform::set_paused(false);
@ -1319,3 +1336,22 @@ void close_rom()
mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
}
}
void do_break_pause()
{
break_pause = true;
update_movie_state();
while(break_pause) {
platform::set_paused(true);
platform::flush_command_queue();
}
}
void convert_break_to_pause()
{
if(break_pause) {
amode = ADVANCE_PAUSE;
break_pause = false;
update_movie_state();
}
}

View file

@ -285,12 +285,13 @@ void handle_registerX(lua::state& L, uint64_t addr, int lfn)
D->_dtor(LL->handle());
});
else
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str) {
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);
do_lua_error(*LL, LL->pcall(2, 0, 0));
LL->pushboolean(true_insn);
do_lua_error(*LL, LL->pcall(3, 0, 0));
}, [LL, D]() {
LL->pushlightuserdata(&D->addr);
LL->pushnil();

View file

@ -1273,6 +1273,10 @@ void wxwin_mainwindow::update_statusbar(const std::map<std::string, std::u32stri
s << U" [" << read_variable_map(vars, "!saveslotinfo") << U"]";
s << U" Speed: " << read_variable_map(vars, "!speed") << U"%";
s << U" ";
if(read_variable_map(vars, "!pause") == U"B")
s << U" Breakpoint";
else if(read_variable_map(vars, "!pause") == U"P")
s << U" Paused";
if(read_variable_map(vars, "!dumping") != U"")
s << U" Dumping";
if(read_variable_map(vars, "!mode") == U"C")

View file

@ -2,7 +2,9 @@
#include "platform/wxwidgets/textrender.hpp"
#include "platform/wxwidgets/scrollbar.hpp"
#include "platform/wxwidgets/loadsave.hpp"
#include "core/command.hpp"
#include "core/debug.hpp"
#include "core/mainloop.hpp"
#include "core/memorymanip.hpp"
#include "core/project.hpp"
#include "interface/disassembler.hpp"
@ -15,6 +17,7 @@
#include <wx/menu.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/listbox.h>
#include <wx/stattext.h>
#include <wx/combobox.h>
#include <wx/textctrl.h>
@ -32,6 +35,10 @@ namespace
wxID_GOTO,
wxID_DISASM,
wxID_DISASM_MORE,
wxID_SINGLESTEP,
wxID_BREAKPOINTS,
wxID_CONTINUE,
wxID_FRAMEADVANCE,
};
int prompt_for_save(wxWindow* parent, const std::string& what)
@ -377,6 +384,123 @@ namespace
ok->Enable(is_ok);
}
class wxwin_tracelog;
class dialog_breakpoint_add : public wxDialog
{
public:
dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> regions);
std::pair<uint64_t, debug_type> get_result();
void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
void on_address_change(wxCommandEvent& e);
private:
std::list<memory_region*> regions;
wxComboBox* vmasel;
wxTextCtrl* address;
wxComboBox* typesel;
wxButton* ok;
wxButton* cancel;
};
dialog_breakpoint_add::dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> _regions)
: wxDialog(parent, wxID_ANY, "Add breakpoint")
{
regions = _regions;
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
SetSizer(top_s);
top_s->Add(new wxStaticText(this, wxID_ANY, "Memory region:"), 0, wxGROW);
top_s->Add(vmasel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
0, NULL, wxCB_READONLY), 1, wxGROW);
vmasel->Append(towxstring(""));
for(auto i : regions)
vmasel->Append(towxstring(i->name));
vmasel->SetSelection(0);
top_s->Add(new wxStaticText(this, wxID_ANY, "Offset (hexadecimal):"), 0, wxGROW);
top_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(350, -1)), 0,
wxGROW);
top_s->Add(new wxStaticText(this, wxID_ANY, "Breakpoint type:"), 0, wxGROW);
top_s->Add(typesel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
0, NULL, wxCB_READONLY), 1, wxGROW);
typesel->Append(towxstring("Read"));
typesel->Append(towxstring("Write"));
typesel->Append(towxstring("Execute"));
typesel->SetSelection(0);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_ok), NULL,
this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_cancel),
NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
top_s->SetSizeHints(this);
Fit();
}
void dialog_breakpoint_add::on_address_change(wxCommandEvent& e)
{
try {
hex::from<uint64_t>(tostdstring(address->GetValue()));
ok->Enable(true);
} catch(...) {
ok->Enable(false);
}
}
std::pair<uint64_t, debug_type> dialog_breakpoint_add::get_result()
{
std::string vmaname = tostdstring(vmasel->GetStringSelection());
std::string addrtext = tostdstring(address->GetValue());
uint64_t base = 0;
if(vmaname != "") {
for(auto i : regions)
if(i->name == vmaname)
base = i->base;
}
uint64_t addr;
try {
addr = base + hex::from<uint64_t>(addrtext);
} catch(std::exception& e) {
addr = base;
}
debug_type dtype = DEBUG_EXEC;
if(typesel->GetSelection() == 0)
dtype = DEBUG_READ;
if(typesel->GetSelection() == 1)
dtype = DEBUG_WRITE;
if(typesel->GetSelection() == 2)
dtype = DEBUG_EXEC;
return std::make_pair(addr, dtype);
}
class dialog_breakpoints : public wxDialog
{
public:
dialog_breakpoints(wxwin_tracelog* parent);
void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
void on_add(wxCommandEvent& e);
void on_delete(wxCommandEvent& e);
void on_selchange(wxCommandEvent& e);
private:
std::string format_line(std::pair<uint64_t, debug_type> entry);
size_t get_insert_pos(std::pair<uint64_t, debug_type> entry);
void populate_breakpoints();
std::list<memory_region*> regions;
wxButton* ok;
wxButton* addb;
wxButton* delb;
wxListBox* brklist;
wxwin_tracelog* pwin;
std::vector<std::pair<uint64_t, debug_type>> listsyms;
};
class wxwin_tracelog : public wxFrame
{
public:
@ -389,6 +513,9 @@ namespace
void on_menu(wxCommandEvent& e);
void process_lines();
uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
std::set<std::pair<uint64_t, debug_type>> get_breakpoints();
void add_breakpoint(uint64_t addr, debug_type dtype);
void remove_breakpoint(uint64_t addr, debug_type dtype);
private:
class _panel : public text_framebuffer_panel
{
@ -400,6 +527,7 @@ namespace
uint64_t pos;
std::vector<std::string> rows;
void on_popup_menu(wxCommandEvent& e);
bool scroll_to_end_on_repaint;
protected:
void prepare_paint();
private:
@ -413,6 +541,8 @@ namespace
int cpuid;
volatile bool trace_active;
debug_handle trace_handle;
void do_rwx_break(uint64_t addr, uint64_t value, debug_type type);
void kill_debug_hooks();
scroll_bar* scroll;
_panel* panel;
bool broken;
@ -426,6 +556,9 @@ namespace
uint64_t find_line;
std::string find_string;
bool dirty;
bool singlestepping;
std::map<std::pair<uint64_t, debug_type>, debug_handle> rwx_breakpoints;
wxMenuItem* m_singlestep;
};
wxwin_tracelog::~wxwin_tracelog()
@ -440,13 +573,27 @@ namespace
return;
}
if(trace_active)
runemufn([this]() { debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle); });
runemufn([this]() { kill_debug_hooks(); });
trace_active = false;
if(!closing)
Destroy();
closing = true;
}
void wxwin_tracelog::kill_debug_hooks()
{
debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle);
umutex_class h(buffer_mutex);
for(auto& i : rwx_breakpoints) {
if(!i.second.handle)
continue;
debug_remove_callback(i.first.first, i.first.second, i.second);
//Dirty hack.
i.second.handle = NULL;
}
convert_break_to_pause();
}
wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent)
: text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL)
{
@ -455,6 +602,7 @@ namespace
pressed_row = 0;
current_row = 0;
holding = false;
scroll_to_end_on_repaint = false;
this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
@ -510,6 +658,11 @@ namespace
void wxwin_tracelog::_panel::prepare_paint()
{
p->get_scroll()->set_range(rows.size());
if(scroll_to_end_on_repaint) {
scroll_to_end_on_repaint = false;
p->get_scroll()->set_position(rows.size());
pos = p->get_scroll()->get_position();
}
uint64_t m = min(pressed_row, current_row);
uint64_t M = max(pressed_row, current_row);
auto s = get_characters();
@ -521,6 +674,8 @@ namespace
uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
write(rows[i], s.first, 0, i - pos, fg, bg);
}
for(uint64_t i = rows.size(); i < pos + s.second; i++)
write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
}
void wxwin_tracelog::process_lines()
@ -535,9 +690,16 @@ namespace
panel->rows.push_back(i);
lines_waiting.clear();
unprocessed_lines = false;
panel->request_paint();
if(panel->rows.size() != osize)
if(panel->rows.size() != osize) {
panel->scroll_to_end_on_repaint = true;
dirty = true;
}
panel->request_paint();
}
void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_type type)
{
debug_request_break();
}
void wxwin_tracelog::on_enabled(wxCommandEvent& e)
@ -545,10 +707,24 @@ namespace
bool enable = enabled->GetValue();
runemufn([this, enable]() {
if(enable) {
umutex_class h(buffer_mutex);
broken = broken2;
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;
});
}
this->trace_handle = debug_add_trace_callback(cpuid, [this](uint64_t proc,
const char* str) {
const char* str, bool true_instruction) {
if(!this->trace_active)
return;
//Got tracelog line, send it.
@ -558,22 +734,31 @@ namespace
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;
this->trace_active = false;
debug_remove_callback(cpuid, DEBUG_TRACE, this->trace_handle);
debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle);
tmp->kill_debug_hooks();
//We can't use this anymore.
runuifun([tmp]() { tmp->enabled->SetValue(false); });
runuifun([tmp]() {
tmp->enabled->SetValue(false);
tmp->m_singlestep->Enable(false);
});
});
this->trace_active = true;
} else if(trace_active) {
this->trace_active = false;
debug_remove_callback(cpuid, DEBUG_TRACE, this->trace_handle);
this->kill_debug_hooks();
}
});
m_singlestep->Enable(enable);
}
bool find_match(const std::string& pattern, const std::string& candidate)
@ -635,8 +820,9 @@ namespace
if(r < 0 || (r > 0 && !do_exit_save()))
return;
}
if(trace_active)
runemufn([this]() { debug_remove_callback(cpuid, DEBUG_TRACE, trace_handle); });
if(trace_active) {
runemufn([this]() { this->kill_debug_hooks(); });
}
trace_active = false;
Destroy();
return;
@ -709,6 +895,22 @@ namespace
find_line--;
}
scroll_pane(find_line);
} else if(e.GetId() == wxID_SINGLESTEP) {
runemufn_async([this]() {
this->singlestepping = true;
lsnes_cmd.invoke("unpause-emulator");
});
} else if(e.GetId() == wxID_FRAMEADVANCE) {
runemufn_async([this]() {
lsnes_cmd.invoke("+advance-frame");
lsnes_cmd.invoke("-advance-frame");
});
} else if(e.GetId() == wxID_CONTINUE) {
runemufn_async([this]() { lsnes_cmd.invoke("unpause-emulator"); });
} else if(e.GetId() == wxID_BREAKPOINTS) {
dialog_breakpoints* d = new dialog_breakpoints(this);
d->ShowModal();
d->Destroy();
}
}
@ -761,7 +963,7 @@ namespace
unsigned offset = r / 2;
if(offset > line)
scroll->set_position(panel->pos = 0);
else if(line + r < panel->rows.size())
else if(line + r <= panel->rows.size())
scroll->set_position(panel->pos = line - offset);
else
scroll->set_position(panel->pos = panel->rows.size() - r);
@ -790,12 +992,52 @@ back:
return true;
}
std::set<std::pair<uint64_t, debug_type>> wxwin_tracelog::get_breakpoints()
{
std::set<std::pair<uint64_t, debug_type>> ret;
runemufn([this, &ret]() {
for(auto i : rwx_breakpoints)
ret.insert(i.first);
});
return ret;
}
void wxwin_tracelog::add_breakpoint(uint64_t addr, debug_type dtype)
{
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();
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;
});
}
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);
rwx_breakpoints.erase(i2);
}
wxwin_tracelog::wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname)
: wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
wxCLIP_CHILDREN)
{
cpuid = _cpuid;
singlestepping = false;
find_active = false;
find_line = 0;
closing = false;
@ -834,6 +1076,13 @@ back:
menu->Append(wxID_FIND, wxT("Find..."));
menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
mb->Append(menu = new wxMenu(), wxT("Debug"));
m_singlestep = menu->Append(wxID_SINGLESTEP, towxstring("Singlestep\tF2"));
menu->Append(wxID_FRAMEADVANCE, towxstring("Frame advance\tF4"));
menu->Append(wxID_CONTINUE, towxstring("Continue\tF5"));
menu->AppendSeparator();
menu->Append(wxID_BREAKPOINTS, towxstring("Breakpoints"));
m_singlestep->Enable(false);
Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
NULL, this);
//Very nasty hack.
@ -1185,23 +1434,27 @@ back:
}
}
std::string format_vma_offset(memory_region& region, uint64_t offset)
{
std::ostringstream y;
y << region.name;
size_t sizedigits = 0;
uint64_t tmp = region.size - 1;
while(tmp > 0) {
tmp >>= 4;
sizedigits++;
}
y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << offset;
return y.str();
}
std::string lookup_address(uint64_t raw)
{
auto g = lsnes_memory.lookup(raw);
if(!g.first)
return hex::to<uint64_t>(raw);
else {
std::ostringstream y;
y << g.first->name;
size_t sizedigits = 0;
uint64_t tmp = g.first->size - 1;
while(tmp > 0) {
tmp >>= 4;
sizedigits++;
}
y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << g.second;
return y.str();
}
else
return format_vma_offset(*g.first, g.second);
}
inline int sign_compare(uint64_t a, uint64_t b)
@ -1456,6 +1709,112 @@ back:
top_s->SetSizeHints(this);
SetClientSize(tmp2);
}
dialog_breakpoints::dialog_breakpoints(wxwin_tracelog* parent)
: wxDialog(parent, wxID_ANY, "Breakpoints")
{
pwin = parent;
regions = lsnes_memory.get_regions();
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
SetSizer(top_s);
top_s->Add(brklist = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400)), 1, wxGROW);
brklist->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
wxCommandEventHandler(dialog_breakpoints::on_selchange), NULL, this);
populate_breakpoints();
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->Add(addb = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
pbutton_s->Add(delb = new wxButton(this, wxID_ANY, wxT("Remove")), 0, wxGROW);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
delb->Enable(false);
addb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_add), NULL,
this);
delb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_delete),
NULL, this);
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_ok), NULL,
this);
top_s->Add(pbutton_s, 0, wxGROW);
top_s->SetSizeHints(this);
Fit();
}
void dialog_breakpoints::populate_breakpoints()
{
auto t = pwin->get_breakpoints();
for(auto i : t) {
std::string line = format_line(i);
unsigned insert_pos = get_insert_pos(i);
brklist->Insert(towxstring(line), insert_pos);
listsyms.insert(listsyms.begin() + insert_pos, i);
}
}
void dialog_breakpoints::on_add(wxCommandEvent& e)
{
uint64_t addr;
debug_type dtype;
dialog_breakpoint_add* d = new dialog_breakpoint_add(this, regions);
if(d->ShowModal() != wxID_OK) {
d->Destroy();
return;
}
rpair(addr, dtype) = d->get_result();
d->Destroy();
runemufn_async([this, addr, dtype]() { pwin->add_breakpoint(addr, dtype); });
auto ent = std::make_pair(addr, dtype);
std::string line = format_line(ent);
unsigned insert_pos = get_insert_pos(ent);
brklist->Insert(towxstring(line), insert_pos);
listsyms.insert(listsyms.begin() + insert_pos, ent);
}
void dialog_breakpoints::on_delete(wxCommandEvent& e)
{
int idx = brklist->GetSelection();
if(idx == wxNOT_FOUND)
return;
uint64_t addr;
debug_type dtype;
addr = listsyms[idx].first;
dtype = listsyms[idx].second;
runemufn_async([this, addr, dtype]() { pwin->remove_breakpoint(addr, dtype); });
brklist->Delete(idx);
listsyms.erase(listsyms.begin() + idx);
}
size_t dialog_breakpoints::get_insert_pos(std::pair<uint64_t, debug_type> entry)
{
size_t i = 0;
for(i = 0; i < listsyms.size(); i++)
if(entry < listsyms[i])
return i;
return i;
}
std::string dialog_breakpoints::format_line(std::pair<uint64_t, debug_type> entry)
{
std::string base = "";
for(auto i : regions) {
if(entry.first >= i->base && entry.first < i->base + i->size) {
base = format_vma_offset(*i, entry.first - i->base);
break;
}
}
if(base == "")
base = hex::to<uint64_t>(entry.first);
if(entry.second == DEBUG_READ)
return base + ": Read";
if(entry.second == DEBUG_WRITE)
return base + ": Write";
if(entry.second == DEBUG_EXEC)
return base + ": Execute";
return base + ": Unknown";
}
void dialog_breakpoints::on_selchange(wxCommandEvent& e)
{
delb->Enable(brklist->GetSelection() != wxNOT_FOUND);
}
}
void wxeditor_tracelog_display(wxWindow* parent, int cpuid, const std::string& cpuname)