debugger: symlist usability + symbol table extensibility (#13694)

Address issue #6655 (symlist command usability), add a bit of plumbing for future extensibility.

symlist with no arguments displays all global *and* :maincpu symbols, with clear header text for each list.  At the bottom, prints helper text to make user aware of the cpu form

To allow for adding new kinds of symbols in the future, this adds an enum field to symbol table for its 'type', for prettier printing from symlist.  Symlist now traverses symbol table chain completely.
This commit is contained in:
dave-br 2025-05-20 13:02:30 -07:00 committed by GitHub
parent efb84a4bb5
commit b39e684622
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 81 additions and 47 deletions

View File

@ -3909,58 +3909,80 @@ void debugger_commands::execute_memdump(const std::vector<std::string_view> &par
void debugger_commands::execute_symlist(const std::vector<std::string_view> &params)
{
const char *namelist[1000];
// Default to CPU "0" if none specified
const char * cpuname = (params.empty()) ? "0" : params[0].cbegin();
device_t *cpu = nullptr;
symbol_table *symtable;
int count = 0;
if (!params.empty())
if (!m_console.validate_cpu_parameter(cpuname, cpu))
{
// validate parameters
device_t *cpu;
if (!m_console.validate_cpu_parameter(params[0], cpu))
return;
symtable = &cpu->debug()->symtable();
m_console.printf("CPU '%s' symbols:\n", cpu->tag());
if (!params.empty())
return; // Explicitly specified cpu is invalid
// Somehow cpu "0" is invalid, so just stick with global symbol table
symtable = &m_machine.debugger().cpu().global_symtable();
}
else
{
symtable = &m_machine.debugger().cpu().global_symtable();
m_console.printf("Global symbols:\n");
symtable = &cpu->debug()->symtable();
}
// gather names for all symbols
for (auto &entry : symtable->entries())
// Traverse symbol_table parent chain, printing each table's symbols in its own block
for (; symtable != nullptr; symtable = symtable->parent())
{
// only display "register" type symbols
if (!entry.second->is_function())
// Skip globals if user explicitly requested CPU
if (symtable->type() == symbol_table::BUILTIN_GLOBALS && !params.empty())
continue;
if (symtable->entries().size() == 0)
continue;
std::vector<const char *> namelist;
// Print heading for table
switch (symtable->type())
{
namelist[count++] = entry.second->name();
if (count >= std::size(namelist))
break;
case symbol_table::CPU_STATE:
m_console.printf("\n**** CPU '%s' symbols ****\n", cpu->tag());
break;
case symbol_table::BUILTIN_GLOBALS:
m_console.printf("\n**** Global symbols ****\n");
break;
default:
assert (!"Unrecognized symbol table type");
}
// gather names for all relevant symbols
for (auto &entry : symtable->entries())
{
// ignore built-in function symbols
if (!entry.second->is_function())
{
namelist.push_back(entry.second->name());
}
}
// sort the symbols
std::sort(
namelist.begin(),
namelist.end(),
[] (const char *item1, const char *item2) { return strcmp(item1, item2) < 0; });
// iterate over symbols and print them
for (const char * symname : namelist)
{
symbol_entry const *const entry = symtable->find(symname);
assert(entry != nullptr);
m_console.printf("%s = %X", symname, entry->value());
if (!entry->is_lval())
m_console.printf(" (read-only)");
m_console.printf("\n");
}
}
// sort the symbols
if (count > 1)
if (params.empty())
{
std::sort(
&namelist[0],
&namelist[count],
[] (const char *item1, const char *item2) { return strcmp(item1, item2) < 0; });
}
// iterate over symbols and print out relevant ones
for (int symnum = 0; symnum < count; symnum++)
{
symbol_entry const *const entry = symtable->find(namelist[symnum]);
assert(entry != nullptr);
u64 value = entry->value();
// only display "register" type symbols
m_console.printf("%s = %X", namelist[symnum], value);
if (!entry->is_lval())
m_console.printf(" (read-only)");
m_console.printf("\n");
m_console.printf(
"\nTo view the symbols for a particular CPU, try symlist <cpu>,\n"
"where <cpu> is the ID number or tag for a CPU.\n");
}
}

View File

@ -57,7 +57,7 @@ debugger_cpu::debugger_cpu(running_machine &machine)
m_tempvar = make_unique_clear<u64[]>(NUM_TEMP_VARIABLES);
/* create a global symbol table */
m_symtable = std::make_unique<symbol_table>(machine);
m_symtable = std::make_unique<symbol_table>(machine, symbol_table::BUILTIN_GLOBALS);
m_symtable->set_memory_modified_func([this]() { set_memory_modified(true); });
/* add "wpaddr", "wpdata", "wpsize" to the global symbol table */
@ -488,7 +488,7 @@ device_debug::device_debug(device_t &device)
, m_state(nullptr)
, m_disasm(nullptr)
, m_flags(0)
, m_symtable(std::make_unique<symbol_table>(device.machine(), &device.machine().debugger().cpu().global_symtable(), &device))
, m_symtable(std::make_unique<symbol_table>(device.machine(), symbol_table::CPU_STATE, &device.machine().debugger().cpu().global_symtable(), &device))
, m_stepaddr(0)
, m_stepsleft(0)
, m_delay_steps(0)

View File

@ -363,8 +363,9 @@ symbol_entry::~symbol_entry()
// symbol_table - constructor
//-------------------------------------------------
symbol_table::symbol_table(running_machine &machine, symbol_table *parent, device_t *device)
symbol_table::symbol_table(running_machine &machine, table_type type, symbol_table *parent, device_t *device)
: m_machine(machine)
, m_type(type)
, m_parent(parent)
, m_memintf(dynamic_cast<device_memory_interface *>(device))
, m_memory_modified(nullptr)

View File

@ -171,11 +171,21 @@ public:
READ_WRITE
};
// Identifies the type of symbols stored in this table. These help symlist create
// useful output
enum table_type
{
CPU_STATE, // CPU registers, etc.
BUILTIN_GLOBALS, // Built-in MAME global symbols (e.g., beamx, beamy, frame, etc.)
// (also used for tables outside debugger: lua scripts, cheat engine)
};
// construction/destruction
symbol_table(running_machine &machine, symbol_table *parent = nullptr, device_t *device = nullptr);
symbol_table(running_machine &machine, table_type type, symbol_table *parent = nullptr, device_t *device = nullptr);
// getters
const std::unordered_map<std::string, std::unique_ptr<symbol_entry>> &entries() const { return m_symlist; }
table_type type() const { return m_type; }
symbol_table *parent() const { return m_parent; }
running_machine &machine() { return m_machine; }
@ -212,6 +222,7 @@ private:
// internal state
running_machine & m_machine; // reference to the machine
table_type m_type; // kind of symbols stored in this table
symbol_table * m_parent; // pointer to the parent symbol table
std::unordered_map<std::string,std::unique_ptr<symbol_entry>> m_symlist; // list of symbols
device_memory_interface *const m_memintf; // pointer to the local memory interface (if any)

View File

@ -683,7 +683,7 @@ void cheat_script::script_entry::output_argument::save(util::core_file &cheatfil
cheat_entry::cheat_entry(cheat_manager &manager, symbol_table &globaltable, std::string const &filename, util::xml::data_node const &cheatnode)
: m_manager(manager)
, m_symbols(manager.machine(), &globaltable)
, m_symbols(manager.machine(), symbol_table::BUILTIN_GLOBALS, &globaltable)
, m_state(SCRIPT_STATE_OFF)
, m_numtemp(DEFAULT_TEMP_VARIABLES)
, m_argindex(0)
@ -1065,7 +1065,7 @@ cheat_manager::cheat_manager(running_machine &machine)
, m_numlines(0)
, m_lastline(0)
, m_disabled(true)
, m_symtable(machine)
, m_symtable(machine, symbol_table::BUILTIN_GLOBALS)
{
// if the cheat engine is disabled, we're done
if (!machine.options().cheat())

View File

@ -91,7 +91,7 @@ public:
symbol_table_wrapper(lua_engine &host, running_machine &machine, std::shared_ptr<symbol_table_wrapper> const &parent, device_t *device)
: m_host(host)
, m_table(machine, parent ? &parent->table() : nullptr, device)
, m_table(machine, symbol_table::BUILTIN_GLOBALS, parent ? &parent->table() : nullptr, device)
, m_parent(parent)
{
}