Adds memory tracking to debugger. This includes two new commands: trackmem and

pcatmem(p|d|i).  [Andrew Gardner]

Fixes left-click selection bug in the memory window. [Andrew Gardner]


Explanation:
------------
Call trackmem to start tracking which PC writes to which address in memory and 
pcatmem(p|d|i) to query a memory region for which PC wrote to it.  Users of 
the QT debugger can also right click on a memory address in the memory window 
to make a popup message appear with the results - right-clicking also 
automatically copies the resultant PC onto the clipboard.  (I'll attach an 
image of this behavior in a follow-up mail).
This commit is contained in:
Andrew Gardner 2013-05-09 19:20:05 +00:00
parent f57d8d2d7b
commit 7964d924b3
14 changed files with 326 additions and 47 deletions

View File

@ -143,6 +143,8 @@ static void execute_traceover(running_machine &machine, int ref, int params, con
static void execute_traceflush(running_machine &machine, int ref, int params, const char **param);
static void execute_history(running_machine &machine, int ref, int params, const char **param);
static void execute_trackpc(running_machine &machine, int ref, int params, const char **param);
static void execute_trackmem(running_machine &machine, int ref, int params, const char **param);
static void execute_pcatmem(running_machine &machine, int ref, int params, const char **param);
static void execute_snap(running_machine &machine, int ref, int params, const char **param);
static void execute_source(running_machine &machine, int ref, int params, const char **param);
static void execute_map(running_machine &machine, int ref, int params, const char **param);
@ -292,10 +294,10 @@ void debug_command_init(running_machine &machine)
debug_console_register_command(machine, "ignore", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, execute_ignore);
debug_console_register_command(machine, "observe", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, execute_observe);
debug_console_register_command(machine, "comadd", CMDFLAG_NONE, 0, 1, 2, execute_comment);
debug_console_register_command(machine, "comadd", CMDFLAG_NONE, 0, 1, 2, execute_comment);
debug_console_register_command(machine, "//", CMDFLAG_NONE, 0, 1, 2, execute_comment);
debug_console_register_command(machine, "comdelete", CMDFLAG_NONE, 0, 1, 1, execute_comment_del);
debug_console_register_command(machine, "comsave", CMDFLAG_NONE, 0, 0, 0, execute_comment_save);
debug_console_register_command(machine, "comdelete", CMDFLAG_NONE, 0, 1, 1, execute_comment_del);
debug_console_register_command(machine, "comsave", CMDFLAG_NONE, 0, 0, 0, execute_comment_save);
debug_console_register_command(machine, "bpset", CMDFLAG_NONE, 0, 1, 3, execute_bpset);
debug_console_register_command(machine, "bp", CMDFLAG_NONE, 0, 1, 3, execute_bpset);
@ -369,6 +371,11 @@ void debug_command_init(running_machine &machine)
debug_console_register_command(machine, "history", CMDFLAG_NONE, 0, 0, 2, execute_history);
debug_console_register_command(machine, "trackpc", CMDFLAG_NONE, 0, 0, 3, execute_trackpc);
debug_console_register_command(machine, "trackmem", CMDFLAG_NONE, 0, 0, 3, execute_trackmem);
debug_console_register_command(machine, "pcatmemp", CMDFLAG_NONE, AS_PROGRAM, 1, 2, execute_pcatmem);
debug_console_register_command(machine, "pcatmemd", CMDFLAG_NONE, AS_DATA, 1, 2, execute_pcatmem);
debug_console_register_command(machine, "pcatmemi", CMDFLAG_NONE, AS_IO, 1, 2, execute_pcatmem);
debug_console_register_command(machine, "snap", CMDFLAG_NONE, 0, 0, 1, execute_snap);
debug_console_register_command(machine, "source", CMDFLAG_NONE, 0, 1, 1, execute_source);
@ -2694,6 +2701,82 @@ static void execute_trackpc(running_machine &machine, int ref, int params, const
}
/*-------------------------------------------------
execute_trackmem - execute the trackmem command
-------------------------------------------------*/
static void execute_trackmem(running_machine &machine, int ref, int params, const char *param[])
{
// Gather the on/off switch (if present)
UINT64 turnOn = true;
if (!debug_command_parameter_number(machine, param[0], &turnOn))
return;
// Gather the cpu id (if present)
device_t *cpu = NULL;
if (!debug_command_parameter_cpu(machine, (params > 1) ? param[1] : NULL, &cpu))
return;
// Should we clear the existing data?
UINT64 clear = false;
if (!debug_command_parameter_number(machine, param[2], &clear))
return;
// Get the address space for the given cpu
address_space *space;
if (!debug_command_parameter_cpu_space(machine, (params > 1) ? param[1] : NULL, AS_PROGRAM, space))
return;
// Inform the CPU it's time to start tracking memory writes
cpu->debug()->set_track_mem(turnOn);
// Use the watchpoint system to catch memory writes
space->enable_write_watchpoints(true);
// Clear out the existing data if requested
if (clear)
space->device().debug()->track_mem_data_clear();
}
/*-------------------------------------------------
execute_pcatmem - execute the pcatmem command
-------------------------------------------------*/
static void execute_pcatmem(running_machine &machine, int ref, int params, const char *param[])
{
// Gather the required address parameter
UINT64 address;
if (!debug_command_parameter_number(machine, param[0], &address))
return;
// Gather the cpu id (if present)
device_t *cpu = NULL;
if (!debug_command_parameter_cpu(machine, (params > 1) ? param[1] : NULL, &cpu))
return;
// Get the address space for the given cpu
address_space *space;
if (!debug_command_parameter_cpu_space(machine, (params > 1) ? param[1] : NULL, ref, space))
return;
// Get the value of memory at the address
const int nativeDataWidth = space->data_width() / 8;
const UINT64 data = debug_read_memory(*space,
space->address_to_byte(address),
nativeDataWidth,
true);
// Recover the pc & print
const address_spacenum spaceNum = (address_spacenum)ref;
const offs_t result = space->device().debug()->track_mem_pc_from_space_address_data(spaceNum, address, data);
if (result != (offs_t)(-1))
debug_console_printf(machine, "%02x\n", result);
else
debug_console_printf(machine, "UNKNOWN PC\n");
}
/*-------------------------------------------------
execute_snap - execute the snapshot command
-------------------------------------------------*/

View File

@ -1663,7 +1663,9 @@ device_debug::device_debug(device_t &device)
m_track_pc_set(),
m_track_pc(false),
m_comment_set(),
m_comment_change(0)
m_comment_change(0),
m_track_mem_set(),
m_track_mem(false)
{
memset(m_pc_history, 0, sizeof(m_pc_history));
memset(m_wplist, 0, sizeof(m_wplist));
@ -2023,6 +2025,15 @@ void device_debug::memory_read_hook(address_space &space, offs_t address, UINT64
void device_debug::memory_write_hook(address_space &space, offs_t address, UINT64 data, UINT64 mem_mask)
{
if (m_track_mem)
{
dasm_memory_access newAccess(space.spacenum(), address, data, history_pc(0));
if (!m_track_mem_set.insert(newAccess))
{
m_track_mem_set.remove(newAccess);
m_track_mem_set.insert(newAccess);
}
}
watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask);
}
@ -2630,6 +2641,25 @@ void device_debug::set_track_pc_visited(const offs_t& pc)
}
//-------------------------------------------------
// track_mem_pc_from_address_data - returns the pc that
// wrote the data to this address or (offs_t)(-1) for
// 'not available'.
//-------------------------------------------------
offs_t device_debug::track_mem_pc_from_space_address_data(const address_spacenum& space,
const offs_t& address,
const UINT64& data) const
{
const offs_t missing = (offs_t)(-1);
if (m_track_mem_set.empty())
return missing;
dasm_memory_access* mem_access = m_track_mem_set.find(dasm_memory_access(space, address, data, 0));
if (mem_access == NULL) return missing;
return mem_access->m_pc;
}
//-------------------------------------------------
// comment_add - adds a comment to the list at
// the given address
@ -2672,7 +2702,7 @@ bool device_debug::comment_remove(offs_t addr)
const char *device_debug::comment_text(offs_t addr) const
{
const UINT32 crc = compute_opcode_crc32(addr);
const UINT32 crc = compute_opcode_crc32(addr);
dasm_comment* comment = m_comment_set.find(dasm_comment(addr, crc, "", 0));
if (comment == NULL) return NULL;
return comment->m_text;
@ -3523,7 +3553,22 @@ void device_debug::tracer::flush()
device_debug::dasm_pc_tag::dasm_pc_tag(const offs_t& address, const UINT32& crc)
: m_address(address),
m_crc(crc)
m_crc(crc)
{
}
//-------------------------------------------------
// dasm_memory_access - constructor
//-------------------------------------------------
device_debug::dasm_memory_access::dasm_memory_access(const address_spacenum& address_space,
const offs_t& address,
const UINT64& data,
const offs_t& pc)
: m_address_space(address_space),
m_address(address),
m_data(data),
m_pc(pc)
{
}
@ -3533,7 +3578,7 @@ device_debug::dasm_pc_tag::dasm_pc_tag(const offs_t& address, const UINT32& crc)
device_debug::dasm_comment::dasm_comment(offs_t address, UINT32 crc, const char *text, rgb_t color)
: dasm_pc_tag(address, crc),
m_text(text),
m_color(color)
m_text(text),
m_color(color)
{
}

View File

@ -156,7 +156,7 @@ public:
// internals
bool hit();
registerpoint * m_next; // next in the list
registerpoint * m_next; // next in the list
int m_index; // user reported index
UINT8 m_enabled; // enabled?
parsed_expression m_condition; // condition
@ -258,6 +258,13 @@ public:
void set_track_pc_visited(const offs_t& pc);
void track_pc_data_clear() { m_track_pc_set.clear(); }
// memory tracking
void set_track_mem(bool value) { m_track_mem = value; }
offs_t track_mem_pc_from_space_address_data(const address_spacenum& space,
const offs_t& address,
const UINT64& data) const;
void track_mem_data_clear() { m_track_mem_set.clear(); }
// tracing
void trace(FILE *file, bool trace_over, const char *action);
void trace_printf(const char *fmt, ...);
@ -298,8 +305,8 @@ private:
device_disasm_interface * m_disasm; // disasm interface, if present
// global state
UINT32 m_flags; // debugging flags for this CPU
symbol_table m_symtable; // symbol table for expression evaluation
UINT32 m_flags; // debugging flags for this CPU
symbol_table m_symtable; // symbol table for expression evaluation
debug_instruction_hook_func m_instrhook; // per-instruction callback hook
// disassembly
@ -377,11 +384,11 @@ private:
bool operator < (const dasm_pc_tag& rhs) const
{
if (m_address == rhs.m_address)
return m_crc < rhs.m_crc;
return m_crc < rhs.m_crc;
return (m_address < rhs.m_address);
}
offs_t m_address;
offs_t m_address; // Stores [nothing] for a given address & crc32
UINT32 m_crc;
};
simple_set<dasm_pc_tag> m_track_pc_set;
@ -393,12 +400,38 @@ private:
public:
dasm_comment(offs_t address, UINT32 crc, const char *text, rgb_t color);
astring m_text; // comment text
rgb_t m_color; // comment color
astring m_text; // Stores comment text & color for a given address & crc32
rgb_t m_color;
};
simple_set<dasm_comment> m_comment_set; // collection of comments
UINT32 m_comment_change; // change counter for comments
// memory tracking
class dasm_memory_access
{
public:
dasm_memory_access(const address_spacenum& address_space,
const offs_t& address,
const UINT64& data,
const offs_t& pc);
// required to be included in a simple_set
bool operator < (const dasm_memory_access& rhs) const
{
if ((m_address == rhs.m_address) && (m_address_space == rhs.m_address_space))
return m_data < rhs.m_data;
return (m_address < rhs.m_address) && (m_address_space == rhs.m_address_space);
}
// Stores the PC for a given address, memory region, and data value
address_spacenum m_address_space;
offs_t m_address;
UINT64 m_data;
offs_t m_pc;
};
simple_set<dasm_memory_access> m_track_mem_set;
bool m_track_mem;
// internal flag values
static const UINT32 DEBUG_FLAG_OBSERVING = 0x00000001; // observing this CPU
static const UINT32 DEBUG_FLAG_HISTORY = 0x00000002; // tracking this CPU's history

View File

@ -88,7 +88,12 @@ static const help_item static_help_list[] =
" logerror <format>[,<item>[,...]] -- outputs one or more <item>s to the error.log\n"
" tracelog <format>[,<item>[,...]] -- outputs one or more <item>s to the trace file using <format>\n"
" history [<cpu>,<length>] -- outputs a brief history of visited opcodes.\n"
" trackpc [<bool>,<cpu>,<bool>] -- toggle to visually track visited opcodes [for the given cpu [& clear]].\n"
" trackpc [<bool>,<cpu>,<bool>] -- visually track visited opcodes [boolean to turn on and off, for the given cpu, clear].\n"
" trackmem [<bool>,<bool>] -- record which PC writes to each memory address [boolean to turn on and off, clear].\n"
" pcatmemp <address>[,<cpu>] -- query which PC wrote to a given program memory address for the current CPU.\n"
" pcatmemd <address>[,<cpu>] -- query which PC wrote to a given data memory address for the current CPU.\n"
" pcatmemi <address>[,<cpu>] -- query which PC wrote to a given I/O memory address for the current CPU.\n"
" (Note: you can also query this info by right clicking in a memory window.\n"
" snap [<filename>] -- save a screen snapshot\n"
" source <filename> -- reads commands from <filename> and executes them one by one\n"
" quit -- exits MAME and the debugger\n"
@ -391,9 +396,44 @@ static const help_item static_help_list[] =
"trackpc 1\n"
" Begin tracking the current cpu's pc.\n"
"\n"
"trackpc 1, 0, 0\n"
"trackpc 1, 0, 1\n"
" Continue tracking pc on cpu 0, but clear existing track info.\n"
},
{
"trackmem",
"\n"
" trackmem [<bool>,<cpu>,<bool>]\n"
"\n"
"The trackmem command logs the PC at each time a memory address is written to. "
"The first boolean argument toggles the process on and off. The second argument is a cpu "
"selector; if no cpu is specified, the current cpu is automatically selected. The third argument "
" is a boolean denoting if the existing data should be cleared or not. Please refer to the "
"pcatmem command for information on how to retrieve this data. Also, right clicking in "
"a memory window will display the logged PC for the given address.\n"
"\n"
"Examples:\n"
"\n"
"trackmem\n"
" Begin tracking the current CPU's pc.\n"
"\n"
"trackmem 1, 0, 1\n"
" Continue tracking memory writes on cpu 0, but clear existing track info.\n"
},
{
"pcatmem",
"\n"
" pcatmem(p/d/i) <address>[,<cpu>]\n"
"\n"
"The pcatmem command returns which PC wrote to a given memory address for the current CPU. "
"The first argument is the requested address. The second argument is a cpu selector; if no "
"cpu is specified, the current cpu is automatically selected. Right clicking in a memory window "
"will also display the logged PC for the given address.\n"
"\n"
"Examples:\n"
"\n"
"pcatmem 400000\n"
" Print which PC wrote this CPU's memory location 0x400000.\n"
},
{
"snap",
"\n"

View File

@ -204,7 +204,7 @@ void debug_view_memory::view_notify(debug_view_notification type)
if (type == VIEW_NOTIFY_CURSOR_CHANGED)
{
// normalize the cursor
set_cursor_pos(get_cursor_pos());
set_cursor_pos(get_cursor_pos(m_cursor));
}
else if (type == VIEW_NOTIFY_SOURCE_CHANGED)
{
@ -315,7 +315,7 @@ void debug_view_memory::view_update()
void debug_view_memory::view_char(int chval)
{
// get the position
cursor_pos pos = get_cursor_pos();
cursor_pos pos = get_cursor_pos(m_cursor);
// handle the incoming key
switch (chval)
@ -437,7 +437,7 @@ void debug_view_memory::view_click(const int button, const debug_view_xy& pos)
/* cursor popup|toggle */
bool cursorVisible = true;
if (m_cursor.y == origcursor.y)
if (m_cursor.y == origcursor.y && m_cursor.x == origcursor.x)
{
cursorVisible = !m_cursor_visible;
}
@ -461,7 +461,7 @@ void debug_view_memory::recompute()
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
// get the current cursor position
cursor_pos pos = get_cursor_pos();
cursor_pos pos = get_cursor_pos(m_cursor);
// determine the maximum address and address format string from the raw information
int addrchars;
@ -565,15 +565,15 @@ bool debug_view_memory::needs_recompute()
// an address and a shift value
//-------------------------------------------------
debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos()
debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view_xy& cursor)
{
// start with the base address for this row
cursor_pos pos;
pos.m_address = m_byte_offset + m_cursor.y * m_bytes_per_chunk * m_chunks_per_row;
pos.m_address = m_byte_offset + cursor.y * m_bytes_per_chunk * m_chunks_per_row;
// determine the X position within the middle section, clamping as necessary
const memory_view_pos &posdata = s_memory_pos_table[m_bytes_per_chunk];
int xposition = m_cursor.x - m_section[1].m_pos - 1;
int xposition = cursor.x - m_section[1].m_pos - 1;
if (xposition < 0)
xposition = 0;
else if (xposition >= posdata.m_spacing * m_chunks_per_row)

View File

@ -87,6 +87,7 @@ public:
bool reverse() const { return m_reverse_view; }
bool ascii() const { return m_ascii_view; }
bool physical() const { return m_no_translation; }
offs_t addressAtCursorPosition(const debug_view_xy& pos) { return get_cursor_pos(pos).m_address; }
// setters
void set_expression(const char *expression);
@ -117,9 +118,9 @@ private:
bool needs_recompute();
// cursor position management
cursor_pos get_cursor_pos();
cursor_pos get_cursor_pos(const debug_view_xy& cursor);
void set_cursor_pos(cursor_pos pos);
cursor_pos begin_update_and_get_cursor_pos() { begin_update(); return get_cursor_pos(); }
cursor_pos begin_update_and_get_cursor_pos() { begin_update(); return get_cursor_pos(m_cursor); }
void end_update_and_set_cursor_pos(cursor_pos pos) { set_cursor_pos(pos); end_update(); }
// memory access

View File

@ -52,13 +52,13 @@ public:
m_rightBar(0)
{
}
~DasmWindowQtConfig() {}
// Settings
int m_cpu;
int m_rightBar;
void buildFromQWidget(QWidget* widget);
void applyToQWidget(QWidget* widget);
void addToXmlDataNode(xml_data_node* node) const;

View File

@ -35,9 +35,9 @@ public:
WindowQtConfig(WIN_TYPE_LOG)
{
}
~LogWindowQtConfig() {}
void buildFromQWidget(QWidget* widget);
void applyToQWidget(QWidget* widget);
void addToXmlDataNode(xml_data_node* node) const;

View File

@ -125,7 +125,7 @@ void MainWindow::setProcessor(device_t* processor)
void MainWindow::closeEvent(QCloseEvent* event)
{
debugActQuit();
// Insure the window doesn't disappear before we get a chance to save its parameters
event->ignore();
}

View File

@ -171,13 +171,13 @@ public:
m_rightBar(0),
m_windowState()
{}
~MainWindowQtConfig() {}
// Settings
int m_rightBar;
QByteArray m_windowState;
void buildFromQWidget(QWidget* widget);
void applyToQWidget(QWidget* widget);
void addToXmlDataNode(xml_data_node* node) const;

View File

@ -35,9 +35,7 @@ MemoryWindow::MemoryWindow(running_machine* machine, QWidget* parent) :
connect(m_memoryComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(memoryRegionChanged(int)));
// The main memory window
m_memTable = new DebuggerView(DVT_MEMORY,
m_machine,
this);
m_memTable = new DebuggerMemView(DVT_MEMORY, m_machine, this);
// Layout
QHBoxLayout* subLayout = new QHBoxLayout(topSubFrame);
@ -264,6 +262,65 @@ QAction* MemoryWindow::chunkSizeMenuItem(const QString& itemName)
}
//=========================================================================
// DebuggerMemView
//=========================================================================
void DebuggerMemView::mousePressEvent(QMouseEvent* event)
{
const bool leftClick = event->button() == Qt::LeftButton;
const bool rightClick = event->button() == Qt::RightButton;
if (leftClick || rightClick)
{
QFontMetrics actualFont = fontMetrics();
const int fontWidth = MAX(1, actualFont.width('_'));
const int fontHeight = MAX(1, actualFont.height());
debug_view_xy topLeft = view()->visible_position();
debug_view_xy clickViewPosition;
clickViewPosition.x = topLeft.x + (event->x() / fontWidth);
clickViewPosition.y = topLeft.y + (event->y() / fontHeight);
if (leftClick)
{
view()->process_click(DCK_LEFT_CLICK, clickViewPosition);
}
else if (rightClick)
{
// Display the last known PC to write to this memory location & copy it onto the clipboard
debug_view_memory* memView = downcast<debug_view_memory*>(view());
const offs_t address = memView->addressAtCursorPosition(clickViewPosition);
const debug_view_memory_source* source = downcast<const debug_view_memory_source*>(memView->source());
address_space* addressSpace = source->space();
const int nativeDataWidth = addressSpace->data_width() / 8;
const UINT64 memValue = debug_read_memory(*addressSpace,
addressSpace->address_to_byte(address),
nativeDataWidth,
true);
const offs_t pc = source->device()->debug()->track_mem_pc_from_space_address_data(addressSpace->spacenum(),
address,
memValue);
if (pc != (offs_t)(-1))
{
// TODO: You can specify a box that the tooltip stays alive within - might be good?
const QString addressAndPc = QString("Address %1 written at PC=%2").arg(address, 2, 16).arg(pc, 2, 16);
QToolTip::showText(QCursor::pos(), addressAndPc, NULL);
// Copy the PC into the clipboard as well
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(QString("%1").arg(pc, 2, 16));
}
else
{
QToolTip::showText(QCursor::pos(), "UNKNOWN PC", NULL);
}
}
viewport()->update();
update();
}
}
//=========================================================================
// MemoryWindowQtConfig
//=========================================================================

View File

@ -6,6 +6,8 @@
#include "debugqtview.h"
#include "debugqtwindow.h"
class DebuggerMemView;
//============================================================
// The Memory Window.
@ -39,7 +41,25 @@ private:
// Widgets
QLineEdit* m_inputEdit;
QComboBox* m_memoryComboBox;
DebuggerView* m_memTable;
DebuggerMemView* m_memTable;
};
//=========================================================================
// The mem window gets its own debugger view to handle right click pop-ups
//=========================================================================
class DebuggerMemView : public DebuggerView
{
public:
DebuggerMemView(const debug_view_type& type,
running_machine* machine,
QWidget* parent=NULL)
: DebuggerView(type, machine, parent)
{}
virtual ~DebuggerMemView() {}
protected:
void mousePressEvent(QMouseEvent* event);
};
@ -59,13 +79,13 @@ public:
}
~MemoryWindowQtConfig() {}
// Settings
int m_reverse;
int m_addressMode;
int m_chunkSize;
int m_memoryRegion;
void buildFromQWidget(QWidget* widget);
void applyToQWidget(QWidget* widget);
void addToXmlDataNode(xml_data_node* node) const;

View File

@ -12,8 +12,8 @@ class DebuggerView : public QAbstractScrollArea
public:
DebuggerView(const debug_view_type& type,
running_machine* machine,
QWidget* parent=NULL);
running_machine* machine,
QWidget* parent=NULL);
virtual ~DebuggerView();
void paintEvent(QPaintEvent* event);

View File

@ -77,17 +77,17 @@ public:
m_next(NULL)
{}
virtual ~WindowQtConfig() {}
// Settings
WindowType m_type;
QPoint m_size;
QPoint m_position;
// Dues for becoming a member of a simple_list
WindowQtConfig* m_next;
WindowQtConfig* next() const { return m_next; }
virtual void buildFromQWidget(QWidget* widget);
virtual void applyToQWidget(QWidget* widget);
virtual void addToXmlDataNode(xml_data_node* node) const;