diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py
index 5326cc5be5..b98cfa1c93 100644
--- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py
+++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py
@@ -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."""
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py
index a019d4b160..30263b3fe0 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py
@@ -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)
diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py
index 66592d6d9a..4a0e04c2fe 100644
--- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py
+++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py
@@ -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)
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
index eecb36a0ef..214c4631e1 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
@@ -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 not 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
* 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) {
*
* 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) {
*
* 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");
}
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
index 133f186892..4f6eb5e6cd 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
@@ -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
+ * 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.
+ *
+ *
+ * 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();
}
}
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracermi/RemoteMethod.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracermi/RemoteMethod.java
index 4838df5716..e4325badd7 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracermi/RemoteMethod.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracermi/RemoteMethod.java
@@ -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.
*
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
index d36d03eab5..f72685d120 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
@@ -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);
diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiConnector.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiConnector.java
index c7fce87c50..7b85272d16 100644
--- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiConnector.java
+++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiConnector.java
@@ -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);
}
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiClient.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiClient.java
index 405b35c3fc..c9fada1ef8 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiClient.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiClient.java
@@ -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);
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiMethodRegistry.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiMethodRegistry.java
index c00d65f37a..a929e0b578 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiMethodRegistry.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiMethodRegistry.java
@@ -32,6 +32,10 @@ public class RmiMethodRegistry {
String display() default "";
String description() default "";
+
+ String okText() default "";
+
+ String icon() default "";
}
Map 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. 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 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
+
+ Set Breakpoint
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png
index 4188c674e0..77722233b8 100644
Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/InvokeActionEntryAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/InvokeActionEntryAction.java
new file mode 100644
index 0000000000..49a566de69
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/InvokeActionEntryAction.java
@@ -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);
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
index 04ffb56e72..8b7a29c28b 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
@@ -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
Enable
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/A2-UITour.html b/GhidraDocs/GhidraClass/Debugger/A2-UITour.html
index 4835a64152..63e0f8913d 100644
--- a/GhidraDocs/GhidraClass/Debugger/A2-UITour.html
+++ b/GhidraDocs/GhidraClass/Debugger/A2-UITour.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.html b/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.html
index 0189f37b89..40731c17b3 100644
--- a/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.html
+++ b/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
@@ -160,22 +161,26 @@ Memory/Hex, and the Decompiler, right-click and select
Set
Breakpoint, press K on the keyboard, or
double-click the margin.
+break main.
and disabled
-breakpoints. If the target is terminated (or not launched yet), you may
-also see ineffective
breakpoints. and
+disabled
breakpoints. If the target is terminated
+(or not launched yet), you may also see ineffective
+breakpoints.
Examining Minesweeper Board Setup
diff --git a/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.md b/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.md
index ebf3c86fbd..954ee14dfd 100644
--- a/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.md
+++ b/GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.md
@@ -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, 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.
diff --git a/GhidraDocs/GhidraClass/Debugger/A4-MachineState.html b/GhidraDocs/GhidraClass/Debugger/A4-MachineState.html
index 6fc4a3ca2a..a0ca3dd35b 100644
--- a/GhidraDocs/GhidraClass/Debugger/A4-MachineState.html
+++ b/GhidraDocs/GhidraClass/Debugger/A4-MachineState.html
@@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html
index 5c0a2be03e..e40e48f46c 100644
--- a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html
+++ b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html
@@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.html b/GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.html
index c72d319760..c35e1f8d6e 100644
--- a/GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.html
+++ b/GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.html
@@ -35,7 +35,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html b/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
index 288fb4200e..0fbe63ac0a 100644
--- a/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
+++ b/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/B2-Emulation.html b/GhidraDocs/GhidraClass/Debugger/B2-Emulation.html
index 0d95b521f4..2d5a708ed3 100644
--- a/GhidraDocs/GhidraClass/Debugger/B2-Emulation.html
+++ b/GhidraDocs/GhidraClass/Debugger/B2-Emulation.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
diff --git a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html
index d382eea0da..0a29e84350 100644
--- a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html
+++ b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
@@ -379,7 +380,7 @@ class="sourceCode numberSource java numberLines"><
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);
diff --git a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md
index bd54ccfbcc..5edd3b3bcd 100644
--- a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md
+++ b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md
@@ -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);
diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
index b1929f6147..837eb91088 100644
--- a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
+++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
@@ -99,7 +99,8 @@
class="advanced" href="B1-RemoteTargets.html">Remote TargetsEmulationScriptingModeling
+ class="advanced" href="B4-Modeling.html">ModelingAdding Debuggers
Ghidra Debugger
@@ -634,46 +635,47 @@ class="sourceCode numberSource java numberLines"><
}
@Override
- public Expr modBeforeStore(int sizeinAddress, Expr inAddress, int sizeinValue,
- Expr inValue) {
+ public Expr modBeforeStore(int sizeinOffset, AddressSpace space, Expr inOffset,
+ int sizeinValue, Expr inValue) {
return inValue;
}
@Override
- public Expr modAfterLoad(int sizeinAddress, Expr inAddress, int sizeinValue, Expr inValue) {
- return inValue;
- }
-
- @Override
- public Expr fromConst(byte[] value) {
- if (endian.isBigEndian()) {
- return new LitExpr(new BigInteger(1, value), value.length);
- }
- byte[] reversed = Arrays.copyOf(value, value.length);
- ArrayUtils.reverse(reversed);
- return new LitExpr(new BigInteger(1, reversed), reversed.length);
- }
-
- @Override
- public Expr fromConst(BigInteger value, int size, boolean isContextreg) {
- return new LitExpr(value, size);
- }
-
- @Override
- public Expr fromConst(long value, int size) {
- return fromConst(BigInteger.valueOf(value), size);
- }
-
- @Override
- public byte[] toConcrete(Expr value, Purpose purpose) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long sizeOf(Expr value) {
- throw new UnsupportedOperationException();
- }
-}
+ public Expr modAfterLoad(int sizeinOffset, AddressSpace space, Expr inOffset,
+ int sizeinValue, Expr inValue) {
+ return inValue;
+ }
+
+ @Override
+ public Expr fromConst(byte[] value) {
+ if (endian.isBigEndian()) {
+ return new LitExpr(new BigInteger(1, value), value.length);
+ }
+ byte[] reversed = Arrays.copyOf(value, value.length);
+ ArrayUtils.reverse(reversed);
+ return new LitExpr(new BigInteger(1, reversed), reversed.length);
+ }
+
+ @Override
+ public Expr fromConst(BigInteger value, int size, boolean isContextreg) {
+ return new LitExpr(value, size);
+ }
+
+ @Override
+ public Expr fromConst(long value, int size) {
+ return fromConst(BigInteger.valueOf(value), size);
+ }
+
+ @Override
+ public byte[] toConcrete(Expr value, Purpose purpose) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long sizeOf(Expr value) {
+ throw new UnsupportedOperationException();
+ }
+}
fromConst(). We must
@@ -706,11 +708,10 @@ storage mechanism. For example, were this a dynamic taint analyzer, we
could use modAfterLoad() to record that a value was
retrieved via a tainted address. The inValue parameter
gives the Expr actually retrieved from the emulator’s
-storage, and inAddress gives the address (really just the
-Expr piece) used to retrieve it. Conversely, in
-modBeforeStore(), inValue gives the value
-about to be stored, and inAddress gives the address used to
-store it.inOffset gives the offset used to retrieve it.
+Conversely, in modBeforeStore(), inValue gives
+the value about to be stored, and inOffset gives the offset
+used to store it.
We implement neither toConcrete() nor
sizeOf(). Since we will be augmenting a concrete emulator,
these methods will be provided by the concrete piece. If this model is
diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java
index c958258858..f2ce28f99b 100644
--- a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java
+++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java
@@ -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) {
diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png
index 0bda62bfb5..3955c95344 100644
Binary files a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png differ
diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png
index a4cf484439..26f01c8170 100644
Binary files a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png differ
diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png
index 9c373cddcc..79b1d06403 100644
Binary files a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png differ
diff --git a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png
index 7660499e0c..73022b83b2 100644
Binary files a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png and b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png differ
diff --git a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png
index 4f6f2a0b7a..a5482ea5f6 100644
Binary files a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png and b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png differ