Merge remote-tracking branch 'origin/GP-4546_Dan_breakExpressionActions--SQUASHED'

This commit is contained in:
Ryan Kurtz 2025-02-06 08:34:30 -05:00
commit 65d6c3b188
44 changed files with 785 additions and 402 deletions

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

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

View file

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

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -288,6 +288,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
}
actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this)
.description(entry.details())
.toolBarIcon(entry.icon())
.enabledWhen(ctx -> entry.isEnabled())
.onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry))
.build());

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,21 +18,19 @@ package ghidra.app.plugin.core.debug.gui.control;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import docking.ActionContext;
import docking.Tool;
import docking.action.*;
import docking.action.DockingActionIf;
import docking.action.MenuData;
import docking.actions.PopupActionProvider;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
import ghidra.app.services.*;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
@ -57,23 +55,10 @@ import ghidra.trace.model.program.TraceProgramView;
public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider {
public static final String GROUP_METHODS = "Debugger Methods";
class InvokeActionEntryAction extends DockingAction {
private final ActionEntry entry;
public InvokeActionEntryAction(ActionEntry entry) {
super(entry.display(), DebuggerMethodActionsPlugin.this.getName());
this.entry = entry;
Icon icon = null;
if (ActionName.REFRESH.equals(entry.name())) {
// TODO: Allow method annotation to specify icon?
icon = DebuggerResources.ICON_REFRESH;
}
setPopupMenuData(new MenuData(new String[] { getName() }, icon, GROUP_METHODS));
}
@Override
public void actionPerformed(ActionContext context) {
TargetActionTask.runAction(tool, entry.display(), entry);
class MethodAction extends InvokeActionEntryAction {
public MethodAction(ActionEntry entry) {
super(DebuggerMethodActionsPlugin.this, entry);
setPopupMenuData(new MenuData(new String[] { getName() }, entry.icon(), GROUP_METHODS));
}
}
@ -136,10 +121,10 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
List<DockingActionIf> result = new ArrayList<>();
for (ActionEntry entry : target.collectActions(null, context).values()) {
//if (entry.requiresPrompt() || entry.builtIn()) {
if (!entry.isEnabled() || entry.builtIn()) {
if (!entry.isEnabled() || !entry.getShow().isShowing(context)) {
continue;
}
result.add(new InvokeActionEntryAction(entry));
result.add(new MethodAction(entry));
}
return result;
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -65,9 +65,11 @@ class TargetDockingAction extends DockingAction {
protected void updateFromContext(ActionContext context) {
entry = findEntry(context);
if (entry == null) {
getToolBarData().setIcon(action.icon());
setDescription(defaultDescription);
}
else {
getToolBarData().setIcon(entry.icon());
setDescription(entry.details());
}
}