GP-885: Better indication of ineffective breakpoints. Toggles on lower table.
@@ -31,6 +31,9 @@ src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.ht
|
||||
src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.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/breakpoints-clear-all.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png||GHIDRA||||END|
|
||||
@@ -120,6 +123,8 @@ src/main/resources/images/blank.png||GHIDRA||||END|
|
||||
src/main/resources/images/breakpoint-clear.png||GHIDRA||||END|
|
||||
src/main/resources/images/breakpoint-disable.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|
|
||||
@@ -165,6 +170,8 @@ src/main/svg/blank.svg||GHIDRA||||END|
|
||||
src/main/svg/breakpoint-clear.svg||GHIDRA||||END|
|
||||
src/main/svg/breakpoint-disable.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|
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -25,7 +25,7 @@
|
||||
<P>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
|
||||
considered, even if the user has stepped a trace backward. Breakpoints which map to the same
|
||||
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. <B>NOTE:</B> The breakpoints window cannot display or
|
||||
manipulate breakpoints from a target until that target is recorded into a trace. See the <A
|
||||
@@ -46,28 +46,46 @@
|
||||
the grouping of breakpoint locations into logical breakpoints by this manager is done
|
||||
<EM>without respect</EM> to the specifications. Often the specification is at a higher stratum
|
||||
than Ghidra natively understands, e.g., the source filename and line number, and so such
|
||||
specifications are not relevant. Note, however, that the model may not permit locations to be
|
||||
specifications are not relevant. Note, however, that the model might not permit locations to be
|
||||
manipulated independently of their specification, which may limit how the manager can operate
|
||||
on each breakpoint location.</P>
|
||||
|
||||
<P>Because of the logical grouping of breakpoints, it is possible for a breakpoint to be in a
|
||||
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 missing location
|
||||
in a logical breakpoint is treated the same as a disabled breakpoint. Thus, the logical
|
||||
breakpoint enters an inconsistent state, because it is "enabled" in the program, but "disabled"
|
||||
in the trace. Toggling such a breakpoint enables all missing locations, bringing it into a
|
||||
consistent enabled state. Toggling it again will disable all locations.</P>
|
||||
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.</P>
|
||||
|
||||
<H2>Tables and Columns</H2>
|
||||
|
||||
<P>The top table, which lists logical breakpoints, has the following columns:</P>
|
||||
|
||||
<UL>
|
||||
<LI>Enabled - displays an icon indicating the state of the breakpoint: <IMG alt="" src=
|
||||
"images/breakpoint-enable.png"> Enabled, <IMG alt="" src="images/breakpoint-disable.png">
|
||||
Disabled, or <IMG alt="" src="images/breakpoint-mixed-ed.png"> Inconsistent. Clicking the
|
||||
icon toggles the breakpoint.</LI>
|
||||
<LI>Enabled - displays an icon indicating the state of the breakpoint. Clicking the icon
|
||||
toggles the breakpoint.</LI>
|
||||
|
||||
<LI style="list-style: none">
|
||||
<UL>
|
||||
<LI><IMG alt="" src="images/breakpoint-enable.png"> Enabled</LI>
|
||||
|
||||
<LI><IMG alt="" src="images/breakpoint-disable.png"> Disabled</LI>
|
||||
|
||||
<LI><IMG alt="" src="images/breakpoint-mixed-ed.png"> Inconsistent: Enabled with disabled
|
||||
locations</LI>
|
||||
|
||||
<LI><IMG alt="" src="images/breakpoint-mixed-de.png"> Inconsistent: Disabled with enabled
|
||||
locations</LI>
|
||||
|
||||
<LI><IMG alt="" src="images/breakpoint-ineffective-e.png"> Enabled but ineffective</LI>
|
||||
|
||||
<LI><IMG alt="" src="images/breakpoint-ineffective-d.png"> Disabled and ineffective</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>Image - gives the name of the Ghidra program, if the breakpoint is mapped to one.</LI>
|
||||
|
||||
@@ -89,6 +107,9 @@
|
||||
<P>The bottom table, which lists trace breakpoint locations, has the following columns:</P>
|
||||
|
||||
<UL>
|
||||
<LI>Enabled - displays an icon indicating the state of the location. Clicking the icon
|
||||
toggles the location.</LI>
|
||||
|
||||
<LI>Name - displays the name given to the location by the connected debugger. This field is
|
||||
user modifiable.</LI>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 302 B |
|
After Width: | Height: | Size: 368 B |
@@ -252,10 +252,14 @@ public interface DebuggerResources {
|
||||
|
||||
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;
|
||||
ImageIcon ICON_BREAKPOINT_ENABLED_MARKER = ICON_ENABLE_BREAKPOINT;
|
||||
@@ -264,6 +268,10 @@ public interface DebuggerResources {
|
||||
ResourceManager.loadImage("images/breakpoint-mixed-ed.png");
|
||||
ImageIcon ICON_BREAKPOINT_MIXED_DE_MARKER =
|
||||
ResourceManager.loadImage("images/breakpoint-mixed-de.png");
|
||||
ImageIcon ICON_BREAKPOINT_INEFFECTIVE_E_MARKER =
|
||||
ResourceManager.loadImage("images/breakpoint-ineffective-e.png");
|
||||
ImageIcon ICON_BREAKPOINT_INEFFECTIVE_D_MARKER =
|
||||
ResourceManager.loadImage("images/breakpoint-ineffective-d.png");
|
||||
|
||||
Icon ICON_UNIQUE_REF_READ =
|
||||
new RotateIcon(ResourceManager.loadImage("images/cursor_arrow.gif"), 180); // TODO
|
||||
@@ -274,6 +282,13 @@ public interface DebuggerResources {
|
||||
Color DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS = new Color(0.875f, 0.75f, 0.75f);
|
||||
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 =
|
||||
"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 =
|
||||
"Colors.Ineffective Disabled Breakpoint Markers";
|
||||
Color DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_MARKERS =
|
||||
DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_MARKERS;
|
||||
|
||||
String OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND =
|
||||
"Colors.Enabled Breakpoint Markers Have Background";
|
||||
@@ -283,6 +298,14 @@ 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 =
|
||||
"Colors.Ineffective Enabled Breakpoint Markers Have Background";
|
||||
boolean DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND = true;
|
||||
|
||||
String OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND =
|
||||
"Colors.Ineffective Disabled Breakpoint Markers Have Background";
|
||||
boolean DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND = false;
|
||||
|
||||
// TODO: Re-assign/name groups
|
||||
String GROUP_GENERAL = "Dbg1. General";
|
||||
String GROUP_CONNECTION = "Dbg2. Connection";
|
||||
|
||||
@@ -17,15 +17,20 @@ package ghidra.app.plugin.core.debug.gui.breakpoint;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class BreakpointLocationRow {
|
||||
private final DebuggerBreakpointsProvider provider;
|
||||
private final TraceBreakpoint loc;
|
||||
|
||||
public BreakpointLocationRow(TraceBreakpoint loc) {
|
||||
public BreakpointLocationRow(DebuggerBreakpointsProvider provider, TraceBreakpoint loc) {
|
||||
this.provider = provider;
|
||||
this.loc = loc;
|
||||
}
|
||||
|
||||
@@ -33,6 +38,27 @@ public class BreakpointLocationRow {
|
||||
return loc.getName();
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return loc.isEnabled();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
TraceRecorder recorder = provider.modelService.getRecorder(loc.getTrace());
|
||||
TargetBreakpointLocation bpt = recorder.getTargetBreakpoint(loc);
|
||||
if (enabled) {
|
||||
bpt.getSpecification().enable().exceptionally(ex -> {
|
||||
Msg.showError(this, null, "Toggle breakpoint", "Could not enable breakpoint", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
bpt.getSpecification().disable().exceptionally(ex -> {
|
||||
Msg.showError(this, null, "Toggle breakpoint", "Could not disable breakpoint", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(loc.getTrace(), "Set breakpoint name", true)) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import docking.widgets.table.GTableFilterPanel;
|
||||
import ghidra.app.services.LogicalBreakpoint.Enablement;
|
||||
|
||||
public class DebuggerBreakpointEnablementTableCellEditor extends AbstractCellEditor
|
||||
implements TableCellEditor, ActionListener {
|
||||
private final GTableFilterPanel<LogicalBreakpointRow> filterPanel;
|
||||
protected final JButton button = new JButton();
|
||||
|
||||
private Enablement value = Enablement.NONE;
|
||||
private LogicalBreakpointRow row;
|
||||
|
||||
public DebuggerBreakpointEnablementTableCellEditor(
|
||||
GTableFilterPanel<LogicalBreakpointRow> filterPanel) {
|
||||
this.filterPanel = filterPanel;
|
||||
|
||||
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.row = filterPanel.getRowObject(row);
|
||||
this.value = (Enablement) value;
|
||||
button.setIcon(DebuggerBreakpointEnablementTableCellRenderer.iconForEnablement(this.value));
|
||||
button.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
return button;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// TODO: Consider the current trace if "current trace only" filter is on?
|
||||
boolean curEn = row.getLogicalBreakpoint().getMappedTraces().isEmpty()
|
||||
? value.enabled // Toggle when no traces are mappable
|
||||
: value == Enablement.ENABLED; // Make consistent as enabled if mappable
|
||||
value = curEn ? Enablement.DISABLED : Enablement.ENABLED;
|
||||
fireEditingStopped();
|
||||
}
|
||||
}
|
||||
@@ -17,36 +17,55 @@ 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<Boolean> {
|
||||
extends AbstractGColumnRenderer<Enablement> {
|
||||
|
||||
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);
|
||||
Boolean enabled = (Boolean) data.getValue();
|
||||
if (enabled == null) {
|
||||
/**
|
||||
* TODO: Distinguish DE from ED. Will need Enablement, not just Boolean. Will also
|
||||
* require custom "cell editor".
|
||||
*/
|
||||
setIcon(DebuggerResources.ICON_BREAKPOINT_MIXED_ED_MARKER);
|
||||
}
|
||||
else if (enabled) {
|
||||
setIcon(DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER);
|
||||
}
|
||||
else {
|
||||
setIcon(DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER);
|
||||
}
|
||||
Enablement en = (Enablement) data.getValue();
|
||||
setIcon(iconForEnablement(en));
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
setText("");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(Boolean t, Settings settings) {
|
||||
return t == null ? "Mixed" : t ? "Enabled" : "Disabled";
|
||||
public String getFilterString(Enablement t, Settings settings) {
|
||||
return t.name();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* ###
|
||||
* 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.docking.settings.Settings;
|
||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||
|
||||
public class DebuggerBreakpointLocEnabledTableCellRenderer
|
||||
extends AbstractGColumnRenderer<Boolean> {
|
||||
|
||||
protected static Icon iconForEnabled(Boolean enabled) {
|
||||
return enabled == null ? null
|
||||
: enabled
|
||||
? DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER
|
||||
: DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER;
|
||||
}
|
||||
|
||||
public DebuggerBreakpointLocEnabledTableCellRenderer() {
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
super.getTableCellRendererComponent(data);
|
||||
Boolean en = (Boolean) data.getValue();
|
||||
setIcon(iconForEnabled(en));
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
setText("");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(Boolean t, Settings settings) {
|
||||
return t == null ? "null" : t ? "enabled" : "disabled";
|
||||
}
|
||||
}
|
||||
@@ -56,21 +56,21 @@ import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@PluginInfo( //
|
||||
shortDescription = "Debugger breakpoint marker service plugin", //
|
||||
description = "Marks logical breakpoints in the listings", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.RELEASED, //
|
||||
eventsConsumed = { //
|
||||
ProgramOpenedPluginEvent.class, //
|
||||
ProgramClosedPluginEvent.class, //
|
||||
TraceOpenedPluginEvent.class, //
|
||||
TraceClosedPluginEvent.class, //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerLogicalBreakpointService.class, //
|
||||
MarkerService.class, //
|
||||
} //
|
||||
shortDescription = "Debugger breakpoint marker service plugin", //
|
||||
description = "Marks logical breakpoints in the listings", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.RELEASED, //
|
||||
eventsConsumed = { //
|
||||
ProgramOpenedPluginEvent.class, //
|
||||
ProgramClosedPluginEvent.class, //
|
||||
TraceOpenedPluginEvent.class, //
|
||||
TraceClosedPluginEvent.class, //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerLogicalBreakpointService.class, //
|
||||
MarkerService.class, //
|
||||
} //
|
||||
)
|
||||
public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
implements PopupActionProvider {
|
||||
@@ -173,8 +173,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
|
||||
protected static long computeDefaultLength(ActionContext context,
|
||||
Collection<TraceBreakpointKind> selected) {
|
||||
if (selected.contains(TraceBreakpointKind.EXECUTE) ||
|
||||
selected.contains(TraceBreakpointKind.SOFTWARE)) {
|
||||
if (selected.contains(TraceBreakpointKind.HW_EXECUTE) ||
|
||||
selected.contains(TraceBreakpointKind.SW_EXECUTE)) {
|
||||
return 1;
|
||||
}
|
||||
return computeLengthFromContext(context);
|
||||
@@ -191,11 +191,11 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
Listing listing = loc.getProgram().getListing();
|
||||
CodeUnit cu = listing.getCodeUnitContaining(loc.getAddress());
|
||||
if (cu instanceof Instruction) {
|
||||
if (supported.contains(TraceBreakpointKind.SOFTWARE)) {
|
||||
return Set.of(TraceBreakpointKind.SOFTWARE);
|
||||
if (supported.contains(TraceBreakpointKind.SW_EXECUTE)) {
|
||||
return Set.of(TraceBreakpointKind.SW_EXECUTE);
|
||||
}
|
||||
else if (supported.contains(TraceBreakpointKind.EXECUTE)) {
|
||||
return Set.of(TraceBreakpointKind.EXECUTE);
|
||||
else if (supported.contains(TraceBreakpointKind.HW_EXECUTE)) {
|
||||
return Set.of(TraceBreakpointKind.HW_EXECUTE);
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
@@ -219,6 +219,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
|
||||
final MarkerSet enabled;
|
||||
final MarkerSet disabled;
|
||||
final MarkerSet ineffectiveE;
|
||||
final MarkerSet ineffectiveD;
|
||||
final MarkerSet mixedED;
|
||||
final MarkerSet mixedDE;
|
||||
|
||||
@@ -240,6 +242,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
|
||||
enabled = getEnabledMarkerSet();
|
||||
disabled = getDisabledMarkerSet();
|
||||
ineffectiveE = getIneffectiveEMarkerSet();
|
||||
ineffectiveD = getIneffectiveDMarkerSet();
|
||||
mixedED = getMixedEDMarkerSet();
|
||||
mixedDE = getMixedDEMarkerSet();
|
||||
}
|
||||
@@ -272,6 +276,36 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
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);
|
||||
@@ -306,6 +340,10 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
return enabled;
|
||||
case DISABLED:
|
||||
return disabled;
|
||||
case INEFFECTIVE_ENABLED:
|
||||
return ineffectiveE;
|
||||
case INEFFECTIVE_DISABLED:
|
||||
return ineffectiveD;
|
||||
case ENABLED_DISABLED:
|
||||
return mixedED;
|
||||
case DISABLED_ENABLED:
|
||||
@@ -335,6 +373,18 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
public void setIneffectiveEnabledMarkerColor(Color color) {
|
||||
if (ineffectiveE != null) {
|
||||
ineffectiveE.setMarkerColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIneffectiveDisabledMarkerColor(Color color) {
|
||||
if (ineffectiveD != null) {
|
||||
ineffectiveD.setMarkerColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEnabledColoringBackground(boolean coloringBackground) {
|
||||
if (enabled != null) {
|
||||
enabled.setColoringBackground(coloringBackground);
|
||||
@@ -353,6 +403,18 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
public void setIneffectiveEnabledColoringBackground(boolean coloringBackground) {
|
||||
if (ineffectiveE != null) {
|
||||
ineffectiveE.setColoringBackground(coloringBackground);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIneffectiveDisabledColoringBackground(boolean coloringBackground) {
|
||||
if (ineffectiveD != null) {
|
||||
ineffectiveD.setColoringBackground(coloringBackground);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (enabled != null) {
|
||||
markerService.removeMarker(enabled, program);
|
||||
@@ -360,6 +422,12 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
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);
|
||||
}
|
||||
@@ -375,6 +443,12 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
if (disabled != null) {
|
||||
disabled.clearAll();
|
||||
}
|
||||
if (ineffectiveE != null) {
|
||||
ineffectiveE.clearAll();
|
||||
}
|
||||
if (ineffectiveD != null) {
|
||||
ineffectiveD.clearAll();
|
||||
}
|
||||
if (mixedED != null) {
|
||||
mixedED.clearAll();
|
||||
}
|
||||
@@ -409,12 +483,14 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
return breakpoint.computeEnablementForTrace(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Document me
|
||||
*
|
||||
* <p>
|
||||
* This is a little different from that in the breakpoint service.
|
||||
*
|
||||
* @param loc
|
||||
@@ -657,30 +733,62 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_MARKERS, //
|
||||
description = "Background color for memory at an enabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_MARKERS, //
|
||||
description = "Background color for memory at an enabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
private Color breakpointEnabledMarkerColor =
|
||||
DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS;
|
||||
|
||||
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND, //
|
||||
description = "Whether or not to color background for memory at an enabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND, //
|
||||
description = "Whether or not to color background for memory at an enabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
private boolean breakpointEnabledColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_MARKERS, //
|
||||
description = "Background color for memory at a disabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_MARKERS, //
|
||||
description = "Background color for memory at a disabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
private Color breakpointDisabledMarkerColor =
|
||||
DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_MARKERS;
|
||||
|
||||
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND, //
|
||||
description = "Whether or not to color background for memory at a disabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND, //
|
||||
description = "Whether or not to color background for memory at a disabled breakpoint", //
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
private boolean breakpointDisabledColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_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;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_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;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_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;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_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;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoOptions.Wiring autoOptionsWiring;
|
||||
|
||||
@@ -728,7 +836,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
private void setEnabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setEnabledColoringBackground(breakpointColoringBackground);
|
||||
@@ -742,13 +851,46 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
private void setDisabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setDisabledColoringBackground(breakpointColoringBackground);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS)
|
||||
private void setIneffectiveEBreakpointMarkerColor(Color breakpointMarkerColor) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveEnabledMarkerColor(breakpointMarkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND)
|
||||
private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS)
|
||||
private void setIneffectiveDBreakpointMarkerColor(Color breakpointMarkerColor) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveDisabledMarkerColor(breakpointMarkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND)
|
||||
private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground);
|
||||
}
|
||||
}
|
||||
|
||||
protected TraceRecorder getRecorderFromContext(ActionContext context) {
|
||||
if (modelService == null) {
|
||||
return null;
|
||||
@@ -879,8 +1021,10 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionSetSoftwareBreakpoint = new SetBreakpointAction(Set.of(TraceBreakpointKind.SOFTWARE));
|
||||
actionSetExecuteBreakpoint = new SetBreakpointAction(Set.of(TraceBreakpointKind.EXECUTE));
|
||||
actionSetSoftwareBreakpoint =
|
||||
new SetBreakpointAction(Set.of(TraceBreakpointKind.SW_EXECUTE));
|
||||
actionSetExecuteBreakpoint =
|
||||
new SetBreakpointAction(Set.of(TraceBreakpointKind.HW_EXECUTE));
|
||||
actionSetReadWriteBreakpoint =
|
||||
new SetBreakpointAction(Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE));
|
||||
actionSetReadBreakpoint = new SetBreakpointAction(Set.of(TraceBreakpointKind.READ));
|
||||
|
||||
@@ -39,6 +39,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.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.AutoService;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
@@ -61,7 +62,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
|
||||
protected enum LogicalBreakpointTableColumns
|
||||
implements EnumeratedTableColumn<LogicalBreakpointTableColumns, LogicalBreakpointRow> {
|
||||
ENABLED("Enabled", Boolean.class, LogicalBreakpointRow::isEnabled, LogicalBreakpointRow::setEnabled, true),
|
||||
ENABLED("Enabled", Enablement.class, LogicalBreakpointRow::getEnablement, LogicalBreakpointRow::setEnablement, true),
|
||||
IMAGE("Image", String.class, LogicalBreakpointRow::getImageName, true),
|
||||
ADDRESS("Address", Address.class, LogicalBreakpointRow::getAddress, true),
|
||||
LENGTH("Length", Long.class, LogicalBreakpointRow::getLength, true),
|
||||
@@ -137,7 +138,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
|
||||
protected enum BreakpointLocationTableColumns
|
||||
implements EnumeratedTableColumn<BreakpointLocationTableColumns, BreakpointLocationRow> {
|
||||
// TODO: ENABLED
|
||||
ENABLED("Enabled", Boolean.class, BreakpointLocationRow::isEnabled, BreakpointLocationRow::setEnabled, true),
|
||||
NAME("Name", String.class, BreakpointLocationRow::getName, BreakpointLocationRow::setName, true),
|
||||
ADDRESS("Address", Address.class, BreakpointLocationRow::getAddress, true),
|
||||
TRACE("Trace", String.class, BreakpointLocationRow::getTraceName, true),
|
||||
@@ -201,9 +202,9 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
extends RowWrappedEnumeratedColumnTableModel< //
|
||||
BreakpointLocationTableColumns, ObjectKey, BreakpointLocationRow, TraceBreakpoint> {
|
||||
|
||||
public BreakpointLocationTableModel() {
|
||||
public BreakpointLocationTableModel(DebuggerBreakpointsProvider provider) {
|
||||
super("Locations", BreakpointLocationTableColumns.class, TraceBreakpoint::getObjectKey,
|
||||
BreakpointLocationRow::new);
|
||||
loc -> new BreakpointLocationRow(provider, loc));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -540,7 +541,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
// @AutoServiceConsumed via method
|
||||
private DebuggerLogicalBreakpointService breakpointService;
|
||||
// @AutoServiceConsumed via method
|
||||
private DebuggerModelService modelService;
|
||||
DebuggerModelService modelService;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerListingService listingService;
|
||||
@AutoServiceConsumed
|
||||
@@ -562,7 +563,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
GhidraTable breakpointTable;
|
||||
GhidraTableFilterPanel<LogicalBreakpointRow> breakpointFilterPanel;
|
||||
|
||||
BreakpointLocationTableModel locationTableModel = new BreakpointLocationTableModel();
|
||||
BreakpointLocationTableModel locationTableModel = new BreakpointLocationTableModel(this);
|
||||
GhidraTable locationTable;
|
||||
GhidraTableFilterPanel<BreakpointLocationRow> locationFilterPanel;
|
||||
private final LocationsBySelectedBreakpointsTableFilter filterLocationsBySelectedBreakpoints =
|
||||
@@ -839,9 +840,12 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
});
|
||||
|
||||
TableColumnModel bptColModel = breakpointTable.getColumnModel();
|
||||
TableColumn enCol = bptColModel.getColumn(LogicalBreakpointTableColumns.ENABLED.ordinal());
|
||||
enCol.setCellRenderer(new DebuggerBreakpointEnablementTableCellRenderer());
|
||||
enCol.setPreferredWidth(30);
|
||||
TableColumn bptEnCol =
|
||||
bptColModel.getColumn(LogicalBreakpointTableColumns.ENABLED.ordinal());
|
||||
bptEnCol.setCellRenderer(new DebuggerBreakpointEnablementTableCellRenderer());
|
||||
bptEnCol.setCellEditor(
|
||||
new DebuggerBreakpointEnablementTableCellEditor(breakpointFilterPanel));
|
||||
bptEnCol.setPreferredWidth(30);
|
||||
TableColumn bptAddrCol =
|
||||
bptColModel.getColumn(LogicalBreakpointTableColumns.ADDRESS.ordinal());
|
||||
bptAddrCol.setPreferredWidth(150);
|
||||
@@ -856,6 +860,11 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
locsCol.setPreferredWidth(20);
|
||||
|
||||
TableColumnModel locColModel = locationTable.getColumnModel();
|
||||
TableColumn locEnCol =
|
||||
locColModel.getColumn(BreakpointLocationTableColumns.ENABLED.ordinal());
|
||||
locEnCol.setCellRenderer(new DebuggerBreakpointLocEnabledTableCellRenderer());
|
||||
locEnCol.setCellEditor(new DebuggerBreakpointLocEnabledTableCellEditor());
|
||||
locEnCol.setPreferredWidth(30);
|
||||
TableColumn locAddrCol =
|
||||
locColModel.getColumn(BreakpointLocationTableColumns.ADDRESS.ordinal());
|
||||
locAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
|
||||
|
||||
@@ -71,8 +71,8 @@ public class DebuggerPlaceBreakpointDialog extends DialogComponentProvider {
|
||||
JLabel labelKinds = new JLabel("Kinds");
|
||||
DefaultComboBoxModel<String> kindModel = new DefaultComboBoxModel<>();
|
||||
// TODO: Let user select whatever combo?
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(SOFTWARE)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(EXECUTE)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(SW_EXECUTE)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(HW_EXECUTE)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(READ)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(WRITE)));
|
||||
kindModel.addElement(TraceBreakpointKindSet.encode(Set.of(READ, WRITE)));
|
||||
|
||||
@@ -39,14 +39,23 @@ public class LogicalBreakpointRow {
|
||||
return lb;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
Enablement en = provider.isFilterByCurrentTrace() && provider.currentTrace != null
|
||||
public Enablement getEnablement() {
|
||||
return provider.isFilterByCurrentTrace() && provider.currentTrace != null
|
||||
? lb.computeEnablementForTrace(provider.currentTrace)
|
||||
: lb.computeEnablement();
|
||||
}
|
||||
|
||||
public void setEnablement(Enablement en) {
|
||||
assert en.consistent && en.effective;
|
||||
setEnabled(en.enabled);
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
Enablement en = getEnablement();
|
||||
if (!en.consistent) {
|
||||
return null;
|
||||
}
|
||||
return en.enabled;
|
||||
return en.enabled && en.effective;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
|
||||
@@ -43,8 +43,8 @@ interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
||||
Set<TraceBreakpointKind> result = TraceBreakpointKindSet.decode(parts[0], false);
|
||||
if (result.isEmpty()) {
|
||||
Msg.warn(TraceBreakpointKind.class,
|
||||
"Decoded empty set of kinds from bookmark. Assuming EXECUTE");
|
||||
return Set.of(TraceBreakpointKind.EXECUTE);
|
||||
"Decoded empty set of kinds from bookmark. Assuming SW_EXECUTE");
|
||||
return Set.of(TraceBreakpointKind.SW_EXECUTE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -109,15 +109,15 @@ interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
||||
return location;
|
||||
}
|
||||
|
||||
public Enablement computeEnablement() {
|
||||
public ProgramEnablement computeEnablement() {
|
||||
if (eBookmark != null) {
|
||||
return Enablement.ENABLED;
|
||||
return ProgramEnablement.ENABLED;
|
||||
}
|
||||
if (dBookmark != null) {
|
||||
return Enablement.DISABLED;
|
||||
return ProgramEnablement.DISABLED;
|
||||
}
|
||||
else {
|
||||
return Enablement.DISABLED;
|
||||
return ProgramEnablement.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,11 +199,11 @@ interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return computeEnablement().enabled;
|
||||
return computeEnablement() == ProgramEnablement.ENABLED;
|
||||
}
|
||||
|
||||
public boolean isDisabled() {
|
||||
return computeEnablement().disabled;
|
||||
return computeEnablement() == ProgramEnablement.DISABLED;
|
||||
}
|
||||
|
||||
public String computeCategory() {
|
||||
@@ -304,16 +304,18 @@ interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
||||
return recorder.getMemoryMapper().traceToTarget(address);
|
||||
}
|
||||
|
||||
public Enablement computeEnablement() {
|
||||
public TraceEnablement computeEnablement() {
|
||||
TraceEnablement en = TraceEnablement.MISSING;
|
||||
if (breakpoints.isEmpty()) {
|
||||
return Enablement.DISABLED;
|
||||
return TraceEnablement.MISSING;
|
||||
}
|
||||
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
|
||||
if (bpt.obj.isEnabled()) {
|
||||
return Enablement.ENABLED;
|
||||
en = en.combine(TraceEnablement.fromBool(bpt.obj.isEnabled()));
|
||||
if (en == TraceEnablement.MIXED) {
|
||||
return en;
|
||||
}
|
||||
}
|
||||
return Enablement.DISABLED;
|
||||
return en;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
@@ -125,12 +125,12 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal {
|
||||
if (trace != breaks.getTrace()) {
|
||||
return Enablement.NONE;
|
||||
}
|
||||
return breaks.computeEnablement();
|
||||
return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enablement computeEnablement() {
|
||||
return breaks.computeEnablement();
|
||||
return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -302,24 +302,26 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal {
|
||||
@Override
|
||||
public Enablement computeEnablementForTrace(Trace trace) {
|
||||
TraceBreakpointSet breaks = traceBreaks.get(trace);
|
||||
ProgramEnablement progEn = progBreak.computeEnablement();
|
||||
if (breaks == null) {
|
||||
return Enablement.NONE;
|
||||
return TraceEnablement.MISSING.combineProgram(progEn);
|
||||
}
|
||||
return breaks.computeEnablement().combine(progBreak.computeEnablement());
|
||||
// NB: Order matters. Trace is primary
|
||||
return breaks.computeEnablement().combineProgram(progEn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enablement computeEnablement() {
|
||||
Enablement en = progBreak.computeEnablement();
|
||||
ProgramEnablement progEn = progBreak.computeEnablement();
|
||||
TraceEnablement traceEn = TraceEnablement.NONE;
|
||||
for (TraceBreakpointSet breaks : traceBreaks.values()) {
|
||||
Enablement tEn = breaks.computeEnablement();
|
||||
assert tEn.consistent;
|
||||
en = en.combine(tEn);
|
||||
if (en.consistent) {
|
||||
return en;
|
||||
TraceEnablement tEn = breaks.computeEnablement();
|
||||
traceEn = traceEn.combine(tEn);
|
||||
if (traceEn == TraceEnablement.MIXED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return en;
|
||||
return progEn.combineTrace(traceEn);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,8 +30,8 @@ import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
||||
@ServiceInfo( //
|
||||
defaultProvider = DebuggerLogicalBreakpointServicePlugin.class, //
|
||||
description = "Aggregate breakpoints for programs and live traces" //
|
||||
defaultProvider = DebuggerLogicalBreakpointServicePlugin.class, //
|
||||
description = "Aggregate breakpoints for programs and live traces" //
|
||||
)
|
||||
public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
@@ -44,6 +44,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Get a map of addresses to collected logical breakpoints for a given program.
|
||||
*
|
||||
* <p>
|
||||
* The program ought to be a program database, not a view of a trace.
|
||||
*
|
||||
* @param program the program database
|
||||
@@ -54,6 +55,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Get a map of addresses to collected logical breakpoints (at present) for a given trace.
|
||||
*
|
||||
* <p>
|
||||
* The trace must be associated with a live target. The returned map collects live breakpoints
|
||||
* in the recorded target, using trace breakpoints from the recorder's current snapshot.
|
||||
*
|
||||
@@ -65,6 +67,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Get the collected logical breakpoints at the given program location.
|
||||
*
|
||||
* <p>
|
||||
* The program ought to be a program database, not a view of a trace.
|
||||
*
|
||||
* @param program the program database
|
||||
@@ -76,6 +79,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Get the collected logical breakpoints (at present) at the given trace location.
|
||||
*
|
||||
* <p>
|
||||
* The trace must be associated with a live target. The returned collection includes live
|
||||
* breakpoints in the recorded target, using trace breakpoints from the recorders' current
|
||||
* snapshot.
|
||||
@@ -89,10 +93,12 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Get the collected logical breakpoints (at present) at the given location.
|
||||
*
|
||||
* <p>
|
||||
* The {@code program} field for the location may be either a program database (static image) or
|
||||
* a view for a trace associated with a live target. If it is the latter, the view's current
|
||||
* snapshot is ignored, in favor of the associated recorder's current snapshot.
|
||||
*
|
||||
* <p>
|
||||
* If {@code program} is a static image, this is equivalent to using
|
||||
* {@link #getBreakpointsAt(Program, Address)}. If {@code program} is a trace view, this is
|
||||
* equivalent to using {@link #getBreakpointsAt(Trace, Address)}.
|
||||
@@ -105,11 +111,13 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Add a listener for logical breakpoint changes.
|
||||
*
|
||||
* <p>
|
||||
* Logical breakpoints may change from time to time for a variety of reasons: A new trace is
|
||||
* started; a static image is opened; the user adds or removes breakpoints; mappings change;
|
||||
* etc. The service reacts to these events, reconciles the breakpoints, and invokes callbacks
|
||||
* for the changes, allowing other UI components and services to update accordingly.
|
||||
*
|
||||
* <p>
|
||||
* The listening component must maintain a strong reference to the listener, otherwise it will
|
||||
* be removed and garbage collected. Automatic removal is merely a resource-management
|
||||
* protection; the listening component should politely remove its listener (see
|
||||
@@ -177,6 +185,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
* Create an enabled breakpoint at the given program location and each mapped live trace
|
||||
* location.
|
||||
*
|
||||
* <p>
|
||||
* The implementation should take care not to create the same breakpoint multiple times. The
|
||||
* risk of this happening derives from the possibility of one module mapped to multiple targets
|
||||
* which are all managed by the same debugger, having a single breakpoint container.
|
||||
@@ -193,9 +202,11 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Create an enabled breakpoint at the given trace location only.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Note, the debugger ultimately determines the placement behavior. If it is managing multiple
|
||||
* targets, it is possible the breakpoint will be effective in another trace. This fact should
|
||||
* be reflected correctly in the resulting logical markings once all resulting events have been
|
||||
@@ -213,6 +224,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Create an enabled breakpoint at the given location.
|
||||
*
|
||||
* <p>
|
||||
* If the given location refers to a static image, this behaves as in
|
||||
* {@link #placeBreakpointAt(Program, Address, TraceBreakpointKind)}. If it refers to a trace
|
||||
* view, this behaves as in {@link #placeBreakpointAt(Trace, Address, TraceBreakpointKind)},
|
||||
@@ -229,6 +241,7 @@ public interface DebuggerLogicalBreakpointService {
|
||||
/**
|
||||
* Enable a collection of logical breakpoints on target, if applicable
|
||||
*
|
||||
* <p>
|
||||
* This method is preferable to calling {@link LogicalBreakpoint#enable()} on each logical
|
||||
* breakpoint, because depending on the debugger, a single breakpoint specification may produce
|
||||
* several effective breakpoints, perhaps spanning multiple targets. While not terribly
|
||||
|
||||
@@ -32,56 +32,260 @@ public interface LogicalBreakpoint {
|
||||
String BREAKPOINT_ENABLED_BOOKMARK_TYPE = "BreakpointEnabled";
|
||||
String BREAKPOINT_DISABLED_BOOKMARK_TYPE = "BreakpointDisabled";
|
||||
|
||||
public enum ProgramEnablement {
|
||||
NONE {
|
||||
@Override
|
||||
public Enablement combineTrace(TraceEnablement traceEn) {
|
||||
switch (traceEn) {
|
||||
case NONE:
|
||||
return Enablement.NONE;
|
||||
case MISSING:
|
||||
return Enablement.NONE;
|
||||
case ENABLED:
|
||||
return Enablement.ENABLED;
|
||||
case MIXED:
|
||||
return Enablement.DISABLED_ENABLED;
|
||||
case DISABLED:
|
||||
return Enablement.DISABLED;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
},
|
||||
MISSING {
|
||||
@Override
|
||||
public Enablement combineTrace(TraceEnablement traceEn) {
|
||||
switch (traceEn) {
|
||||
case NONE:
|
||||
return Enablement.NONE;
|
||||
case MISSING:
|
||||
return Enablement.NONE;
|
||||
case ENABLED:
|
||||
case MIXED:
|
||||
return Enablement.DISABLED_ENABLED;
|
||||
case DISABLED:
|
||||
return Enablement.DISABLED;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
},
|
||||
ENABLED {
|
||||
@Override
|
||||
public Enablement combineTrace(TraceEnablement traceEn) {
|
||||
switch (traceEn) {
|
||||
case NONE:
|
||||
case MISSING:
|
||||
return Enablement.INEFFECTIVE_ENABLED;
|
||||
case ENABLED:
|
||||
return Enablement.ENABLED;
|
||||
case DISABLED:
|
||||
case MIXED:
|
||||
return Enablement.ENABLED_DISABLED;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
},
|
||||
DISABLED {
|
||||
@Override
|
||||
public Enablement combineTrace(TraceEnablement traceEn) {
|
||||
switch (traceEn) {
|
||||
case NONE:
|
||||
case MISSING:
|
||||
return Enablement.INEFFECTIVE_DISABLED;
|
||||
case ENABLED:
|
||||
case MIXED:
|
||||
return Enablement.DISABLED_ENABLED;
|
||||
case DISABLED:
|
||||
return Enablement.DISABLED;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public abstract Enablement combineTrace(TraceEnablement traceEn);
|
||||
}
|
||||
|
||||
public enum TraceEnablement {
|
||||
NONE {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
},
|
||||
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();
|
||||
}
|
||||
}
|
||||
},
|
||||
ENABLED {
|
||||
@Override
|
||||
public TraceEnablement combine(TraceEnablement 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;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
},
|
||||
DISABLED {
|
||||
@Override
|
||||
public TraceEnablement combine(TraceEnablement 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;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
},
|
||||
MIXED {
|
||||
@Override
|
||||
public TraceEnablement combine(TraceEnablement that) {
|
||||
return MIXED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enablement combineProgram(ProgramEnablement progEn) {
|
||||
return Enablement.ENABLED_DISABLED;
|
||||
}
|
||||
};
|
||||
|
||||
public static TraceEnablement fromBool(boolean en) {
|
||||
return en ? ENABLED : DISABLED;
|
||||
}
|
||||
|
||||
public abstract TraceEnablement combine(TraceEnablement that);
|
||||
|
||||
public abstract Enablement combineProgram(ProgramEnablement progEn);
|
||||
}
|
||||
|
||||
public enum Enablement {
|
||||
NONE(false, false, true) {
|
||||
NONE(false, false, true, false) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return NONE;
|
||||
}
|
||||
},
|
||||
ENABLED(true, false, true) {
|
||||
ENABLED(true, false, true, true) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return ENABLED;
|
||||
}
|
||||
},
|
||||
DISABLED(false, true, true) {
|
||||
DISABLED(false, true, true, true) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return DISABLED;
|
||||
}
|
||||
},
|
||||
ENABLED_DISABLED(true, false, false) {
|
||||
INEFFECTIVE_ENABLED(true, false, true, false) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return ENABLED;
|
||||
}
|
||||
},
|
||||
DISABLED_ENABLED(false, true, false) {
|
||||
INEFFECTIVE_DISABLED(false, true, true, false) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return DISABLED;
|
||||
}
|
||||
},
|
||||
ENABLED_DISABLED(true, false, false, true) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return ENABLED;
|
||||
}
|
||||
},
|
||||
DISABLED_ENABLED(false, true, false, true) {
|
||||
@Override
|
||||
public Enablement getPrimary() {
|
||||
return DISABLED;
|
||||
}
|
||||
};
|
||||
|
||||
public final boolean enabled;
|
||||
public final boolean disabled;
|
||||
public final boolean consistent;
|
||||
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) {
|
||||
Enablement(boolean enabled, boolean disabled, boolean consistent, boolean effective) {
|
||||
this.enabled = enabled;
|
||||
this.disabled = disabled;
|
||||
this.consistent = consistent;
|
||||
}
|
||||
|
||||
public Enablement combine(Enablement that) {
|
||||
if (this == NONE) {
|
||||
return that;
|
||||
}
|
||||
if (that == NONE) {
|
||||
return this;
|
||||
}
|
||||
return fromBools(this.enabled, that.enabled);
|
||||
this.effective = effective;
|
||||
}
|
||||
|
||||
public Enablement sameAdddress(Enablement that) {
|
||||
@@ -91,6 +295,9 @@ public interface LogicalBreakpoint {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,9 @@ public interface TraceRecorder {
|
||||
case WRITE:
|
||||
return TraceBreakpointKind.WRITE;
|
||||
case HW_EXECUTE:
|
||||
return TraceBreakpointKind.EXECUTE;
|
||||
return TraceBreakpointKind.HW_EXECUTE;
|
||||
case SW_EXECUTE:
|
||||
return TraceBreakpointKind.SOFTWARE;
|
||||
return TraceBreakpointKind.SW_EXECUTE;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
@@ -109,9 +109,9 @@ public interface TraceRecorder {
|
||||
return TargetBreakpointKind.READ;
|
||||
case WRITE:
|
||||
return TargetBreakpointKind.WRITE;
|
||||
case EXECUTE:
|
||||
case HW_EXECUTE:
|
||||
return TargetBreakpointKind.HW_EXECUTE;
|
||||
case SOFTWARE:
|
||||
case SW_EXECUTE:
|
||||
return TargetBreakpointKind.SW_EXECUTE;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
|
||||
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 302 B |
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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"
|
||||
version="1.1"
|
||||
id="SVGRoot">
|
||||
<defs
|
||||
id="defs5287" />
|
||||
<metadata
|
||||
id="metadata5290">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="opacity:1;fill:#7f7f7f;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 8 4 A 4 4 0 0 0 4 8 A 4 4 0 0 0 8 12 A 4 4 0 0 0 12 8 A 4 4 0 0 0 8 4 z M 8 6 A 2 2 0 0 1 10 8 A 2 2 0 0 1 8 10 A 2 2 0 0 1 6 8 A 2 2 0 0 1 8 6 z "
|
||||
id="path821" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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"
|
||||
version="1.1"
|
||||
id="SVGRoot">
|
||||
<defs
|
||||
id="defs5287" />
|
||||
<metadata
|
||||
id="metadata5290">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<circle
|
||||
style="opacity:1;fill:#7f7f7f;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path821"
|
||||
cx="8"
|
||||
cy="7.9999981"
|
||||
r="4" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 974 B |
@@ -70,7 +70,7 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG
|
||||
|
||||
Msg.debug(this, "Placing breakpoint");
|
||||
breakpointService.placeBreakpointAt(program, addr(program, 0x00401c60), 1,
|
||||
Set.of(TraceBreakpointKind.SOFTWARE));
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE));
|
||||
|
||||
Msg.debug(this, "Disabling breakpoint");
|
||||
LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne(
|
||||
@@ -79,7 +79,7 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG
|
||||
|
||||
Msg.debug(this, "Placing another");
|
||||
breakpointService.placeBreakpointAt(program, addr(program, 0x00401c63), 1,
|
||||
Set.of(TraceBreakpointKind.SOFTWARE));
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE));
|
||||
|
||||
Msg.debug(this, "Saving program");
|
||||
program.save("Placed breakpoints", TaskMonitor.DUMMY);
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.gui.breakpoint;
|
||||
|
||||
import static ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest.waitForPass;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -138,7 +139,8 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
|
||||
waitOn(bc3.placeBreakpoint(mb.addr(0x7fac1234), Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
||||
|
||||
TraceBreakpoint bpt = waitForValue(() -> Unique.assertAtMostOne(
|
||||
trace3.getBreakpointManager().getBreakpointsAt(0, addr(trace3, 0x7fac1234))));
|
||||
trace3.getBreakpointManager()
|
||||
.getBreakpointsAt(recorder3.getSnap(), addr(trace3, 0x7fac1234))));
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace3, "Disable breakpoint", true)) {
|
||||
bpt.setEnabled(false);
|
||||
@@ -147,14 +149,15 @@ 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, "SOFTWARE;1", "");
|
||||
LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", "");
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(addr(program, 0x00402345),
|
||||
LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, "SOFTWARE;1", "");
|
||||
LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", "");
|
||||
}
|
||||
|
||||
waitForPass(() -> {
|
||||
assertEquals(3, breakpointService.getAllBreakpoints().size());
|
||||
assertFalse(bpt.isEnabled());
|
||||
});
|
||||
|
||||
captureIsolatedProvider(provider, 600, 600);
|
||||
|
||||
@@ -115,7 +115,7 @@ public class DebuggerMemviewPluginScreenShots extends GhidraScreenShotGenerator
|
||||
Set<TraceThread> threads = new HashSet<TraceThread>();
|
||||
Set<TraceBreakpointKind> kinds = new HashSet<TraceBreakpointKind>();
|
||||
threads.add(thread1);
|
||||
kinds.add(TraceBreakpointKind.EXECUTE);
|
||||
kinds.add(TraceBreakpointKind.HW_EXECUTE);
|
||||
tb.trace.getBreakpointManager()
|
||||
.addBreakpoint("bpt1", Range.closed(17L, 25L), tb.range(0x7fac1234, 0x7fc1238),
|
||||
threads, kinds, true, "break here");
|
||||
|
||||
@@ -110,7 +110,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
|
||||
TaskMonitor.DUMMY, false);
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(addr(program, 0x00400123),
|
||||
LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SOFTWARE;1", "");
|
||||
LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
|
||||
Address addr = addr(program, 0x00400123);
|
||||
hackMarkerBackgroundColors(program);
|
||||
|
||||
assertEquals(E_COLOR, getBackgroundColor(program, addr));
|
||||
waitForPass(() -> assertEquals(E_COLOR, getBackgroundColor(program, addr)));
|
||||
|
||||
lb.disableForProgram();
|
||||
waitForDomainObject(program);
|
||||
@@ -265,7 +265,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
|
||||
AbstractClearBreakpointAction.NAME);
|
||||
|
||||
protected static final Set<String> SET_ACTIONS =
|
||||
Set.of("SOFTWARE", "EXECUTE", "READ,WRITE", "READ", "WRITE");
|
||||
Set.of("SW_EXECUTE", "HW_EXECUTE", "READ,WRITE", "READ", "WRITE");
|
||||
|
||||
@Test
|
||||
public void testProgramNoBreakPopupMenus() throws Exception {
|
||||
@@ -458,7 +458,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
|
||||
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
|
||||
assertEquals(Enablement.ENABLED, lb.computeEnablement());
|
||||
// TODO: Different cases for different expected default kinds?
|
||||
assertEquals(Set.of(TraceBreakpointKind.SOFTWARE), lb.getKinds());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), lb.getKinds());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -566,25 +566,25 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
|
||||
@Test
|
||||
public void testActionSetSoftwareBreakpointProgram() throws Exception {
|
||||
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetSoftwareBreakpoint,
|
||||
Set.of(TraceBreakpointKind.SOFTWARE));
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionSetSoftwareBreakpointTrace() throws Exception {
|
||||
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetSoftwareBreakpoint,
|
||||
Set.of(TraceBreakpointKind.SOFTWARE));
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionSetExecuteBreakpointProgram() throws Exception {
|
||||
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetExecuteBreakpoint,
|
||||
Set.of(TraceBreakpointKind.EXECUTE));
|
||||
Set.of(TraceBreakpointKind.HW_EXECUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionSetExecuteBreakpointTrace() throws Exception {
|
||||
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetExecuteBreakpoint,
|
||||
Set.of(TraceBreakpointKind.EXECUTE));
|
||||
Set.of(TraceBreakpointKind.HW_EXECUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -694,7 +694,9 @@ 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.DISABLED_ENABLED, lb.computeEnablementForTrace(trace)));
|
||||
() -> assertEquals(Enablement.INEFFECTIVE_ENABLED,
|
||||
lb.computeEnablementForTrace(trace)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider.LogicalBreakpointTableModel;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.services.LogicalBreakpoint.Enablement;
|
||||
import ghidra.async.AsyncTestUtils;
|
||||
import ghidra.dbg.model.TestTargetProcess;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
@@ -95,7 +96,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
TaskMonitor.DUMMY, false);
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(addr(program, 0x00400123),
|
||||
LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SOFTWARE;1", "");
|
||||
LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +130,8 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
|
||||
assertEquals("55550123", row.getAddress().toString());
|
||||
assertEquals(trace, row.getDomainObject());
|
||||
assertEquals("SOFTWARE", row.getKinds());
|
||||
assertTrue(row.isEnabled());
|
||||
assertEquals("SW_EXECUTE", row.getKinds());
|
||||
assertEquals(Enablement.ENABLED, row.getEnablement());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,16 +150,16 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
|
||||
LogicalBreakpointRow row =
|
||||
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
|
||||
assertTrue(row.isEnabled());
|
||||
assertEquals(Enablement.ENABLED, row.getEnablement());
|
||||
|
||||
// NB, the row does not take the value immediately, but via async callbacks
|
||||
row.setEnabled(false);
|
||||
|
||||
waitForPass(() -> assertEquals(Boolean.FALSE, row.isEnabled()));
|
||||
waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement()));
|
||||
|
||||
row.setEnabled(true);
|
||||
|
||||
waitForPass(() -> assertEquals(Boolean.TRUE, row.isEnabled()));
|
||||
waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,8 +173,8 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
|
||||
assertEquals("00400123", row.getAddress().toString());
|
||||
assertEquals(program, row.getDomainObject());
|
||||
assertEquals("SOFTWARE", row.getKinds());
|
||||
assertTrue(row.isEnabled());
|
||||
assertEquals("SW_EXECUTE", row.getKinds());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -185,17 +186,17 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
|
||||
LogicalBreakpointRow row =
|
||||
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
|
||||
assertTrue(row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
|
||||
row.setEnabled(false); // Synchronous, but on swing thread
|
||||
waitForDomainObject(program);
|
||||
|
||||
assertEquals(Boolean.FALSE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
|
||||
|
||||
row.setEnabled(true);
|
||||
waitForDomainObject(program);
|
||||
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -221,7 +222,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
LogicalBreakpoint lb = row.getLogicalBreakpoint();
|
||||
assertEquals(program, lb.getProgram());
|
||||
assertEquals(Set.of(trace), lb.getParticipatingTraces());
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.ENABLED, row.getEnablement());
|
||||
});
|
||||
|
||||
LogicalBreakpointRow row =
|
||||
@@ -231,24 +232,22 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
lb.disableForProgram();
|
||||
waitForDomainObject(program);
|
||||
|
||||
// TODO: As of now, the table displays "N/A", which is not ideal
|
||||
// Would be nicer to have icons, but still want click to toggle.
|
||||
assertNull(row.isEnabled());
|
||||
assertEquals(Enablement.DISABLED_ENABLED, row.getEnablement());
|
||||
|
||||
// NOTE: This acts on the corresponding target, not directly on trace
|
||||
lb.disableForTrace(trace);
|
||||
|
||||
waitForPass(() -> assertEquals(Boolean.FALSE, row.isEnabled()));
|
||||
waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement()));
|
||||
|
||||
lb.enableForProgram();
|
||||
waitForDomainObject(program);
|
||||
|
||||
assertNull(row.isEnabled());
|
||||
assertEquals(Enablement.ENABLED_DISABLED, row.getEnablement());
|
||||
|
||||
// This duplicates the initial case, but without it, I just feel incomplete
|
||||
lb.enableForTrace(trace);
|
||||
|
||||
waitForPass(() -> assertEquals(Boolean.TRUE, row.isEnabled()));
|
||||
waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement()));
|
||||
}
|
||||
|
||||
// TODO: Test a scenario where one spec manifests two breaks, select both, and perform actions
|
||||
@@ -256,7 +255,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
// TODO: Test a scenario where one spec manifests the same mapped breakpoint in two traces
|
||||
|
||||
@Test
|
||||
public void testActionEnableSelectedBreakpoints() throws Exception {
|
||||
public void testActionEnableSelectedBreakpoints() throws Throwable {
|
||||
createProgram();
|
||||
programManager.openProgram(program);
|
||||
waitForSwing();
|
||||
@@ -274,12 +273,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(Boolean.FALSE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
performAction(breakpointsProvider.actionEnableSelectedBreakpointsAction);
|
||||
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
breakpointsProvider.breakpointTable.clearSelection();
|
||||
@@ -293,7 +292,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
// Bookmark part should actually be synchronous.
|
||||
row.getLogicalBreakpoint().delete().get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
waitOn(row.getLogicalBreakpoint().delete());
|
||||
waitForDomainObject(program);
|
||||
|
||||
assertFalse(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
|
||||
@@ -317,12 +316,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
row.setEnabled(false);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(Boolean.FALSE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
|
||||
|
||||
performAction(breakpointsProvider.actionEnableAllBreakpointsAction);
|
||||
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
|
||||
|
||||
// Bookmark part should actually be synchronous.
|
||||
@@ -333,7 +332,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionDisableSelectedBreakpoints() throws Exception {
|
||||
public void testActionDisableSelectedBreakpoints() throws Throwable {
|
||||
createProgram();
|
||||
programManager.openProgram(program);
|
||||
waitForSwing();
|
||||
@@ -350,12 +349,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
performAction(breakpointsProvider.actionDisableSelectedBreakpointsAction);
|
||||
|
||||
assertEquals(Boolean.FALSE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
breakpointsProvider.breakpointTable.clearSelection();
|
||||
@@ -369,7 +368,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
|
||||
|
||||
// Bookmark part should actually be synchronous.
|
||||
row.getLogicalBreakpoint().delete().get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
waitOn(row.getLogicalBreakpoint().delete());
|
||||
waitForDomainObject(program);
|
||||
|
||||
assertFalse(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
|
||||
@@ -389,12 +388,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
|
||||
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
|
||||
LogicalBreakpointRow row =
|
||||
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
|
||||
assertEquals(Boolean.TRUE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
|
||||
|
||||
performAction(breakpointsProvider.actionDisableAllBreakpointsAction);
|
||||
|
||||
assertEquals(Boolean.FALSE, row.isEnabled());
|
||||
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
|
||||
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
|
||||
|
||||
// Bookmark part should actually be synchronous.
|
||||
|
||||
@@ -377,7 +377,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
LogicalBreakpoint enLb = Unique
|
||||
.assertOne(breakpointService.getBreakpointsAt(trace, addr(trace, offset)));
|
||||
assertNull(enLb.getProgramLocation());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SOFTWARE), enLb.getKinds());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds());
|
||||
|
||||
TraceBreakpoint bpt = Unique.assertOne(trace.getBreakpointManager().getAllBreakpoints());
|
||||
assertEquals(Set.of(trace), enLb.getMappedTraces());
|
||||
@@ -394,7 +394,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
.assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
|
||||
assertNull(enLb.getProgramBookmark());
|
||||
assertEquals(Enablement.DISABLED_ENABLED, enLb.computeEnablementForProgram(program));
|
||||
assertEquals(Set.of(TraceBreakpointKind.SOFTWARE), enLb.getKinds());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds());
|
||||
|
||||
TraceBreakpoint bpt = Unique.assertOne(trace.getBreakpointManager().getAllBreakpoints());
|
||||
assertEquals(Set.of(trace), enLb.getMappedTraces());
|
||||
@@ -414,7 +414,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
assertEquals(enBm, enLb.getProgramBookmark());
|
||||
assertTrue(enLb.getMappedTraces().isEmpty());
|
||||
assertEquals(Enablement.ENABLED, enLb.computeEnablementForProgram(program));
|
||||
assertEquals(Set.of(TraceBreakpointKind.SOFTWARE), enLb.getKinds());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds());
|
||||
|
||||
LogicalBreakpoint disLb = Unique
|
||||
.assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321)));
|
||||
@@ -423,7 +423,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
assertEquals(disBm, disLb.getProgramBookmark());
|
||||
assertTrue(disLb.getMappedTraces().isEmpty());
|
||||
assertEquals(Enablement.DISABLED, disLb.computeEnablementForProgram(program));
|
||||
assertEquals(Set.of(TraceBreakpointKind.SOFTWARE), disLb.getKinds());
|
||||
assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), disLb.getKinds());
|
||||
}
|
||||
|
||||
protected void assertLogicalBreakpointsForMappedBookmarks(Trace trace) {
|
||||
|
||||
@@ -22,8 +22,10 @@ import org.apache.commons.collections4.set.AbstractSetDecorator;
|
||||
/**
|
||||
* The kind of breakpoint
|
||||
*
|
||||
* <p>
|
||||
* This identifies the sort of access that would trap execution
|
||||
*
|
||||
* <p>
|
||||
* TODO: This is identical to {@code TargetBreakpointKind} (not in the classpath here). Is there a
|
||||
* common place we could factor both? Should we? CAUTION: Encoding in a trace database depends on
|
||||
* this enum's {@code bits} field, so we must take care not to introduce a dependency that would
|
||||
@@ -32,8 +34,8 @@ import org.apache.commons.collections4.set.AbstractSetDecorator;
|
||||
public enum TraceBreakpointKind {
|
||||
READ(1 << 0),
|
||||
WRITE(1 << 1),
|
||||
EXECUTE(1 << 2),
|
||||
SOFTWARE(1 << 3);
|
||||
HW_EXECUTE(1 << 2),
|
||||
SW_EXECUTE(1 << 3);
|
||||
|
||||
public static class TraceBreakpointKindSet extends AbstractSetDecorator<TraceBreakpointKind> {
|
||||
public static TraceBreakpointKindSet of(TraceBreakpointKind... kinds) {
|
||||
|
||||