diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest index c6c0360437..2e722617b4 100644 --- a/Ghidra/Debug/Debugger/certification.manifest +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -29,12 +29,12 @@ src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disab src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png||GHIDRA||||END| @@ -132,35 +132,21 @@ src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html||GHID src/main/help/help/topics/DebuggerWatchesPlugin/images/DebuggerWatchesPlugin.png||GHIDRA||||END| src/main/resources/defaultTools/Debugger.tool||GHIDRA||||END| src/main/resources/images/add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/resources/images/alt-breakpoint-clear.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-disable.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-enable.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-mixed-de.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-mixed-ed.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-set.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-clear-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-disable-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-enable-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-make-effective.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints.png||GHIDRA||||END| src/main/resources/images/attach.png||GHIDRA||||END| src/main/resources/images/autoread.png||GHIDRA||||END| src/main/resources/images/blank.png||GHIDRA||||END| src/main/resources/images/breakpoint-clear.png||GHIDRA||||END| +src/main/resources/images/breakpoint-disable-ineff.png||GHIDRA||||END| src/main/resources/images/breakpoint-disable.png||GHIDRA||||END| +src/main/resources/images/breakpoint-enable-ineff.png||GHIDRA||||END| src/main/resources/images/breakpoint-enable.png||GHIDRA||||END| -src/main/resources/images/breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/resources/images/breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/resources/images/breakpoint-mixed-de.png||GHIDRA||||END| -src/main/resources/images/breakpoint-mixed-ed.png||GHIDRA||||END| -src/main/resources/images/breakpoint-set.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed-ineff.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed.png||GHIDRA||||END| +src/main/resources/images/breakpoint-overlay-inconsistent.png||GHIDRA||||END| src/main/resources/images/breakpoints-clear-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-disable-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-enable-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-make-effective.png||GHIDRA||||END| -src/main/resources/images/breakpoints.png||GHIDRA||||END| src/main/resources/images/closedFolder.png||Modified Nuvola Icons - LGPL 2.1||||END| src/main/resources/images/conf.png||GHIDRA||||END| src/main/resources/images/connect.png||GHIDRA||||END| @@ -204,18 +190,17 @@ src/main/resources/images/write-trace.png||Tango Icons - Public Domain||||END| src/main/svg/attach.svg||GHIDRA||||END| src/main/svg/blank.svg||GHIDRA||||END| src/main/svg/breakpoint-clear.svg||GHIDRA||||END| +src/main/svg/breakpoint-disable-ineff.svg||GHIDRA||||END| src/main/svg/breakpoint-disable.svg||GHIDRA||||END| +src/main/svg/breakpoint-enable-ineff.svg||GHIDRA||||END| src/main/svg/breakpoint-enable.svg||GHIDRA||||END| -src/main/svg/breakpoint-ineffective-d.svg||GHIDRA||||END| -src/main/svg/breakpoint-ineffective-e.svg||GHIDRA||||END| -src/main/svg/breakpoint-mixed-de.svg||GHIDRA||||END| -src/main/svg/breakpoint-mixed-ed.svg||GHIDRA||||END| -src/main/svg/breakpoint-set.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed-ineff.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed.svg||GHIDRA||||END| +src/main/svg/breakpoint-overlay-inconsistent.svg||GHIDRA||||END| src/main/svg/breakpoints-clear-all.svg||GHIDRA||||END| src/main/svg/breakpoints-disable-all.svg||GHIDRA||||END| src/main/svg/breakpoints-enable-all.svg||GHIDRA||||END| src/main/svg/breakpoints-make-effective.svg||GHIDRA||||END| -src/main/svg/breakpoints.svg||GHIDRA||||END| src/main/svg/connect.svg||GHIDRA||||END| src/main/svg/console.svg||GHIDRA||||END| src/main/svg/continue.svg||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html index 8c45dd570b..d79889ac73 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html @@ -24,38 +24,41 @@

For a description of how breakpoints are managed logically in Ghidra, please read about the Breakpoints - window. Each individual breakpoint location is placed in its respective listing. By default, - disabled breakpoints are not readily visible in the dynamic listing. Breakpoints are best - controlled using the static program listing, where they are stored as bookmarks. When this - plugin is active, additional actions are available for managing those bookmarks and their - mapped breakpoints on target, when applicable. The actions in the listing manipulate the - logical breakpoint as a whole, not just the individual location. Furthermore, depending on the - target, locations' states may be bound to others' via a common breakpoint specification.

+ window. Each individual breakpoint location is placed in its respective listing. Breakpoints + are best controlled using the static program listing, where they are stored as bookmarks. When + this plugin is active, additional actions are available for managing breakpoints and their + locations on target. The actions in the static listing manipulate the logical breakpoint as a + whole; whereas, the actions in the dynamic listing tend to manipulate just the locations for + the current target. NOTE: Depending on the connected debugger, locations resulting from + a common specification may not be independently manipulated.

Actions

-

The following actions are added to all listings by the breakpoint marker plugin. They allow - the placement and toggling of breakpoints by address, kind, and length. To set breakpoints on - arbitrary expressions, use the The following actions are added to all disassembly listings by the breakpoint marker plugin. + They allow the placement and toggling of breakpoints by address, kind, and length. To set + breakpoints on arbitrary expressions, use the Set - Breakpoint action of the Commands and Objects window.

+ Breakpoint action of the Objects window. NOTE: These actions may also appear in + other address-based contexts, e.g., the decompiler listing; however, those contexts often lack + any indication of breakpoint presence or state.

-

Toggle +

Toggle Breakpoint (K)

This action is always available, and it is suitable for almost all cases. If there is a breakpoint at the cursor, this simply toggles its state. If there is no breakpoint at the - cursor, this will prompt to set one, giving a reasonable set of default parameters based on the - context at the cursor. At an instruction, it will prefer to set a Software Execution - breakpoint. At defined data, it will prefer to set a Read/Write breakpoint of the size of data. - At undefined data, or if the target does not support the suggested default, the default kind is - left unselected. Please use one of the Set Breakpoint actions to force specific commands. - Please beware: the default parameters are not always acceptable to the connected debugger.

+ cursor, this will behave like Set Breakpoint, giving a reasonable set of default + parameters based on the context at the cursor. At an instruction, it will prefer to set a + Software Execution breakpoint. At defined data, it will prefer to set a Read/Write breakpoint + of the size of data. At undefined data, or if the target does not support the suggested + default, the default kind is left unselected. Please use one of the Set Breakpoint + actions to force specific commands. NOTE: The default parameters are not guaranteed to + be accepted by the connected debugger.

Set Breakpoint

-

This action is available on the dynamic listing when the target supports at least one +

This menu is available on the dynamic listing when the target supports at least one breakpoint kind. This menu is always available on the static listing. It displays set breakpoint actions for each reasonable combination of kinds supported by the target. In the static listing, all reasonable combinations are available, regardless of target support; @@ -74,19 +77,19 @@

Enable @@ -111,7 +114,7 @@

The background coloring of enabled and disabled, effective and ineffective breakpoints can be configured in the tool's options. By default, enabled breakpoints are colored a desaturated - red, ineffective breakpoints are colored grey, and disabled breakpoints have no background at + blue, ineffective breakpoints are colored grey, and disabled breakpoints have no background at all.

diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png index 99a16b7d9f..871b2c670e 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png index e031698377..85735d19c2 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png index 6386998ebc..4d3a03b13c 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png index ea1e926811..2d618a99a7 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html index 5970c45476..6c1a7e9efa 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html @@ -22,18 +22,27 @@ -

Breakpoints refer to any mechanism which may trap execution based on an address. The - breakpoints manager presents all active breakpoints among all open programs and live traces. - Note that dead traces are not considered, and only the breakpoints at the present are - displayed, even if the user has stepped a trace backward. Breakpoints which map to the same - address in the same module, i.e., program image, and otherwise share the same attributes, are - grouped into a logical breakpoint. NOTE: The breakpoints window cannot display or - manipulate breakpoints from a target until that target is recorded into a trace. See the Static - Mappings window for the finer details of mapping traces to imported modules. In this - manner, breakpoints stored in Ghidra programs comprise the current breakpoint set, organized by - address — except for breakpoints outside a known imported module. The top table of the - provider displays logical breakpoints; the bottom table displays breakpoint locations.

+

The breakpoints window tabulates and manipulates breakpoints among all live and traced + targets. Only address-based breakpoints are tabulated. For other traps, e.g., "break on + exception," see the Objects Window. Breakpoints + can also be manipulated from address-based views, especially the disassembly listings. See Breakpoints in + the Listings. Display of breakpoints in other views, e.g., the decompiler, is not yet + implemented.

+ +

Individual breakpoint locations from among the targets are consolidated into logical + breakpoints, based on their addresses in the static listing. The static locations are typically + stored as bookmarks in their respective Ghidra programs, comprising the current breakpoint set. + See the Static Mappings + window for the finer details of address mapping. A breakpoint which cannot be mapped to a + static address becomes its own logical breakpoint at its dynamic address. The top table of the + provider displays logical breakpoints; the bottom table displays breakpoint locations. + NOTE: The breakpoints window cannot display or manipulate breakpoints from a target + until that target is recorded into a trace. Furthermore, dead traces are not included. Nor are + breakpoints from the past included, even when viewing past machine state.

Depending on what is supported by the connected debugger, breakpoints can trap a target when an address or range is executed, read, or written; using software or hardware mechanisms. In @@ -54,75 +63,84 @@ mixed or inconsistent state. This happens quite commonly, e.g., when a breakpoint is placed in a Ghidra program before that program is mapped in any traced target. Once mapped in, the location of that breakpoint in the trace is computed and noted as missing. A logical breakpoint - without any location in a trace (i.e., on an actual target) is called "ineffective" and is - drawn in grey. An enabled logical breakpoint having a disabled location is called - "inconsistent" and its icon will indicate that state. A disabled logical breakpoint having an - enabled location is similarly "inconsistent." Toggling ineffective or inconsistent breakpoints - enables and/or places all locations, bringing it into a consistent enabled state. Toggling it - again disables all locations.

+ without any location in a trace (i.e., on an actual target) is called ineffective and + is drawn in grey, e.g.: . An enabled + logical breakpoint having a disabled location is called inconsistent and its icon will + include an exclamation mark: . A + disabled logical breakpoint having an enabled location is similarly inconsistent. Toggling an + ineffective or inconsistent logical breakpoint enables and/or places all its locations, aiming + for a consistent enabled state. Toggling it again disables all locations.

Tables and Columns

The top table, which lists logical breakpoints, has the following columns:

-

The bottom table, which lists trace breakpoint locations, has the following columns:

+

The bottom table, which lists breakpoint locations, has the following columns:

@@ -131,10 +149,10 @@

The primary purpose of this provider is to manipulate existing breakpoints. It provides the following actions to that end. Breakpoints can also be managed via the Breakpoint - Marker Actions in the disassembly listings.

+ Marker Actions in the listings.

-

Enable - Selected Breakpoints

+

+ Enable

This action is available when one or more breakpoints or locations are selected. It enables each selected breakpoint. For any breakpoint that is already enabled, no action is taken.

@@ -145,8 +163,8 @@

This action is always available. It enables every breakpoint. For any breakpoint that is already enabled, no action is taken.

-

Disable - Selected Breakpoints

+

+ Disable

This action is available when one or more breakpoints or locations are selected. It disables each selected breakpoint. For any breakpoint that is already disabled, no action is taken.

@@ -165,8 +183,7 @@ breakpoints where possible. This action is also offered as a resolution in the console. It appears in the log any time this action is available.

-

Clear - Selected Breakpoints

+

Clear

This action is available when one or more breakpoints or locations are selected. It clears (deletes) each selected 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 9eca75a21e..41ffdea30a 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/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png new file mode 100644 index 0000000000..85735d19c2 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png index 6386998ebc..4d3a03b13c 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png new file mode 100644 index 0000000000..b417f1f897 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png index ea1e926811..2d618a99a7 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png deleted file mode 100644 index cb1d378235..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png deleted file mode 100644 index 43fb693a6a..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png deleted file mode 100644 index 201d4a298d..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png deleted file mode 100644 index 0f9d808294..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png new file mode 100644 index 0000000000..f3a2d726e9 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png new file mode 100644 index 0000000000..6eafda829e Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png index 2988e9da8c..a41a13c678 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png index 3d80965e63..e461ffaad9 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png index 0c2e466dba..9b593e5e8b 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png index b1e1ab4ede..d220d56af1 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png differ diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java index 980e65edb1..c9b1867215 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java @@ -52,7 +52,6 @@ import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesPlugin; import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer; import ghidra.app.services.DebuggerStateEditingService.StateEditingMode; import ghidra.app.services.DebuggerTraceManagerService.BooleanChangeAdapter; -import ghidra.app.services.MarkerService; import ghidra.async.AsyncUtils; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.util.PluginUtils; @@ -94,32 +93,18 @@ public interface DebuggerResources { ImageIcon ICON_SNAP_BACKWARD = ResourceManager.loadImage("images/2leftarrow.png"); ImageIcon ICON_SEEK_PRESENT = ICON_RESUME; - boolean altIcons = Boolean.getBoolean("debugger.breakpoints.alt.icons"); - - ImageIcon ICON_SET_BREAKPOINT = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-set.png") - : ResourceManager.loadImage("images/breakpoint-set.png"); - ImageIcon ICON_CLEAR_BREAKPOINT = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-clear.png") - : ResourceManager.loadImage("images/breakpoint-clear.png"); - ImageIcon ICON_ENABLE_BREAKPOINT = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-enable.png") - : ResourceManager.loadImage("images/breakpoint-enable.png"); + ImageIcon ICON_SET_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-enable.png"); + ImageIcon ICON_CLEAR_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-clear.png"); + ImageIcon ICON_ENABLE_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-enable.png"); ImageIcon ICON_ENABLE_ALL_BREAKPOINTS = - altIcons ? ResourceManager.loadImage("images/alt-breakpoints-enable-all.png") - : ResourceManager.loadImage("images/breakpoints-enable-all.png"); - ImageIcon ICON_DISABLE_BREAKPOINT = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-disable.png") - : ResourceManager.loadImage("images/breakpoint-disable.png"); + ResourceManager.loadImage("images/breakpoints-enable-all.png"); + ImageIcon ICON_DISABLE_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-disable.png"); ImageIcon ICON_DISABLE_ALL_BREAKPOINTS = - altIcons ? ResourceManager.loadImage("images/alt-breakpoints-disable-all.png") - : ResourceManager.loadImage("images/breakpoints-disable-all.png"); + ResourceManager.loadImage("images/breakpoints-disable-all.png"); ImageIcon ICON_CLEAR_ALL_BREAKPOINTS = - altIcons ? ResourceManager.loadImage("images/alt-breakpoints-clear-all.png") - : ResourceManager.loadImage("images/breakpoints-clear-all.png"); + ResourceManager.loadImage("images/breakpoints-clear-all.png"); ImageIcon ICON_MAKE_BREAKPOINTS_EFFECTIVE = - altIcons ? ResourceManager.loadImage("images/alt-breakpoints-make-effective.png") - : ResourceManager.loadImage("images/breakpoints-make-effective.png"); + ResourceManager.loadImage("images/breakpoints-make-effective.png"); // TODO: Some overlay to indicate dynamic, or new icon altogether ImageIcon ICON_LISTING = ResourceManager.loadImage("images/Browser.gif"); @@ -127,7 +112,7 @@ public interface DebuggerResources { ImageIcon ICON_CONSOLE = ResourceManager.loadImage("images/console.png"); ImageIcon ICON_REGISTERS = ResourceManager.loadImage("images/registers.png"); ImageIcon ICON_STACK = ResourceManager.loadImage("images/stack.png"); - ImageIcon ICON_BREAKPOINTS = ResourceManager.loadImage("images/breakpoints.png"); + ImageIcon ICON_BREAKPOINTS = ResourceManager.loadImage("images/breakpoint-mixed.png"); ImageIcon ICON_MODULES = ResourceManager.loadImage("images/modules.png"); ImageIcon ICON_MAPPINGS = ICON_PROGRAM; // TODO: A better icon ImageIcon ICON_PCODE = ResourceManager.loadImage("images/stepinto.png"); // TODO @@ -312,33 +297,36 @@ public interface DebuggerResources { String OPTION_NAME_COLORS_PCODE_COUNTER = "Colors.Pcode Counter"; Color DEFAULT_COLOR_PCODE_COUNTER = new Color(0.75f, 0.875f, 0.75f); - String MARKER_NAME_BREAKPOINT_ENABLED = "Enabled Breakpoint"; - String MARKER_NAME_BREAKPOINT_DISABLED = "Disabled Breakpoint"; - String MARKER_NAME_BREAKPOINT_INEFFECTIVE_E = "Ineffective Enabled Breakpoint"; - String MARKER_NAME_BREAKPOINT_INEFFECTIVE_D = "Ineffective Disabled Breakpoint"; - String MARKER_NAME_BREAKPOINT_MIXED_ED = "Mixed Enabled-Disabled Breakpont"; - String MARKER_NAME_BREAKPOINT_MIXED_DE = "Mixed Disabled-Enabled Breakpont"; - int PRIORITY_BREAKPOINT_ENABLED_MARKER = MarkerService.BREAKPOINT_PRIORITY; - int PRIORITY_BREAKPOINT_DISABLED_MARKER = MarkerService.BREAKPOINT_PRIORITY; - int PRIORITY_BREAKPOINT_INEFFECTIVE_E_MARKER = MarkerService.BREAKPOINT_PRIORITY; - int PRIORITY_BREAKPOINT_INEFFECTIVE_D_MARKER = MarkerService.BREAKPOINT_PRIORITY; - int PRIORITY_BREAKPOINT_MIXED_ED_MARKER = MarkerService.BREAKPOINT_PRIORITY; - int PRIORITY_BREAKPOINT_MIXED_DE_MARKER = MarkerService.BREAKPOINT_PRIORITY; + String NAME_BREAKPOINT_MARKER_ENABLED = "Enabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_DISABLED = "Disabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_MIXED = "Mixed Breakpoint"; + String NAME_BREAKPOINT_MARKER_INEFF_EN = "Ineffective Enabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_INEFF_DIS = "Ineffective Disabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_INEFF_MIX = "Ineffective Mixed Breakpoint"; + String NAME_BREAKPOINT_MARKER_INCON_EN = "Inconsistent Enabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_INCON_DIS = "Inconsistent Disabled Breakpoint"; + String NAME_BREAKPOINT_MARKER_INCON_MIX = "Inconsistent Mixed Breakpoint"; - ImageIcon ICON_BREAKPOINT_ENABLED_MARKER = ICON_ENABLE_BREAKPOINT; - ImageIcon ICON_BREAKPOINT_DISABLED_MARKER = ICON_DISABLE_BREAKPOINT; - ImageIcon ICON_BREAKPOINT_MIXED_ED_MARKER = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-mixed-ed.png") - : ResourceManager.loadImage("images/breakpoint-mixed-ed.png"); - ImageIcon ICON_BREAKPOINT_MIXED_DE_MARKER = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-mixed-de.png") - : ResourceManager.loadImage("images/breakpoint-mixed-de.png"); - ImageIcon ICON_BREAKPOINT_INEFFECTIVE_E_MARKER = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-ineffective-e.png") - : ResourceManager.loadImage("images/breakpoint-ineffective-e.png"); - ImageIcon ICON_BREAKPOINT_INEFFECTIVE_D_MARKER = - altIcons ? ResourceManager.loadImage("images/alt-breakpoint-ineffective-d.png") - : ResourceManager.loadImage("images/breakpoint-ineffective-d.png"); + ImageIcon ICON_BREAKPOINT_OVERLAY_INCONSISTENT = + ResourceManager.loadImage("images/breakpoint-overlay-inconsistent.png"); + ImageIcon ICON_BREAKPOINT_MARKER_ENABLED = ICON_ENABLE_BREAKPOINT; + ImageIcon ICON_BREAKPOINT_MARKER_DISABLED = ICON_DISABLE_BREAKPOINT; + ImageIcon ICON_BREAKPOINT_MARKER_MIXED = + ResourceManager.loadImage("images/breakpoint-mixed.png"); + + ImageIcon ICON_BREAKPOINT_MARKER_INEFF_EN = + ResourceManager.loadImage("images/breakpoint-enable-ineff.png"); + ImageIcon ICON_BREAKPOINT_MARKER_INEFF_DIS = + ResourceManager.loadImage("images/breakpoint-disable-ineff.png"); + ImageIcon ICON_BREAKPOINT_MARKER_INEFF_MIX = + ResourceManager.loadImage("images/breakpoint-mixed-ineff.png"); + + Icon ICON_BREAKPOINT_MARKER_INCON_EN = + new MultiIcon(ICON_BREAKPOINT_MARKER_ENABLED, ICON_BREAKPOINT_OVERLAY_INCONSISTENT); + Icon ICON_BREAKPOINT_MARKER_INCON_DIS = + new MultiIcon(ICON_BREAKPOINT_MARKER_DISABLED, ICON_BREAKPOINT_OVERLAY_INCONSISTENT); + Icon ICON_BREAKPOINT_MARKER_INCON_MIX = + new MultiIcon(ICON_BREAKPOINT_MARKER_MIXED, ICON_BREAKPOINT_OVERLAY_INCONSISTENT); Icon ICON_UNIQUE_REF_READ = new RotateIcon(ResourceManager.loadImage("images/cursor_arrow.gif"), 180); // TODO @@ -346,16 +334,16 @@ public interface DebuggerResources { Icon ICON_UNIQUE_REF_RW = new MultiIcon(ICON_UNIQUE_REF_READ, ICON_UNIQUE_REF_WRITE); // TODO String OPTION_NAME_COLORS_ENABLED_BREAKPOINT_MARKERS = "Colors.Enabled Breakpoint Markers"; - Color DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS = new Color(0.875f, 0.75f, 0.75f); + Color DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS = new Color(0.75f, 0.75f, 0.875f); String OPTION_NAME_COLORS_DISABLED_BREAKPOINT_MARKERS = "Colors.Disabled Breakpoint Markers"; Color DEFAULT_COLOR_DISABLED_BREAKPOINT_MARKERS = DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS; - String OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS = + String OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_MARKERS = "Colors.Ineffective Enabled Breakpoint Markers"; - Color DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_MARKERS = new Color(0.75f, 0.75f, 0.75f); - String OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS = + Color DEFAULT_COLOR_INEFF_EN_BREAKPOINT_MARKERS = new Color(0.75f, 0.75f, 0.75f); + String OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_MARKERS = "Colors.Ineffective Disabled Breakpoint Markers"; - Color DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_MARKERS = - DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_MARKERS; + Color DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_MARKERS = + DEFAULT_COLOR_INEFF_EN_BREAKPOINT_MARKERS; String OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND = "Colors.Enabled Breakpoint Markers Have Background"; @@ -365,13 +353,13 @@ public interface DebuggerResources { "Colors.Disabled Breakpoint Markers Have Background"; boolean DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND = false; - String OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND = + String OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND = "Colors.Ineffective Enabled Breakpoint Markers Have Background"; - boolean DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND = true; + boolean DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND = true; - String OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND = + String OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND = "Colors.Ineffective Disabled Breakpoint Markers Have Background"; - boolean DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND = false; + boolean DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND = false; String OPTION_NAME_LOG_BUFFER_LIMIT = "Log Buffer Size"; int DEFAULT_LOG_BUFFER_LIMIT = 100; @@ -1165,7 +1153,7 @@ public interface DebuggerResources { abstract class AbstractToggleBreakpointAction extends DockingAction { public static final String NAME = "Toggle Breakpoint"; // TODO: A "toggle breakpoint" icon - public static final Icon ICON = ICON_BREAKPOINT_MIXED_ED_MARKER; + public static final Icon ICON = ICON_BREAKPOINT_MARKER_MIXED; public static final String HELP_ANCHOR = "toggle_breakpoint"; public AbstractToggleBreakpointAction(Plugin owner) { @@ -1210,7 +1198,7 @@ public interface DebuggerResources { } abstract class AbstractEnableSelectedBreakpointsAction extends DockingAction { - public static final String NAME = "Enable Breakpoints"; + public static final String NAME = "Enable"; public static final Icon ICON = ICON_ENABLE_BREAKPOINT; public static final String HELP_ANCHOR = "enable_breakpoints"; @@ -1247,7 +1235,7 @@ public interface DebuggerResources { } abstract class AbstractDisableSelectedBreakpointsAction extends DockingAction { - public static final String NAME = "Disable Breakpoints"; + public static final String NAME = "Disable"; public static final Icon ICON = ICON_DISABLE_BREAKPOINT; public static final String HELP_ANCHOR = "disable_breakpoints"; @@ -1286,7 +1274,7 @@ public interface DebuggerResources { } abstract class AbstractClearSelectedBreakpointsAction extends DockingAction { - public static final String NAME = "Clear Breakpoints"; + public static final String NAME = "Clear"; public static final Icon ICON = ICON_CLEAR_BREAKPOINT; public static final String HELP_ANCHOR = "clear_breakpoints"; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/BreakpointLocationRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/BreakpointLocationRow.java index b83dc0a1c3..796c32a484 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/BreakpointLocationRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/BreakpointLocationRow.java @@ -15,10 +15,12 @@ */ package ghidra.app.plugin.core.debug.gui.breakpoint; +import java.util.Set; import java.util.stream.Collectors; +import ghidra.app.services.LogicalBreakpoint; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.services.TraceRecorder; -import ghidra.dbg.target.TargetBreakpointLocation; import ghidra.program.model.address.Address; import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.thread.TraceThread; @@ -42,24 +44,34 @@ public class BreakpointLocationRow { return recorder != null && loc.isEnabled(recorder.getSnap()); } + public State getState() { + LogicalBreakpoint lb = provider.breakpointService.getBreakpoint(loc); + if (lb == null) { + return State.NONE; // Should only happen in transition + } + return lb.computeStateForLocation(loc); + } + public void setEnabled(boolean enabled) { - // TODO: Make this toggle the individual location, if possible, not the whole spec. - TraceRecorder recorder = provider.modelService.getRecorder(loc.getTrace()); - TargetBreakpointLocation bpt = recorder.getTargetBreakpoint(loc); if (enabled) { - bpt.getSpecification().enable().exceptionally(ex -> { + provider.breakpointService.enableLocs(Set.of(loc)).exceptionally(ex -> { provider.breakpointError("Toggle breakpoint", "Could not enable breakpoint", ex); return null; }); } else { - bpt.getSpecification().disable().exceptionally(ex -> { + provider.breakpointService.disableLocs(Set.of(loc)).exceptionally(ex -> { provider.breakpointError("Toggle breakpoint", "Could not disable breakpoint", ex); return null; }); } } + public void setState(State state) { + assert state.isNormal(); + setEnabled(state.isEnabled()); + } + public void setName(String name) { try (UndoableTransaction tid = UndoableTransaction.start(loc.getTrace(), "Set breakpoint name", true)) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java deleted file mode 100644 index a8a09b7050..0000000000 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* ### - * 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.breakpoint; - -import java.awt.Component; - -import javax.swing.Icon; -import javax.swing.SwingConstants; - -import docking.widgets.table.GTableCellRenderingData; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; -import ghidra.app.services.LogicalBreakpoint.Enablement; -import ghidra.docking.settings.Settings; -import ghidra.util.table.column.AbstractGColumnRenderer; - -public class DebuggerBreakpointEnablementTableCellRenderer - extends AbstractGColumnRenderer { - - protected static Icon iconForEnablement(Enablement en) { - switch (en) { - case NONE: - return null; - case ENABLED: - return DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER; - case DISABLED: - return DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER; - case INEFFECTIVE_ENABLED: - return DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_E_MARKER; - case INEFFECTIVE_DISABLED: - return DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_D_MARKER; - case ENABLED_DISABLED: - return DebuggerResources.ICON_BREAKPOINT_MIXED_ED_MARKER; - case DISABLED_ENABLED: - return DebuggerResources.ICON_BREAKPOINT_MIXED_DE_MARKER; - default: - throw new AssertionError(en); - } - } - - public DebuggerBreakpointEnablementTableCellRenderer() { - setHorizontalAlignment(SwingConstants.CENTER); - } - - @Override - public Component getTableCellRendererComponent(GTableCellRenderingData data) { - super.getTableCellRendererComponent(data); - Enablement en = (Enablement) data.getValue(); - setIcon(iconForEnablement(en)); - setHorizontalAlignment(SwingConstants.CENTER); - setText(""); - setToolTipText(en.name()); - return this; - } - - @Override - public String getFilterString(Enablement t, Settings settings) { - return t.name(); - } -} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java deleted file mode 100644 index 30f3651230..0000000000 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java +++ /dev/null @@ -1,67 +0,0 @@ -/* ### - * 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.breakpoint; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicButtonUI; -import javax.swing.table.TableCellEditor; - -public class DebuggerBreakpointLocEnabledTableCellEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener { - protected final JButton button = new JButton(); - - private Boolean value = false; - - public DebuggerBreakpointLocEnabledTableCellEditor() { - button.setHorizontalAlignment(SwingConstants.CENTER); - button.setOpaque(true); - button.setBorder(BorderFactory.createEmptyBorder()); - button.setUI(new BasicButtonUI()); - - button.addActionListener(this); - } - - @Override - public Object getCellEditorValue() { - return value; - } - - @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, - int row, int column) { - if (isSelected) { - button.setBackground(table.getSelectionBackground()); - } - else { - // TODO: Alternating colors? Can't inherit GTableCellRenderer.... - button.setBackground(table.getBackground()); - } - this.value = (Boolean) value; - button.setIcon(DebuggerBreakpointLocEnabledTableCellRenderer.iconForEnabled(this.value)); - button.setHorizontalAlignment(SwingConstants.CENTER); - return button; - } - - @Override - public void actionPerformed(ActionEvent e) { - value = value == null ? true : !value; - fireEditingStopped(); - } -} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java index 75b7bcf84d..a57df3770c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java @@ -16,18 +16,23 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.Collection; +import java.util.stream.Collectors; import docking.ActionContext; import ghidra.trace.model.breakpoint.TraceBreakpoint; public class DebuggerBreakpointLocationsActionContext extends ActionContext { - private final Collection selection; + private final Collection selection; - public DebuggerBreakpointLocationsActionContext(Collection selection) { + public DebuggerBreakpointLocationsActionContext(Collection selection) { this.selection = selection; } - public Collection getSelection() { + public Collection getSelection() { return selection; } + + public Collection getLocations() { + return selection.stream().map(row -> row.getTraceBreakpoint()).collect(Collectors.toList()); + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java index bf24ac7145..f706b909d9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java @@ -36,7 +36,7 @@ import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.MarkerClickedListener; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; @@ -216,18 +216,51 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return result; } + protected Color colorForState(State state) { + if (state.isEnabled()) { + if (state.isEffective()) { + return breakpointEnabledMarkerColor; + } + else { + return breakpointIneffEnMarkerColor; + } + } + else { + if (state.isEffective()) { + return breakpointDisabledMarkerColor; + } + else { + return breakpointIneffDisMarkerColor; + } + } + } + + protected boolean stateColorsBackground(State state) { + if (state.isEnabled()) { + if (state.isEffective()) { + return breakpointEnabledColoringBackground; + } + else { + return breakpointIneffEnColoringBackground; + } + } + else { + if (state.isEffective()) { + return breakpointDisabledColoringBackground; + } + else { + return breakpointIneffDisColoringBackground; + } + } + } + /** * A variety of marker sets (one for each logical state) attached to a program or trace view */ protected class BreakpointMarkerSets { final Program program; - final MarkerSet enabled; - final MarkerSet disabled; - final MarkerSet ineffectiveE; - final MarkerSet ineffectiveD; - final MarkerSet mixedED; - final MarkerSet mixedDE; + final Map sets = new HashMap<>(); protected BreakpointMarkerSets(Program program) { this.program = program; @@ -238,227 +271,122 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin manager.defineType(LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, DebuggerResources.ICON_BLANK, DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS, - DebuggerResources.PRIORITY_BREAKPOINT_ENABLED_MARKER - 1); + MarkerService.BREAKPOINT_PRIORITY - 1); manager.defineType(LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, DebuggerResources.ICON_BLANK, DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS, - DebuggerResources.PRIORITY_BREAKPOINT_DISABLED_MARKER - 1); + MarkerService.BREAKPOINT_PRIORITY - 1); } - enabled = getEnabledMarkerSet(); - disabled = getDisabledMarkerSet(); - ineffectiveE = getIneffectiveEMarkerSet(); - ineffectiveD = getIneffectiveDMarkerSet(); - mixedED = getMixedEDMarkerSet(); - mixedDE = getMixedDEMarkerSet(); + for (State state : State.values()) { + getMarkerSet(state); + } } - private MarkerSet getEnabledMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, program); + MarkerSet getMarkerSet(State state) { + return sets.computeIfAbsent(state, this::doGetMarkerSet); + } + + MarkerSet doGetMarkerSet(State state) { + if (state.icon == null) { + return null; + } + MarkerSet set = markerService.getMarkerSet(state.display, program); if (set != null) { return set; } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, - DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, program, - DebuggerResources.PRIORITY_BREAKPOINT_ENABLED_MARKER, true, true, true, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER, - true); - } - - private MarkerSet getDisabledMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, - DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, program, - DebuggerResources.PRIORITY_BREAKPOINT_DISABLED_MARKER, true, false, false, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER, - false); - } - - private MarkerSet getIneffectiveEMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, program, - DebuggerResources.PRIORITY_BREAKPOINT_INEFFECTIVE_E_MARKER, true, false, true, - breakpointIneffectiveEMarkerColor, - DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_E_MARKER, - false); - } - - private MarkerSet getIneffectiveDMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, program, - DebuggerResources.PRIORITY_BREAKPOINT_INEFFECTIVE_D_MARKER, true, false, false, - breakpointIneffectiveDMarkerColor, - DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_D_MARKER, - false); - } - - private MarkerSet getMixedEDMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, program, - DebuggerResources.PRIORITY_BREAKPOINT_MIXED_ED_MARKER, true, true, true, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_MIXED_ED_MARKER, - false); - } - - private MarkerSet getMixedDEMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, program, - DebuggerResources.PRIORITY_BREAKPOINT_MIXED_DE_MARKER, true, false, false, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_MIXED_DE_MARKER, - false); - } - - MarkerSet get(Enablement en) { - switch (en) { - case ENABLED: - return enabled; - case DISABLED: - return disabled; - case INEFFECTIVE_ENABLED: - return ineffectiveE; - case INEFFECTIVE_DISABLED: - return ineffectiveD; - case ENABLED_DISABLED: - return mixedED; - case DISABLED_ENABLED: - return mixedDE; - case NONE: - return null; - default: - throw new AssertionError(); - } + return markerService.createPointMarker(state.display, state.display, program, + MarkerService.BREAKPOINT_PRIORITY, true, true, stateColorsBackground(state), + colorForState(state), state.icon, true); } public void setEnabledMarkerColor(Color color) { - if (enabled != null) { - enabled.setMarkerColor(color); - } - if (mixedED != null) { - mixedED.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setDisabledMarkerColor(Color color) { - if (disabled != null) { - disabled.setMarkerColor(color); - } - if (mixedDE != null) { - mixedDE.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setIneffectiveEnabledMarkerColor(Color color) { - if (ineffectiveE != null) { - ineffectiveE.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setIneffectiveDisabledMarkerColor(Color color) { - if (ineffectiveD != null) { - ineffectiveD.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setEnabledColoringBackground(boolean coloringBackground) { - if (enabled != null) { - enabled.setColoringBackground(coloringBackground); - } - if (mixedED != null) { - mixedED.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setDisabledColoringBackground(boolean coloringBackground) { - if (disabled != null) { - disabled.setColoringBackground(coloringBackground); - } - if (mixedDE != null) { - mixedDE.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setIneffectiveEnabledColoringBackground(boolean coloringBackground) { - if (ineffectiveE != null) { - ineffectiveE.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setIneffectiveDisabledColoringBackground(boolean coloringBackground) { - if (ineffectiveD != null) { - ineffectiveD.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void dispose() { - if (enabled != null) { - markerService.removeMarker(enabled, program); - } - if (disabled != null) { - markerService.removeMarker(disabled, program); - } - if (ineffectiveE != null) { - markerService.removeMarker(ineffectiveE, program); - } - if (ineffectiveD != null) { - markerService.removeMarker(ineffectiveD, program); - } - if (mixedED != null) { - markerService.removeMarker(mixedED, program); - } - if (mixedDE != null) { - markerService.removeMarker(mixedDE, program); + for (State state : State.values()) { + MarkerSet set = sets.get(state); + if (set != null) { + markerService.removeMarker(set, program); + } } } public void clear() { - if (enabled != null) { - enabled.clearAll(); - } - if (disabled != null) { - disabled.clearAll(); - } - if (ineffectiveE != null) { - ineffectiveE.clearAll(); - } - if (ineffectiveD != null) { - ineffectiveD.clearAll(); - } - if (mixedED != null) { - mixedED.clearAll(); - } - if (mixedDE != null) { - mixedDE.clearAll(); + for (State state : State.values()) { + MarkerSet set = sets.get(state); + if (set != null) { + set.clearAll(); + } } } } @@ -493,35 +421,27 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } } - protected static Enablement computeEnablement(LogicalBreakpoint breakpoint, - Program programOrView) { + protected static State computeState(LogicalBreakpoint breakpoint, Program programOrView) { if (programOrView instanceof TraceProgramView) { TraceProgramView view = (TraceProgramView) programOrView; - return breakpoint.computeEnablementForTrace(view.getTrace()); + return breakpoint.computeStateForTrace(view.getTrace()); } // Program view should consider all trace placements // TODO: A mode for only considering the current trace (for effectiveness in program) - return breakpoint.computeEnablement(); + return breakpoint.computeState(); } /** - * TODO: Document me - * - *

- * This is a little different from that in the breakpoint service. + * It seems the purpose of this was to omit the program mode from the dynamic listing. I don't + * think we need that anymore, so I've just delegated to exactly the same as the breakpoint + * service, which will include the program mode, if applicable. TODO: Remove this and just call + * the service's version directly? * * @param loc * @return */ - protected Enablement computeEnablement(ProgramLocation loc) { - Program programOrView = loc.getProgram(); - if (programOrView instanceof TraceProgramView) { - return breakpointService.computeEnablement(loc).getPrimary(); - } - // Program view should consider all trace breakpoints, too - // breakpointService.computeEnablement(loc) only considers program breakpoint - Set bs = breakpointService.getBreakpointsAt(loc); - return breakpointService.computeEnablement(bs); + protected State computeState(ProgramLocation loc) { + return breakpointService.computeState(loc); } protected class ToggleBreakpointAction extends AbstractToggleBreakpointAction { @@ -623,8 +543,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.ENABLED || en == Enablement.NONE) { + State state = computeState(location); + if (state == State.ENABLED || state == State.NONE) { return false; } return true; @@ -660,8 +580,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.DISABLED || en == Enablement.NONE) { + State state = computeState(location); + if (state == State.DISABLED || state == State.NONE) { return false; } return true; @@ -699,8 +619,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.NONE) { + State state = computeState(location); + if (state == State.NONE) { return false; } return true; @@ -751,32 +671,32 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_MARKERS, // description = "Background color for memory at an enabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private Color breakpointIneffectiveEMarkerColor = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_MARKERS; + private Color breakpointIneffEnMarkerColor = + DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_MARKERS; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND, // description = "Whether or not to color background for memory at an enabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private boolean breakpointIneffectiveEColoringBackground = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND; + private boolean breakpointIneffEnColoringBackground = + DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_MARKERS, // description = "Background color for memory at an disabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private Color breakpointIneffectiveDMarkerColor = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_MARKERS; + private Color breakpointIneffDisMarkerColor = + DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_MARKERS; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND, // description = "Whether or not to color background for memory at an disabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private boolean breakpointIneffectiveDColoringBackground = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND; + private boolean breakpointIneffDisColoringBackground = + DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND; @SuppressWarnings("unused") private final AutoOptions.Wiring autoOptionsWiring; @@ -789,7 +709,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin new ToggleBreakpointsMarkerClickedListener(); private final AsyncDebouncer updateDebouncer = - new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 100); + new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 50); // package access for testing SetBreakpointAction actionSetSoftwareBreakpoint; @@ -851,7 +771,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_MARKERS) private void setIneffectiveEBreakpointMarkerColor(Color breakpointMarkerColor) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveEnabledMarkerColor(breakpointMarkerColor); @@ -859,7 +779,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND) private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground); @@ -867,7 +787,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_MARKERS) private void setIneffectiveDBreakpointMarkerColor(Color breakpointMarkerColor) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveDisabledMarkerColor(breakpointMarkerColor); @@ -875,7 +795,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND) private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground); @@ -961,7 +881,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin placeBreakpointDialog.prompt(tool, breakpointService, title, loc, length, kinds, ""); return; } - Enablement en = breakpointService.computeEnablement(bs, loc); + State state = breakpointService.computeState(bs, loc); /** * If we're in the static listing, this will return null, indicating we should use the * program's perspective. The methods taking trace should accept a null trace and behave @@ -969,8 +889,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin */ Trace trace = getTraceFromContext(context); boolean mapped = breakpointService.anyMapped(bs, trace); - Enablement toggled = en.getToggled(mapped && trace == null); - if (toggled.enabled) { + State toggled = state.getToggled(mapped); + if (toggled.isEnabled()) { breakpointService.enableAll(bs, trace).exceptionally(ex -> { breakpointError(title, "Could not enable breakpoints", ex); return null; @@ -1008,17 +928,17 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin protected void doMarks(BreakpointMarkerSets marks, Map> byAddress, - java.util.function.Function enFunc) { + java.util.function.Function stateFunc) { for (Map.Entry> bEnt : byAddress.entrySet()) { - Map en = new HashMap<>(); + Map byLength = new HashMap<>(); for (LogicalBreakpoint lb : bEnt.getValue()) { - en.compute(lb.getLength(), (l, e) -> (e == null ? Enablement.NONE : e) - .sameAdddress(enFunc.apply(lb))); + byLength.compute(lb.getLength(), (l, e) -> (e == null ? State.NONE : e) + .sameAdddress(stateFunc.apply(lb))); } Address start = bEnt.getKey(); - for (Map.Entry eEnt : en.entrySet()) { - Address end = start.add(eEnt.getKey() - 1); - MarkerSet set = marks.get(eEnt.getValue()); + for (Map.Entry sEnt : byLength.entrySet()) { + Address end = start.add(sEnt.getKey() - 1); + MarkerSet set = marks.getMarkerSet(sEnt.getValue()); if (set != null) { set.add(start, end); } @@ -1041,11 +961,11 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin TraceProgramView view = (TraceProgramView) program; Trace trace = view.getTrace(); doMarks(marks, breakpointService.getBreakpoints(trace), - lb -> lb.computeEnablementForTrace(trace)); + lb -> lb.computeStateForTrace(trace)); } else { doMarks(marks, breakpointService.getBreakpoints(program), - lb -> lb.computeEnablementForProgram(program)); + lb -> lb.computeStateForProgram(program)); } } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java similarity index 75% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java index 2a136d91f8..90f34e4b39 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java @@ -24,18 +24,18 @@ import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.table.TableCellEditor; import docking.widgets.table.GTableFilterPanel; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; -public class DebuggerBreakpointEnablementTableCellEditor extends AbstractCellEditor +public abstract class DebuggerBreakpointStateTableCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { - private final GTableFilterPanel filterPanel; + private final GTableFilterPanel filterPanel; protected final JButton button = new JButton(); - private Enablement value = Enablement.NONE; - private LogicalBreakpointRow row; + private State value = State.NONE; + private T row; - public DebuggerBreakpointEnablementTableCellEditor( - GTableFilterPanel filterPanel) { + public DebuggerBreakpointStateTableCellEditor( + GTableFilterPanel filterPanel) { this.filterPanel = filterPanel; button.setHorizontalAlignment(SwingConstants.CENTER); @@ -62,16 +62,17 @@ public class DebuggerBreakpointEnablementTableCellEditor extends AbstractCellEdi button.setBackground(table.getBackground()); } this.row = filterPanel.getRowObject(row); - this.value = (Enablement) value; - button.setIcon(DebuggerBreakpointEnablementTableCellRenderer.iconForEnablement(this.value)); + this.value = (State) value; + button.setIcon(this.value.icon); button.setHorizontalAlignment(SwingConstants.CENTER); return button; } @Override public void actionPerformed(ActionEvent e) { - boolean mapped = row.isMapped(); - value = value.getToggled(mapped); + value = getToggledState(row, value); fireEditingStopped(); } + + protected abstract State getToggledState(T row, State current); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java similarity index 61% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java index ed04b0b6da..f970509342 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java @@ -17,41 +17,32 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.awt.Component; -import javax.swing.Icon; import javax.swing.SwingConstants; import docking.widgets.table.GTableCellRenderingData; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.docking.settings.Settings; import ghidra.util.table.column.AbstractGColumnRenderer; -public class DebuggerBreakpointLocEnabledTableCellRenderer - extends AbstractGColumnRenderer { +public class DebuggerBreakpointStateTableCellRenderer extends AbstractGColumnRenderer { - protected static Icon iconForEnabled(Boolean enabled) { - return enabled == null ? null - : enabled - ? DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER - : DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER; - } - - public DebuggerBreakpointLocEnabledTableCellRenderer() { + public DebuggerBreakpointStateTableCellRenderer() { setHorizontalAlignment(SwingConstants.CENTER); } @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); - Boolean en = (Boolean) data.getValue(); - setIcon(iconForEnabled(en)); + State state = (State) data.getValue(); + setIcon(state.icon); setHorizontalAlignment(SwingConstants.CENTER); setText(""); - setToolTipText(en ? "ENABLED" : "DISABLED"); + setToolTipText(state.name()); return this; } @Override - public String getFilterString(Boolean t, Settings settings) { - return t == null ? "null" : t ? "enabled" : "disabled"; + public String getFilterString(State t, Settings settings) { + return t.name(); } } 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 f250acc9f2..c27f2a0187 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 @@ -36,7 +36,7 @@ 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.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.AutoService; import ghidra.framework.plugintool.ComponentProviderAdapter; @@ -59,8 +59,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter protected enum LogicalBreakpointTableColumns implements EnumeratedTableColumn { - ENABLED("", Enablement.class, LogicalBreakpointRow::getEnablement, // - LogicalBreakpointRow::setEnablement, true), + STATE("State", State.class, LogicalBreakpointRow::getState, LogicalBreakpointRow::setState, true), NAME("Name", String.class, LogicalBreakpointRow::getName, LogicalBreakpointRow::setName, // LogicalBreakpointRow::isNamable, true), ADDRESS("Address", Address.class, LogicalBreakpointRow::getAddress, true), @@ -147,7 +146,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter protected enum BreakpointLocationTableColumns implements EnumeratedTableColumn { - ENABLED("", Boolean.class, BreakpointLocationRow::isEnabled, BreakpointLocationRow::setEnabled, true), + STATE("State", State.class, BreakpointLocationRow::getState, BreakpointLocationRow::setState, true), NAME("Name", String.class, BreakpointLocationRow::getName, BreakpointLocationRow::setName, true), ADDRESS("Address", Address.class, BreakpointLocationRow::getAddress, true), TRACE("Trace", String.class, BreakpointLocationRow::getTraceName, true), @@ -222,23 +221,29 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter } } - protected static boolean contextIsNonEmptyBreakpoints(ActionContext context) { + protected static boolean contextHasMatchingBreakpoints(ActionContext context, + Predicate logicalCase, + Predicate locationCase) { if (context == null) { return false; } if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - return !ctx.getSelection().isEmpty(); + return ctx.getSelection().stream().anyMatch(logicalCase); } if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - return !ctx.getSelection().isEmpty(); + return ctx.getSelection().stream().anyMatch(locationCase); } return false; } + protected static boolean contextIsNonEmptyBreakpoints(ActionContext context) { + return contextHasMatchingBreakpoints(context, lb -> true, loc -> true); + } + protected class EnableSelectedBreakpointsAction extends AbstractEnableSelectedBreakpointsAction { public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS; @@ -246,7 +251,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public EnableSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -259,7 +264,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.enableAll(sel, null).exceptionally(ex -> { breakpointError("Enable Breakpoints", "Could not enable breakpoints", ex); return null; @@ -268,7 +273,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.enableLocs(sel).exceptionally(ex -> { breakpointError("Enable Breakpoints", "Could not enable breakpoints", ex); return null; @@ -278,7 +283,14 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter @Override public boolean isEnabledForContext(ActionContext context) { - return contextIsNonEmptyBreakpoints(context); + return contextHasMatchingBreakpoints(context, + row -> row.getState() != State.ENABLED, + row -> row.getState() != State.ENABLED); + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabledForContext(context); } } @@ -314,7 +326,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public DisableSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -327,7 +339,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.disableAll(sel, null).exceptionally(ex -> { breakpointError("Disable Breakpoints", "Could not disable breakpoints", ex); return null; @@ -336,7 +348,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.disableLocs(sel).exceptionally(ex -> { breakpointError("Disable Breakpoints", "Could not disable breakpoints", ex); return null; @@ -346,8 +358,16 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter @Override public boolean isEnabledForContext(ActionContext context) { - return contextIsNonEmptyBreakpoints(context); + return contextHasMatchingBreakpoints(context, + row -> row.getState() != State.DISABLED, + row -> row.getState() != State.DISABLED); } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabledForContext(context); + } + } protected class DisableAllBreakpointsAction extends AbstractDisableAllBreakpointsAction { @@ -381,7 +401,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public ClearSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -391,7 +411,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.deleteAll(sel, null).exceptionally(ex -> { breakpointError("Clear Breakpoints", "Could not clear breakpoints", ex); return null; @@ -400,7 +420,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.deleteLocs(sel).exceptionally(ex -> { breakpointError("Clear Breakpoints", "Could not clear breakpoints", ex); return null; @@ -452,7 +472,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void actionPerformed(ActionContext context) { Set enablable = breakpointService.getAllBreakpoints() .stream() - .filter(lb -> lb.computeEnablement() == Enablement.INEFFECTIVE_ENABLED && + .filter(lb -> lb.computeState() == State.INEFFECTIVE_ENABLED && !lb.getMappedTraces().isEmpty()) .collect(Collectors.toSet()); breakpointService.enableAll(enablable, null).exceptionally(ex -> { @@ -476,7 +496,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter } Set all = breakpointService.getAllBreakpoints(); for (LogicalBreakpoint lb : all) { - if (lb.computeEnablement() != Enablement.INEFFECTIVE_ENABLED) { + if (lb.computeState() != State.INEFFECTIVE_ENABLED) { continue; } if (lb.getMappedTraces().isEmpty()) { @@ -609,7 +629,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter private final DebuggerBreakpointsPlugin plugin; // @AutoServiceConsumed via method - private DebuggerLogicalBreakpointService breakpointService; + DebuggerLogicalBreakpointService breakpointService; // @AutoServiceConsumed via method, package access for BreakpointLogicalRow DebuggerModelService modelService; @AutoServiceConsumed @@ -763,6 +783,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void breakpointUpdated(LogicalBreakpoint lb) { Swing.runIfSwingOrRunLater(() -> { breakpointTableModel.updateItem(lb); + breakpointLocationsUpdated(lb.getTraceBreakpoints()); contextChanged(); }); } @@ -771,6 +792,9 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void breakpointsUpdated(Collection clb) { Swing.runIfSwingOrRunLater(() -> { breakpointTableModel.updateAllItems(clb); + breakpointLocationsUpdated(clb.stream() + .flatMap(lb -> lb.getTraceBreakpoints().stream()) + .collect(Collectors.toSet())); contextChanged(); }); } @@ -817,6 +841,10 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter locationTableModel.updateItem(location); } + private void breakpointLocationsUpdated(Collection locations) { + locationTableModel.updateAllItems(locations); + } + private void breakpointLocationRemoved(TraceBreakpoint location) { locationTableModel.deleteItem(location); } @@ -891,16 +919,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter mainPanel.setResizeWeight(0.5); breakpointTable.getSelectionModel().addListSelectionListener(evt -> { - List set = breakpointFilterPanel.getSelectedItems() - .stream() - .map(LogicalBreakpointRow::getLogicalBreakpoint) - .collect(Collectors.toList()); + List sel = breakpointFilterPanel.getSelectedItems(); // Do this first to prevent overriding context in event chain - if (!set.isEmpty()) { + if (!sel.isEmpty()) { locationTable.clearSelection(); locationTable.getSelectionManager().clearSavedSelection(); } - myActionContext = new DebuggerLogicalBreakpointsActionContext(set); + myActionContext = new DebuggerLogicalBreakpointsActionContext(sel); if (isFilterLocationsByBreakpoints()) { locationTableModel.fireTableDataChanged(); } @@ -930,16 +955,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter }); locationTable.getSelectionModel().addListSelectionListener(evt -> { - List set = locationFilterPanel.getSelectedItems() - .stream() - .map(BreakpointLocationRow::getTraceBreakpoint) - .collect(Collectors.toList()); + List sel = locationFilterPanel.getSelectedItems(); // Do this first to avoid overriding context in event chain - if (!set.isEmpty()) { + if (!sel.isEmpty()) { breakpointTable.clearSelection(); breakpointTable.getSelectionManager().clearSavedSelection(); } - myActionContext = new DebuggerBreakpointLocationsActionContext(set); + myActionContext = new DebuggerBreakpointLocationsActionContext(sel); contextChanged(); }); locationTable.addMouseListener(new MouseAdapter() { @@ -966,10 +988,15 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter TableColumnModel bptColModel = breakpointTable.getColumnModel(); TableColumn bptEnCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.ENABLED.ordinal()); - bptEnCol.setCellRenderer(new DebuggerBreakpointEnablementTableCellRenderer()); + bptColModel.getColumn(LogicalBreakpointTableColumns.STATE.ordinal()); + bptEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); bptEnCol.setCellEditor( - new DebuggerBreakpointEnablementTableCellEditor(breakpointFilterPanel)); + new DebuggerBreakpointStateTableCellEditor<>(breakpointFilterPanel) { + @Override + protected State getToggledState(LogicalBreakpointRow row, State current) { + return current.getToggled(row.isMapped()); + } + }); bptEnCol.setMaxWidth(24); bptEnCol.setMinWidth(24); TableColumn bptNameCol = @@ -991,16 +1018,24 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter bptColModel.getColumn(LogicalBreakpointTableColumns.LOCATIONS.ordinal()); locsCol.setPreferredWidth(20); - TableColumnModel locColModel = locationTable.getColumnModel(); + GTableColumnModel locColModel = (GTableColumnModel) locationTable.getColumnModel(); TableColumn locEnCol = - locColModel.getColumn(BreakpointLocationTableColumns.ENABLED.ordinal()); - locEnCol.setCellRenderer(new DebuggerBreakpointLocEnabledTableCellRenderer()); - locEnCol.setCellEditor(new DebuggerBreakpointLocEnabledTableCellEditor()); + locColModel.getColumn(BreakpointLocationTableColumns.STATE.ordinal()); + locEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); + locEnCol.setCellEditor(new DebuggerBreakpointStateTableCellEditor<>(locationFilterPanel) { + @Override + protected State getToggledState(BreakpointLocationRow row, State current) { + return current.getToggled(false); + } + }); locEnCol.setMaxWidth(24); locEnCol.setMinWidth(24); TableColumn locAddrCol = locColModel.getColumn(BreakpointLocationTableColumns.ADDRESS.ordinal()); locAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); + TableColumn locThreadsCol = + locColModel.getColumn(BreakpointLocationTableColumns.THREADS.ordinal()); + locColModel.setVisible(locThreadsCol, false); } protected void navigateToSelectedBreakpoint() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java index 9b865934e5..5798c96979 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java @@ -16,18 +16,25 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.Collection; +import java.util.stream.Collectors; import docking.ActionContext; import ghidra.app.services.LogicalBreakpoint; public class DebuggerLogicalBreakpointsActionContext extends ActionContext { - private final Collection selection; + private final Collection selection; - public DebuggerLogicalBreakpointsActionContext(Collection selection) { + public DebuggerLogicalBreakpointsActionContext(Collection selection) { this.selection = selection; } - public Collection getSelection() { + public Collection getSelection() { return selection; } + + public Collection getBreakpoints() { + return selection.stream() + .map(row -> row.getLogicalBreakpoint()) + .collect(Collectors.toList()); + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java index 6af9fc4a67..21124be93e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java @@ -18,7 +18,8 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.concurrent.CompletableFuture; import ghidra.app.services.LogicalBreakpoint; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.Mode; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; import ghidra.program.model.address.Address; @@ -43,23 +44,19 @@ public class LogicalBreakpointRow { return lb; } - public Enablement getEnablement() { + public State getState() { return provider.isFilterByCurrentTrace() && provider.currentTrace != null - ? lb.computeEnablementForTrace(provider.currentTrace) - : lb.computeEnablement(); + ? lb.computeStateForTrace(provider.currentTrace) + : lb.computeState(); } - public void setEnablement(Enablement en) { - assert en.consistent && en.effective; - setEnabled(en.enabled); + public void setState(State state) { + assert state.isNormal(); + setEnabled(state.isEnabled()); } - public Boolean isEnabled() { - Enablement en = getEnablement(); - if (!en.consistent) { - return null; - } - return en.enabled && en.effective; + public Mode getMode() { + return getState().mode; } public void setEnabled(boolean enabled) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java index c6ad2933f2..371101b879 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java @@ -47,7 +47,7 @@ import resources.ResourceManager; public class ObjectTree implements ObjectPane { public static final ImageIcon ICON_TREE = - ResourceManager.loadImage("images/breakpoint-disable.png"); + ResourceManager.loadImage("images/object-unpopulated.png"); private ObjectNode root; private GTree tree; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java index e88bfe088d..4a9e099ef3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java @@ -19,6 +19,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.commons.collections4.IteratorUtils; @@ -33,6 +34,7 @@ import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent; import ghidra.app.plugin.core.debug.service.breakpoint.LogicalBreakpointInternal.ProgramBreakpoint; import ghidra.app.services.*; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.dbg.target.TargetBreakpointLocation; import ghidra.dbg.target.TargetObject; import ghidra.dbg.util.PathUtils; @@ -368,7 +370,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin public AbstractInfo() { } - protected abstract void dispose(); + protected abstract void dispose(RemoveCollector r); protected abstract LogicalBreakpointInternal createLogicalBreakpoint(Address address, long length, Collection kinds); @@ -489,12 +491,9 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin } @Override - protected void dispose() { + protected void dispose(RemoveCollector r) { trace.removeListener(breakpointListener); - - try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { - forgetAllBreakpoints(r); - } + forgetAllBreakpoints(r); } protected void reloadBreakpoints() { @@ -559,7 +558,8 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin trackTraceBreakpoint(b, collector, false); } catch (TrackedTooSoonException e) { - throw new AssertionError(e); + // This can still happen during reload (on OBJECT_RESTORED) + Msg.warn(this, "Might have lost track of a breakpoint: " + b); } } } @@ -592,7 +592,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin lb = getOrCreateLogicalBreakpointFor(traceAddr, breakpoint, c); } assert breakpointsByAddress.get(traceAddr).contains(lb); - if (forceUpdate || lb.trackBreakpoint(breakpoint)) { + if (lb.trackBreakpoint(breakpoint) || forceUpdate) { c.updated(lb); } } @@ -691,12 +691,9 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin } @Override - protected void dispose() { + protected void dispose(RemoveCollector r) { program.removeListener(breakpointListener); - - try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { - forgetAllBreakpoints(r); - } + forgetAllBreakpoints(r); } protected void reloadBreakpoints() { @@ -877,8 +874,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin if (program instanceof TraceProgramView) { return; } - programInfos.remove(program).dispose(); - // The mapping removals, if applicable, will clean up related traces + + try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { + programInfos.remove(program).dispose(r); + } + // The mapping removals, if any, will clean up related traces } } @@ -894,9 +894,20 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin private void doUntrackTrace(Trace trace) { synchronized (lock) { - InfoPerTrace info = traceInfos.remove(trace); - if (info != null) { // Could be from trace close or recorder stop - info.dispose(); + InfoPerTrace tInfo = traceInfos.remove(trace); + if (tInfo == null) { // Could be from trace close or recorder stop + return; + } + try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { + tInfo.dispose(r); + for (InfoPerProgram pInfo : programInfos.values()) { + for (Set set : pInfo.breakpointsByAddress.values()) { + for (LogicalBreakpointInternal lb : set) { + lb.removeTrace(trace); + r.updated(lb); + } + } + } } } } @@ -1001,10 +1012,25 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin if (info == null) { return Set.of(); } - return doGetBreakpointsAt(info, address); + return doGetBreakpointsAt(info, address).stream() + .filter(lb -> lb.computeStateForTrace(trace) != State.NONE) + .collect(Collectors.toSet()); } } + @Override + public LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt) { + Trace trace = bpt.getTrace(); + synchronized (lock) { + for (LogicalBreakpoint lb : getBreakpointsAt(trace, bpt.getMinAddress())) { + if (lb.getTraceBreakpoints(trace).contains(bpt)) { + return lb; + } + } + } + return null; + } + @Override public Set getBreakpointsAt(ProgramLocation loc) { return DebuggerLogicalBreakpointService.programOrTrace(loc, @@ -1022,13 +1048,13 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin changeListeners.remove(l); } - @Override - public CompletableFuture placeBreakpointAt(Program program, Address address, long length, - Collection kinds, String name) { + protected MappedLogicalBreakpoint synthesizeLogicalBreakpoint(Program program, Address address, + long length, Collection kinds) { /** - * TODO: This is quite a waste, but it gets the job done. The instance created here is - * dropped. It's just used for its breakpoint placement logic, part of which I had to - * re-implement here, anyway. The actual logical breakpoint is created by event processors. + * TODO: This is a bit of a waste, but it gets the job done. The instance created here is + * dropped by its caller. It's just used for its breakpoint placement logic, part of which I + * had to re-implement here, anyway. The actual logical breakpoint is created by event + * processors. */ MappedLogicalBreakpoint lb = new MappedLogicalBreakpoint(program, address, length, kinds); synchronized (lock) { @@ -1040,21 +1066,36 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin lb.setTraceAddress(ti.recorder, loc.getAddress()); } } + return lb; + } + + @Override + public CompletableFuture placeBreakpointAt(Program program, Address address, long length, + Collection kinds, String name) { + MappedLogicalBreakpoint lb = synthesizeLogicalBreakpoint(program, address, length, kinds); return lb.enableWithName(name); } @Override public CompletableFuture placeBreakpointAt(Trace trace, Address address, long length, - Collection kinds) { + Collection kinds, String name) { TraceRecorder recorder = modelService.getRecorder(trace); if (recorder == null) { throw new IllegalArgumentException("Given trace is not live"); } - /** - * TODO: This is similarly wasteful, but given the complexity of the breakpoint placement - * logic it offers, this works really well, and is probably justified. - */ - return new LoneLogicalBreakpoint(recorder, address, length, kinds).enableForTrace(trace); + + ProgramLocation staticLocation = mappingService.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Range.singleton(recorder.getSnap()), address)); + if (staticLocation == null) { + return new LoneLogicalBreakpoint(recorder, address, length, kinds) + .enableForTrace(trace); + } + + MappedLogicalBreakpoint lb = new MappedLogicalBreakpoint(staticLocation.getProgram(), + staticLocation.getAddress(), length, kinds); + lb.setTraceAddress(recorder, address); + lb.enableForProgramWithName(name); + return lb.enableForTrace(trace); } @Override @@ -1062,7 +1103,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin Collection kinds, String name) { return DebuggerLogicalBreakpointService.programOrTrace(loc, (p, a) -> placeBreakpointAt(p, a, length, kinds, name), - (t, a) -> placeBreakpointAt(t, a, length, kinds)); + (t, a) -> placeBreakpointAt(t, a, length, kinds, name)); } protected CompletableFuture actOnAll(Collection col, Trace trace, @@ -1070,7 +1111,8 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin BiConsumer consumerForTarget) { BreakpointActionSet actions = new BreakpointActionSet(); for (LogicalBreakpoint lb : col) { - if (trace == null) { + Set participants = lb.getParticipatingTraces(); + if (trace == null || participants.isEmpty() || participants.equals(Set.of(trace))) { consumerForProgram.accept(lb); } if (!(lb instanceof LogicalBreakpointInternal)) { @@ -1096,14 +1138,23 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin @Override public CompletableFuture deleteAll(Collection col, Trace trace) { - return actOnAll(col, trace, LogicalBreakpoint::deleteForProgram, - (actions, lbi) -> lbi.planDelete(actions, trace)); + return actOnAll(col, trace, lb -> { + // Do not delete bookmark from dynamic context + if (trace == null) { + lb.deleteForProgram(); + } + }, (actions, lbi) -> lbi.planDelete(actions, trace)); } protected CompletableFuture actOnLocs(Collection col, - BiConsumer consumer) { + BiConsumer locConsumer, + Consumer progConsumer) { BreakpointActionSet actions = new BreakpointActionSet(); for (TraceBreakpoint tb : col) { + LogicalBreakpoint lb = getBreakpoint(tb); + if (col.containsAll(lb.getTraceBreakpoints())) { + progConsumer.accept(lb); + } TraceRecorder recorder = modelService.getRecorder(tb.getTrace()); if (recorder == null) { continue; @@ -1115,24 +1166,27 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin continue; } TargetBreakpointLocation loc = (TargetBreakpointLocation) object; - consumer.accept(actions, loc); + locConsumer.accept(actions, loc); } return actions.execute(); } @Override public CompletableFuture enableLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planEnable); + return actOnLocs(col, BreakpointActionSet::planEnable, LogicalBreakpoint::enableForProgram); } @Override public CompletableFuture disableLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planDisable); + return actOnLocs(col, BreakpointActionSet::planDisable, + LogicalBreakpoint::disableForProgram); } @Override public CompletableFuture deleteLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planDelete); + return actOnLocs(col, BreakpointActionSet::planDelete, lb -> { + // Never delete bookmark when user requests deleting locations + }); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java index d9e9cd6939..2c976714ed 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java @@ -134,15 +134,15 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } } - public ProgramEnablement computeEnablement() { + public ProgramMode computeMode() { if (eBookmark != null) { - return ProgramEnablement.ENABLED; + return ProgramMode.ENABLED; } if (dBookmark != null) { - return ProgramEnablement.DISABLED; + return ProgramMode.DISABLED; } else { - return ProgramEnablement.MISSING; + return ProgramMode.MISSING; } } @@ -233,11 +233,11 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } public boolean isEnabled() { - return computeEnablement() == ProgramEnablement.ENABLED; + return computeMode() == ProgramMode.ENABLED; } public boolean isDisabled() { - return computeEnablement() == ProgramEnablement.DISABLED; + return computeMode() == ProgramMode.DISABLED; } public String computeCategory() { @@ -313,19 +313,22 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } public Address computeTargetAddress() { - // TODO: Can this change? If not, can compute in constructor. return recorder.getMemoryMapper().traceToTarget(address); } - public TraceEnablement computeEnablement() { - TraceEnablement en = TraceEnablement.MISSING; + public TraceMode computeMode() { + TraceMode mode = TraceMode.NONE; for (IDHashed bpt : breakpoints) { - en = en.combine(TraceEnablement.fromBool(bpt.obj.isEnabled(recorder.getSnap()))); - if (en == TraceEnablement.MIXED) { - return en; + mode = mode.combine(computeMode(bpt.obj)); + if (mode == TraceMode.MISSING) { + return mode; } } - return en; + return mode; + } + + public TraceMode computeMode(TraceBreakpoint bpt) { + return TraceMode.fromBool(bpt.isEnabled(recorder.getSnap())); } public boolean isEmpty() { @@ -446,6 +449,16 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { */ void setTraceAddress(TraceRecorder recorder, Address address); + /** + * Remove the given trace from this set + * + *

+ * This happens when a trace's recorder stops or when a trace is closed. + * + * @param trace the trace no longer participating + */ + void removeTrace(Trace trace); + boolean canMerge(Program program, Bookmark bookmark); /** diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java index 7c687c667d..9312271557 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java @@ -89,6 +89,11 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal { throw new AssertionError(); } + @Override + public void removeTrace(Trace trace) { + throw new AssertionError(); + } + @Override public Set getTraceBreakpoints() { return new HashSet<>(breaks.getBreakpoints()); @@ -131,21 +136,29 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal { } @Override - public Enablement computeEnablementForProgram(Program program) { - return Enablement.NONE; + public State computeStateForProgram(Program program) { + return State.NONE; } @Override - public Enablement computeEnablementForTrace(Trace trace) { + public State computeStateForTrace(Trace trace) { if (trace != breaks.getTrace()) { - return Enablement.NONE; + return State.NONE; } - return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement()); + return ProgramMode.NONE.combineTrace(breaks.computeMode(), Perspective.TRACE); } @Override - public Enablement computeEnablement() { - return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement()); + public State computeStateForLocation(TraceBreakpoint loc) { + if (!breaks.getBreakpoints().contains(loc)) { + return State.NONE; + } + return ProgramMode.NONE.combineTrace(breaks.computeMode(loc), Perspective.TRACE); + } + + @Override + public State computeState() { + return ProgramMode.NONE.combineTrace(breaks.computeMode(), Perspective.LOGICAL); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java index ee415dc731..142c939a7d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java @@ -40,7 +40,6 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { // Debuggers tend to allow multiple breakpoints (even of the same kind) at the same address // They may have different names and/or different conditions private final Map traceBreaks = new HashMap<>(); - private final Set tracesView = Collections.unmodifiableSet(traceBreaks.keySet()); protected MappedLogicalBreakpoint(Program program, Address progAddr, long length, Collection kinds) { @@ -51,8 +50,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public String toString() { - return String.format("<%s prog=%s, traces=%s>", getClass().getSimpleName(), progBreak, - traceBreaks.values()); + synchronized (traceBreaks) { + return String.format("<%s prog=%s, traces=%s>", getClass().getSimpleName(), progBreak, + traceBreaks.values()); + } } protected boolean hasProgramBreakpoint() { @@ -64,9 +65,11 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { if (!progBreak.isEmpty()) { return false; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - if (!breaks.isEmpty()) { - return false; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + if (!breaks.isEmpty()) { + return false; + } } } return true; @@ -100,6 +103,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { progBreak.enable(); } + public void enableForProgramWithName(String name) { + progBreak.enableWithComment(name); + } + @Override public void disableForProgram() { progBreak.disable(); @@ -113,7 +120,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture enableForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -124,7 +134,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture disableForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -135,7 +148,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture deleteForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -146,15 +162,20 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void planEnable(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planEnable(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planEnable(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planEnable(actions, length, kinds); + } } } @@ -167,28 +188,33 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { public CompletableFuture enableWithName(String name) { // TODO: Consider more fields than name in comment - progBreak.enableWithComment(name); + enableForProgramWithName(name); return enableForTraces(); } @Override public CompletableFuture enable() { - progBreak.enable(); + enableForProgram(); return enableForTraces(); } @Override public void planDisable(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planDisable(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planDisable(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planDisable(actions, length, kinds); + } } } @@ -204,15 +230,20 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void planDelete(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planDelete(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planDelete(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planDelete(actions, length, kinds); + } } } @@ -247,44 +278,65 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void setTraceAddress(TraceRecorder recorder, Address address) { - traceBreaks.put(recorder.getTrace(), new TraceBreakpointSet(recorder, address)); + synchronized (traceBreaks) { + traceBreaks.put(recorder.getTrace(), new TraceBreakpointSet(recorder, address)); + } + } + + @Override + public void removeTrace(Trace trace) { + synchronized (traceBreaks) { + traceBreaks.remove(trace); + } } @Override public Set getTraceBreakpoints() { Set result = new HashSet<>(); - for (TraceBreakpointSet breaks : traceBreaks.values()) { - result.addAll(breaks.getBreakpoints()); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + result.addAll(breaks.getBreakpoints()); + } } return result; } @Override public Set getTraceBreakpoints(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } return breaks == null ? Set.of() : new HashSet<>(breaks.getBreakpoints()); } @Override public Set getMappedTraces() { - return tracesView; + synchronized (traceBreaks) { + return Set.copyOf(traceBreaks.keySet()); + } } @Override public Set getParticipatingTraces() { Set result = new HashSet<>(); - for (TraceBreakpointSet breaks : traceBreaks.values()) { - if (breaks.isEmpty()) { - continue; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + if (breaks.isEmpty()) { + continue; + } + result.add(breaks.getTrace()); } - result.add(breaks.getTrace()); } return result; } @Override public Address getTraceAddress(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return null; } @@ -312,36 +364,61 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { } @Override - public Enablement computeEnablementForProgram(Program program) { + public State computeStateForProgram(Program program) { if (progBreak.getProgram() != program) { - return Enablement.NONE; + return State.NONE; } - return computeEnablement(); + return computeState(); } @Override - public Enablement computeEnablementForTrace(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); - ProgramEnablement progEn = progBreak.computeEnablement(); - if (breaks == null) { - return TraceEnablement.MISSING.combineProgram(progEn); + public State computeStateForTrace(Trace trace) { + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); } - // NB: Order matters. Trace is primary - return breaks.computeEnablement().combineProgram(progEn); + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = breaks == null ? TraceMode.NONE : breaks.computeMode(); + return progMode.combineTrace(traceMode, Perspective.TRACE); + } + + protected TraceMode computeTraceModeForLocation(TraceBreakpoint loc) { + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(loc.getTrace()); + } + if (breaks == null || !breaks.getBreakpoints().contains(loc)) { + return TraceMode.NONE; + } + return breaks.computeMode(loc); } @Override - public Enablement computeEnablement() { - ProgramEnablement progEn = progBreak.computeEnablement(); - TraceEnablement traceEn = TraceEnablement.NONE; - for (TraceBreakpointSet breaks : traceBreaks.values()) { - TraceEnablement tEn = breaks.computeEnablement(); - traceEn = traceEn.combine(tEn); - if (traceEn == TraceEnablement.MIXED) { - break; + public State computeStateForLocation(TraceBreakpoint loc) { + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = computeTraceModeForLocation(loc); + return progMode.combineTrace(traceMode, Perspective.TRACE); + } + + protected TraceMode computeTraceMode() { + TraceMode traceMode = TraceMode.NONE; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + TraceMode tm = breaks.computeMode(); + traceMode = traceMode.combine(tm); + if (traceMode == TraceMode.MISSING) { + break; + } } } - return progEn.combineTrace(traceEn); + return traceMode; + } + + @Override + public State computeState() { + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = computeTraceMode(); + return progMode.combineTrace(traceMode, Perspective.LOGICAL); } @Override @@ -351,7 +428,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public boolean canMerge(TraceBreakpoint breakpoint) throws TrackedTooSoonException { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } if (breaks == null) { /** * This happens when the trace is first added to the manager, between the listener being @@ -377,10 +457,25 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { return progBreak.add(bookmark); } + protected void makeBookmarkConsistent() { + TraceMode traceMode = computeTraceMode(); + if (traceMode == TraceMode.ENABLED) { + progBreak.enable(); + } + else if (traceMode == TraceMode.DISABLED) { + progBreak.disable(); + } + } + @Override public boolean trackBreakpoint(TraceBreakpoint breakpoint) { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); - return breaks.add(breakpoint); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } + boolean result = breaks.add(breakpoint); + makeBookmarkConsistent(); + return result; } @Override @@ -391,11 +486,16 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public boolean untrackBreakpoint(TraceBreakpoint breakpoint) { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } if (breaks == null) { return false; } - return breaks.remove(breakpoint); + boolean result = breaks.remove(breakpoint); + makeBookmarkConsistent(); + return result; } public boolean appliesTo(Program program) { @@ -403,7 +503,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { } public boolean appliesTo(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(Objects.requireNonNull(trace)); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(Objects.requireNonNull(trace)); + } return !breaks.isEmpty(); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java index f0894526ac..946f419778 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java @@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.plugintool.ServiceInfo; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; @@ -90,6 +90,18 @@ public interface DebuggerLogicalBreakpointService { */ Set getBreakpointsAt(Trace trace, Address address); + /** + * Get the logical breakpoint of which the given trace breakpoint is a part + * + *

+ * If the given trace breakpoint is not part of any logical breakpoint, e.g., because it is not + * on a live target, then null is returned. + * + * @param bpt the trace breakpoint + * @return the logical breakpoint, or null + */ + LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt); + /** * Get the collected logical breakpoints (at present) at the given location. * @@ -146,39 +158,45 @@ public interface DebuggerLogicalBreakpointService { return progFunc.apply(progOrView, loc.getByteAddress()); } - default Enablement computeEnablement(Collection col) { - Enablement en = Enablement.NONE; + default State computeState(Collection col) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablement()); + state = state.sameAdddress(lb.computeState()); } - return en; + return state; } - default Enablement computeEnablement(Collection col, Program program) { - Enablement en = Enablement.NONE; + default State computeState(Collection col, Program program) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablementForProgram(program)); + state = state.sameAdddress(lb.computeStateForProgram(program)); } - return en; + return state; } - default Enablement computeEnablement(Collection col, Trace trace) { - Enablement en = Enablement.NONE; + default State computeState(Collection col, Trace trace) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablementForTrace(trace)); + state = state.sameAdddress(lb.computeStateForTrace(trace)); } - return en; + return state; } - default Enablement computeEnablement(Collection col, ProgramLocation loc) { + default State computeState(Collection col, ProgramLocation loc) { return programOrTrace(loc, - (p, a) -> computeEnablement(col, p), - (t, a) -> computeEnablement(col, t)); + (p, a) -> computeState(col, p), + (t, a) -> computeState(col, t)); } - default Enablement computeEnablement(ProgramLocation loc) { + /** + * Compute the state for a given address and program or trace view + * + * @param loc the location + * @return the breakpoint state + */ + default State computeState(ProgramLocation loc) { Set col = getBreakpointsAt(loc); - return computeEnablement(col, loc); + return computeState(col, loc); } default boolean anyMapped(Collection col, Trace trace) { @@ -222,11 +240,11 @@ public interface DebuggerLogicalBreakpointService { Collection kinds, String name); /** - * Create an enabled breakpoint at the given trace location only. + * Create an enabled breakpoint at the given trace location and its mapped program location. * *

- * If the given location is mapped to a static module, this still only creates the breakpoint in - * the given trace. However, a logical breakpoint mark will appear at all mapped locations. + * If the breakpoint has no static location, then only the trace location is placed. Note, if + * this is the case, the breakpoint will have no name. * *

* Note, the debugger ultimately determines the placement behavior. If it is managing multiple @@ -238,10 +256,11 @@ public interface DebuggerLogicalBreakpointService { * @param address the address in the trace (as viewed in the present) * @param length size of the breakpoint, may be ignored by debugger * @param kinds the kinds of breakpoint + * @param name a name for the breakpoint * @return a future which completes when the breakpoint has been placed */ CompletableFuture placeBreakpointAt(Trace trace, Address address, long length, - Collection kinds); + Collection kinds, String name); /** * Create an enabled breakpoint at the given location. diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java index 7d90ff9dcf..4a506dd361 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java @@ -15,10 +15,12 @@ */ package ghidra.app.services; -import java.util.Collection; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; +import javax.swing.Icon; + +import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.framework.model.DomainObject; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Bookmark; @@ -35,24 +37,24 @@ public interface LogicalBreakpoint { /** * The state of a logical breakpoint's program bookmark */ - public enum ProgramEnablement { + public enum ProgramMode { /** * A placeholder state when the program bookmark state is not applicable */ NONE { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: - return Enablement.NONE; + return State.NONE; case MISSING: - return Enablement.NONE; + return State.NONE; case ENABLED: - return Enablement.ENABLED; - case MIXED: - return Enablement.DISABLED_ENABLED; + return State.INCONSISTENT_ENABLED; case DISABLED: - return Enablement.DISABLED; + return State.INCONSISTENT_DISABLED; + case MIXED: + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -67,17 +69,18 @@ public interface LogicalBreakpoint { */ MISSING { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: - return Enablement.NONE; + return State.NONE; case MISSING: - return Enablement.NONE; + return State.NONE; case ENABLED: - case MIXED: - return Enablement.DISABLED_ENABLED; + return State.INCONSISTENT_ENABLED; case DISABLED: - return Enablement.DISABLED; + return State.INCONSISTENT_DISABLED; + case MIXED: + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -88,16 +91,32 @@ public interface LogicalBreakpoint { */ ENABLED { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: case MISSING: - return Enablement.INEFFECTIVE_ENABLED; + switch (perspective) { + case LOGICAL: + return State.INEFFECTIVE_ENABLED; + case TRACE: + return State.NONE; + } case ENABLED: - return Enablement.ENABLED; + return State.ENABLED; case DISABLED: + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_ENABLED; + case TRACE: + return State.INCONSISTENT_DISABLED; + } case MIXED: - return Enablement.ENABLED_DISABLED; + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_ENABLED; + case TRACE: + return State.INCONSISTENT_MIXED; + } default: throw new AssertionError(); } @@ -108,16 +127,33 @@ public interface LogicalBreakpoint { */ DISABLED { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: case MISSING: - return Enablement.INEFFECTIVE_DISABLED; + switch (perspective) { + case LOGICAL: + return State.INEFFECTIVE_DISABLED; + case TRACE: + return State.NONE; + } case ENABLED: - case MIXED: - return Enablement.DISABLED_ENABLED; + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_DISABLED; + case TRACE: + return State.INCONSISTENT_ENABLED; + } case DISABLED: - return Enablement.DISABLED; + return State.DISABLED; + case MIXED: + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_DISABLED; + case TRACE: + return State.INCONSISTENT_MIXED; + } + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -132,183 +168,209 @@ public interface LogicalBreakpoint { * This state is generally considered the state of the logical breakpoint. In other words, * the program's perspective is the default. * - * @param traceEn the state of its locations + * @param traceMode the mode of its locations + * @param perspective the perspective * @return the logical state */ - public abstract Enablement combineTrace(TraceEnablement traceEn); + public abstract State combineTrace(TraceMode traceMode, Perspective perspective); + } + + public enum Perspective { + LOGICAL, TRACE; } /** * The state of a logical breakpoint's trace/target locations */ - public enum TraceEnablement { + public enum TraceMode { /** - * A placeholder state when the breakpoint is not mapped to any trace + * A placeholder mode when no traces are involved */ NONE { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { return that; } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: - case MISSING: - return Enablement.NONE; - case ENABLED: - return Enablement.INEFFECTIVE_ENABLED; - case DISABLED: - return Enablement.INEFFECTIVE_DISABLED; - default: - throw new AssertionError(); - } - } }, /** - * The state when the breakpoint is mapped to at least one trace, but no locations are - * placed + * The mode when the breakpoint is missing from one or more of its mapped locations */ MISSING { @Override - public TraceEnablement combine(TraceEnablement that) { - return that; - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: - case MISSING: - return Enablement.NONE; - case ENABLED: - return Enablement.INEFFECTIVE_ENABLED; - case DISABLED: - return Enablement.INEFFECTIVE_DISABLED; - default: - throw new AssertionError(); - } + public TraceMode combine(TraceMode that) { + return MISSING; } }, /** - * The state when all mapped locations are placed and enabled + * The mode when all mapped locations are placed and enabled */ ENABLED { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { switch (that) { case NONE: - case MISSING: case ENABLED: return ENABLED; case DISABLED: case MIXED: return MIXED; - default: - throw new AssertionError(); - } - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: case MISSING: - case DISABLED: - return Enablement.ENABLED_DISABLED; - case ENABLED: - return Enablement.ENABLED; + return MISSING; default: throw new AssertionError(); } } }, /** - * The state when all mapped locations are placed and disabled + * The mode when all mapped locations are placed and disabled */ DISABLED { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { switch (that) { case NONE: - case MISSING: case DISABLED: return DISABLED; case ENABLED: case MIXED: return MIXED; - default: - throw new AssertionError(); - } - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: case MISSING: - case DISABLED: - return Enablement.DISABLED; - case ENABLED: - return Enablement.DISABLED_ENABLED; + return MISSING; default: throw new AssertionError(); } } }, /** - * The state when some mapped locations are enabled and some are disabled + * The mode when all mapped locations are placed, but some are enabled, and some are + * disabled */ MIXED { @Override - public TraceEnablement combine(TraceEnablement that) { - return MIXED; - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - return Enablement.ENABLED_DISABLED; + public TraceMode combine(TraceMode that) { + switch (that) { + case NONE: + case ENABLED: + case DISABLED: + case MIXED: + return MIXED; + case MISSING: + return MISSING; + default: + throw new AssertionError(); + } } }; /** - * Convert a boolean to trace enablement + * Convert a boolean to trace breakpoint mode * - * @param en true for {@link #ENABLED}, false for {@link #DISABLED} + * @param enabled true for {@link #ENABLED}, false for {@link #DISABLED} * @return the state */ - public static TraceEnablement fromBool(boolean en) { - return en ? ENABLED : DISABLED; + public static TraceMode fromBool(boolean enabled) { + return enabled ? ENABLED : DISABLED; } /** - * For locations of the same logical breakpoint, compose the state + * For locations of the same logical breakpoint, compose the mode * * @param that the other state * @return the composed state */ - public abstract TraceEnablement combine(TraceEnablement that); - - /** - * Compose the logical breakpoint state from the perspective of the trace, given the state - * of its program bookmark. - * - *

- * Typically, this is used not on a composed trace state, but on the one trace whose - * perspective to consider. This should only be used when choosing how to render a - * breakpoint in that trace's listing. - * - * @param progEn the state of the program bookmark - * @return the per-trace logical state - */ - public abstract Enablement combineProgram(ProgramEnablement progEn); + public abstract TraceMode combine(TraceMode that); } /** - * The state of a logical breakpoint, i.e., whether or not its parts are enabled + * The mode of a logical breakpoint + * + *

+ * Depending on context this may describe the mode from the perspective of a program, where + * breakpoints are saved from session to session; or this may describe the mode from the + * perspective of one or more traces/targets: + * + *

+ * If the breakpoint is a lone breakpoint, meaning Ghidra cannot determine to what program it + * belongs, then this describes the mode of that trace breakpoint. + * + *

+ * If the breakpoint is mapped, meaning Ghidra can determine to what program it belongs and at + * what address, but it is not bookmarked, then for the static context, this describes the mode + * of the participating trace breakpoints. If the breakpoint is bookmarked, then for the static + * context, this describes the mode of that bookmark. For the dynamic context, this describes + * the mode of the trace's breakpoint, ignoring the presence or state of the bookmark. Note that + * the bookmark and trace modes may disagree. The displayed mode is still determined by context, + * but it will be marked as inconsistent. See {@link Consistency}. */ - public enum Enablement { + public enum Mode { + /** All locations are enabled */ + ENABLED, + /** All locations are disabled */ + DISABLED, + /** Has both enabled and disabled trace locations */ + MIXED; + + public Mode sameAddress(Mode that) { + if (this == Objects.requireNonNull(that)) { + return this; + } + return MIXED; + } + } + + /** + * The consistency of a logical breakpoint + * + *

+ * When operating as designed, all breakpoints should be in the {@link #NORMAL} state. This + * indicates that the breakpoint's bookmark and all trace locations agree on the mode. + * Exceptions do happen, and they should be indicated to the user: + * + *

+ * If the breakpoint is a lone breakpoint, meaning Ghidra cannot determine to what program it + * belongs, then the breakpoint is always {@link #INCONSISTENT}, because Ghidra uses program + * bookmarks to save breakpoints. + * + *

+ * If the breakpoint is mapped, meaning Ghidra can determine to what program it belongs and at + * what address, but it is not bookmarked, then the breakpoint is {@link #INCONSISTENT}. If it + * is bookmarked, but the bookmark disagrees, then the breakpoint is {@link #INCONSISTENT}. A + * breakpoint that is bookmarked but has no trace locations, or is missing from any + * participating trace, is {@link #INEFFECTIVE}. + * + * @implNote These are ordered by priority, highest last. + */ + public enum Consistency { + /** the bookmark and locations all agree */ + NORMAL, + /** has a bookmark but one or more trace locations is missing */ + INEFFECTIVE, + /** has a trace location but is not bookmarked, or the bookmark disagrees */ + INCONSISTENT; + + public Consistency sameAddress(Consistency that) { + return Consistency.values()[Math.max(this.ordinal(), that.ordinal())]; + } + } + + /** + * The state of a logical breakpoint + * + *

+ * Because a breakpoint is comprised of possibly many locations on target or among several + * targets, as well as a saved bookmark in a program, the "state" can get fairly complex. This + * is an attempt to enumerate these states while preserving enough information about the + * breakpoint to display it in various contexts, hopefully informing more than confusing. + * + *

+ * In essence, this is the cross product of {@link Mode} and {@link Consistency} with an + * additional {@link #NONE} option. + * + *

+ * A breakpoint is simply {@link #ENABLED} or {@link #DISABLED} if it is maped and all its + * locations and bookmark agree. Ideally, all breakpoints would be in one of these two states. + */ + public enum State { /** * A placeholder state, usually indicating the logical breakpoint should not exist * @@ -317,77 +379,95 @@ public interface LogicalBreakpoint { * breakpoint is ephemeral and about to be removed. This value may appear during * computations and is a suitable default placeholder for editors and renderers. */ - NONE(false, false, true, false) { - @Override - public Enablement getPrimary() { + NONE(null, null, null, null), + /** + * The breakpoint is enabled, and all locations and its bookmark agree + */ + ENABLED(Mode.ENABLED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_ENABLED, DebuggerResources.ICON_BREAKPOINT_MARKER_ENABLED), + /** + * The breakpoint is disabled, and all locations and its bookmark agree + */ + DISABLED(Mode.DISABLED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_DISABLED, DebuggerResources.ICON_BREAKPOINT_MARKER_DISABLED), + /** + * There are multiple logical breakpoints at this address, and they are all saved and + * effective, but some are enabled, and some are disabled. + */ + MIXED(Mode.MIXED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_MIXED, DebuggerResources.ICON_BREAKPOINT_MARKER_MIXED), + /** + * The breakpoint is saved as enabled, but one or more trace locations are absent. + */ + INEFFECTIVE_ENABLED(Mode.ENABLED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_EN, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_EN), + /** + * The breakpoint is saved as disabled, and one or more trace locations are absent. + */ + INEFFECTIVE_DISABLED(Mode.DISABLED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_DIS, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_DIS), + /** + * There are multiple logical breakpoints at this address, and they are all saved, but at + * least one is ineffective; furthermore, some are enabled, and some are disabled. + */ + INEFFECTIVE_MIXED(Mode.MIXED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_MIX, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_MIX), + /** + * The breakpoint is enabled, and all locations agree, but the bookmark is absent or + * disagrees. + */ + INCONSISTENT_ENABLED(Mode.ENABLED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_EN, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_EN), + /** + * The breakpoint is disabled, and all locations agree, but the bookmark is absent or + * disagrees. + */ + INCONSISTENT_DISABLED(Mode.DISABLED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_DIS, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_DIS), + /** + * The breakpoint is terribly inconsistent: its locations disagree, and the bookmark may be + * absent. + */ + INCONSISTENT_MIXED(Mode.MIXED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_MIX, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_MIX); + + public final Mode mode; + public final Consistency consistency; + public final String display; + public final Icon icon; + + State(Mode mode, Consistency consistency, String display, Icon icon) { + this.mode = mode; + this.consistency = consistency; + this.display = display; + this.icon = icon; + } + + public static State fromFields(Mode mode, Consistency consistency) { + if (mode == null && consistency == null) { return NONE; } - }, - /** - * The breakpoint's bookmark and all mapped locations are placed and enabled - */ - ENABLED(true, false, true, true) { - @Override - public Enablement getPrimary() { - return ENABLED; + switch (mode) { + case ENABLED: + switch (consistency) { + case NORMAL: + return ENABLED; + case INEFFECTIVE: + return INEFFECTIVE_ENABLED; + case INCONSISTENT: + return INCONSISTENT_ENABLED; + } + case DISABLED: + switch (consistency) { + case NORMAL: + return DISABLED; + case INEFFECTIVE: + return INEFFECTIVE_DISABLED; + case INCONSISTENT: + return INCONSISTENT_DISABLED; + } + case MIXED: + switch (consistency) { + case NORMAL: + return MIXED; + case INEFFECTIVE: + return INEFFECTIVE_MIXED; + case INCONSISTENT: + return INCONSISTENT_MIXED; + } } - }, - /** - * The breakpoint's bookmark and all mapped locations are placed and disabled - */ - DISABLED(false, true, true, true) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }, - /** - * The breakpoint's bookmark is enabled, but it has no mapped locations placed - */ - INEFFECTIVE_ENABLED(true, false, true, false) { - @Override - public Enablement getPrimary() { - return ENABLED; - } - }, - /** - * The breakpoint's bookmark is disabled, and it has no mapped locations placed - */ - INEFFECTIVE_DISABLED(false, true, true, false) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }, - /** - * The breakpoint's bookmark is enabled, but at least one mapped location is disabled - */ - ENABLED_DISABLED(true, false, false, true) { - @Override - public Enablement getPrimary() { - return ENABLED; - } - }, - /** - * The breakpoint's bookmark is disabled, but at least one mapped location is enabled - */ - DISABLED_ENABLED(false, true, false, true) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }; - - public final boolean enabled; // indicates any enabled location - public final boolean disabled; // indicates any disabled location - public final boolean consistent; // bookmark and target locations all agree - public final boolean effective; // has a target location, even if disabled - - Enablement(boolean enabled, boolean disabled, boolean consistent, boolean effective) { - this.enabled = enabled; - this.disabled = disabled; - this.consistent = consistent; - this.effective = effective; + throw new AssertionError(); } /** @@ -401,65 +481,33 @@ public interface LogicalBreakpoint { * @param that the other state. * @return the composed state */ - public Enablement sameAdddress(Enablement that) { + public State sameAdddress(State that) { if (this == NONE) { return that; } if (that == NONE) { return this; } - if (!this.effective && !that.effective) { - return this.enabled || that.enabled ? INEFFECTIVE_ENABLED : INEFFECTIVE_DISABLED; - } - return fromBools(this.enabled || that.enabled, this.enabled && that.enabled); - } + Mode mode = this.mode.sameAddress(that.mode); + Consistency consistency = this.consistency.sameAddress(that.consistency); + return fromFields(mode, consistency); + }; /** * For logical breakpoints which appear at the same address, compose their state * - * @see #sameAdddress(Enablement) + * @see #sameAdddress(State) * @param col a collection of states derived from logical breakpoints at the same address * @return the composed state */ - public static Enablement sameAddress(Collection col) { - Enablement result = NONE; - for (Enablement e : col) { - result = result.sameAdddress(e); + public static State sameAddress(Collection col) { + State result = NONE; + for (State state : col) { + result = result.sameAdddress(state); } return result; } - public static Enablement fromBools(boolean firstEn, boolean secondEn) { - if (firstEn) { - if (secondEn) { - return ENABLED; - } - else { - return ENABLED_DISABLED; - } - } - else { - if (secondEn) { - return DISABLED_ENABLED; - } - else { - return DISABLED; - } - } - } - - /** - * Get the "primary" state represented by this logical state - * - *

- * Generally, this is the state of the bookmark, i.e., program breakpoint. Not all logical - * breakpoints have a placed bookmark, though. In that case, this still returns a reasonable - * "primary" state. - * - * @return the state - */ - public abstract Enablement getPrimary(); - /** * Get the desired state were the the logical breakpoint to be toggled * @@ -479,11 +527,31 @@ public interface LogicalBreakpoint { * @param mapped true if the breakpoint is mapped, as interpreted by the action context * @return the resulting state */ - public Enablement getToggled(boolean mapped) { - // If not mapped, just toggle. If mapped, consider any other state "disabled" - // This will cause most first toggles to make it consistent enabled-effective. - boolean en = mapped ? this == ENABLED : enabled; - return en ? DISABLED : ENABLED; // The actual toggle (and type conversion) + public State getToggled(boolean mapped) { + if (mapped && isIneffective()) { + return ENABLED; + } + return isDisabled() ? ENABLED : DISABLED; + } + + public boolean isNormal() { + return consistency == Consistency.NORMAL; + } + + public boolean isEnabled() { + return mode != Mode.DISABLED; // mixed is considered, in part, enabled + } + + boolean isDisabled() { + return mode != Mode.ENABLED; // mixed is considered, in part, disabled + } + + public boolean isEffective() { + return consistency != Consistency.INEFFECTIVE; + } + + public boolean isIneffective() { + return consistency == Consistency.INEFFECTIVE; } } @@ -584,7 +652,7 @@ public interface LogicalBreakpoint { * trace, but rather that for each returned trace, the logical breakpoint can be mapped to an * address in that trace. See {@link #getParticipatingTraces()}. * - * @return the set of traces + * @return a copy of the set of traces */ Set getMappedTraces(); @@ -622,27 +690,38 @@ public interface LogicalBreakpoint { Address getAddress(); /** - * Compute the enablement status for the given program. + * Compute the state for the given program. * * @param program the program - * @return the enablement + * @return the state */ - Enablement computeEnablementForProgram(Program program); + State computeStateForProgram(Program program); /** - * Compute the enablement status for the given trace. + * Compute the state for the given trace. * * @param trace the trace - * @return the enablement + * @return the state */ - Enablement computeEnablementForTrace(Trace trace); + State computeStateForTrace(Trace trace); /** - * Compute the enablement status for all involved traces and program. + * Compute the state for the given location. * - * @return the enablement + *

+ * This is just the location's mode combined with that of the static bookmark. + * + * @param loc the location + * @return the state */ - Enablement computeEnablement(); + State computeStateForLocation(TraceBreakpoint loc); + + /** + * Compute the state for all involved traces and program. + * + * @return the state + */ + State computeState(); /** * Place an "enabled breakpoint" bookmark in the mapped program, if applicable. diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png deleted file mode 100644 index bb783d7247..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png deleted file mode 100755 index 344e4a2e24..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-enable.png deleted file mode 100755 index b5907a734b..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-enable.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png deleted file mode 100644 index cb1d378235..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-e.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-e.png deleted file mode 100644 index 43fb693a6a..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-e.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png deleted file mode 100755 index 56823827c2..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-ed.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-ed.png deleted file mode 100755 index ecb9c8153e..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-ed.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-set.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-set.png deleted file mode 100755 index 482c7c0aff..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-set.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-clear-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-clear-all.png deleted file mode 100755 index eb75b54579..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-clear-all.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-disable-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-disable-all.png deleted file mode 100755 index 9b85001ce5..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-disable-all.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png deleted file mode 100755 index 6e5e936d4c..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png deleted file mode 100755 index 60d9bbe7a1..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png deleted file mode 100755 index 2bdaccc11b..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png index e031698377..85735d19c2 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable-ineff.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable-ineff.png new file mode 100644 index 0000000000..cbd45b7f2a Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable-ineff.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable.png index 6386998ebc..4d3a03b13c 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable-ineff.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable-ineff.png new file mode 100644 index 0000000000..b417f1f897 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable-ineff.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png index ea1e926811..2d618a99a7 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-d.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-d.png deleted file mode 100644 index cb1d378235..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-d.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-e.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-e.png deleted file mode 100644 index 43fb693a6a..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-e.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png deleted file mode 100644 index 201d4a298d..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ed.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ed.png deleted file mode 100644 index 0f9d808294..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ed.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png new file mode 100644 index 0000000000..45e794d438 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed.png new file mode 100644 index 0000000000..f3a2d726e9 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png new file mode 100644 index 0000000000..6eafda829e Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png deleted file mode 100644 index e12b41d306..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-clear-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-clear-all.png index 2988e9da8c..a41a13c678 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-clear-all.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-clear-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-disable-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-disable-all.png index 3d80965e63..e461ffaad9 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-disable-all.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-disable-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-enable-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-enable-all.png index 0c2e466dba..9b593e5e8b 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-enable-all.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-enable-all.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png index b1e1ab4ede..d220d56af1 100644 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png and b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints.png deleted file mode 100644 index 4d27be0a40..0000000000 Binary files a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints.png and /dev/null differ diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-clear.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-clear.svg index 130acabf85..31b960a6be 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-clear.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-clear.svg @@ -20,23 +20,18 @@ image/svg+xml - + - - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg similarity index 55% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg index bfb8ea0539..3778a7e78a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg @@ -20,21 +20,18 @@ image/svg+xml - + - - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg index a3d85e2ff0..ac1fb6a8d9 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - id="SVGRoot" - version="1.1" - viewBox="0 0 16 16" + width="16px" height="16px" - width="16px"> + viewBox="0 0 16 16" + version="1.1" + id="SVGRoot"> image/svg+xml - + - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg similarity index 55% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg index e0732afc1e..9c37ecdce6 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - width="16px" - height="16px" - viewBox="0 0 16 16" + id="SVGRoot" version="1.1" - id="SVGRoot"> + viewBox="0 0 16 16" + height="16px" + width="16px"> - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg index 63f316fab7..9bcad1474a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - id="SVGRoot" - version="1.1" - viewBox="0 0 16 16" + width="16px" height="16px" - width="16px"> + viewBox="0 0 16 16" + version="1.1" + id="SVGRoot"> image/svg+xml - + - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg deleted file mode 100644 index 49bd3efaee..0000000000 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg similarity index 56% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg index 980d822578..2187a30cbe 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg @@ -24,11 +24,14 @@ - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg similarity index 53% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg index 2defba113a..3c6da09bc6 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - width="16px" - height="16px" - viewBox="0 0 16 16" + id="SVGRoot" version="1.1" - id="SVGRoot"> + viewBox="0 0 16 16" + height="16px" + width="16px"> - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg new file mode 100644 index 0000000000..2b69ed0cfa --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg @@ -0,0 +1,46 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg index 078e83d778..dc4c966d9a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg @@ -20,39 +20,29 @@ image/svg+xml - + + + + id="g4631"> + id="rect4552" + d="m 11.865234,0.4 -1.730468,1.0007567 0.634765,1.0984867 H 9.5 v 2.0015133 h 1.269531 L 10.134766,5.5992434 11.865234,6.6 12.5,5.4995587 13.134766,6.6 14.865234,5.5992434 14.230469,4.5007567 H 15.5 V 2.4992434 H 14.230469 L 14.865234,1.4007567 13.134766,0.4 12.5,1.5004413 Z" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.50056732;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - - - + id="rect818" + d="m 11.683594,1 -0.867188,0.5173808 0.816406,1.4652385 H 10 v 1.0347615 h 1.632812 L 10.816406,5.4826192 11.683594,6 12.5,4.5367825 13.316406,6 14.183594,5.4826192 13.367188,4.0173808 H 15 V 2.9826193 H 13.367188 L 14.183594,1.5173808 13.316406,1 12.5,2.4632175 Z" + style="fill:#d4aa00;fill-opacity:1;stroke:none;stroke-width:1.52584839;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg index 09fee1b484..92c66774a1 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg @@ -20,23 +20,29 @@ image/svg+xml - + + + + id="g4631"> + id="rect4552" + d="m 11.865234,0.4 -1.730468,1.0007567 0.634765,1.0984867 H 9.5 v 2.0015133 h 1.269531 L 10.134766,5.5992434 11.865234,6.6 12.5,5.4995587 13.134766,6.6 14.865234,5.5992434 14.230469,4.5007567 H 15.5 V 2.4992434 H 14.230469 L 14.865234,1.4007567 13.134766,0.4 12.5,1.5004413 Z" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.50056732;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - + id="rect818" + d="m 11.683594,1 -0.867188,0.5173808 0.816406,1.4652385 H 10 v 1.0347615 h 1.632812 L 10.816406,5.4826192 11.683594,6 12.5,4.5367825 13.316406,6 14.183594,5.4826192 13.367188,4.0173808 H 15 V 2.9826193 H 13.367188 L 14.183594,1.5173808 13.316406,1 12.5,2.4632175 Z" + style="fill:#d4aa00;fill-opacity:1;stroke:none;stroke-width:1.52584839;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg index 3e033f91bb..8d68697416 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg @@ -24,17 +24,28 @@ + + - - + id="g4588"> + + + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg index cfb79a2e09..1aa938dc7b 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg @@ -5,35 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16px" - height="16px" - viewBox="0 0 16 16" - version="1.1" id="SVGRoot" - sodipodi:docname="breakpoints-make-effective.svg" - inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> - + version="1.1" + viewBox="0 0 16 16" + height="16px" + width="16px"> + cx="11" + id="path821" + style="opacity:1;fill:#0166a9;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:1;fill:#7f7f7f;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg deleted file mode 100644 index 69ec53e769..0000000000 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java index 9ce12258c8..790667fded 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java @@ -21,25 +21,41 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.Range; + import generic.Unique; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; +import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest.TestDebuggerTargetTraceMapper; import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin; +import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; +import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; import ghidra.app.services.*; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.ListingPanel; +import ghidra.dbg.model.TestDebuggerModelBuilder; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; +import ghidra.trace.model.DefaultTraceLocation; +import ghidra.trace.model.Trace; import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.util.Msg; +import ghidra.util.database.UndoableTransaction; import ghidra.util.task.TaskMonitor; import help.screenshot.GhidraScreenShotGenerator; public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotGenerator { + private DebuggerModelService modelService; + private DebuggerTraceManagerService traceManager; + private DebuggerStaticMappingService mappingService; private DebuggerLogicalBreakpointService breakpointService; private DebuggerBreakpointMarkerPlugin breakpointMarkerPlugin; private ProgramManager programManager; + private TestDebuggerModelBuilder mb; + private CodeViewerProvider listing; protected static Address addr(Program program, long offset) { @@ -48,6 +64,9 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG @Before public void setUpMine() throws Exception { + modelService = addPlugin(tool, DebuggerModelServiceProxyPlugin.class); + traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class); + mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class); breakpointService = addPlugin(tool, DebuggerLogicalBreakpointServicePlugin.class); breakpointMarkerPlugin = addPlugin(tool, DebuggerBreakpointMarkerPlugin.class); programManager = addPlugin(tool, ProgramManagerPlugin.class); @@ -55,17 +74,36 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG listing = waitForComponentProvider(CodeViewerProvider.class); program = programManager.getCurrentProgram(); + + mb = new TestDebuggerModelBuilder(); } @Test public void testCaptureDebuggerBreakpointMarkerPlugin() throws Throwable { ListingPanel panel = listing.getListingPanel(); + mb.createTestModel(); + modelService.addModel(mb.testModel); + mb.createTestProcessesAndThreads(); + TestDebuggerTargetTraceMapper mapper = new TestDebuggerTargetTraceMapper(mb.testProcess1); + TraceRecorder recorder = + modelService.recordTarget(mb.testProcess1, mapper, ActionSource.AUTOMATIC); + Trace trace = recorder.getTrace(); + + traceManager.openTrace(trace); + traceManager.activateTrace(trace); + tool.getProject() .getProjectData() .getRootFolder() .createFile("WinHelloCPP", program, TaskMonitor.DUMMY); + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add Mapping", true)) { + mappingService.addIdentityMapping(trace, program, Range.atLeast(0L), true); + } + waitForValue(() -> mappingService.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Range.singleton(0L), mb.addr(0x00401c60)))); + Msg.debug(this, "Placing breakpoint"); breakpointService.placeBreakpointAt(program, addr(program, 0x00401c60), 1, Set.of(TraceBreakpointKind.SW_EXECUTE), ""); @@ -73,7 +111,9 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG Msg.debug(this, "Disabling breakpoint"); LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne( breakpointService.getBreakpointsAt(program, addr(program, 0x00401c60)))); + lb.disable(); + waitForCondition(() -> lb.computeState() == State.DISABLED); Msg.debug(this, "Placing another"); breakpointService.placeBreakpointAt(program, addr(program, 0x00401c63), 1, diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java index eae0530f72..145dd242ef 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java @@ -162,15 +162,17 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera try (UndoableTransaction tid = UndoableTransaction.start(program, "Add breakpoint", true)) { program.getBookmarkManager() .setBookmark(addr(program, 0x00401234), - LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", ""); + LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", + "before connect"); program.getBookmarkManager() - .setBookmark(addr(program, 0x00402345), - LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", ""); + .setBookmark(addr(program, 0x00604321), + LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "WRITE;4", + "write version"); } waitForPass(() -> { Set allBreakpoints = breakpointService.getAllBreakpoints(); - assertEquals(3, allBreakpoints.size()); + assertEquals(2, allBreakpoints.size()); }); waitForPass(() -> { assertFalse(bpt.isEnabled(0)); @@ -180,7 +182,7 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera * there are 3 for just a moment, and then additional callbacks mess things up. */ waitForPass(() -> { - assertEquals(3, provider.breakpointTable.getRowCount()); + assertEquals(2, provider.breakpointTable.getRowCount()); assertEquals(3, provider.locationTable.getRowCount()); }); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java index ccc24403b5..ae6bc62311 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java @@ -21,8 +21,11 @@ import static org.junit.Assert.assertTrue; import java.awt.*; import java.awt.event.MouseEvent; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.swing.MenuElement; import javax.swing.SwingUtilities; @@ -40,12 +43,11 @@ import generic.test.category.NightlyCategory; import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; import ghidra.dbg.target.TargetBreakpointSpecContainer; @@ -72,10 +74,8 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu protected static final long TIMEOUT_MILLIS = SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; - protected static final Color D_COLOR = new Color(255, 192, 192); - protected static final Color E_COLOR = new Color(255, 128, 128); - protected static final Color DE_COLOR = new Color(255, 192, 128); - protected static final Color ED_COLOR = new Color(255, 128, 192); + protected static final Map COLOR_FOR_STATE = + Stream.of(State.values()).collect(Collectors.toMap(s -> s, s -> new Color(s.ordinal()))); protected DebuggerBreakpointMarkerPlugin breakpointMarkerPlugin; protected DebuggerListingPlugin listingPlugin; @@ -130,7 +130,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); assertEquals(program, lb.getProgram()); assertEquals(Set.of(trace), lb.getParticipatingTraces()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); }); } @@ -164,22 +164,14 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu */ protected void hackMarkerBackgroundColors(Program p) throws Exception { SwingUtilities.invokeAndWait(() -> { - MarkerSet dd = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, p); - dd.setMarkerColor(D_COLOR); - dd.setColoringBackground(true); - MarkerSet ee = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, p); - ee.setMarkerColor(E_COLOR); - ee.setColoringBackground(true); - MarkerSet de = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, p); - de.setMarkerColor(DE_COLOR); - de.setColoringBackground(true); - MarkerSet ed = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, p); - ed.setMarkerColor(ED_COLOR); - ed.setColoringBackground(true); + for (State state : State.values()) { + if (state == State.NONE) { + continue; + } + MarkerSet ms = markerService.getMarkerSet(state.display, p); + ms.setMarkerColor(COLOR_FOR_STATE.get(state)); + ms.setColoringBackground(true); + } }); } @@ -208,56 +200,56 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu } @Test - public void testProgramBreakpointMarked() throws Exception { + public void testBreakpointMarked() throws Exception { TraceRecorder recorder = addMappedBreakpointOpenAndWait(); Trace trace = recorder.getTrace(); + traceManager.activateTrace(trace); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - Address addr = addr(program, 0x00400123); - hackMarkerBackgroundColors(program); - - waitForPass(() -> assertEquals(E_COLOR, getBackgroundColor(program, addr))); - - lb.disableForProgram(); - waitForDomainObject(program); - - waitForPass(() -> assertEquals(DE_COLOR, getBackgroundColor(program, addr))); - - lb.disableForTrace(trace); - waitForDomainObject(trace); - - waitForPass(() -> assertEquals(D_COLOR, getBackgroundColor(program, addr))); - - lb.enableForProgram(); - waitForDomainObject(program); - - waitForPass(() -> assertEquals(ED_COLOR, getBackgroundColor(program, addr))); - } - - @Test - public void testTraceBreakpointMarked() throws Exception { - TraceRecorder recorder = addMappedBreakpointOpenAndWait(); - Trace trace = recorder.getTrace(); - LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - Address addr = addr(trace, 0x55550123); + Address dAddr = addr(trace, 0x55550123); + Address sAddr = addr(program, 0x00400123); TraceProgramView view = trace.getProgramView(); hackMarkerBackgroundColors(view); + hackMarkerBackgroundColors(program); - assertEquals(E_COLOR, getBackgroundColor(view, addr)); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.ENABLED), + getBackgroundColor(view, dAddr))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.ENABLED), + getBackgroundColor(program, sAddr))); lb.disableForProgram(); waitForDomainObject(program); - waitForPass(() -> assertEquals(ED_COLOR, getBackgroundColor(view, addr))); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_ENABLED), + getBackgroundColor(view, dAddr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_DISABLED), + getBackgroundColor(program, sAddr))); lb.disableForTrace(trace); waitForDomainObject(trace); - waitForPass(() -> assertEquals(D_COLOR, getBackgroundColor(view, addr))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.DISABLED), + getBackgroundColor(view, dAddr))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.DISABLED), + getBackgroundColor(program, sAddr))); lb.enableForProgram(); waitForDomainObject(program); - waitForPass(() -> assertEquals(DE_COLOR, getBackgroundColor(view, addr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_DISABLED), + getBackgroundColor(view, dAddr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_ENABLED), + getBackgroundColor(program, sAddr))); } protected static final Set POPUP_ACTIONS = Set.of(AbstractSetBreakpointAction.NAME, @@ -366,14 +358,15 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu AbstractDisableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); pressEscape(); - lb.disableForProgram(); // Should not change anything + lb.disableForProgram(); // Adds "enable", which will only affect bookmark waitForDomainObject(program); clickListing(listingPlugin.getListingPanel(), addr(trace, 0x55550123), MouseEvent.BUTTON3); assertMenu(POPUP_ACTIONS, Set.of(AbstractSetBreakpointAction.NAME, AbstractToggleBreakpointAction.NAME, - AbstractDisableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); + AbstractEnableBreakpointAction.NAME, AbstractDisableBreakpointAction.NAME, + AbstractClearBreakpointAction.NAME)); pressEscape(); lb.disableForTrace(trace); @@ -386,17 +379,18 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu AbstractEnableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); pressEscape(); - lb.enableForProgram(); // Again, no change + lb.enableForProgram(); // This time, adds "disable", which will only affect bookmark waitForDomainObject(program); clickListing(listingPlugin.getListingPanel(), addr(trace, 0x55550123), MouseEvent.BUTTON3); assertMenu(POPUP_ACTIONS, Set.of(AbstractSetBreakpointAction.NAME, AbstractToggleBreakpointAction.NAME, - AbstractEnableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); + AbstractEnableBreakpointAction.NAME, AbstractDisableBreakpointAction.NAME, + AbstractClearBreakpointAction.NAME)); // TODO: Should mixed trace enablement be considered? - // TODO: Margin, too? (Is there one?) + // TODO: Margin, too? } protected ProgramLocationActionContext staticCtx(Address address) { @@ -433,7 +427,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu waitForPass(() -> { LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); // TODO: Different cases for different expected default kinds? assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), lb.getKinds()); }); @@ -460,7 +454,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu waitForPass(() -> { LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); // TODO: Different cases for different expected default kinds? assertEquals(Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE), lb.getKinds()); @@ -475,12 +469,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionToggleBreakpoint, staticCtx(addr(program, 0x00400123)), false); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionToggleBreakpoint, staticCtx(addr(program, 0x00400123)), false); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeState())); } @Test @@ -489,30 +483,39 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu Trace trace = recorder.getTrace(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); + // NB. addMappedBreakpointOpenAndWait already makes this assertion. Just a reminder: + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + + // TODO: Add a second trace breakpoint and verify program toggling behavior + // For now, just force an inconsistent state and see what happens when we toggle + + lb.disableForProgram(); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + // NB. toggling from dynamic view, this toggles trace bpt, not logical/program bpt + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + + lb.enableForProgram(); waitForPass( - () -> assertEquals(Enablement.DISABLED_ENABLED, lb.computeEnablementForTrace(trace))); + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForTrace(trace))); performAction(breakpointMarkerPlugin.actionToggleBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablementForTrace(trace))); - - lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); - - performAction(breakpointMarkerPlugin.actionToggleBreakpoint, - dynamicCtx(trace, addr(trace, 0x55550123)), true); - - waitForPass( - () -> assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace))); - - performAction(breakpointMarkerPlugin.actionToggleBreakpoint, - dynamicCtx(trace, addr(trace, 0x55550123)), true); - - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); } protected void testActionSetBreakpointProgram(DockingAction action, @@ -529,7 +532,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique.assertOne( breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); assertEquals(expectedKinds, lb.getKinds()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); assertEquals("Test name", lb.getName()); }); } @@ -548,8 +551,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique .assertOne(breakpointService.getBreakpointsAt(trace, addr(trace, 0x55550321))); assertEquals(expectedKinds, lb.getKinds()); - assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, lb.computeStateForTrace(trace)); }); + /** + * TODO: Test with a second trace? ATM, the program state is auto-synced with single trace, + * so might be difficult to assess more complex state changes. + */ } @Test @@ -617,12 +624,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu addMappedBreakpointOpenAndWait(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionEnableBreakpoint, staticCtx(addr(program, 0x00400123)), true); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeState())); } @Test @@ -631,13 +638,13 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu Trace trace = recorder.getTrace(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionEnableBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass( - () -> assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } @Test @@ -648,7 +655,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionDisableBreakpoint, staticCtx(addr(program, 0x00400123)), true); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); } @Test @@ -660,8 +667,8 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionDisableBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass( - () -> assertEquals(Enablement.DISABLED_ENABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } @Test @@ -683,8 +690,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionClearBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - // NB. Because it was deleted from the *trace context* - waitForPass(() -> assertEquals(Enablement.INEFFECTIVE_ENABLED, - lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.NONE, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java index f1e79b3f75..9538e2b30e 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java @@ -37,7 +37,7 @@ import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider.L import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.async.AsyncTestUtils; import ghidra.dbg.model.TestTargetProcess; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; @@ -137,7 +137,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertEquals("55550123", row.getAddress().toString()); assertEquals(trace, row.getDomainObject()); assertEquals("SW_EXECUTE", row.getKinds()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, row.getState()); } @Test @@ -156,16 +156,16 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, row.getState()); // NB, the row does not take the value immediately, but via async callbacks row.setEnabled(false); - waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.INCONSISTENT_DISABLED, row.getState())); row.setEnabled(true); - waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, row.getState())); } @Test @@ -180,7 +180,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertEquals("00400123", row.getAddress().toString()); assertEquals(program, row.getDomainObject()); assertEquals("SW_EXECUTE", row.getKinds()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); } @Test @@ -192,17 +192,17 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); row.setEnabled(false); // Synchronous, but on swing thread waitForDomainObject(program); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); row.setEnabled(true); waitForDomainObject(program); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); } @Test @@ -228,7 +228,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpoint lb = row.getLogicalBreakpoint(); assertEquals(program, lb.getProgram()); assertEquals(Set.of(trace), lb.getParticipatingTraces()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.ENABLED, row.getState()); }); LogicalBreakpointRow row = @@ -238,22 +238,24 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge lb.disableForProgram(); waitForDomainObject(program); - assertEquals(Enablement.DISABLED_ENABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INCONSISTENT_DISABLED, row.getState())); // NOTE: This acts on the corresponding target, not directly on trace lb.disableForTrace(trace); + waitForDomainObject(trace); - waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, row.getState())); lb.enableForProgram(); waitForDomainObject(program); - assertEquals(Enablement.ENABLED_DISABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, row.getState())); // This duplicates the initial case, but without it, I just feel incomplete lb.enableForTrace(trace); + waitForDomainObject(trace); - waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, row.getState())); } @Test @@ -302,12 +304,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge breakpointsProvider.breakpointFilterPanel.setSelectedItem(row); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled()); performAction(breakpointsProvider.actionEnableSelectedBreakpoints); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled()); breakpointsProvider.breakpointTable.clearSelection(); @@ -345,12 +347,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge row.setEnabled(false); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled()); performAction(breakpointsProvider.actionEnableAllBreakpoints); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled()); // Bookmark part should actually be synchronous. @@ -378,12 +380,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge breakpointsProvider.breakpointFilterPanel.setSelectedItem(row); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INEFFECTIVE_ENABLED, row.getState())); assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled()); performAction(breakpointsProvider.actionDisableSelectedBreakpoints); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INEFFECTIVE_DISABLED, row.getState())); assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled()); breakpointsProvider.breakpointTable.clearSelection(); @@ -417,12 +419,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); performAction(breakpointsProvider.actionDisableAllBreakpoints); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); // Bookmark part should actually be synchronous. diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java index c1beba7a53..a28c446881 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java @@ -28,7 +28,7 @@ import generic.Unique; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.async.AsyncReference; import ghidra.dbg.model.TestTargetMemoryRegion; import ghidra.dbg.model.TestTargetProcess; @@ -292,18 +292,22 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } } - protected void addTargetAccessBreakpoint(TraceRecorder r) throws Throwable { + protected void addTargetAccessBreakpoint(TraceRecorder r, TestTargetMemoryRegion region) + throws Throwable { + TraceMemoryRegion traceRegion = + waitFor(() -> r.getTraceMemoryRegion(region), "Recorder missed region: " + region); + long offset = traceRegion.getMinAddress().getOffset() + 0x0123; TargetBreakpointSpecContainer cont = getBreakpointContainer(r); - cont.placeBreakpoint(mb.testModel.getAddress("ram", 0x56550123), + cont.placeBreakpoint(mb.addr(offset), Set.of(TargetBreakpointKind.READ, TargetBreakpointKind.WRITE)) .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } protected void addTargetSoftwareBreakpoint(TraceRecorder r, TestTargetMemoryRegion region) throws Throwable { - TraceMemoryRegion textRegion = + TraceMemoryRegion traceRegion = waitFor(() -> r.getTraceMemoryRegion(region), "Recorder missed region: " + region); - long offset = textRegion.getMinAddress().getOffset() + 0x0123; + long offset = traceRegion.getMinAddress().getOffset() + 0x0123; TargetBreakpointSpecContainer cont = getBreakpointContainer(r); cont.placeBreakpoint(mb.addr(offset), Set.of(TargetBreakpointKind.SW_EXECUTE)) .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); @@ -367,11 +371,11 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x56550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INCONSISTENT_ENABLED, enLb.computeStateForTrace(trace)); } - protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace) { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 0x55550123, 1); + protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace, int total) { + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 0x55550123, total); } protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace, long offset, @@ -388,7 +392,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, offset), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INCONSISTENT_ENABLED, enLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointForMappedSoftwareBreakpoint(Trace trace) { @@ -396,8 +400,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe LogicalBreakpoint enLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); - assertNull(enLb.getProgramBookmark()); - assertEquals(Enablement.DISABLED_ENABLED, enLb.computeEnablementForProgram(program)); + assertNotNull(enLb.getProgramBookmark()); // Created automatically when trace breakpoint set. + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds()); TraceBreakpoint bpt = Unique.assertOne(trace.getBreakpointManager().getAllBreakpoints()); @@ -405,7 +409,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED_DISABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointsForUnmappedBookmarks() { @@ -417,7 +421,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(program, 0x00400123), enLb.getProgramLocation().getAddress()); assertEquals(enBm, enLb.getProgramBookmark()); assertTrue(enLb.getMappedTraces().isEmpty()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForProgram(program)); + assertEquals(State.INEFFECTIVE_ENABLED, enLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds()); LogicalBreakpoint disLb = Unique @@ -426,7 +430,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(program, 0x00400321), disLb.getProgramLocation().getAddress()); assertEquals(disBm, disLb.getProgramBookmark()); assertTrue(disLb.getMappedTraces().isEmpty()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), disLb.getKinds()); } @@ -442,8 +446,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), enLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.NONE, enLb.computeStateForTrace(trace)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -454,8 +458,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550321), disLb.getTraceAddress(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(Trace trace1, @@ -479,9 +483,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertNotEquals(Set.of(bpt1), enLb.getTraceBreakpoints(trace2)); // Sanity check assertEquals(Set.of(bpt1, bpt2), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace1)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace2)); + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace1)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace2)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -494,9 +498,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(Set.of(), disLb.getTraceBreakpoints(trace1)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace2)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace1)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace2)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace1)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace2)); } protected void assertLogicalBreakpointForMappedBookmarkAnd1TraceBreakpoint(Trace trace) { @@ -514,8 +518,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -526,8 +530,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550321), disLb.getTraceAddress(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace)); } @Test @@ -551,8 +555,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe Trace trace = recorder1.getTrace(); traceManager.openTrace(trace); - addTargetDataRegion(mb.testProcess1); - addTargetAccessBreakpoint(recorder1); + TestTargetMemoryRegion data = addTargetDataRegion(mb.testProcess1); + addTargetAccessBreakpoint(recorder1, data); waitForPass(() -> { assertLogicalBreakpointForLoneAccessBreakpoint(trace); @@ -564,8 +568,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe startRecorder1(); Trace trace = recorder1.getTrace(); - addTargetDataRegion(mb.testProcess1); - addTargetAccessBreakpoint(recorder1); + TestTargetMemoryRegion data = addTargetDataRegion(mb.testProcess1); + addTargetAccessBreakpoint(recorder1, data); waitForDomainObject(trace); traceManager.openTrace(trace); @@ -698,7 +702,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); changeListener.assertAccurate(); @@ -784,14 +788,12 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTextMapping(recorder1, text, program); addTargetSoftwareBreakpoint(recorder1, text); - waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); - }); + waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); expectMappingChange(() -> programManager.openProgram(program)); waitForSwing(); - assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); + waitForPass(() -> assertLogicalBreakpointForMappedSoftwareBreakpoint(trace)); } @Test @@ -908,7 +910,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe removeTargetSoftwareBreakpoint(recorder1); waitForPass(() -> { - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // NB. The bookmark remains + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); }); } @@ -936,7 +940,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe expectMappingChange(() -> removeTextMapping(recorder1, program)); waitForSwing(); - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + // NB. Bookmark remains + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2); } @Test @@ -1175,7 +1180,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); /** * NB. The recorder could still be mid transaction. If we open this transaction too soon, @@ -1196,7 +1201,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + // NB. The bookmark is left over, so total increases + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2); }); } @@ -1226,7 +1232,10 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } waitForDomainObject(trace); // Duplicative, but for form's sake.... - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // Left over, because it was bookmarked automatically in program + // Still, there should be no trace breakpoint in it + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); } @Test @@ -1280,7 +1289,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe expectMappingChange(() -> undo(trace)); - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // NB. The bookmark remains + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); expectMappingChange(() -> redo(trace)); @@ -1331,14 +1342,14 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); waitOn(lb.disable()); waitForPass(() -> { - assertEquals(Enablement.DISABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeState()); }); // Simulate a step, which should also cause snap advance in recorder @@ -1347,12 +1358,12 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe waitOn(mb.testModel.flushEvents()); waitForPass(() -> { assertEquals(oldSnap + 1, recorder1.getSnap()); - assertEquals(Enablement.DISABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeState()); }); waitOn(lb.enable()); waitForPass(() -> { - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); }); } @@ -1367,7 +1378,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); @@ -1390,7 +1401,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForDomainObject(trace); - waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace)); + waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); @@ -1419,7 +1430,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); // NOTE: Still recording in the background @@ -1428,4 +1439,105 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(0, breakpointService.getAllBreakpoints().size()); } + + @Test + public void testRecordThenAddTwoBreakpointsDisable1Mixed() throws Throwable { + startRecorder1(); + Trace trace = recorder1.getTrace(); + traceManager.openTrace(trace); + + createProgramFromTrace(trace); + intoProject(program); + programManager.openProgram(program); + + addProgramTextBlock(program); + TestTargetMemoryRegion text = addTargetTextRegion(mb.testProcess1); + + addTextMapping(recorder1, text, program); + waitForSwing(); + + addProgramBreakpoints(program); + addTargetSoftwareBreakpoint(recorder1, text); + addTargetSoftwareBreakpoint(recorder1, text); + + waitForPass(() -> { + assertEquals(2, breakpointService.getAllBreakpoints().size()); + + LogicalBreakpoint lb = Unique.assertOne( + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); + assertEquals(program, lb.getProgram()); + assertEquals(Set.of(trace), lb.getMappedTraces()); + + assertEquals(2, lb.getTraceBreakpoints().size()); + }); + + LogicalBreakpoint lb = Unique + .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); + Set locs = lb.getTraceBreakpoints(); + + TraceBreakpoint bpt0 = + locs.stream().filter(b -> b.getName().equals("0")).findAny().orElseThrow(); + TraceBreakpoint bpt1 = + locs.stream().filter(b -> b.getName().equals("1")).findAny().orElseThrow(); + breakpointService.disableLocs(Set.of(bpt0)); + + waitForPass(() -> { + assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); + assertEquals(State.INCONSISTENT_MIXED, lb.computeStateForTrace(trace)); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForLocation(bpt0)); + assertEquals(State.ENABLED, lb.computeStateForLocation(bpt1)); + }); + } + + @Test + public void testRecordThenAddTwoKindsOfBreakpointsDisable1Mixed() throws Throwable { + startRecorder1(); + Trace trace = recorder1.getTrace(); + traceManager.openTrace(trace); + + createProgramFromTrace(trace); + intoProject(program); + programManager.openProgram(program); + + addProgramTextBlock(program); + TestTargetMemoryRegion text = addTargetTextRegion(mb.testProcess1); + + addTextMapping(recorder1, text, program); + waitForSwing(); + + addProgramBreakpoints(program); + addTargetSoftwareBreakpoint(recorder1, text); + addTargetAccessBreakpoint(recorder1, text); + + waitForPass(() -> { + assertEquals(3, breakpointService.getAllBreakpoints().size()); + + Set lbs = + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)); + assertEquals(2, lbs.size()); + lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE)) + .findAny() + .orElseThrow(); + lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.READ)) + .findAny() + .orElseThrow(); + }); + Set lbs = + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)); + LogicalBreakpoint lbEx = lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE)) + .findAny() + .orElseThrow(); + LogicalBreakpoint lbRw = lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.READ)) + .findAny() + .orElseThrow(); + waitOn(lbEx.disable()); + + // TODO: This is more a test for the marker plugin, no? + waitForPass( + () -> assertEquals(State.MIXED, lbEx.computeState().sameAdddress(lbRw.computeState()))); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java index d43dda9ffb..ea3aad4482 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java @@ -20,7 +20,7 @@ import java.awt.Graphics; import java.awt.event.MouseEvent; import java.util.*; -import javax.swing.ImageIcon; +import javax.swing.Icon; import javax.swing.JToolTip; import javax.swing.event.ChangeListener; @@ -135,7 +135,7 @@ public class MarkerManager implements MarkerService { @Override public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon) { + Color color, Icon icon) { MarkerSetImpl markers = new PointMarkerSet(this, name, markerDescription, priority, showMarkers, showNavigation, colorBackground, color, icon, program); @@ -146,7 +146,7 @@ public class MarkerManager implements MarkerService { @Override public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon, boolean isPreferred) { + Color color, Icon icon, boolean isPreferred) { MarkerSetImpl markers = new PointMarkerSet(this, name, markerDescription, priority, showMarkers, showNavigation, colorBackground, color, icon, isPreferred, program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java index c8cbc2c4bb..5e241effab 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java @@ -21,6 +21,7 @@ import java.awt.image.ImageObserver; import java.util.Iterator; import java.util.List; +import javax.swing.Icon; import javax.swing.ImageIcon; import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap; @@ -55,16 +56,16 @@ class PointMarkerSet extends MarkerSetImpl { */ PointMarkerSet(MarkerManager navigationManager, String name, String desc, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor, - ImageIcon icon, boolean isPreferred, Program program) { + Icon icon, boolean isPreferred, Program program) { super(navigationManager, program, name, desc, priority, showMarkers, showNavigation, colorBackground, markerColor, isPreferred); if (icon == null) { icon = ResourceManager.loadImage("images/warning.png"); } - icon = ResourceManager.getScaledIcon(icon, 16, 16, Image.SCALE_SMOOTH); - image = icon.getImage(); - imageObserver = icon.getImageObserver(); + ImageIcon imageIcon = ResourceManager.getScaledIcon(icon, 16, 16, Image.SCALE_SMOOTH); + image = imageIcon.getImage(); + imageObserver = imageIcon.getImageObserver(); if (markerColor != null) { fillColor = getFillColor(markerColor); } @@ -86,7 +87,7 @@ class PointMarkerSet extends MarkerSetImpl { */ PointMarkerSet(MarkerManager navigationManager, String name, String desc, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor, - ImageIcon icon, Program program) { + Icon icon, Program program) { this(navigationManager, name, desc, priority, showMarkers, showNavigation, colorBackground, markerColor, icon, true, program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java index 3a4bc250e8..8d8e2e0528 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java @@ -17,7 +17,7 @@ package ghidra.app.services; import java.awt.Color; -import javax.swing.ImageIcon; +import javax.swing.Icon; import javax.swing.event.ChangeListener; import ghidra.app.plugin.core.marker.*; @@ -166,7 +166,7 @@ public interface MarkerService { */ public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon); + Color color, Icon icon); /** * Create a Marker display which shows point type markers. @@ -186,7 +186,7 @@ public interface MarkerService { */ public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon, boolean isPreferred); + Color color, Icon icon, boolean isPreferred); /** * Remove the marker set