GP-4546: Add 'Set breakpoint' multi-action to Breakpoints window.

This commit is contained in:
Dan 2025-02-06 13:15:45 +00:00
parent d23e67a088
commit fb9a7c62a2
44 changed files with 785 additions and 402 deletions

View file

@ -405,7 +405,7 @@ def kill(process: sch.Schema('Process')):
commands.ghidra_trace_kill() commands.ghidra_trace_kill()
@REGISTRY.method(action='resume') @REGISTRY.method(action='resume', display="Go")
def go(process: sch.Schema('Process')): def go(process: sch.Schema('Process')):
"""Continue execution of the process.""" """Continue execution of the process."""
util.dbg.run_async(lambda: dbg().go()) util.dbg.run_async(lambda: dbg().go())
@ -456,7 +456,7 @@ def break_address(process: sch.Schema('Process'), address: Address):
dbg().bp(expr=address.offset) dbg().bp(expr=address.offset)
@REGISTRY.method(action='break_sw_execute') @REGISTRY.method(action='break_ext', display='Set Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_expression(expression: str): def break_expression(expression: str):
"""Set a breakpoint.""" """Set a breakpoint."""
@ -472,7 +472,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
dbg().ba(expr=address.offset) dbg().ba(expr=address.offset)
@REGISTRY.method(action='break_hw_execute') @REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_hw_expression(expression: str): def break_hw_expression(expression: str):
"""Set a hardware-assisted breakpoint.""" """Set a hardware-assisted breakpoint."""
@ -482,50 +482,50 @@ def break_hw_expression(expression: str):
@REGISTRY.method(action='break_read') @REGISTRY.method(action='break_read')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_read_range(process: sch.Schema('Process'), range: AddressRange): def break_read_range(process: sch.Schema('Process'), range: AddressRange):
"""Set a read watchpoint.""" """Set a read breakpoint."""
find_proc_by_obj(process) find_proc_by_obj(process)
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ) dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ)
@REGISTRY.method(action='break_read') @REGISTRY.method(action='break_ext', display='Set Read Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_read_expression(expression: str): def break_read_expression(expression: str):
"""Set a read watchpoint.""" """Set a read breakpoint."""
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ) dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
@REGISTRY.method(action='break_write') @REGISTRY.method(action='break_write')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_write_range(process: sch.Schema('Process'), range: AddressRange): def break_write_range(process: sch.Schema('Process'), range: AddressRange):
"""Set a watchpoint.""" """Set a write breakpoint."""
find_proc_by_obj(process) find_proc_by_obj(process)
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE) dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE)
@REGISTRY.method(action='break_write') @REGISTRY.method(action='break_ext', display='Set Write Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_write_expression(expression: str): def break_write_expression(expression: str):
"""Set a watchpoint.""" """Set a write breakpoint."""
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE) dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
@REGISTRY.method(action='break_access') @REGISTRY.method(action='break_access')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_access_range(process: sch.Schema('Process'), range: AddressRange): def break_access_range(process: sch.Schema('Process'), range: AddressRange):
"""Set an access watchpoint.""" """Set an access breakpoint."""
find_proc_by_obj(process) find_proc_by_obj(process)
dbg().ba(expr=range.min, size=range.length(), dbg().ba(expr=range.min, size=range.length(),
access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE) access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
@REGISTRY.method(action='break_access') @REGISTRY.method(action='break_ext', display='Set Access Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def break_access_expression(expression: str): def break_access_expression(expression: str):
"""Set an access watchpoint.""" """Set an access breakpoint."""
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE) dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
@REGISTRY.method(action='toggle') @REGISTRY.method(action='toggle', display='Toggle Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool): def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
"""Toggle a breakpoint.""" """Toggle a breakpoint."""
@ -536,7 +536,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
dbg().bd(bpt.GetId()) dbg().bd(bpt.GetId())
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display='Delete Breakpoint')
@util.dbg.eng_thread @util.dbg.eng_thread
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')): def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint.""" """Delete a breakpoint."""

View file

@ -391,19 +391,19 @@ def refresh_sections(node: sch.Schema('Module')):
gdb.execute(f'ghidra trace put-sections "{modname}"') gdb.execute(f'ghidra trace put-sections "{modname}"')
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display="Activate Inferior")
def activate_inferior(inferior: sch.Schema('Inferior')): def activate_inferior(inferior: sch.Schema('Inferior')):
"""Switch to the inferior.""" """Switch to the inferior."""
switch_inferior(find_inf_by_obj(inferior)) switch_inferior(find_inf_by_obj(inferior))
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display="Activate Thread")
def activate_thread(thread: sch.Schema('Thread')): def activate_thread(thread: sch.Schema('Thread')):
"""Switch to the thread.""" """Switch to the thread."""
find_thread_by_obj(thread).switch() find_thread_by_obj(thread).switch()
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display="Activate Frame")
def activate_frame(frame: sch.Schema('StackFrame')): def activate_frame(frame: sch.Schema('StackFrame')):
"""Select the frame.""" """Select the frame."""
find_frame_by_obj(frame).select() find_frame_by_obj(frame).select()
@ -415,7 +415,7 @@ def add_inferior(container: sch.Schema('InferiorContainer')):
gdb.execute('add-inferior') gdb.execute('add-inferior')
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display="Delete Inferior")
def delete_inferior(inferior: sch.Schema('Inferior')): def delete_inferior(inferior: sch.Schema('Inferior')):
"""Remove the inferior.""" """Remove the inferior."""
inf = find_inf_by_obj(inferior) inf = find_inf_by_obj(inferior)
@ -577,7 +577,7 @@ def break_sw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
gdb.execute(f'break *0x{offset:x}') gdb.execute(f'break *0x{offset:x}')
@REGISTRY.method(action='break_sw_execute') @REGISTRY.method(action='break_ext', display="Set Breakpoint")
def break_sw_execute_expression(expression: str): def break_sw_execute_expression(expression: str):
"""Set a breakpoint (break).""" """Set a breakpoint (break)."""
# TODO: Escape? # TODO: Escape?
@ -592,7 +592,7 @@ def break_hw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
gdb.execute(f'hbreak *0x{offset:x}') gdb.execute(f'hbreak *0x{offset:x}')
@REGISTRY.method(action='break_hw_execute') @REGISTRY.method(action='break_ext', display="Set Hardware Breakpoint")
def break_hw_execute_expression(expression: str): def break_hw_execute_expression(expression: str):
"""Set a hardware-assisted breakpoint (hbreak).""" """Set a hardware-assisted breakpoint (hbreak)."""
# TODO: Escape? # TODO: Escape?
@ -609,7 +609,7 @@ def break_read_range(inferior: sch.Schema('Inferior'), range: AddressRange):
f'rwatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})') f'rwatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
@REGISTRY.method(action='break_read') @REGISTRY.method(action='break_ext', display="Set Read Watchpoint")
def break_read_expression(expression: str): def break_read_expression(expression: str):
"""Set a read watchpoint (rwatch).""" """Set a read watchpoint (rwatch)."""
gdb.execute(f'rwatch {expression}') gdb.execute(f'rwatch {expression}')
@ -625,7 +625,7 @@ def break_write_range(inferior: sch.Schema('Inferior'), range: AddressRange):
f'watch -location *((char(*)[{range.length()}]) 0x{offset_start:x})') f'watch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
@REGISTRY.method(action='break_write') @REGISTRY.method(action='break_ext', display="Set Watchpoint")
def break_write_expression(expression: str): def break_write_expression(expression: str):
"""Set a watchpoint (watch).""" """Set a watchpoint (watch)."""
gdb.execute(f'watch {expression}') gdb.execute(f'watch {expression}')
@ -641,7 +641,7 @@ def break_access_range(inferior: sch.Schema('Inferior'), range: AddressRange):
f'awatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})') f'awatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
@REGISTRY.method(action='break_access') @REGISTRY.method(action='break_ext', display="Set Access Watchpoint")
def break_access_expression(expression: str): def break_access_expression(expression: str):
"""Set an access watchpoint (awatch).""" """Set an access watchpoint (awatch)."""
gdb.execute(f'awatch {expression}') gdb.execute(f'awatch {expression}')
@ -653,21 +653,23 @@ def break_event(inferior: sch.Schema('Inferior'), spec: str):
gdb.execute(f'catch {spec}') gdb.execute(f'catch {spec}')
@REGISTRY.method(action='toggle') @REGISTRY.method(action='toggle', display="Toggle Breakpoint")
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool): def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
"""Toggle a breakpoint.""" """Toggle a breakpoint."""
bpt = find_bpt_by_obj(breakpoint) bpt = find_bpt_by_obj(breakpoint)
bpt.enabled = enabled bpt.enabled = enabled
@REGISTRY.method(action='toggle', condition=util.GDB_VERSION.major >= 13) @REGISTRY.method(action='toggle', display="Toggle Breakpoint Location",
condition=util.GDB_VERSION.major >= 13)
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool): def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
"""Toggle a breakpoint location.""" """Toggle a breakpoint location."""
loc = find_bpt_loc_by_obj(location) loc = find_bpt_loc_by_obj(location)
loc.enabled = enabled loc.enabled = enabled
@REGISTRY.method(action='toggle', condition=util.GDB_VERSION.major < 13) @REGISTRY.method(action='toggle', display="Toggle Breakpoint Location",
condition=util.GDB_VERSION.major < 13)
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool): def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
"""Toggle a breakpoint location.""" """Toggle a breakpoint location."""
bptnum, locnum = find_bptlocnum_by_obj(location) bptnum, locnum = find_bptlocnum_by_obj(location)
@ -675,7 +677,7 @@ def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabl
gdb.execute(f'{cmd} {bptnum}.{locnum}') gdb.execute(f'{cmd} {bptnum}.{locnum}')
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display="Delete Breakpoint")
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')): def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint.""" """Delete a breakpoint."""
bpt = find_bpt_by_obj(breakpoint) bpt = find_bpt_by_obj(breakpoint)

View file

@ -17,11 +17,11 @@ from concurrent.futures import Future, ThreadPoolExecutor
import re import re
import sys import sys
import lldb
from ghidratrace import sch from ghidratrace import sch
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
import lldb
from . import commands, util from . import commands, util
@ -248,7 +248,7 @@ def execute(cmd: str, to_string: bool=False):
return exec_convert_errors(cmd, to_string) return exec_convert_errors(cmd, to_string)
@REGISTRY.method @REGISTRY.method(display='Evaluate')
def evaluate(expr: str): def evaluate(expr: str):
"""Evaluate an expression.""" """Evaluate an expression."""
value = util.get_target().EvaluateExpression(expr) value = util.get_target().EvaluateExpression(expr)
@ -257,7 +257,7 @@ def evaluate(expr: str):
return commands.convert_value(value) return commands.convert_value(value)
@REGISTRY.method @REGISTRY.method(display="Python Evaluate")
def pyeval(expr: str): def pyeval(expr: str):
return eval(expr) return eval(expr)
@ -345,28 +345,28 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
exec_convert_errors('ghidra trace put-modules') exec_convert_errors('ghidra trace put-modules')
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display='Activate Process')
def activate_process(process: sch.Schema('Process')): def activate_process(process: sch.Schema('Process')):
"""Switch to the process.""" """Switch to the process."""
# TODO # TODO
return return
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display='Activate Thread')
def activate_thread(thread: sch.Schema('Thread')): def activate_thread(thread: sch.Schema('Thread')):
"""Switch to the thread.""" """Switch to the thread."""
t = find_thread_by_obj(thread) t = find_thread_by_obj(thread)
t.process.SetSelectedThread(t) t.process.SetSelectedThread(t)
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate', display='Activate Frame')
def activate_frame(frame: sch.Schema('StackFrame')): def activate_frame(frame: sch.Schema('StackFrame')):
"""Select the frame.""" """Select the frame."""
f = find_frame_by_obj(frame) f = find_frame_by_obj(frame)
f.thread.SetSelectedFrame(f.GetFrameID()) f.thread.SetSelectedFrame(f.GetFrameID())
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display='Remove Process')
def remove_process(process: sch.Schema('Process')): def remove_process(process: sch.Schema('Process')):
"""Remove the process.""" """Remove the process."""
proc = find_proc_by_obj(process) proc = find_proc_by_obj(process)
@ -442,7 +442,7 @@ def kill(process: sch.Schema('Process')):
exec_convert_errors('process kill') exec_convert_errors('process kill')
@REGISTRY.method(name='continue', action='resume') @REGISTRY.method(name='continue', action='resume', display="Continue")
def _continue(process: sch.Schema('Process')): def _continue(process: sch.Schema('Process')):
"""Continue execution of the process.""" """Continue execution of the process."""
exec_convert_errors('process continue') exec_convert_errors('process continue')
@ -510,7 +510,7 @@ def break_address(process: sch.Schema('Process'), address: Address):
exec_convert_errors(f'breakpoint set -a 0x{offset:x}') exec_convert_errors(f'breakpoint set -a 0x{offset:x}')
@REGISTRY.method(action='break_sw_execute') @REGISTRY.method(action='break_ext', display='Set Breakpoint')
def break_expression(expression: str): def break_expression(expression: str):
"""Set a breakpoint.""" """Set a breakpoint."""
# TODO: Escape? # TODO: Escape?
@ -525,7 +525,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
exec_convert_errors(f'breakpoint set -H -a 0x{offset:x}') exec_convert_errors(f'breakpoint set -H -a 0x{offset:x}')
@REGISTRY.method(action='break_hw_execute') @REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
def break_hw_expression(expression: str): def break_hw_expression(expression: str):
"""Set a hardware-assisted breakpoint.""" """Set a hardware-assisted breakpoint."""
# TODO: Escape? # TODO: Escape?
@ -543,7 +543,7 @@ def break_read_range(process: sch.Schema('Process'), range: AddressRange):
f'watchpoint set expression -s {sz} -w read -- {offset_start}') f'watchpoint set expression -s {sz} -w read -- {offset_start}')
@REGISTRY.method(action='break_read') @REGISTRY.method(action='break_ext', display='Set Read Watchpoint')
def break_read_expression(expression: str, size=None): def break_read_expression(expression: str, size=None):
"""Set a read watchpoint.""" """Set a read watchpoint."""
size_part = '' if size is None else f'-s {size}' size_part = '' if size is None else f'-s {size}'
@ -562,7 +562,7 @@ def break_write_range(process: sch.Schema('Process'), range: AddressRange):
f'watchpoint set expression -s {sz} -- {offset_start}') f'watchpoint set expression -s {sz} -- {offset_start}')
@REGISTRY.method(action='break_write') @REGISTRY.method(action='break_ext', display='Set Watchpoint')
def break_write_expression(expression: str, size=None): def break_write_expression(expression: str, size=None):
"""Set a watchpoint.""" """Set a watchpoint."""
size_part = '' if size is None else f'-s {size}' size_part = '' if size is None else f'-s {size}'
@ -572,7 +572,7 @@ def break_write_expression(expression: str, size=None):
@REGISTRY.method(action='break_access') @REGISTRY.method(action='break_access')
def break_access_range(process: sch.Schema('Process'), range: AddressRange): def break_access_range(process: sch.Schema('Process'), range: AddressRange):
"""Set an access watchpoint.""" """Set a read/write watchpoint."""
proc = find_proc_by_obj(process) proc = find_proc_by_obj(process)
offset_start = process.trace.memory_mapper.map_back( offset_start = process.trace.memory_mapper.map_back(
proc, Address(range.space, range.min)) proc, Address(range.space, range.min))
@ -581,9 +581,9 @@ def break_access_range(process: sch.Schema('Process'), range: AddressRange):
f'watchpoint set expression -s {sz} -w read_write -- {offset_start}') f'watchpoint set expression -s {sz} -w read_write -- {offset_start}')
@REGISTRY.method(action='break_access') @REGISTRY.method(action='break_ext', display='Set Read/Write Watchpoint')
def break_access_expression(expression: str, size=None): def break_access_expression(expression: str, size=None):
"""Set an access watchpoint.""" """Set a read/write watchpoint."""
size_part = '' if size is None else f'-s {size}' size_part = '' if size is None else f'-s {size}'
exec_convert_errors( exec_convert_errors(
f'watchpoint set expression {size_part} -w read_write -- {expression}') f'watchpoint set expression {size_part} -w read_write -- {expression}')
@ -595,7 +595,7 @@ def break_exception(lang: str):
exec_convert_errors(f'breakpoint set -E {lang}') exec_convert_errors(f'breakpoint set -E {lang}')
@REGISTRY.method(action='toggle') @REGISTRY.method(action='toggle', display='Toggle Watchpoint')
def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool): def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
"""Toggle a watchpoint.""" """Toggle a watchpoint."""
wpt = find_wpt_by_obj(watchpoint) wpt = find_wpt_by_obj(watchpoint)
@ -604,7 +604,7 @@ def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
exec_convert_errors(f'watchpoint {cmd} {wpt.GetID()}') exec_convert_errors(f'watchpoint {cmd} {wpt.GetID()}')
@REGISTRY.method(action='toggle') @REGISTRY.method(action='toggle', display='Toggle Breakpoint')
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool): def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
"""Toggle a breakpoint.""" """Toggle a breakpoint."""
bpt = find_bpt_by_obj(breakpoint) bpt = find_bpt_by_obj(breakpoint)
@ -612,7 +612,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
exec_convert_errors(f'breakpoint {cmd} {bpt.GetID()}') exec_convert_errors(f'breakpoint {cmd} {bpt.GetID()}')
@REGISTRY.method(action='toggle') @REGISTRY.method(action='toggle', display='Toggle Breakpoint Location')
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool): def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
"""Toggle a breakpoint location.""" """Toggle a breakpoint location."""
bptnum, locnum = find_bptlocnum_by_obj(location) bptnum, locnum = find_bptlocnum_by_obj(location)
@ -620,7 +620,7 @@ def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabl
exec_convert_errors(f'breakpoint {cmd} {bptnum}.{locnum}') exec_convert_errors(f'breakpoint {cmd} {bptnum}.{locnum}')
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display='Delete Watchpoint')
def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')): def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
"""Delete a watchpoint.""" """Delete a watchpoint."""
wpt = find_wpt_by_obj(watchpoint) wpt = find_wpt_by_obj(watchpoint)
@ -628,7 +628,7 @@ def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
exec_convert_errors(f'watchpoint delete {wptnum}') exec_convert_errors(f'watchpoint delete {wptnum}')
@REGISTRY.method(action='delete') @REGISTRY.method(action='delete', display='Delete Breakpoint')
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')): def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint.""" """Delete a breakpoint."""
bpt = find_bpt_by_obj(breakpoint) bpt = find_bpt_by_obj(breakpoint)

View file

@ -15,8 +15,16 @@
*/ */
package ghidra.debug.api.target; package ghidra.debug.api.target;
import java.util.HashMap; import java.awt.event.InputEvent;
import java.util.Map; import java.util.*;
import javax.swing.Icon;
import docking.ActionContext;
import generic.theme.GIcon;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.target.TraceObject;
/** /**
* A name for a commonly-recognized target action. * A name for a commonly-recognized target action.
@ -35,21 +43,137 @@ import java.util.Map;
* the UI to decide what is presented where. * the UI to decide what is presented where.
* *
* @param name the name of the action (given as the action attribute on method annotations) * @param name the name of the action (given as the action attribute on method annotations)
* @param builtIn true if the action should <em>not</em> be presented in generic contexts, but * @param show when to show the general UI action for this debugger action
* reserved for built-in, purpose-specific actions * @param enabler determines when the action is enabled, based on the object
* @param display the default text to display
* @param icon the default icon for menus and dialogs
* @param okText the default text for confirm buttons in dialogs
*/ */
public record ActionName(String name, boolean builtIn) { public record ActionName(String name, Show show, Enabler enabler, String display, Icon icon,
String okText) {
private static final Icon ICON_ATTACH = new GIcon("icon.debugger.attach");
private static final Icon ICON_CONNECT = new GIcon("icon.debugger.connect");
private static final Icon ICON_DETACH = new GIcon("icon.debugger.detach");
private static final Icon ICON_INTERRUPT = new GIcon("icon.debugger.interrupt");
private static final Icon ICON_KILL = new GIcon("icon.debugger.kill");
private static final Icon ICON_LAUNCH = new GIcon("icon.debugger.launch");
private static final Icon ICON_REFRESH = new GIcon("icon.debugger.refresh");
private static final Icon ICON_RESUME = new GIcon("icon.debugger.resume");
private static final Icon ICON_STEP_BACK = new GIcon("icon.debugger.step.back");
private static final Icon ICON_STEP_INTO = new GIcon("icon.debugger.step.into");
private static final Icon ICON_STEP_LAST = new GIcon("icon.debugger.step.last");
private static final Icon ICON_STEP_OUT = new GIcon("icon.debugger.step.finish");
private static final Icon ICON_STEP_OVER = new GIcon("icon.debugger.step.over");
private static final Icon ICON_SKIP_OVER = new GIcon("icon.debugger.skip.over");
private static final Icon ICON_SET_BREAKPOINT = new GIcon("icon.debugger.breakpoint.set");
private static final Map<String, ActionName> NAMES = new HashMap<>(); private static final Map<String, ActionName> NAMES = new HashMap<>();
/**
* Specifies when an action should appear in the menus. For diagnostics, a user may override
* this by holding SHIFT when right-clicking, causing all applicable general actions to appear.
*/
public enum Show {
/**
* Don't show general actions. The tool has built-in actions that already know how to invoke
* this.
*/
BUILTIN {
@Override
boolean doIsShowing(ActionContext context) {
return false;
}
},
/**
* Only show general actions in address-based context, e.g., when right-clicking in the
* listing.
*/
ADDRESS {
@Override
boolean doIsShowing(ActionContext context) {
return context instanceof ProgramLocationActionContext;
}
},
/**
* Show in all contexts. This is the default.
*/
EXTENDED {
@Override
boolean doIsShowing(ActionContext context) {
return true;
}
};
public boolean isShowing(ActionContext context) {
if (isOverriden(context)) {
return true;
}
return doIsShowing(context);
}
abstract boolean doIsShowing(ActionContext context);
private boolean isOverriden(ActionContext context) {
return (context.getEventClickModifiers() & InputEvent.SHIFT_DOWN_MASK) != 0;
}
}
public enum Enabler {
ALWAYS {
@Override
public boolean isEnabled(TraceObject obj, long snap) {
return true;
}
},
NOT_RUNNING {
@Override
boolean doIsEnabled(TraceExecutionState state) {
return state != null && state != TraceExecutionState.RUNNING;
}
},
NOT_STOPPED {
@Override
boolean doIsEnabled(TraceExecutionState state) {
return state != TraceExecutionState.STOPPED;
}
},
NOT_DEAD {
@Override
boolean doIsEnabled(TraceExecutionState state) {
return state != TraceExecutionState.TERMINATED;
}
};
private TraceExecutionState getState(TraceObject obj, long snap) {
try {
return obj.getExecutionState(snap);
}
catch (NoSuchElementException e) {
return TraceExecutionState.TERMINATED;
}
}
boolean doIsEnabled(TraceExecutionState state) {
return true;
}
public boolean isEnabled(TraceObject obj, long snap) {
return doIsEnabled(getState(obj, snap));
}
}
public static ActionName name(String name) { public static ActionName name(String name) {
synchronized (NAMES) { synchronized (NAMES) {
return NAMES.computeIfAbsent(name, n -> new ActionName(n, false)); return NAMES.computeIfAbsent(name,
n -> new ActionName(n, Show.EXTENDED, Enabler.ALWAYS, n, null, "OK"));
} }
} }
private static ActionName builtIn(String name) { private static ActionName create(String name, Show show, Enabler enabler, String display,
Icon icon, String okText) {
synchronized (NAMES) { synchronized (NAMES) {
ActionName action = new ActionName(name, true); ActionName action = new ActionName(name, show, enabler, display, icon, okText);
if (NAMES.put(name, action) != null) { if (NAMES.put(name, action) != null) {
throw new AssertionError(); throw new AssertionError();
} }
@ -57,24 +181,16 @@ public record ActionName(String name, boolean builtIn) {
} }
} }
private static ActionName extended(String name) { public static final ActionName REFRESH =
synchronized (NAMES) { create("refresh", Show.EXTENDED, Enabler.ALWAYS, "Refresh", ICON_REFRESH, "Refresh");
ActionName action = new ActionName(name, false);
if (NAMES.put(name, action) != null) {
throw new AssertionError();
}
return action;
}
}
public static final ActionName REFRESH = extended("refresh");
/** /**
* Activate a given object and optionally a time * Activate a given object and optionally a time
* *
* <p> * <p>
* Forms: (focus:Object), (focus:Object, snap:LONG), (focus:Object, time:STR) * Forms: (focus:Object), (focus:Object, snap:LONG), (focus:Object, time:STR)
*/ */
public static final ActionName ACTIVATE = builtIn("activate"); public static final ActionName ACTIVATE =
create("activate", Show.BUILTIN, Enabler.ALWAYS, "Activate", null, "Activate");
/** /**
* A weaker form of activate. * A weaker form of activate.
* *
@ -83,9 +199,12 @@ public record ActionName(String name, boolean builtIn) {
* used to communicate selection (i.e., highlight) of the object. Whereas, double-clicking or * used to communicate selection (i.e., highlight) of the object. Whereas, double-clicking or
* pressing enter would more likely invoke 'activate.' * pressing enter would more likely invoke 'activate.'
*/ */
public static final ActionName FOCUS = builtIn("focus"); public static final ActionName FOCUS =
public static final ActionName TOGGLE = builtIn("toggle"); create("focus", Show.BUILTIN, Enabler.ALWAYS, "Focus", null, "Focus");
public static final ActionName DELETE = builtIn("delete"); public static final ActionName TOGGLE =
create("toggle", Show.BUILTIN, Enabler.ALWAYS, "Toggle", null, "Toggle");
public static final ActionName DELETE =
create("delete", Show.BUILTIN, Enabler.ALWAYS, "Delete", null, "Delete");
/** /**
* Execute a CLI command * Execute a CLI command
@ -93,7 +212,8 @@ public record ActionName(String name, boolean builtIn) {
* <p> * <p>
* Forms: (cmd:STRING):STRING; Optional arguments: capture:BOOL * Forms: (cmd:STRING):STRING; Optional arguments: capture:BOOL
*/ */
public static final ActionName EXECUTE = builtIn("execute"); public static final ActionName EXECUTE =
create("execute", Show.BUILTIN, Enabler.ALWAYS, "Execute", null, "Execute");
/** /**
* Connect the back-end to a (usually remote) target * Connect the back-end to a (usually remote) target
@ -101,23 +221,31 @@ public record ActionName(String name, boolean builtIn) {
* <p> * <p>
* Forms: (spec:STRING) * Forms: (spec:STRING)
*/ */
public static final ActionName CONNECT = extended("connect"); public static final ActionName CONNECT =
create("connect", Show.EXTENDED, Enabler.ALWAYS, "Connect", ICON_CONNECT, "Connect");
/** /**
* Forms: (target:Attachable), (pid:INT), (spec:STRING) * Forms: (target:Attachable), (pid:INT), (spec:STRING)
*/ */
public static final ActionName ATTACH = extended("attach"); public static final ActionName ATTACH =
public static final ActionName DETACH = extended("detach"); create("attach", Show.EXTENDED, Enabler.ALWAYS, "Attach", ICON_ATTACH, "Attach");
public static final ActionName DETACH =
create("detach", Show.EXTENDED, Enabler.ALWAYS, "Detach", ICON_DETACH, "Detach");
/** /**
* Forms: (command_line:STRING), (file:STRING,args:STRING), (file:STRING,args:STRING_ARRAY), * Forms: (command_line:STRING), (file:STRING,args:STRING), (file:STRING,args:STRING_ARRAY),
* (ANY*) * (ANY*)
*/ */
public static final ActionName LAUNCH = extended("launch"); public static final ActionName LAUNCH =
public static final ActionName KILL = builtIn("kill"); create("launch", Show.EXTENDED, Enabler.ALWAYS, "Launch", ICON_LAUNCH, "Launch");
public static final ActionName KILL =
create("kill", Show.BUILTIN, Enabler.NOT_DEAD, "Kill", ICON_KILL, "Kill");
public static final ActionName RESUME = builtIn("resume"); public static final ActionName RESUME =
public static final ActionName INTERRUPT = builtIn("interrupt"); create("resume", Show.BUILTIN, Enabler.NOT_RUNNING, "Resume", ICON_RESUME, "Resume");
public static final ActionName INTERRUPT =
create("interrupt", Show.BUILTIN, Enabler.NOT_STOPPED, "Interrupt", ICON_INTERRUPT,
"Interrupt");
/** /**
* All of these will show in the "step" portion of the control toolbar, if present. The * All of these will show in the "step" portion of the control toolbar, if present. The
@ -128,25 +256,31 @@ public record ActionName(String name, boolean builtIn) {
* context. (Multiple will appear, but may confuse the user.) You can have as many extended step * context. (Multiple will appear, but may confuse the user.) You can have as many extended step
* actions as you like. They will be ordered lexicographically by name. * actions as you like. They will be ordered lexicographically by name.
*/ */
public static final ActionName STEP_INTO = builtIn("step_into"); public static final ActionName STEP_INTO =
public static final ActionName STEP_OVER = builtIn("step_over"); create("step_into", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Into", ICON_STEP_INTO, "Step");
public static final ActionName STEP_OUT = builtIn("step_out"); public static final ActionName STEP_OVER =
create("step_over", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Over", ICON_STEP_OVER, "Step");
public static final ActionName STEP_OUT =
create("step_out", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Out", ICON_STEP_OUT, "Step");
/** /**
* Skip is not typically available, except in emulators. If the back-end debugger does not have * Skip is not typically available, except in emulators. If the back-end debugger does not have
* a command for this action out-of-the-box, we do not recommend trying to implement it * a command for this action out-of-the-box, we do not recommend trying to implement it
* yourself. The purpose of these actions just to expose/map each command to the UI, not to * yourself. The purpose of these actions just to expose/map each command to the UI, not to
* invent new features for the back-end debugger. * invent new features for the back-end debugger.
*/ */
public static final ActionName STEP_SKIP = builtIn("step_skip"); public static final ActionName STEP_SKIP =
create("step_skip", Show.BUILTIN, Enabler.NOT_RUNNING, "Skip Over", ICON_SKIP_OVER, "Skip");
/** /**
* Step back is not typically available, except in emulators and timeless (or time-travel) * Step back is not typically available, except in emulators and timeless (or time-travel)
* debuggers. * debuggers.
*/ */
public static final ActionName STEP_BACK = builtIn("step_back"); public static final ActionName STEP_BACK =
create("step_back", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Back", ICON_STEP_BACK, "Back");
/** /**
* The action for steps that don't fit one of the common stepping actions. * The action for steps that don't fit one of the common stepping actions.
*/ */
public static final ActionName STEP_EXT = extended("step_ext"); public static final ActionName STEP_EXT =
create("step_ext", Show.ADDRESS, Enabler.NOT_RUNNING, null, ICON_STEP_LAST, "Step");
/** /**
* Forms: (addr:ADDRESS), R/W(rng:RANGE), (expr:STRING) * Forms: (addr:ADDRESS), R/W(rng:RANGE), (expr:STRING)
@ -158,25 +292,39 @@ public record ActionName(String name, boolean builtIn) {
* The client may pass either null or "" for condition and/or commands to indicate omissions of * The client may pass either null or "" for condition and/or commands to indicate omissions of
* those arguments. * those arguments.
*/ */
public static final ActionName BREAK_SW_EXECUTE = builtIn("break_sw_execute"); public static final ActionName BREAK_SW_EXECUTE =
public static final ActionName BREAK_HW_EXECUTE = builtIn("break_hw_execute"); create("break_sw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Software Breakpoint",
public static final ActionName BREAK_READ = builtIn("break_read"); ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_WRITE = builtIn("break_write"); public static final ActionName BREAK_HW_EXECUTE =
public static final ActionName BREAK_ACCESS = builtIn("break_access"); create("break_hw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Hardware Breakpoint",
public static final ActionName BREAK_EXT = extended("break_ext"); ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_READ =
create("break_read", Show.BUILTIN, Enabler.ALWAYS, "Set Read Breakpoint",
ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_WRITE =
create("break_write", Show.BUILTIN, Enabler.ALWAYS, "Set Write Breakpoint",
ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_ACCESS =
create("break_access", Show.BUILTIN, Enabler.ALWAYS, "Set Access Breakpont",
ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_EXT =
create("break_ext", Show.BUILTIN, Enabler.ALWAYS, null, ICON_SET_BREAKPOINT, "Set");
/** /**
* Forms: (rng:RANGE) * Forms: (rng:RANGE)
*/ */
public static final ActionName READ_MEM = builtIn("read_mem"); public static final ActionName READ_MEM =
create("read_mem", Show.BUILTIN, Enabler.ALWAYS, "Read Memory", null, "Read");
/** /**
* Forms: (addr:ADDRESS,data:BYTES) * Forms: (addr:ADDRESS,data:BYTES)
*/ */
public static final ActionName WRITE_MEM = builtIn("write_mem"); public static final ActionName WRITE_MEM =
create("write_mem", Show.BUILTIN, Enabler.ALWAYS, "Write Memory", null, "Write");
// NOTE: no read_reg. Use refresh(RegContainer), refresh(RegGroup), refresh(Register) // NOTE: no read_reg. Use refresh(RegContainer), refresh(RegGroup), refresh(Register)
/** /**
* Forms: (frame:Frame,name:STRING,value:BYTES), (register:Register,value:BYTES) * Forms: (frame:Frame,name:STRING,value:BYTES), (register:Register,value:BYTES)
*/ */
public static final ActionName WRITE_REG = builtIn("write_reg"); public static final ActionName WRITE_REG =
create("write_reg", Show.BUILTIN, Enabler.NOT_RUNNING, "Write Register", null, "Write");
} }

View file

@ -18,16 +18,17 @@ package ghidra.debug.api.target;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.BooleanSupplier;
import java.util.function.Function; import javax.swing.Icon;
import docking.ActionContext; import docking.ActionContext;
import ghidra.debug.api.target.ActionName.Show;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
@ -58,28 +59,72 @@ public interface Target {
* just invoked implicitly. Often, the two suppliers are implemented using lambda functions, and * just invoked implicitly. Often, the two suppliers are implemented using lambda functions, and
* those functions will keep whatever some means of querying UI and/or target context in their * those functions will keep whatever some means of querying UI and/or target context in their
* closures. * closures.
*
* @param display the text to display on UI actions associated with this entry
* @param name the name of a common debugger command this action implements
* @param details text providing more details, usually displayed in a tool tip
* @param requiresPrompt true if invoking the action requires further user interaction
* @param specificity a relative score of specificity. These are only meaningful when compared
* among entries returned in the same collection.
* @param enabled a supplier to determine whether an associated action in the UI is enabled.
* @param action a function for invoking this action asynchronously
*/ */
record ActionEntry(String display, ActionName name, String details, boolean requiresPrompt, interface ActionEntry {
long specificity, BooleanSupplier enabled,
Function<Boolean, CompletableFuture<?>> action) { /**
* Get the text to display on UI actions associated with this entry
*
* @return the display
*/
String display();
/**
* Get the name of a common debugger command this action implements
*
* @return the name
*/
ActionName name();
/**
* Get the icon to display in menus and dialogs
*
* @return the icon
*/
Icon icon();
/**
* Get the text providing more details, usually displayed in a tool tip
*
* @return the details
*/
String details();
/**
* Check whether invoking the action requires further user interaction
*
* @return true if prompting is required
*/
boolean requiresPrompt();
/**
* Get a relative score of specificity.
*
* <p>
* These are only meaningful when compared among entries returned in the same collection.
*
* @return the specificity
*/
long specificity();
/**
* Invoke the action asynchronously, prompting if desired.
*
* <p>
* The implementation is not required to provide a timeout; however, downstream components
* may.
*
* @param prompt whether or not to prompt the user for arguments
* @return the future result, often {@link Void}
*/
CompletableFuture<?> invokeAsyncWithoutTimeout(boolean prompt);
/** /**
* Check if this action is currently enabled * Check if this action is currently enabled
* *
* @return true if enabled * @return true if enabled
*/ */
public boolean isEnabled() { boolean isEnabled();
return enabled.getAsBoolean();
}
/** /**
* Invoke the action asynchronously, prompting if desired * Invoke the action asynchronously, prompting if desired
@ -90,8 +135,9 @@ public interface Target {
* @param prompt whether or not to prompt the user for arguments * @param prompt whether or not to prompt the user for arguments
* @return the future result, often {@link Void} * @return the future result, often {@link Void}
*/ */
public CompletableFuture<?> invokeAsync(boolean prompt) { default CompletableFuture<?> invokeAsync(boolean prompt) {
return action.apply(prompt).orTimeout(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); return invokeAsyncWithoutTimeout(prompt).orTimeout(TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
} }
/** /**
@ -103,7 +149,7 @@ public interface Target {
* *
* @param prompt whether or not to prompt the user for arguments * @param prompt whether or not to prompt the user for arguments
*/ */
public void run(boolean prompt) { default void run(boolean prompt) {
get(prompt); get(prompt);
} }
@ -113,7 +159,7 @@ public interface Target {
* @param prompt whether or not to prompt the user for arguments * @param prompt whether or not to prompt the user for arguments
* @return the resulting value, if applicable * @return the resulting value, if applicable
*/ */
public Object get(boolean prompt) { default Object get(boolean prompt) {
if (Swing.isSwingThread()) { if (Swing.isSwingThread()) {
throw new AssertionError("Refusing to block the Swing thread. Use a Task."); throw new AssertionError("Refusing to block the Swing thread. Use a Task.");
} }
@ -130,8 +176,8 @@ public interface Target {
* *
* @return true if built in. * @return true if built in.
*/ */
public boolean builtIn() { default Show getShow() {
return name != null && name.builtIn(); return name() == null ? Show.EXTENDED : name().show();
} }
} }

View file

@ -20,6 +20,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.Icon;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.debug.api.target.ActionName; import ghidra.debug.api.target.ActionName;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
@ -62,6 +64,20 @@ public interface RemoteMethod {
*/ */
String display(); String display();
/**
* The icon to display in menu's and in the prompt dialog.
*
* @return the icon
*/
Icon icon();
/**
* Text to display in the OK button of any prompt dialog.
*
* @return the text
*/
String okText();
/** /**
* A description of the method. * A description of the method.
* *

View file

@ -49,7 +49,8 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryOperations; import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.*; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@ -1740,7 +1741,7 @@ public interface FlatDebuggerAPI {
* @return true if alive * @return true if alive
*/ */
default boolean isTargetAlive(Trace trace) { default boolean isTargetAlive(Trace trace) {
return getExecutionState(trace).isAlive(); return getExecutionState(trace) != TraceExecutionState.TERMINATED;
} }
/** /**
@ -1763,7 +1764,7 @@ public interface FlatDebuggerAPI {
* @return true if alive * @return true if alive
*/ */
default boolean isThreadAlive(TraceThread thread) { default boolean isThreadAlive(TraceThread thread) {
return getExecutionState(thread).isAlive(); return getExecutionState(thread) != TraceExecutionState.TERMINATED;
} }
/** /**
@ -1792,7 +1793,7 @@ public interface FlatDebuggerAPI {
* @throws TimeoutException if the timeout expires * @throws TimeoutException if the timeout expires
*/ */
default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException { default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException {
if (!getExecutionState(trace).isRunning()) { if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
return; return;
} }
var listener = new DomainObjectListener() { var listener = new DomainObjectListener() {
@ -1800,14 +1801,14 @@ public interface FlatDebuggerAPI {
@Override @Override
public void domainObjectChanged(DomainObjectChangedEvent ev) { public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (!getExecutionState(trace).isRunning()) { if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
future.complete(null); future.complete(null);
} }
} }
}; };
trace.addListener(listener); trace.addListener(listener);
try { try {
if (!getExecutionState(trace).isRunning()) { if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
return; return;
} }
listener.future.get(timeout, unit); listener.future.get(timeout, unit);

View file

@ -144,20 +144,10 @@ public class JdiConnector {
} }
public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) { public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) {
String action = name;
String display = name;
String description = name;
TraceMethod annot = m.getAnnotation(TraceMethod.class); TraceMethod annot = m.getAnnotation(TraceMethod.class);
if (annot == null) { if (annot == null) {
return; return;
} }
action = annot.action();
if (annot.display() != null) {
display = annot.display();
}
if (annot.description() != null) {
description = annot.description();
}
int pcount = m.getParameterCount(); int pcount = m.getParameterCount();
if (pcount < 1) { if (pcount < 1) {
return; return;
@ -167,8 +157,8 @@ public class JdiConnector {
* collection routines currently use the return type, so just use ANY for now. * collection routines currently use the return type, so just use ANY for now.
*/ */
TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY; TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY;
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display, RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, annot.action(),
description, schema, methods, m); annot.display(), annot.description(), annot.okText(), annot.icon(), schema, methods, m);
remoteMethodRegistry.putMethod(name, method); remoteMethodRegistry.putMethod(name, method);
} }

View file

@ -605,9 +605,11 @@ public class RmiClient {
private Method buildMethod(RmiRemoteMethod method) { private Method buildMethod(RmiRemoteMethod method) {
Method.Builder builder = Method.newBuilder() Method.Builder builder = Method.newBuilder()
.setName(method.getName()) .setName(method.getName())
.setDescription(method.getDescription())
.setAction(method.getAction()) .setAction(method.getAction())
.setDisplay(method.getDisplay()); .setDisplay(method.getDisplay())
.setDescription(method.getDescription())
.setOkText(method.getOkText())
.setIcon(method.getIcon());
int i = 0; int i = 0;
for (RmiRemoteMethodParameter p : method.getParameters()) { for (RmiRemoteMethodParameter p : method.getParameters()) {
MethodParameter param = buildParameter(p); MethodParameter param = buildParameter(p);

View file

@ -32,6 +32,10 @@ public class RmiMethodRegistry {
String display() default ""; String display() default "";
String description() default ""; String description() default "";
String okText() default "";
String icon() default "";
} }
Map<String, RmiRemoteMethod> map = new HashMap<>(); Map<String, RmiRemoteMethod> map = new HashMap<>();

View file

@ -25,22 +25,27 @@ import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
public class RmiRemoteMethod { public class RmiRemoteMethod {
private final SchemaContext schemaContext; private final SchemaContext schemaContext;
private String name; private final String name;
private String action; private final String action;
private String display; private final String display;
private String description; private final String description;
private RmiRemoteMethodParameter[] params; private final String okText;
private TraceObjectSchema schema; private final String icon;
private RmiMethods instance; private final RmiRemoteMethodParameter[] params;
private Method m; private final TraceObjectSchema schema;
private final RmiMethods instance;
private final Method m;
public RmiRemoteMethod(SchemaContext schemaContext, String name, String action, String display, public RmiRemoteMethod(SchemaContext schemaContext, String name, String action, String display,
String description, TraceObjectSchema schema, RmiMethods instance, Method m) { String description, String okText, String icon, TraceObjectSchema schema,
RmiMethods instance, Method m) {
this.schemaContext = schemaContext; this.schemaContext = schemaContext;
this.name = name; this.name = name;
this.action = action; this.action = action;
this.display = display; this.display = display;
this.description = description; this.description = description;
this.okText = okText;
this.icon = icon;
this.params = new RmiRemoteMethodParameter[m.getParameterCount()]; this.params = new RmiRemoteMethodParameter[m.getParameterCount()];
this.schema = schema; this.schema = schema;
this.instance = instance; this.instance = instance;
@ -69,6 +74,14 @@ public class RmiRemoteMethod {
return description; return description;
} }
public String getOkText() {
return okText;
}
public String getIcon() {
return icon;
}
public String getAction() { public String getAction() {
return action; return action;
} }

View file

@ -17,14 +17,33 @@ package ghidra.app.plugin.core.debug.service.tracermi;
import java.util.Map; import java.util.Map;
import javax.swing.Icon;
import ghidra.debug.api.target.ActionName; import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.tracermi.*; import ghidra.debug.api.tracermi.RemoteMethod;
import ghidra.debug.api.tracermi.RemoteParameter;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
public record RecordRemoteMethod(TraceRmiHandler handler, String name, ActionName action, public record RecordRemoteMethod(TraceRmiHandler handler, String name, ActionName action,
String display, String description, Map<String, RemoteParameter> parameters, String display, Icon icon, String okText, String description,
SchemaName retType) implements RemoteMethod { Map<String, RemoteParameter> parameters, SchemaName retType) implements RemoteMethod {
public RecordRemoteMethod {
if (display == null || display.isBlank()) {
display = action.display();
}
if (display == null || display.isBlank()) { // still
display = name;
}
if (icon == null) {
icon = action.icon();
}
if (okText.isEmpty() || okText.isBlank()) {
okText = action.okText();
}
}
@Override @Override
public DefaultRemoteAsyncResult invokeAsync(Map<String, Object> arguments) { public DefaultRemoteAsyncResult invokeAsync(Map<String, Object> arguments) {
Trace trace = validate(arguments); Trace trace = validate(arguments);

View file

@ -31,6 +31,7 @@ import org.apache.commons.lang3.ArrayUtils;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import db.Transaction; import db.Transaction;
import generic.theme.GIcon;
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin; import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand; import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.services.DebuggerControlService; import ghidra.app.services.DebuggerControlService;
@ -1027,7 +1028,9 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
} }
for (Method m : req.getMethodsList()) { for (Method m : req.getMethodsList()) {
RemoteMethod rm = new RecordRemoteMethod(this, m.getName(), RemoteMethod rm = new RecordRemoteMethod(this, m.getName(),
ActionName.name(m.getAction()), m.getDisplay(), m.getDescription(), ActionName.name(m.getAction()), m.getDisplay(),
m.getIcon().isBlank() ? null : new GIcon(m.getIcon()), m.getOkText(),
m.getDescription(),
m.getParametersList() m.getParametersList()
.stream() .stream()
.collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)), .collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)),

View file

@ -19,9 +19,11 @@ import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.*; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Icon;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import docking.ActionContext; import docking.ActionContext;
@ -60,6 +62,70 @@ import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class TraceRmiTarget extends AbstractTarget { public class TraceRmiTarget extends AbstractTarget {
class TraceRmiActionEntry implements ActionEntry {
private final RemoteMethod method;
private final Map<String, Object> args;
private final boolean requiresPrompt;
private final long specificity;
private final ParamAndObjectArg first;
public TraceRmiActionEntry(RemoteMethod method, Map<String, Object> args) {
this.method = method;
this.args = args;
this.requiresPrompt = args.values().contains(Missing.MISSING);
this.specificity = computeSpecificity(args);
this.first = getFirstObjectArgument(method, args);
}
@Override
public String display() {
return method.display();
}
@Override
public ActionName name() {
return method.action();
}
@Override
public Icon icon() {
return method.icon();
}
@Override
public String details() {
return method.description();
}
@Override
public boolean requiresPrompt() {
return requiresPrompt;
}
@Override
public long specificity() {
return specificity;
}
@Override
public CompletableFuture<?> invokeAsyncWithoutTimeout(boolean prompt) {
return invokeMethod(prompt, method, args);
}
@Override
public boolean isEnabled() {
if (first == null) {
return true;
}
if (first.obj == null) {
return false;
}
return name().enabler().isEnabled(first.obj, getSnap());
}
}
private final TraceRmiConnection connection; private final TraceRmiConnection connection;
private final Trace trace; private final Trace trace;
@ -224,8 +290,12 @@ public class TraceRmiTarget extends AbstractTarget {
return null; return null;
} }
/**
* A singleton to indicate missing arguments
*/
public enum Missing { public enum Missing {
MISSING; // The argument requires a prompt /** The argument requires a prompt */
MISSING;
} }
protected Object findArgument(ActionName action, RemoteParameter parameter, protected Object findArgument(ActionName action, RemoteParameter parameter,
@ -266,28 +336,7 @@ public class TraceRmiTarget extends AbstractTarget {
return args; return args;
} }
private TraceExecutionState getStateOf(TraceObject object) { protected static long computeSpecificity(Map<String, Object> args) {
try {
return object.getExecutionState(getSnap());
}
catch (NoSuchElementException e) {
return TraceExecutionState.TERMINATED;
}
}
private boolean whenState(TraceObject object,
Predicate<TraceExecutionState> predicate) {
try {
TraceExecutionState state = getStateOf(object);
return state == null || predicate.test(state);
}
catch (Exception e) {
Msg.error(this, "Could not get state: " + e);
return false;
}
}
protected long computeSpecificity(Map<String, Object> args) {
long score = 0; long score = 0;
for (Object o : args.values()) { for (Object o : args.values()) {
if (o instanceof TraceObject obj) { if (o instanceof TraceObject obj) {
@ -297,46 +346,34 @@ public class TraceRmiTarget extends AbstractTarget {
return score; return score;
} }
protected BooleanSupplier chooseEnabler(RemoteMethod method, Map<String, Object> args) { protected RemoteParameter getFirstObjectParameter(RemoteMethod method) {
ActionName name = method.action();
SchemaContext ctx = getSchemaContext(); SchemaContext ctx = getSchemaContext();
if (ctx == null) { if (ctx == null) {
return () -> true; return null;
} }
RemoteParameter firstParam = method.parameters() return method.parameters()
.values() .values()
.stream() .stream()
.filter( .filter(
p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType())) p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType()))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
}
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {}
protected ParamAndObjectArg getFirstObjectArgument(RemoteMethod method,
Map<String, Object> args) {
RemoteParameter firstParam = getFirstObjectParameter(method);
if (firstParam == null) { if (firstParam == null) {
return () -> true; return null;
} }
Object firstArg = args.get(firstParam.name()); Object firstArg = args.get(firstParam.name());
if (firstArg == null || firstArg == Missing.MISSING) { if (firstArg == null || !(firstArg instanceof TraceObject obj)) {
Msg.trace(this, "MISSING first argument for " + method + "(" + firstParam + ")"); Msg.trace(this, "MISSING first argument for " + method + "(" + firstParam + ")");
return () -> false; return new ParamAndObjectArg(firstParam, null);
} }
TraceObject obj = (TraceObject) firstArg; return new ParamAndObjectArg(firstParam, obj);
if (ActionName.RESUME.equals(name) ||
ActionName.STEP_BACK.equals(name) ||
ActionName.STEP_EXT.equals(name) ||
ActionName.STEP_INTO.equals(name) ||
ActionName.STEP_OUT.equals(name) ||
ActionName.STEP_OVER.equals(name) ||
ActionName.STEP_SKIP.equals(name)) {
return () -> whenState(obj,
state -> state != null && (state.isStopped() || state.isUnknown()));
}
else if (ActionName.INTERRUPT.equals(name)) {
return () -> whenState(obj,
state -> state == null || state.isRunning() || state.isUnknown());
}
else if (ActionName.KILL.equals(name)) {
return () -> whenState(obj, state -> state == null || !state.isTerminated());
}
return () -> true;
} }
private Map<String, Object> promptArgs(RemoteMethod method, Map<String, Object> defaults) { private Map<String, Object> promptArgs(RemoteMethod method, Map<String, Object> defaults) {
@ -346,7 +383,7 @@ public class TraceRmiTarget extends AbstractTarget {
*/ */
Map<String, ValStr<?>> defs = ValStr.fromPlainMap(defaults); Map<String, ValStr<?>> defs = ValStr.fromPlainMap(defaults);
RemoteMethodInvocationDialog dialog = new RemoteMethodInvocationDialog(tool, RemoteMethodInvocationDialog dialog = new RemoteMethodInvocationDialog(tool,
getSchemaContext(), method.display(), method.display(), null); getSchemaContext(), method.display(), method.okText(), method.icon());
Map<String, ValStr<?>> args = dialog.promptArguments(method.parameters(), defs, defs); Map<String, ValStr<?>> args = dialog.promptArguments(method.parameters(), defs, defs);
return args == null ? null : ValStr.toPlainMap(args); return args == null ? null : ValStr.toPlainMap(args);
} }
@ -374,10 +411,7 @@ public class TraceRmiTarget extends AbstractTarget {
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) { boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
Map<String, Object> args = collectArguments(method, context, allowContextObject, Map<String, Object> args = collectArguments(method, context, allowContextObject,
allowCoordsObject, allowSuitableObject); allowCoordsObject, allowSuitableObject);
boolean requiresPrompt = args.values().contains(Missing.MISSING); return new TraceRmiActionEntry(method, args);
return new ActionEntry(method.display(), method.action(), method.description(),
requiresPrompt, computeSpecificity(args), chooseEnabler(method, args),
prompt -> invokeMethod(prompt, method, args));
} }
protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods, protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods,
@ -432,6 +466,17 @@ public class TraceRmiTarget extends AbstractTarget {
true); true);
} }
@Override
public Map<String, ActionEntry> collectActions(ActionName name, ActionContext context) {
if (name == null) {
if (context instanceof ProgramLocationActionContext ctx) {
return collectAddressActions(ctx);
}
return collectAllActions(context);
}
return collectByName(name, context);
}
@Override @Override
protected Map<String, ActionEntry> collectResumeActions(ActionContext context) { protected Map<String, ActionEntry> collectResumeActions(ActionContext context) {
return collectByName(ActionName.RESUME, context); return collectByName(ActionName.RESUME, context);

View file

@ -423,6 +423,8 @@ message Method {
// I'd like to make them all void, but I think executing a command and capturing its output // I'd like to make them all void, but I think executing a command and capturing its output
// justifies being able to return a result. It should be used very sparingly. // justifies being able to return a result. It should be used very sparingly.
ValueType return_type = 6; ValueType return_type = 6;
string ok_text = 7;
string icon = 8;
} }
message RequestNegotiate { message RequestNegotiate {

View file

@ -27,6 +27,7 @@ from . import sch
from . import trace_rmi_pb2 as bufs from . import trace_rmi_pb2 as bufs
from .util import send_delimited, recv_delimited from .util import send_delimited, recv_delimited
# This need not be incremented every Ghidra release. When a breaking protocol # This need not be incremented every Ghidra release. When a breaking protocol
# change is made, this should be updated to match the first Ghidra release that # change is made, this should be updated to match the first Ghidra release that
# includes the change. # includes the change.
@ -98,7 +99,8 @@ class Receiver(Thread):
while not self._is_shutdown: while not self._is_shutdown:
#print("Receiving message") #print("Receiving message")
try: try:
reply = recv_delimited(self.client.s, bufs.RootMessage(), dbg_seq) reply = recv_delimited(
self.client.s, bufs.RootMessage(), dbg_seq)
except BaseException as e: except BaseException as e:
self._is_shutdown = True self._is_shutdown = True
return return
@ -448,6 +450,8 @@ class RemoteMethod:
name: str name: str
action: str action: str
display: str display: str
icon: str
ok_text: str
description: str description: str
parameters: List[RemoteParameter] parameters: List[RemoteParameter]
return_schema: sch.Schema return_schema: sch.Schema
@ -508,30 +512,28 @@ class MethodRegistry(object):
@classmethod @classmethod
def create_method(cls, function, name=None, action=None, display=None, def create_method(cls, function, name=None, action=None, display=None,
description=None) -> RemoteMethod: icon=None, ok_text=None, description=None) -> RemoteMethod:
if name is None: if name is None:
name = function.__name__ name = function.__name__
if action is None: if action is None:
action = name action = name
if display is None:
display = name
if description is None: if description is None:
description = function.__doc__ or '' description = function.__doc__
sig = inspect.signature(function) sig = inspect.signature(function)
params = [] params = []
for p in sig.parameters.values(): for p in sig.parameters.values():
params.append(cls._make_param(p)) params.append(cls._make_param(p))
return_schema = cls._to_schema(sig, sig.return_annotation) return_schema = cls._to_schema(sig, sig.return_annotation)
return RemoteMethod(name, action, display, description, params, return RemoteMethod(name, action, display, icon, ok_text, description,
return_schema, function) params, return_schema, function)
def method(self, func=None, *, name=None, action=None, display=None, def method(self, func=None, *, name=None, action=None, display=None,
description='', condition=True): icon=None, ok_text=None, description=None, condition=True):
def _method(func): def _method(func):
if condition: if condition:
method = self.create_method(func, name, action, display, method = self.create_method(func, name, action, display,
description) icon, ok_text, description)
self.register_method(method) self.register_method(method)
return func return func
@ -705,8 +707,10 @@ class Client(object):
def _write_method(to: bufs.Method, method: RemoteMethod): def _write_method(to: bufs.Method, method: RemoteMethod):
to.name = method.name to.name = method.name
to.action = method.action to.action = method.action
to.display = method.display to.display = method.display or ''
to.description = method.description to.icon = method.icon or ''
to.ok_text = method.ok_text or ''
to.description = method.description or ''
Client._write_parameters(to.parameters, method.parameters) Client._write_parameters(to.parameters, method.parameters)
to.return_type.name = method.return_schema.name to.return_type.name = method.return_schema.name

View file

@ -24,6 +24,8 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.swing.Icon;
import db.Transaction; import db.Transaction;
import ghidra.app.services.DebuggerTargetService; import ghidra.app.services.DebuggerTargetService;
import ghidra.async.*; import ghidra.async.*;
@ -56,6 +58,11 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
String description, Map<String, RemoteParameter> parameters, SchemaName retType, String description, Map<String, RemoteParameter> parameters, SchemaName retType,
AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue) AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue)
implements RemoteMethod { implements RemoteMethod {
public TestRemoteMethod {
Objects.requireNonNull(action);
}
public TestRemoteMethod(String name, ActionName action, String display, String description, public TestRemoteMethod(String name, ActionName action, String display, String description,
Map<String, RemoteParameter> parameters, SchemaName retType) { Map<String, RemoteParameter> parameters, SchemaName retType) {
this(name, action, display, description, parameters, retType, new AsyncPairingQueue<>(), this(name, action, display, description, parameters, retType, new AsyncPairingQueue<>(),
@ -106,6 +113,16 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
result.thenApply(ar -> ar.ret).handle(AsyncUtils.copyTo(retQueue().give())); result.thenApply(ar -> ar.ret).handle(AsyncUtils.copyTo(retQueue().give()));
return result.thenApply(ar -> ar.args); return result.thenApply(ar -> ar.args);
} }
@Override
public String okText() {
return "OK";
}
@Override
public Icon icon() {
return null;
}
} }
public record TestRemoteParameter(String name, SchemaName type, boolean required, public record TestRemoteParameter(String name, SchemaName type, boolean required,

View file

@ -163,6 +163,13 @@
"help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint "help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint
Marker Actions</A> in the listings.</P> Marker Actions</A> in the listings.</P>
<H3><A name="set_breakpoint"></A><IMG alt="" src="icon.debugger.add">Set Breakpoint</H3>
<P>This is a dropdown of actions provided by the back-end debugger, usually for setting
breakpoints by symbol, expression, etc. Setting breakpoints by address is typically done from
the Listings. If no such actions are available, or there is no live target, this action is
disabled.</P>
<H3><A name="enable_breakpoints"></A><IMG alt="" src="icon.debugger.breakpoint.enable"> <H3><A name="enable_breakpoints"></A><IMG alt="" src="icon.debugger.breakpoint.enable">
Enable</H3> Enable</H3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

@ -0,0 +1,39 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
import ghidra.debug.api.target.Target.ActionEntry;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
public class InvokeActionEntryAction extends DockingAction {
protected final PluginTool tool;
protected final ActionEntry entry;
public InvokeActionEntryAction(Plugin plugin, ActionEntry entry) {
super(entry.display(), plugin.getName());
this.tool = plugin.getTool();
this.entry = entry;
}
@Override
public void actionPerformed(ActionContext context) {
TargetActionTask.runAction(tool, entry.display(), entry);
}
}

View file

@ -29,19 +29,23 @@ import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import docking.action.*; import docking.action.*;
import docking.action.builder.ActionBuilder; import docking.action.builder.ActionBuilder;
import docking.menu.MultiActionDockingAction;
import docking.widgets.table.*; import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener; import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
import ghidra.debug.api.breakpoint.LogicalBreakpoint; import ghidra.debug.api.breakpoint.LogicalBreakpoint;
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State; import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener; import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener;
import ghidra.debug.api.control.ControlMode; import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target; import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent; import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
@ -263,6 +267,78 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
return contextHasMatchingBreakpoints(context, lb -> true, loc -> true); return contextHasMatchingBreakpoints(context, lb -> true, loc -> true);
} }
protected class GenericSetBreakpointAction extends InvokeActionEntryAction {
public GenericSetBreakpointAction(ActionEntry entry) {
super(plugin, entry);
setMenuBarData(new MenuData(new String[] { getName() }, entry.icon()));
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
}
}
protected class StubSetBreakpointAction extends DockingAction {
public StubSetBreakpointAction() {
super("(Use the Listings to Set Breakpoints)", plugin.getName());
setMenuBarData(new MenuData(new String[] { getName() }));
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
}
}
protected class SetBreakpointAction extends MultiActionDockingAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
private final List<DockingActionIf> stub = List.of(new StubSetBreakpointAction());
public SetBreakpointAction() {
super("Set Breakpoint", plugin.getName());
// TODO: Different icon?
setToolBarData(new ToolBarData(DebuggerResources.ICON_ADD, GROUP));
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
addLocalAction(this);
}
@Override
public List<DockingActionIf> getActionList(ActionContext context) {
if (traceManager == null) {
return stub;
}
Trace trace = traceManager.getCurrentTrace();
if (trace == null) {
return stub;
}
// TODO: Set-by-address (like the listing one) always present?
if (controlService == null) {
return stub;
}
ControlMode mode = controlService.getCurrentMode(trace);
if (!mode.isTarget()) {
return stub;
// TODO: Consider a Sleigh expression for emulation?
// Actually, any "Address" field could be a Sleigh expression....
}
Target target = traceManager.getCurrent().getTarget();
if (target == null) {
return stub;
}
List<DockingActionIf> result = new ArrayList<>();
for (ActionEntry entry : target.collectActions(ActionName.BREAK_EXT, context)
.values()) {
result.add(new GenericSetBreakpointAction(entry));
}
if (result.isEmpty()) {
return stub;
}
Collections.sort(result, Comparator.comparing(a -> a.getName()));
return result;
}
}
protected class EnableSelectedBreakpointsAction protected class EnableSelectedBreakpointsAction
extends AbstractEnableSelectedBreakpointsAction { extends AbstractEnableSelectedBreakpointsAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS; public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
@ -708,6 +784,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
new DebuggerMakeBreakpointsEffectiveActionContext(); new DebuggerMakeBreakpointsEffectiveActionContext();
// package access for testing // package access for testing
SetBreakpointAction actionSetBreakpoint;
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints; EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
EnableAllBreakpointsAction actionEnableAllBreakpoints; EnableAllBreakpointsAction actionEnableAllBreakpoints;
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints; DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
@ -1163,6 +1240,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
} }
protected void createActions() { protected void createActions() {
actionSetBreakpoint = new SetBreakpointAction();
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction(); actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
actionEnableAllBreakpoints = new EnableAllBreakpointsAction(); actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction(); actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();

View file

@ -288,6 +288,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
} }
actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this) actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this)
.description(entry.details()) .description(entry.details())
.toolBarIcon(entry.icon())
.enabledWhen(ctx -> entry.isEnabled()) .enabledWhen(ctx -> entry.isEnabled())
.onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry)) .onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry))
.build()); .build());

View file

@ -18,21 +18,19 @@ package ghidra.app.plugin.core.debug.gui.control;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.Icon;
import docking.ActionContext; import docking.ActionContext;
import docking.Tool; import docking.Tool;
import docking.action.*; import docking.action.DockingActionIf;
import docking.action.MenuData;
import docking.actions.PopupActionProvider; import docking.actions.PopupActionProvider;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.debug.api.control.ControlMode; import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.model.DebuggerObjectActionContext; import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target; import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry; import ghidra.debug.api.target.Target.ActionEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
@ -57,23 +55,10 @@ import ghidra.trace.model.program.TraceProgramView;
public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider { public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider {
public static final String GROUP_METHODS = "Debugger Methods"; public static final String GROUP_METHODS = "Debugger Methods";
class InvokeActionEntryAction extends DockingAction { class MethodAction extends InvokeActionEntryAction {
private final ActionEntry entry; public MethodAction(ActionEntry entry) {
super(DebuggerMethodActionsPlugin.this, entry);
public InvokeActionEntryAction(ActionEntry entry) { setPopupMenuData(new MenuData(new String[] { getName() }, entry.icon(), GROUP_METHODS));
super(entry.display(), DebuggerMethodActionsPlugin.this.getName());
this.entry = entry;
Icon icon = null;
if (ActionName.REFRESH.equals(entry.name())) {
// TODO: Allow method annotation to specify icon?
icon = DebuggerResources.ICON_REFRESH;
}
setPopupMenuData(new MenuData(new String[] { getName() }, icon, GROUP_METHODS));
}
@Override
public void actionPerformed(ActionContext context) {
TargetActionTask.runAction(tool, entry.display(), entry);
} }
} }
@ -136,10 +121,10 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
List<DockingActionIf> result = new ArrayList<>(); List<DockingActionIf> result = new ArrayList<>();
for (ActionEntry entry : target.collectActions(null, context).values()) { for (ActionEntry entry : target.collectActions(null, context).values()) {
//if (entry.requiresPrompt() || entry.builtIn()) { //if (entry.requiresPrompt() || entry.builtIn()) {
if (!entry.isEnabled() || entry.builtIn()) { if (!entry.isEnabled() || !entry.getShow().isShowing(context)) {
continue; continue;
} }
result.add(new InvokeActionEntryAction(entry)); result.add(new MethodAction(entry));
} }
return result; return result;
} }

View file

@ -65,9 +65,11 @@ class TargetDockingAction extends DockingAction {
protected void updateFromContext(ActionContext context) { protected void updateFromContext(ActionContext context) {
entry = findEntry(context); entry = findEntry(context);
if (entry == null) { if (entry == null) {
getToolBarData().setIcon(action.icon());
setDescription(defaultDescription); setDescription(defaultDescription);
} }
else { else {
getToolBarData().setIcon(entry.icon());
setDescription(entry.details()); setDescription(entry.details());
} }
} }

View file

@ -26,7 +26,7 @@ public enum TraceExecutionState {
* This may apply, e.g., to a GDB "Inferior," which has no yet been used to launch or attach to * This may apply, e.g., to a GDB "Inferior," which has no yet been used to launch or attach to
* a process. * a process.
*/ */
INACTIVE(false, false, false, false), INACTIVE,
/** /**
* The object is alive, but its execution state is unspecified * The object is alive, but its execution state is unspecified
@ -38,12 +38,12 @@ public enum TraceExecutionState {
* <em>all</em> of its threads are stopped. For the clients' sakes, all models should implement * <em>all</em> of its threads are stopped. For the clients' sakes, all models should implement
* these conventions internally. * these conventions internally.
*/ */
ALIVE(true, false, false, false), ALIVE,
/** /**
* The object is alive, but not executing * The object is alive, but not executing
*/ */
STOPPED(true, false, true, false), STOPPED,
/** /**
* The object is alive and executing * The object is alive and executing
@ -53,7 +53,7 @@ public enum TraceExecutionState {
* thread is currently executing, waiting on an event, or scheduled for execution. It does not * thread is currently executing, waiting on an event, or scheduled for execution. It does not
* necessarily mean it is executing on a CPU at this exact moment. * necessarily mean it is executing on a CPU at this exact moment.
*/ */
RUNNING(true, true, false, false), RUNNING,
/** /**
* The object is no longer alive * The object is no longer alive
@ -63,63 +63,5 @@ public enum TraceExecutionState {
* stale handles to objects which may still be queried (e.g., for a process exit code), or e.g., * stale handles to objects which may still be queried (e.g., for a process exit code), or e.g.,
* a GDB "Inferior," which could be re-used to launch or attach to another process. * a GDB "Inferior," which could be re-used to launch or attach to another process.
*/ */
TERMINATED(false, false, false, true); TERMINATED;
private final boolean alive;
private final boolean running;
private final boolean stopped;
private final boolean terminated;
private TraceExecutionState(boolean alive, boolean running, boolean stopped,
boolean terminated) {
this.alive = alive;
this.running = running;
this.stopped = stopped;
this.terminated = terminated;
}
/**
* Check if this state implies the object is alive
*
* @return true if alive
*/
public boolean isAlive() {
return alive;
}
/**
* Check if this state implies the object is running
*
* @return true if running
*/
public boolean isRunning() {
return running;
}
/**
* Check if this state implies the object is stopped
*
* @return true if stopped
*/
public boolean isStopped() {
return stopped;
}
/**
* Check if this state implies the object was terminated
*
* @return true if terminated
*/
public boolean isTerminated() {
return terminated;
}
/**
* Check if this state is ambiguous
*
* @return true if terminated
*/
public boolean isUnknown() {
return !stopped && !running && !terminated;
}
} }

View file

@ -31,6 +31,7 @@ import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.*; import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.*;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.target.ActionName;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.Lifespan;
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema; import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema;
@ -57,7 +58,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
protected void addMethods() { protected void addMethods() {
TestRemoteMethodRegistry reg = rmiCx.getMethods(); TestRemoteMethodRegistry reg = rmiCx.getMethods();
rmiMethodAdvance = new TestRemoteMethod("advance", null, "Advance", rmiMethodAdvance = new TestRemoteMethod("advance", ActionName.STEP_EXT, "Advance",
"Advance to the given address", PrimitiveTraceObjectSchema.VOID, "Advance to the given address", PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread", new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"), "The thread to advance"),
@ -65,14 +66,15 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
"Target", "The target address")); "Target", "The target address"));
reg.add(rmiMethodAdvance); reg.add(rmiMethodAdvance);
rmiMethodStepExt = new TestRemoteMethod("step_ext", null, "StepExt", rmiMethodStepExt = new TestRemoteMethod("step_ext", ActionName.STEP_EXT, "StepExt",
"Step in some special way", PrimitiveTraceObjectSchema.VOID, "Step in some special way", PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread", new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to step")); "The thread to step"));
reg.add(rmiMethodStepExt); reg.add(rmiMethodStepExt);
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", null, "Advance With Flag", rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", ActionName.STEP_EXT,
"Advance to the given address, with flag", PrimitiveTraceObjectSchema.VOID, "Advance With Flag", "Advance to the given address, with flag",
PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread", new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"), "The thread to advance"),
new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null, new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null,
@ -81,7 +83,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
"Flag", "The flag")); "Flag", "The flag"));
reg.add(rmiMethodAdvanceWithFlag); reg.add(rmiMethodAdvanceWithFlag);
rmiMethodBetween = new TestRemoteMethod("between", null, "Between", rmiMethodBetween = new TestRemoteMethod("between", ActionName.STEP_EXT, "Between",
"Advance between two given addresses", PrimitiveTraceObjectSchema.VOID, "Advance between two given addresses", PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread", new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"), "The thread to advance"),

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>
@ -160,22 +161,26 @@ Memory/Hex, and the Decompiler, right-click and select <img
src="images/breakpoint-enable.png" alt="set breakpoint" /> Set src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
double-click the margin.</li> double-click the margin.</li>
<li>From the Breakpoints window, use the <strong>Set Breakpoint</strong>
dropdown to to access the various breakpoint actions defined by
GDB.</li>
<li>From the Terminal window, use the GDB command, e.g., <li>From the Terminal window, use the GDB command, e.g.,
<code>break main</code>.</li> <code>break main</code>.</li>
</ol> </ol>
<p>The advantage of using the listings is that you can quickly set a <p>The advantage of using the listings is that you can quickly set a
breakpoint at any address. The advantage of using the Terminal window is breakpoint at any address. The advantage of using the dropdown action or
that you can specify something other than an address. Often, those Terminal window is that you can specify something other than an address.
specifications still resolve to addresses, and Ghidra will display them. Often, those specifications still resolve to addresses, and Ghidra will
Ghidra will memorize breakpoints by recording them as special bookmarks display them. Ghidra will memorize breakpoints by recording them as
in the program database. There is some iconography to communicate the special bookmarks in the program database. There is some iconography to
various states of a breakpoint. When all is well and normal, you should communicate the various states of a breakpoint. When all is well and
only see enabled <img src="images/breakpoint-enable.png" normal, you should only see enabled <img
alt="enabled breakpoint" /> and disabled <img src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
src="images/breakpoint-disable.png" alt="disabled breakpoint" /> disabled <img src="images/breakpoint-disable.png"
breakpoints. If the target is terminated (or not launched yet), you may alt="disabled breakpoint" /> breakpoints. If the target is terminated
also see ineffective <img src="images/breakpoint-enable-ineff.png" (or not launched yet), you may also see ineffective <img
alt="ineffective breakpoint" /> breakpoints.</p> src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
breakpoints.</p>
</section> </section>
<section id="examining-minesweeper-board-setup" class="level2"> <section id="examining-minesweeper-board-setup" class="level2">
<h2>Examining Minesweeper Board Setup</h2> <h2>Examining Minesweeper Board Setup</h2>

View file

@ -17,10 +17,11 @@ From here, you can toggle and delete existing breakpoints.
There are several ways to set a new breakpoint: There are several ways to set a new breakpoint:
1. From any static or dynamic listing window, including Disassembly, Memory/Hex, and the Decompiler, right-click and select ![set breakpoint](images/breakpoint-enable.png) Set Breakpoint, press **`K`** on the keyboard, or double-click the margin. 1. From any static or dynamic listing window, including Disassembly, Memory/Hex, and the Decompiler, right-click and select ![set breakpoint](images/breakpoint-enable.png) Set Breakpoint, press **`K`** on the keyboard, or double-click the margin.
1. From the Breakpoints window, use the **Set Breakpoint** dropdown to to access the various breakpoint actions defined by GDB.
1. From the Terminal window, use the GDB command, e.g., `break main`. 1. From the Terminal window, use the GDB command, e.g., `break main`.
The advantage of using the listings is that you can quickly set a breakpoint at any address. The advantage of using the listings is that you can quickly set a breakpoint at any address.
The advantage of using the Terminal window is that you can specify something other than an address. The advantage of using the dropdown action or Terminal window is that you can specify something other than an address.
Often, those specifications still resolve to addresses, and Ghidra will display them. Often, those specifications still resolve to addresses, and Ghidra will display them.
Ghidra will memorize breakpoints by recording them as special bookmarks in the program database. Ghidra will memorize breakpoints by recording them as special bookmarks in the program database.
There is some iconography to communicate the various states of a breakpoint. There is some iconography to communicate the various states of a breakpoint.

View file

@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>
@ -379,7 +380,7 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
<span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;The current program must be termmines&quot;</span><span class="op">);</span></span> <span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;The current program must be termmines&quot;</span><span class="op">);</span></span>
<span id="cb7-8"><a href="#cb7-8"></a><span class="op">}</span></span> <span id="cb7-8"><a href="#cb7-8"></a><span class="op">}</span></span>
<span id="cb7-9"><a href="#cb7-9"></a></span> <span id="cb7-9"><a href="#cb7-9"></a></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="cf">if</span> <span class="op">(</span><span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">).</span><span class="fu">isRunning</span><span class="op">())</span> <span class="op">{</span></span> <span id="cb7-10"><a href="#cb7-10"></a><span class="cf">if</span> <span class="op">(</span><span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">)</span> <span class="op">!=</span> TraceExecutionState<span class="op">.</span><span class="fu">STOPPED</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-11"><a href="#cb7-11"></a> monitor<span class="op">.</span><span class="fu">setMessage</span><span class="op">(</span><span class="st">&quot;Interrupting target and waiting for STOPPED&quot;</span><span class="op">);</span></span> <span id="cb7-11"><a href="#cb7-11"></a> monitor<span class="op">.</span><span class="fu">setMessage</span><span class="op">(</span><span class="st">&quot;Interrupting target and waiting for STOPPED&quot;</span><span class="op">);</span></span>
<span id="cb7-12"><a href="#cb7-12"></a> <span class="fu">interrupt</span><span class="op">();</span></span> <span id="cb7-12"><a href="#cb7-12"></a> <span class="fu">interrupt</span><span class="op">();</span></span>
<span id="cb7-13"><a href="#cb7-13"></a> <span class="fu">waitForBreak</span><span class="op">(</span><span class="dv">3</span><span class="op">,</span> <span class="bu">TimeUnit</span><span class="op">.</span><span class="fu">SECONDS</span><span class="op">);</span></span> <span id="cb7-13"><a href="#cb7-13"></a> <span class="fu">waitForBreak</span><span class="op">(</span><span class="dv">3</span><span class="op">,</span> <span class="bu">TimeUnit</span><span class="op">.</span><span class="fu">SECONDS</span><span class="op">);</span></span>

View file

@ -210,7 +210,7 @@ if (!"termmines".equals(currentProgram.getName())) {
throw new AssertionError("The current program must be termmines"); throw new AssertionError("The current program must be termmines");
} }
if (getExecutionState(trace).isRunning()) { if (getExecutionState(trace) != TraceExecutionState.STOPPED) {
monitor.setMessage("Interrupting target and waiting for STOPPED"); monitor.setMessage("Interrupting target and waiting for STOPPED");
interrupt(); interrupt();
waitForBreak(3, TimeUnit.SECONDS); waitForBreak(3, TimeUnit.SECONDS);

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a> class="advanced" href="B4-Modeling.html">Modeling</a><a
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
</header> </header>
<header id="title-block-header"> <header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1> <h1 class="title">Ghidra Debugger</h1>
@ -634,46 +635,47 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
<span id="cb7-39"><a href="#cb7-39"></a> <span class="op">}</span></span> <span id="cb7-39"><a href="#cb7-39"></a> <span class="op">}</span></span>
<span id="cb7-40"><a href="#cb7-40"></a></span> <span id="cb7-40"><a href="#cb7-40"></a></span>
<span id="cb7-41"><a href="#cb7-41"></a> <span class="at">@Override</span></span> <span id="cb7-41"><a href="#cb7-41"></a> <span class="at">@Override</span></span>
<span id="cb7-42"><a href="#cb7-42"></a> <span class="kw">public</span> Expr <span class="fu">modBeforeStore</span><span class="op">(</span><span class="dt">int</span> sizeinAddress<span class="op">,</span> Expr inAddress<span class="op">,</span> <span class="dt">int</span> sizeinValue<span class="op">,</span></span> <span id="cb7-42"><a href="#cb7-42"></a> <span class="kw">public</span> Expr <span class="fu">modBeforeStore</span><span class="op">(</span><span class="dt">int</span> sizeinOffset<span class="op">,</span> AddressSpace space<span class="op">,</span> Expr inOffset<span class="op">,</span></span>
<span id="cb7-43"><a href="#cb7-43"></a> Expr inValue<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-43"><a href="#cb7-43"></a> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-44"><a href="#cb7-44"></a> <span class="cf">return</span> inValue<span class="op">;</span></span> <span id="cb7-44"><a href="#cb7-44"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
<span id="cb7-45"><a href="#cb7-45"></a> <span class="op">}</span></span> <span id="cb7-45"><a href="#cb7-45"></a> <span class="op">}</span></span>
<span id="cb7-46"><a href="#cb7-46"></a></span> <span id="cb7-46"><a href="#cb7-46"></a></span>
<span id="cb7-47"><a href="#cb7-47"></a> <span class="at">@Override</span></span> <span id="cb7-47"><a href="#cb7-47"></a> <span class="at">@Override</span></span>
<span id="cb7-48"><a href="#cb7-48"></a> <span class="kw">public</span> Expr <span class="fu">modAfterLoad</span><span class="op">(</span><span class="dt">int</span> sizeinAddress<span class="op">,</span> Expr inAddress<span class="op">,</span> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-48"><a href="#cb7-48"></a> <span class="kw">public</span> Expr <span class="fu">modAfterLoad</span><span class="op">(</span><span class="dt">int</span> sizeinOffset<span class="op">,</span> AddressSpace space<span class="op">,</span> Expr inOffset<span class="op">,</span></span>
<span id="cb7-49"><a href="#cb7-49"></a> <span class="cf">return</span> inValue<span class="op">;</span></span> <span id="cb7-49"><a href="#cb7-49"></a> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-50"><a href="#cb7-50"></a> <span class="op">}</span></span> <span id="cb7-50"><a href="#cb7-50"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
<span id="cb7-51"><a href="#cb7-51"></a></span> <span id="cb7-51"><a href="#cb7-51"></a> <span class="op">}</span></span>
<span id="cb7-52"><a href="#cb7-52"></a> <span class="at">@Override</span></span> <span id="cb7-52"><a href="#cb7-52"></a></span>
<span id="cb7-53"><a href="#cb7-53"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">byte</span><span class="op">[]</span> value<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-53"><a href="#cb7-53"></a> <span class="at">@Override</span></span>
<span id="cb7-54"><a href="#cb7-54"></a> <span class="cf">if</span> <span class="op">(</span>endian<span class="op">.</span><span class="fu">isBigEndian</span><span class="op">())</span> <span class="op">{</span></span> <span id="cb7-54"><a href="#cb7-54"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">byte</span><span class="op">[]</span> value<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-55"><a href="#cb7-55"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> value<span class="op">),</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span> <span id="cb7-55"><a href="#cb7-55"></a> <span class="cf">if</span> <span class="op">(</span>endian<span class="op">.</span><span class="fu">isBigEndian</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb7-56"><a href="#cb7-56"></a> <span class="op">}</span></span> <span id="cb7-56"><a href="#cb7-56"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> value<span class="op">),</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
<span id="cb7-57"><a href="#cb7-57"></a> <span class="dt">byte</span><span class="op">[]</span> reversed <span class="op">=</span> <span class="bu">Arrays</span><span class="op">.</span><span class="fu">copyOf</span><span class="op">(</span>value<span class="op">,</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span> <span id="cb7-57"><a href="#cb7-57"></a> <span class="op">}</span></span>
<span id="cb7-58"><a href="#cb7-58"></a> ArrayUtils<span class="op">.</span><span class="fu">reverse</span><span class="op">(</span>reversed<span class="op">);</span></span> <span id="cb7-58"><a href="#cb7-58"></a> <span class="dt">byte</span><span class="op">[]</span> reversed <span class="op">=</span> <span class="bu">Arrays</span><span class="op">.</span><span class="fu">copyOf</span><span class="op">(</span>value<span class="op">,</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
<span id="cb7-59"><a href="#cb7-59"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> reversed<span class="op">),</span> reversed<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span> <span id="cb7-59"><a href="#cb7-59"></a> ArrayUtils<span class="op">.</span><span class="fu">reverse</span><span class="op">(</span>reversed<span class="op">);</span></span>
<span id="cb7-60"><a href="#cb7-60"></a> <span class="op">}</span></span> <span id="cb7-60"><a href="#cb7-60"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> reversed<span class="op">),</span> reversed<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
<span id="cb7-61"><a href="#cb7-61"></a></span> <span id="cb7-61"><a href="#cb7-61"></a> <span class="op">}</span></span>
<span id="cb7-62"><a href="#cb7-62"></a> <span class="at">@Override</span></span> <span id="cb7-62"><a href="#cb7-62"></a></span>
<span id="cb7-63"><a href="#cb7-63"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> <span class="dt">boolean</span> isContextreg<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-63"><a href="#cb7-63"></a> <span class="at">@Override</span></span>
<span id="cb7-64"><a href="#cb7-64"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span>value<span class="op">,</span> size<span class="op">);</span></span> <span id="cb7-64"><a href="#cb7-64"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> <span class="dt">boolean</span> isContextreg<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-65"><a href="#cb7-65"></a> <span class="op">}</span></span> <span id="cb7-65"><a href="#cb7-65"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span>value<span class="op">,</span> size<span class="op">);</span></span>
<span id="cb7-66"><a href="#cb7-66"></a></span> <span id="cb7-66"><a href="#cb7-66"></a> <span class="op">}</span></span>
<span id="cb7-67"><a href="#cb7-67"></a> <span class="at">@Override</span></span> <span id="cb7-67"><a href="#cb7-67"></a></span>
<span id="cb7-68"><a href="#cb7-68"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">long</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-68"><a href="#cb7-68"></a> <span class="at">@Override</span></span>
<span id="cb7-69"><a href="#cb7-69"></a> <span class="cf">return</span> <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>value<span class="op">),</span> size<span class="op">);</span></span> <span id="cb7-69"><a href="#cb7-69"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">long</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-70"><a href="#cb7-70"></a> <span class="op">}</span></span> <span id="cb7-70"><a href="#cb7-70"></a> <span class="cf">return</span> <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>value<span class="op">),</span> size<span class="op">);</span></span>
<span id="cb7-71"><a href="#cb7-71"></a></span> <span id="cb7-71"><a href="#cb7-71"></a> <span class="op">}</span></span>
<span id="cb7-72"><a href="#cb7-72"></a> <span class="at">@Override</span></span> <span id="cb7-72"><a href="#cb7-72"></a></span>
<span id="cb7-73"><a href="#cb7-73"></a> <span class="kw">public</span> <span class="dt">byte</span><span class="op">[]</span> <span class="fu">toConcrete</span><span class="op">(</span>Expr value<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-73"><a href="#cb7-73"></a> <span class="at">@Override</span></span>
<span id="cb7-74"><a href="#cb7-74"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span> <span id="cb7-74"><a href="#cb7-74"></a> <span class="kw">public</span> <span class="dt">byte</span><span class="op">[]</span> <span class="fu">toConcrete</span><span class="op">(</span>Expr value<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-75"><a href="#cb7-75"></a> <span class="op">}</span></span> <span id="cb7-75"><a href="#cb7-75"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
<span id="cb7-76"><a href="#cb7-76"></a></span> <span id="cb7-76"><a href="#cb7-76"></a> <span class="op">}</span></span>
<span id="cb7-77"><a href="#cb7-77"></a> <span class="at">@Override</span></span> <span id="cb7-77"><a href="#cb7-77"></a></span>
<span id="cb7-78"><a href="#cb7-78"></a> <span class="kw">public</span> <span class="dt">long</span> <span class="fu">sizeOf</span><span class="op">(</span>Expr value<span class="op">)</span> <span class="op">{</span></span> <span id="cb7-78"><a href="#cb7-78"></a> <span class="at">@Override</span></span>
<span id="cb7-79"><a href="#cb7-79"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span> <span id="cb7-79"><a href="#cb7-79"></a> <span class="kw">public</span> <span class="dt">long</span> <span class="fu">sizeOf</span><span class="op">(</span>Expr value<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-80"><a href="#cb7-80"></a> <span class="op">}</span></span> <span id="cb7-80"><a href="#cb7-80"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
<span id="cb7-81"><a href="#cb7-81"></a><span class="op">}</span></span></code></pre></div> <span id="cb7-81"><a href="#cb7-81"></a> <span class="op">}</span></span>
<span id="cb7-82"><a href="#cb7-82"></a><span class="op">}</span></span></code></pre></div>
<p>We have implemented two arithmetic models: one for big-endian <p>We have implemented two arithmetic models: one for big-endian
languages and one for little-endian. The endianness comes into play when languages and one for little-endian. The endianness comes into play when
we encode constant values passed to <code>fromConst()</code>. We must we encode constant values passed to <code>fromConst()</code>. We must
@ -706,11 +708,10 @@ storage mechanism. For example, were this a dynamic taint analyzer, we
could use <code>modAfterLoad()</code> to record that a value was could use <code>modAfterLoad()</code> to record that a value was
retrieved via a tainted address. The <code>inValue</code> parameter retrieved via a tainted address. The <code>inValue</code> parameter
gives the <code>Expr</code> actually retrieved from the emulators gives the <code>Expr</code> actually retrieved from the emulators
storage, and <code>inAddress</code> gives the address (really just the storage, and <code>inOffset</code> gives the offset used to retrieve it.
<code>Expr</code> piece) used to retrieve it. Conversely, in Conversely, in <code>modBeforeStore()</code>, <code>inValue</code> gives
<code>modBeforeStore()</code>, <code>inValue</code> gives the value the value about to be stored, and <code>inOffset</code> gives the offset
about to be stored, and <code>inAddress</code> gives the address used to used to store it.</p>
store it.</p>
<p>We implement neither <code>toConcrete()</code> nor <p>We implement neither <code>toConcrete()</code> nor
<code>sizeOf()</code>. Since we will be augmenting a concrete emulator, <code>sizeOf()</code>. Since we will be augmenting a concrete emulator,
these methods will be provided by the concrete piece. If this model is these methods will be provided by the concrete piece. If this model is

View file

@ -27,8 +27,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI { public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
@Override @Override
@ -43,7 +43,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
throw new AssertionError("The current program must be termmines"); throw new AssertionError("The current program must be termmines");
} }
if (getExecutionState(trace).isRunning()) { if (getExecutionState(trace) != TraceExecutionState.STOPPED) {
monitor.setMessage("Interrupting target and waiting for STOPPED"); monitor.setMessage("Interrupting target and waiting for STOPPED");
interrupt(); interrupt();
waitForBreak(3, TimeUnit.SECONDS); waitForBreak(3, TimeUnit.SECONDS);
@ -87,7 +87,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
// -------------------------------- // --------------------------------
while (true) { while (true) {
monitor.checkCanceled(); monitor.checkCancelled();
TraceExecutionState execState = getExecutionState(trace); TraceExecutionState execState = getExecutionState(trace);
switch (execState) { switch (execState) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before After
Before After