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

@ -144,8 +144,8 @@ def find_thread_by_regs_obj(object):
def find_frame_by_level(level):
for f in util.dbg._base.backtrace_list():
if f.FrameNumber == level:
return f
#return dbg().backtrace_list()[level]
return f
# return dbg().backtrace_list()[level]
def find_frame_by_pattern(pattern, object, err_msg):
@ -206,8 +206,8 @@ def execute(cmd: str, to_string: bool=False):
@REGISTRY.method(action='evaluate', display='Evaluate')
# @util.dbg.eng_thread
def evaluate(
session: sch.Schema('Session'),
expr: ParamDesc(str, display='Expr')):
session: sch.Schema('Session'),
expr: ParamDesc(str, display='Expr')):
"""Evaluate a Python3 expression."""
return str(eval(expr, shared_globals))
@ -328,8 +328,8 @@ def remove_process(process: sch.Schema('Process')):
@REGISTRY.method(action='connect', display='Connect')
@util.dbg.eng_thread
def target(
session: sch.Schema('Session'),
cmd: ParamDesc(str, display='Command')):
session: sch.Schema('Session'),
cmd: ParamDesc(str, display='Command')):
"""Connect to a target machine or process."""
dbg().attach_kernel(cmd)
@ -345,8 +345,8 @@ def attach_obj(target: sch.Schema('Attachable')):
@REGISTRY.method(action='attach', display='Attach by pid')
@util.dbg.eng_thread
def attach_pid(
session: sch.Schema('Session'),
pid: ParamDesc(str, display='PID')):
session: sch.Schema('Session'),
pid: ParamDesc(str, display='PID')):
"""Attach the process to the given target."""
dbg().attach_proc(int(pid))
@ -354,8 +354,8 @@ def attach_pid(
@REGISTRY.method(action='attach', display='Attach by name')
@util.dbg.eng_thread
def attach_name(
session: sch.Schema('Session'),
name: ParamDesc(str, display='Name')):
session: sch.Schema('Session'),
name: ParamDesc(str, display='Name')):
"""Attach the process to the given target."""
dbg().attach_proc(name)
@ -369,7 +369,7 @@ def detach(process: sch.Schema('Process')):
@REGISTRY.method(action='launch', display='Launch')
def launch_loader(
session: sch.Schema('Session'),
session: sch.Schema('Session'),
file: ParamDesc(str, display='File'),
args: ParamDesc(str, display='Arguments')=''):
"""
@ -383,7 +383,7 @@ def launch_loader(
@REGISTRY.method(action='launch', display='LaunchEx')
def launch(
session: sch.Schema('Session'),
session: sch.Schema('Session'),
file: ParamDesc(str, display='File'),
args: ParamDesc(str, display='Arguments')='',
initial_break: ParamDesc(bool, display='Initial Break')=True,
@ -405,7 +405,7 @@ def kill(process: sch.Schema('Process')):
commands.ghidra_trace_kill()
@REGISTRY.method(action='resume')
@REGISTRY.method(action='resume', display="Go")
def go(process: sch.Schema('Process')):
"""Continue execution of the process."""
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)
@REGISTRY.method(action='break_sw_execute')
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
@util.dbg.eng_thread
def break_expression(expression: str):
"""Set a breakpoint."""
@ -472,7 +472,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
dbg().ba(expr=address.offset)
@REGISTRY.method(action='break_hw_execute')
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
@util.dbg.eng_thread
def break_hw_expression(expression: str):
"""Set a hardware-assisted breakpoint."""
@ -482,50 +482,50 @@ def break_hw_expression(expression: str):
@REGISTRY.method(action='break_read')
@util.dbg.eng_thread
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
"""Set a read watchpoint."""
"""Set a read breakpoint."""
find_proc_by_obj(process)
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
def break_read_expression(expression: str):
"""Set a read watchpoint."""
"""Set a read breakpoint."""
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
@REGISTRY.method(action='break_write')
@util.dbg.eng_thread
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
"""Set a watchpoint."""
"""Set a write breakpoint."""
find_proc_by_obj(process)
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
def break_write_expression(expression: str):
"""Set a watchpoint."""
"""Set a write breakpoint."""
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
@REGISTRY.method(action='break_access')
@util.dbg.eng_thread
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
"""Set an access watchpoint."""
"""Set an access breakpoint."""
find_proc_by_obj(process)
dbg().ba(expr=range.min, size=range.length(),
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
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)
@REGISTRY.method(action='toggle')
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
@util.dbg.eng_thread
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
"""Toggle a breakpoint."""
@ -536,7 +536,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
dbg().bd(bpt.GetId())
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display='Delete Breakpoint')
@util.dbg.eng_thread
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint."""

View file

@ -391,19 +391,19 @@ def refresh_sections(node: sch.Schema('Module')):
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')):
"""Switch to the 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')):
"""Switch to the thread."""
find_thread_by_obj(thread).switch()
@REGISTRY.method(action='activate')
@REGISTRY.method(action='activate', display="Activate Frame")
def activate_frame(frame: sch.Schema('StackFrame')):
"""Select the frame."""
find_frame_by_obj(frame).select()
@ -415,7 +415,7 @@ def add_inferior(container: sch.Schema('InferiorContainer')):
gdb.execute('add-inferior')
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display="Delete Inferior")
def delete_inferior(inferior: sch.Schema('Inferior')):
"""Remove the inferior."""
inf = find_inf_by_obj(inferior)
@ -433,7 +433,7 @@ def connect(inferior: sch.Schema('Inferior'), spec: str):
@REGISTRY.method(action='attach', display='Attach')
def attach_obj(target: sch.Schema('Attachable')):
"""Attach the inferior to the given target."""
#switch_inferior(find_inf_by_obj(inferior))
# switch_inferior(find_inf_by_obj(inferior))
pid = find_availpid_by_obj(target)
gdb.execute(f'attach {pid}')
@ -577,7 +577,7 @@ def break_sw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
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):
"""Set a breakpoint (break)."""
# TODO: Escape?
@ -592,7 +592,7 @@ def break_hw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
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):
"""Set a hardware-assisted breakpoint (hbreak)."""
# 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})')
@REGISTRY.method(action='break_read')
@REGISTRY.method(action='break_ext', display="Set Read Watchpoint")
def break_read_expression(expression: str):
"""Set a read watchpoint (rwatch)."""
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})')
@REGISTRY.method(action='break_write')
@REGISTRY.method(action='break_ext', display="Set Watchpoint")
def break_write_expression(expression: str):
"""Set a watchpoint (watch)."""
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})')
@REGISTRY.method(action='break_access')
@REGISTRY.method(action='break_ext', display="Set Access Watchpoint")
def break_access_expression(expression: str):
"""Set an access watchpoint (awatch)."""
gdb.execute(f'awatch {expression}')
@ -653,21 +653,23 @@ def break_event(inferior: sch.Schema('Inferior'), spec: str):
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):
"""Toggle a breakpoint."""
bpt = find_bpt_by_obj(breakpoint)
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):
"""Toggle a breakpoint location."""
loc = find_bpt_loc_by_obj(location)
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):
"""Toggle a breakpoint 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}')
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display="Delete Breakpoint")
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint."""
bpt = find_bpt_by_obj(breakpoint)

View file

@ -17,11 +17,11 @@ from concurrent.futures import Future, ThreadPoolExecutor
import re
import sys
import lldb
from ghidratrace import sch
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
import lldb
from . import commands, util
@ -248,7 +248,7 @@ def execute(cmd: str, to_string: bool=False):
return exec_convert_errors(cmd, to_string)
@REGISTRY.method
@REGISTRY.method(display='Evaluate')
def evaluate(expr: str):
"""Evaluate an expression."""
value = util.get_target().EvaluateExpression(expr)
@ -257,7 +257,7 @@ def evaluate(expr: str):
return commands.convert_value(value)
@REGISTRY.method
@REGISTRY.method(display="Python Evaluate")
def pyeval(expr: str):
return eval(expr)
@ -345,28 +345,28 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
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')):
"""Switch to the process."""
# TODO
return
@REGISTRY.method(action='activate')
@REGISTRY.method(action='activate', display='Activate Thread')
def activate_thread(thread: sch.Schema('Thread')):
"""Switch to the thread."""
t = find_thread_by_obj(thread)
t.process.SetSelectedThread(t)
@REGISTRY.method(action='activate')
@REGISTRY.method(action='activate', display='Activate Frame')
def activate_frame(frame: sch.Schema('StackFrame')):
"""Select the frame."""
f = find_frame_by_obj(frame)
f.thread.SetSelectedFrame(f.GetFrameID())
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display='Remove Process')
def remove_process(process: sch.Schema('Process')):
"""Remove the process."""
proc = find_proc_by_obj(process)
@ -442,7 +442,7 @@ def kill(process: sch.Schema('Process')):
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')):
"""Continue execution of the process."""
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}')
@REGISTRY.method(action='break_sw_execute')
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
def break_expression(expression: str):
"""Set a breakpoint."""
# 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}')
@REGISTRY.method(action='break_hw_execute')
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
def break_hw_expression(expression: str):
"""Set a hardware-assisted breakpoint."""
# 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}')
@REGISTRY.method(action='break_read')
@REGISTRY.method(action='break_ext', display='Set Read Watchpoint')
def break_read_expression(expression: str, size=None):
"""Set a read watchpoint."""
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}')
@REGISTRY.method(action='break_write')
@REGISTRY.method(action='break_ext', display='Set Watchpoint')
def break_write_expression(expression: str, size=None):
"""Set a watchpoint."""
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')
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)
offset_start = process.trace.memory_mapper.map_back(
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}')
@REGISTRY.method(action='break_access')
@REGISTRY.method(action='break_ext', display='Set Read/Write Watchpoint')
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}'
exec_convert_errors(
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}')
@REGISTRY.method(action='toggle')
@REGISTRY.method(action='toggle', display='Toggle Watchpoint')
def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
"""Toggle a 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()}')
@REGISTRY.method(action='toggle')
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
"""Toggle a 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()}')
@REGISTRY.method(action='toggle')
@REGISTRY.method(action='toggle', display='Toggle Breakpoint Location')
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
"""Toggle a breakpoint 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}')
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display='Delete Watchpoint')
def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
"""Delete a 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}')
@REGISTRY.method(action='delete')
@REGISTRY.method(action='delete', display='Delete Breakpoint')
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
"""Delete a breakpoint."""
bpt = find_bpt_by_obj(breakpoint)

View file

@ -4,9 +4,9 @@
* 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.
@ -15,8 +15,16 @@
*/
package ghidra.debug.api.target;
import java.util.HashMap;
import java.util.Map;
import java.awt.event.InputEvent;
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.
@ -35,21 +43,137 @@ import java.util.Map;
* the UI to decide what is presented where.
*
* @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
* reserved for built-in, purpose-specific actions
* @param show when to show the general UI action for this debugger action
* @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<>();
/**
* 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) {
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) {
ActionName action = new ActionName(name, true);
ActionName action = new ActionName(name, show, enabler, display, icon, okText);
if (NAMES.put(name, action) != null) {
throw new AssertionError();
}
@ -57,24 +181,16 @@ public record ActionName(String name, boolean builtIn) {
}
}
private static ActionName extended(String name) {
synchronized (NAMES) {
ActionName action = new ActionName(name, false);
if (NAMES.put(name, action) != null) {
throw new AssertionError();
}
return action;
}
}
public static final ActionName REFRESH = extended("refresh");
public static final ActionName REFRESH =
create("refresh", Show.EXTENDED, Enabler.ALWAYS, "Refresh", ICON_REFRESH, "Refresh");
/**
* Activate a given object and optionally a time
*
* <p>
* 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.
*
@ -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
* pressing enter would more likely invoke 'activate.'
*/
public static final ActionName FOCUS = builtIn("focus");
public static final ActionName TOGGLE = builtIn("toggle");
public static final ActionName DELETE = builtIn("delete");
public static final ActionName FOCUS =
create("focus", Show.BUILTIN, Enabler.ALWAYS, "Focus", null, "Focus");
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
@ -93,7 +212,8 @@ public record ActionName(String name, boolean builtIn) {
* <p>
* 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
@ -101,23 +221,31 @@ public record ActionName(String name, boolean builtIn) {
* <p>
* 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)
*/
public static final ActionName ATTACH = extended("attach");
public static final ActionName DETACH = extended("detach");
public static final ActionName ATTACH =
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),
* (ANY*)
*/
public static final ActionName LAUNCH = extended("launch");
public static final ActionName KILL = builtIn("kill");
public static final ActionName LAUNCH =
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 INTERRUPT = builtIn("interrupt");
public static final ActionName RESUME =
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
@ -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
* 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_OVER = builtIn("step_over");
public static final ActionName STEP_OUT = builtIn("step_out");
public static final ActionName STEP_INTO =
create("step_into", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Into", ICON_STEP_INTO, "Step");
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
* 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
* 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)
* 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.
*/
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)
@ -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
* those arguments.
*/
public static final ActionName BREAK_SW_EXECUTE = builtIn("break_sw_execute");
public static final ActionName BREAK_HW_EXECUTE = builtIn("break_hw_execute");
public static final ActionName BREAK_READ = builtIn("break_read");
public static final ActionName BREAK_WRITE = builtIn("break_write");
public static final ActionName BREAK_ACCESS = builtIn("break_access");
public static final ActionName BREAK_EXT = extended("break_ext");
public static final ActionName BREAK_SW_EXECUTE =
create("break_sw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Software Breakpoint",
ICON_SET_BREAKPOINT, "Set");
public static final ActionName BREAK_HW_EXECUTE =
create("break_hw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Hardware Breakpoint",
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)
*/
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)
*/
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)
/**
* 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.Set;
import java.util.concurrent.*;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import javax.swing.Icon;
import docking.ActionContext;
import ghidra.debug.api.target.ActionName.Show;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
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
* those functions will keep whatever some means of querying UI and/or target context in their
* 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,
long specificity, BooleanSupplier enabled,
Function<Boolean, CompletableFuture<?>> action) {
interface ActionEntry {
/**
* 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
*
* @return true if enabled
*/
public boolean isEnabled() {
return enabled.getAsBoolean();
}
boolean isEnabled();
/**
* 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
* @return the future result, often {@link Void}
*/
public CompletableFuture<?> invokeAsync(boolean prompt) {
return action.apply(prompt).orTimeout(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
default CompletableFuture<?> invokeAsync(boolean prompt) {
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
*/
public void run(boolean prompt) {
default void run(boolean prompt) {
get(prompt);
}
@ -113,7 +159,7 @@ public interface Target {
* @param prompt whether or not to prompt the user for arguments
* @return the resulting value, if applicable
*/
public Object get(boolean prompt) {
default Object get(boolean prompt) {
if (Swing.isSwingThread()) {
throw new AssertionError("Refusing to block the Swing thread. Use a Task.");
}
@ -130,8 +176,8 @@ public interface Target {
*
* @return true if built in.
*/
public boolean builtIn() {
return name != null && name.builtIn();
default Show getShow() {
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.function.Function;
import javax.swing.Icon;
import ghidra.async.AsyncUtils;
import ghidra.debug.api.target.ActionName;
import ghidra.trace.model.Trace;
@ -62,6 +64,20 @@ public interface RemoteMethod {
*/
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.
*

View file

@ -49,7 +49,8 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemorySpace;
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.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
@ -1740,7 +1741,7 @@ public interface FlatDebuggerAPI {
* @return true if alive
*/
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
*/
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
*/
default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException {
if (!getExecutionState(trace).isRunning()) {
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
return;
}
var listener = new DomainObjectListener() {
@ -1800,14 +1801,14 @@ public interface FlatDebuggerAPI {
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (!getExecutionState(trace).isRunning()) {
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
future.complete(null);
}
}
};
trace.addListener(listener);
try {
if (!getExecutionState(trace).isRunning()) {
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
return;
}
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) {
String action = name;
String display = name;
String description = name;
TraceMethod annot = m.getAnnotation(TraceMethod.class);
if (annot == null) {
return;
}
action = annot.action();
if (annot.display() != null) {
display = annot.display();
}
if (annot.description() != null) {
description = annot.description();
}
int pcount = m.getParameterCount();
if (pcount < 1) {
return;
@ -167,8 +157,8 @@ public class JdiConnector {
* collection routines currently use the return type, so just use ANY for now.
*/
TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY;
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display,
description, schema, methods, m);
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, annot.action(),
annot.display(), annot.description(), annot.okText(), annot.icon(), schema, methods, m);
remoteMethodRegistry.putMethod(name, method);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -31,6 +31,7 @@ import org.apache.commons.lang3.ArrayUtils;
import com.google.protobuf.ByteString;
import db.Transaction;
import generic.theme.GIcon;
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.services.DebuggerControlService;
@ -1027,7 +1028,9 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
}
for (Method m : req.getMethodsList()) {
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()
.stream()
.collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)),

View file

@ -19,9 +19,11 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.Icon;
import org.apache.commons.lang3.exception.ExceptionUtils;
import docking.ActionContext;
@ -60,6 +62,70 @@ import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
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 Trace trace;
@ -224,8 +290,12 @@ public class TraceRmiTarget extends AbstractTarget {
return null;
}
/**
* A singleton to indicate missing arguments
*/
public enum Missing {
MISSING; // The argument requires a prompt
/** The argument requires a prompt */
MISSING;
}
protected Object findArgument(ActionName action, RemoteParameter parameter,
@ -266,28 +336,7 @@ public class TraceRmiTarget extends AbstractTarget {
return args;
}
private TraceExecutionState getStateOf(TraceObject object) {
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) {
protected static long computeSpecificity(Map<String, Object> args) {
long score = 0;
for (Object o : args.values()) {
if (o instanceof TraceObject obj) {
@ -297,46 +346,34 @@ public class TraceRmiTarget extends AbstractTarget {
return score;
}
protected BooleanSupplier chooseEnabler(RemoteMethod method, Map<String, Object> args) {
ActionName name = method.action();
protected RemoteParameter getFirstObjectParameter(RemoteMethod method) {
SchemaContext ctx = getSchemaContext();
if (ctx == null) {
return () -> true;
return null;
}
RemoteParameter firstParam = method.parameters()
return method.parameters()
.values()
.stream()
.filter(
p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType()))
.findFirst()
.orElse(null);
}
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {}
protected ParamAndObjectArg getFirstObjectArgument(RemoteMethod method,
Map<String, Object> args) {
RemoteParameter firstParam = getFirstObjectParameter(method);
if (firstParam == null) {
return () -> true;
return null;
}
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 + ")");
return () -> false;
return new ParamAndObjectArg(firstParam, null);
}
TraceObject obj = (TraceObject) firstArg;
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;
return new ParamAndObjectArg(firstParam, obj);
}
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);
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);
return args == null ? null : ValStr.toPlainMap(args);
}
@ -374,10 +411,7 @@ public class TraceRmiTarget extends AbstractTarget {
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
Map<String, Object> args = collectArguments(method, context, allowContextObject,
allowCoordsObject, allowSuitableObject);
boolean requiresPrompt = args.values().contains(Missing.MISSING);
return new ActionEntry(method.display(), method.action(), method.description(),
requiresPrompt, computeSpecificity(args), chooseEnabler(method, args),
prompt -> invokeMethod(prompt, method, args));
return new TraceRmiActionEntry(method, args);
}
protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods,
@ -432,6 +466,17 @@ public class TraceRmiTarget extends AbstractTarget {
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
protected Map<String, ActionEntry> collectResumeActions(ActionContext context) {
return collectByName(ActionName.RESUME, context);

View file

@ -4,9 +4,9 @@
* 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.
@ -423,6 +423,8 @@ message Method {
// 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.
ValueType return_type = 6;
string ok_text = 7;
string icon = 8;
}
message RequestNegotiate {

View file

@ -27,10 +27,11 @@ from . import sch
from . import trace_rmi_pb2 as bufs
from .util import send_delimited, recv_delimited
# 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
# includes the change.
#
#
# Other places to change:
# * every pyproject.toml file (incl. deps)
# * TraceRmiHandler.VERSION
@ -98,7 +99,8 @@ class Receiver(Thread):
while not self._is_shutdown:
#print("Receiving message")
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:
self._is_shutdown = True
return
@ -448,6 +450,8 @@ class RemoteMethod:
name: str
action: str
display: str
icon: str
ok_text: str
description: str
parameters: List[RemoteParameter]
return_schema: sch.Schema
@ -508,30 +512,28 @@ class MethodRegistry(object):
@classmethod
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:
name = function.__name__
if action is None:
action = name
if display is None:
display = name
if description is None:
description = function.__doc__ or ''
description = function.__doc__
sig = inspect.signature(function)
params = []
for p in sig.parameters.values():
params.append(cls._make_param(p))
return_schema = cls._to_schema(sig, sig.return_annotation)
return RemoteMethod(name, action, display, description, params,
return_schema, function)
return RemoteMethod(name, action, display, icon, ok_text, description,
params, return_schema, function)
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):
if condition:
method = self.create_method(func, name, action, display,
description)
icon, ok_text, description)
self.register_method(method)
return func
@ -705,8 +707,10 @@ class Client(object):
def _write_method(to: bufs.Method, method: RemoteMethod):
to.name = method.name
to.action = method.action
to.display = method.display
to.description = method.description
to.display = method.display or ''
to.icon = method.icon or ''
to.ok_text = method.ok_text or ''
to.description = method.description or ''
Client._write_parameters(to.parameters, method.parameters)
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.Stream;
import javax.swing.Icon;
import db.Transaction;
import ghidra.app.services.DebuggerTargetService;
import ghidra.async.*;
@ -56,6 +58,11 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
String description, Map<String, RemoteParameter> parameters, SchemaName retType,
AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue)
implements RemoteMethod {
public TestRemoteMethod {
Objects.requireNonNull(action);
}
public TestRemoteMethod(String name, ActionName action, String display, String description,
Map<String, RemoteParameter> parameters, SchemaName retType) {
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()));
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,

View file

@ -163,6 +163,13 @@
"help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint
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">
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.action.*;
import docking.action.builder.ActionBuilder;
import docking.menu.MultiActionDockingAction;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.context.ProgramLocationActionContext;
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.InvokeActionEntryAction;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.*;
@ -263,6 +267,78 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
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
extends AbstractEnableSelectedBreakpointsAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
@ -708,6 +784,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
new DebuggerMakeBreakpointsEffectiveActionContext();
// package access for testing
SetBreakpointAction actionSetBreakpoint;
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
EnableAllBreakpointsAction actionEnableAllBreakpoints;
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
@ -1163,6 +1240,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
protected void createActions() {
actionSetBreakpoint = new SetBreakpointAction();
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();

View file

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

View file

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

View file

@ -4,9 +4,9 @@
* 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.
@ -65,9 +65,11 @@ class TargetDockingAction extends DockingAction {
protected void updateFromContext(ActionContext context) {
entry = findEntry(context);
if (entry == null) {
getToolBarData().setIcon(action.icon());
setDescription(defaultDescription);
}
else {
getToolBarData().setIcon(entry.icon());
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
* a process.
*/
INACTIVE(false, false, false, false),
INACTIVE,
/**
* 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
* these conventions internally.
*/
ALIVE(true, false, false, false),
ALIVE,
/**
* The object is alive, but not executing
*/
STOPPED(true, false, true, false),
STOPPED,
/**
* 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
* necessarily mean it is executing on a CPU at this exact moment.
*/
RUNNING(true, true, false, false),
RUNNING,
/**
* 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.,
* a GDB "Inferior," which could be re-used to launch or attach to another process.
*/
TERMINATED(false, false, false, true);
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;
}
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.tracermi.TestTraceRmiConnection.*;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.target.ActionName;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema;
@ -57,7 +58,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
protected void addMethods() {
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,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"),
@ -65,14 +66,15 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
"Target", "The target address"));
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,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to step"));
reg.add(rmiMethodStepExt);
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", null, "Advance With Flag",
"Advance to the given address, with flag", PrimitiveTraceObjectSchema.VOID,
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", ActionName.STEP_EXT,
"Advance With Flag", "Advance to the given address, with flag",
PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"),
new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null,
@ -81,7 +83,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
"Flag", "The flag"));
reg.add(rmiMethodAdvanceWithFlag);
rmiMethodBetween = new TestRemoteMethod("between", null, "Between",
rmiMethodBetween = new TestRemoteMethod("between", ActionName.STEP_EXT, "Between",
"Advance between two given addresses", PrimitiveTraceObjectSchema.VOID,
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
"The thread to advance"),

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
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.,
<code>break main</code>.</li>
</ol>
<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
that you can specify something other than an address. 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. There is some iconography to communicate the
various states of a breakpoint. When all is well and normal, you should
only see enabled <img src="images/breakpoint-enable.png"
alt="enabled breakpoint" /> and disabled <img
src="images/breakpoint-disable.png" alt="disabled breakpoint" />
breakpoints. If the target is terminated (or not launched yet), you may
also see ineffective <img src="images/breakpoint-enable-ineff.png"
alt="ineffective breakpoint" /> breakpoints.</p>
breakpoint at any 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. 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. When all is well and
normal, you should only see enabled <img
src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
disabled <img src="images/breakpoint-disable.png"
alt="disabled breakpoint" /> breakpoints. If the target is terminated
(or not launched yet), you may also see ineffective <img
src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
breakpoints.</p>
</section>
<section id="examining-minesweeper-board-setup" class="level2">
<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:
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`.
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.
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.

View file

@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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="B2-Emulation.html">Emulation</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 id="title-block-header">
<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-8"><a href="#cb7-8"></a><span class="op">}</span></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-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>

View file

@ -210,7 +210,7 @@ if (!"termmines".equals(currentProgram.getName())) {
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");
interrupt();
waitForBreak(3, TimeUnit.SECONDS);

View file

@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</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 id="title-block-header">
<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-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-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-43"><a href="#cb7-43"></a> Expr inValue<span class="op">)</span> <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> <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-45"><a href="#cb7-45"></a> <span class="op">}</span></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-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-49"><a href="#cb7-49"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
<span id="cb7-50"><a href="#cb7-50"></a> <span class="op">}</span></span>
<span id="cb7-51"><a href="#cb7-51"></a></span>
<span id="cb7-52"><a href="#cb7-52"></a> <span class="at">@Override</span></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-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-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-56"><a href="#cb7-56"></a> <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-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-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-60"><a href="#cb7-60"></a> <span class="op">}</span></span>
<span id="cb7-61"><a href="#cb7-61"></a></span>
<span id="cb7-62"><a href="#cb7-62"></a> <span class="at">@Override</span></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-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-65"><a href="#cb7-65"></a> <span class="op">}</span></span>
<span id="cb7-66"><a href="#cb7-66"></a></span>
<span id="cb7-67"><a href="#cb7-67"></a> <span class="at">@Override</span></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-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-70"><a href="#cb7-70"></a> <span class="op">}</span></span>
<span id="cb7-71"><a href="#cb7-71"></a></span>
<span id="cb7-72"><a href="#cb7-72"></a> <span class="at">@Override</span></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-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-75"><a href="#cb7-75"></a> <span class="op">}</span></span>
<span id="cb7-76"><a href="#cb7-76"></a></span>
<span id="cb7-77"><a href="#cb7-77"></a> <span class="at">@Override</span></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-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-80"><a href="#cb7-80"></a> <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-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="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="cf">return</span> inValue<span class="op">;</span></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>
<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="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">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="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="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> 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="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 class="op">}</span></span>
<span id="cb7-62"><a href="#cb7-62"></a></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="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="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 class="op">}</span></span>
<span id="cb7-67"><a href="#cb7-67"></a></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="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="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 class="op">}</span></span>
<span id="cb7-72"><a href="#cb7-72"></a></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="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="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 class="op">}</span></span>
<span id="cb7-77"><a href="#cb7-77"></a></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="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="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>
<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
languages and one for little-endian. The endianness comes into play when
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
retrieved via a tainted address. The <code>inValue</code> parameter
gives the <code>Expr</code> actually retrieved from the emulators
storage, and <code>inAddress</code> gives the address (really just the
<code>Expr</code> piece) used to retrieve it. Conversely, in
<code>modBeforeStore()</code>, <code>inValue</code> gives the value
about to be stored, and <code>inAddress</code> gives the address used to
store it.</p>
storage, and <code>inOffset</code> gives the offset used to retrieve it.
Conversely, in <code>modBeforeStore()</code>, <code>inValue</code> gives
the value about to be stored, and <code>inOffset</code> gives the offset
used to store it.</p>
<p>We implement neither <code>toConcrete()</code> nor
<code>sizeOf()</code>. Since we will be augmenting a concrete emulator,
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.symbol.Symbol;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
@Override
@ -43,7 +43,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
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");
interrupt();
waitForBreak(3, TimeUnit.SECONDS);
@ -87,7 +87,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
// --------------------------------
while (true) {
monitor.checkCanceled();
monitor.checkCancelled();
TraceExecutionState execState = getExecutionState(trace);
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