diff options
author | 2020-05-25 11:15:39 -0400 | |
---|---|---|
committer | 2020-05-25 11:15:39 -0400 | |
commit | 0fa6e7eb86d3b247c46f840ef230c9331f3cc948 (patch) | |
tree | e01509e44adcff62a7d8e087c58572fce6b90e13 /src/emu/debug/points.cpp | |
parent | ad7a31ad57b09e1e0e9bb1adb03767f9dd386605 (diff) |
Debugger expression and memory access overhaul
- Memory references in expressions no longer default to the console's visible CPU if no device name was specified, except when entered through the console itself. Expressions in view windows now use the context of the currently selected device instead.
- The pcatmem debug command and similar qt mouseover function now produce an error message if the initial address translation fails.
Related internal changes (nw)
- The debugger_cpu class no longer interprets memory accesses. The existing routines have been moved into symbol_table (which used to invoke them as callbacks), and reimplemented in most other places. Thecode duplication is a bit messy, but could be potentially improved in the future with new utility classes.
- The cheat engine no longer needs to hook into the debugger_cpu class or instantiate a dummy instance of it.
- The inclusion of debug/express.h within emu.h has been undone. Some debugging structures now need unique_ptr to wrap the resulting incomplete classes; hopefully the performance impact of this is negligible. Another direct consequence is that the breakpoint, watchpoint and registerpoint classes are no longer inside device_debug and have their own source file.
- The breakpoint list is now a std::multimap, using the addresses as keys to hopefully expedite lookup.
- The visible CPU pointer has been removed from the debugger_cpu class, being now considered a property of the console instead.
- Many minor bits of code have been simplified.
Diffstat (limited to 'src/emu/debug/points.cpp')
-rw-r--r-- | src/emu/debug/points.cpp | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp new file mode 100644 index 00000000000..1cde2bbf486 --- /dev/null +++ b/src/emu/debug/points.cpp @@ -0,0 +1,441 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + points.cpp + + Debugger breakpoints, watchpoints, etc. + +***************************************************************************/ + +#include "emu.h" +#include "points.h" +#include "debugger.h" +#include "debugcon.h" + + +//************************************************************************** +// DEBUG BREAKPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_breakpoint - constructor +//------------------------------------------------- + +debug_breakpoint::debug_breakpoint(device_debug* debugInterface, + symbol_table &symbols, + int index, + offs_t address, + const char *condition, + const char *action) + : m_debugInterface(debugInterface), + m_index(index), + m_enabled(true), + m_address(address), + m_condition(symbols, (condition != nullptr) ? condition : "1"), + m_action((action != nullptr) ? action : "") +{ +} + + +//------------------------------------------------- +// hit - detect a hit +//------------------------------------------------- + +bool debug_breakpoint::hit(offs_t pc) +{ + // don't hit if disabled + if (!m_enabled) + return false; + + // must match our address + if (m_address != pc) + return false; + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + return (m_condition.execute() != 0); + } + catch (expression_error &) + { + return false; + } + } + + return true; +} + + + +//************************************************************************** +// DEBUG WATCHPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_watchpoint - constructor +//------------------------------------------------- + +debug_watchpoint::debug_watchpoint(device_debug* debugInterface, + symbol_table &symbols, + int index, + address_space &space, + read_or_write type, + offs_t address, + offs_t length, + const char *condition, + const char *action) + : m_debugInterface(debugInterface), + m_phr(nullptr), + m_phw(nullptr), + m_space(space), + m_index(index), + m_enabled(true), + m_type(type), + m_address(address & space.addrmask()), + m_length(length), + m_condition(symbols, (condition != nullptr) ? condition : "1"), + m_action((action != nullptr) ? action : ""), + m_installing(false) +{ + std::fill(std::begin(m_start_address), std::end(m_start_address), 0); + std::fill(std::begin(m_end_address), std::end(m_end_address), 0); + std::fill(std::begin(m_masks), std::end(m_masks), 0); + + int ashift = m_space.addr_shift(); + endianness_t endian = m_space.endianness(); + offs_t subamask = m_space.alignment() - 1; + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + offs_t start = m_address; + offs_t end = (m_address + m_length - 1) & space.addrmask(); + if (end < start) + end = space.addrmask(); + offs_t rstart = start & ~subamask; + offs_t rend = end | subamask; + u64 smask, mmask, emask; + smask = mmask = emask = make_bitmask<u64>(m_space.data_width()); + if (start != rstart) + { + if (endian == ENDIANNESS_LITTLE) + smask &= ~make_bitmask<u64>((start - rstart) * unit_size); + else + smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size); + } + if (end != rend) + { + if (endian == ENDIANNESS_LITTLE) + emask &= make_bitmask<u64>((subamask + 1 + end - rend) * unit_size); + else + emask &= ~make_bitmask<u64>((rend - end) * unit_size); + } + + if (rend == (rstart | subamask) || smask == emask) + { + m_start_address[0] = rstart; + m_end_address[0] = rend; + m_masks[0] = smask & emask; + } + else + { + int idx = 0; + if (smask != mmask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rstart | subamask; + m_masks[idx] = smask; + idx++; + rstart += subamask + 1; + } + if (mmask == emask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + else + { + if (rstart < rend - subamask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend - subamask - 1; + m_masks[idx] = mmask; + idx++; + } + m_start_address[idx] = rend - subamask; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + } + + install(read_or_write::READWRITE); + m_notifier = m_space.add_change_notifier([this](read_or_write mode) { + if (m_enabled) + { + install(mode); + } + }); +} + +debug_watchpoint::~debug_watchpoint() +{ + m_space.remove_change_notifier(m_notifier); + if (m_phr) + m_phr->remove(); + if (m_phw) + m_phw->remove(); +} + +void debug_watchpoint::setEnabled(bool value) +{ + if (m_enabled != value) + { + m_enabled = value; + if (m_enabled) + install(read_or_write::READWRITE); + else + { + m_installing = true; + if(m_phr) + m_phr->remove(); + if(m_phw) + m_phw->remove(); + m_installing = false; + } + } +} + +void debug_watchpoint::install(read_or_write mode) +{ + if (m_installing) + return; + m_installing = true; + if ((u32(mode) & u32(read_or_write::READ)) && m_phr) + m_phr->remove(); + if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw) + m_phw->remove(); + std::string name = util::string_format("wp@%x", m_address); + switch (m_space.data_width()) + { + case 8: + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + break; + + case 16: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u16 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + + case 32: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u32 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + + case 64: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u64 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + } + m_installing = false; +} + +void debug_watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask) +{ + running_machine &machine = m_debugInterface->device().machine(); + debugger_manager &debug = machine.debugger(); + + // if we're within debugger code, don't trigger + if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled()) + return; + + // adjust address, size & value_to_write based on mem_mask. + offs_t size = 0; + int ashift = m_space.addr_shift(); + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + u64 unit_mask = make_bitmask<u64>(unit_size); + + offs_t address_offset = 0; + + if(!mem_mask) + mem_mask = 0xff; + + while (!(mem_mask & unit_mask)) + { + address_offset++; + data >>= unit_size; + mem_mask >>= unit_size; + } + + while (mem_mask) + { + size++; + mem_mask >>= unit_size; + } + + data &= make_bitmask<u64>(size * unit_size); + + if (m_space.endianness() == ENDIANNESS_LITTLE) + address += address_offset; + else + address += m_space.alignment() - size - address_offset; + + // stash the value that will be written or has just been read + debug.cpu().set_wpinfo(address, data); + + // protect against recursion + debug.cpu().set_within_instruction(true); + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + if (!m_condition.execute()) + { + debug.cpu().set_within_instruction(false); + return; + } + } + catch (expression_error &) + { + debug.cpu().set_within_instruction(false); + return; + } + } + + // halt in the debugger by default + bool was_stopped = debug.cpu().is_stopped(); + debug.cpu().set_execution_stopped(); + + // evaluate the action + if (!m_action.empty()) + debug.console().execute_command(m_action, false); + + // print a notification, unless the action made us go again + if (debug.cpu().is_stopped()) + { + std::string buffer; + + buffer = string_format(type == read_or_write::READ ? + "Stopped at watchpoint %X reading %0*X from %08X" : + "Stopped at watchpoint %X writing %0*X to %08X", + m_index, + size * unit_size / 4, + data, + address); + + if (debug.cpu().live_cpu() == &m_space.device()) + { + offs_t pc = m_space.device().state().pcbase(); + debug.console().printf("%s (PC=%X)\n", buffer, pc); + m_debugInterface->compute_debug_flags(); + } + else if (!was_stopped) + { + debug.console().printf("%s\n", buffer); + debug.cpu().set_execution_running(); + debug.cpu().set_break_cpu(&m_space.device()); + } + m_debugInterface->set_triggered_watchpoint(this); + } + + debug.cpu().set_within_instruction(false); +} + +//************************************************************************** +// DEBUG REGISTERPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_registerpoint - constructor +//------------------------------------------------- + +debug_registerpoint::debug_registerpoint(symbol_table &symbols, int index, const char *condition, const char *action) + : m_index(index), + m_enabled(true), + m_condition(symbols, (condition != nullptr) ? condition : "1"), + m_action((action != nullptr) ? action : "") +{ +} + + +//------------------------------------------------- +// hit - detect a hit +//------------------------------------------------- + +bool debug_registerpoint::hit() +{ + // don't hit if disabled + if (!m_enabled) + return false; + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + return (m_condition.execute() != 0); + } + catch (expression_error &) + { + return false; + } + } + + return true; +} |