Merge remote-tracking branch 'origin/debugger'

This commit is contained in:
ghidra1
2021-05-19 13:40:17 -04:00
38 changed files with 1044 additions and 202 deletions
@@ -54,7 +54,7 @@ jar {
task configureNodepJar {
doLast {
configurations.runtimeOnly.files.forEach {
configurations.default.files.forEach {
if (filterJar(it)) {
nodepJar.from(zipTree(it))
}
@@ -15,10 +15,10 @@
*/
package agent.dbgeng.manager.cmd;
import agent.dbgeng.dbgeng.DebugControl;
import agent.dbgeng.manager.DbgEvent;
import agent.dbgeng.manager.DbgManager;
import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent;
import agent.dbgeng.manager.evt.DbgConsoleOutputEvent;
import agent.dbgeng.manager.evt.*;
import agent.dbgeng.manager.impl.DbgManagerImpl;
/**
@@ -63,6 +63,8 @@ public class DbgConsoleExecCommand extends AbstractDbgCommand<String> {
@Override
public void invoke() {
manager.getControl().execute(command);
DebugControl control = manager.getControl();
control.execute(command);
manager.processEvent(new DbgPromptChangedEvent(control.getPromptText()));
}
}
@@ -0,0 +1,26 @@
/* ###
* 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 agent.dbgeng.manager.evt;
public class DbgPromptChangedEvent extends AbstractDbgEvent<String> {
public DbgPromptChangedEvent(String prompt) {
super(prompt);
}
public String getPrompt() {
return getInfo();
}
}
@@ -41,7 +41,8 @@ import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
import agent.dbgeng.manager.cmd.*;
import agent.dbgeng.manager.evt.*;
import agent.dbgeng.model.iface1.*;
import agent.dbgeng.model.iface1.DbgModelTargetActiveScope;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import agent.dbgeng.model.iface2.DbgModelTargetObject;
import agent.dbgeng.model.iface2.DbgModelTargetThread;
import ghidra.async.*;
@@ -597,6 +598,7 @@ public class DbgManagerImpl implements DbgManager {
handlerMap.putVoid(DbgStoppedEvent.class, this::processDefault);
handlerMap.putVoid(DbgRunningEvent.class, this::processDefault);
handlerMap.putVoid(DbgConsoleOutputEvent.class, this::processConsoleOutput);
handlerMap.putVoid(DbgPromptChangedEvent.class, this::processPromptChanged);
handlerMap.putVoid(DbgBreakpointCreatedEvent.class, this::processBreakpointCreated);
handlerMap.putVoid(DbgBreakpointModifiedEvent.class, this::processBreakpointModified);
handlerMap.putVoid(DbgBreakpointDeletedEvent.class, this::processBreakpointDeleted);
@@ -925,6 +927,7 @@ public class DbgManagerImpl implements DbgManager {
dbgState = DbgState.STOPPED;
//System.err.println("STOPPED " + id);
processEvent(new DbgStoppedEvent(eventThread.getId()));
processEvent(new DbgPromptChangedEvent(getControl().getPromptText()));
}
if (status.threadState.equals(ExecutionState.RUNNING)) {
//System.err.println("RUNNING " + id);
@@ -949,6 +952,7 @@ public class DbgManagerImpl implements DbgManager {
if (process != null) {
processEvent(new DbgProcessSelectedEvent(process));
}
processEvent(new DbgPromptChangedEvent(getControl().getPromptText()));
return DebugStatus.BREAK;
}
if (status.equals(DebugStatus.GO)) {
@@ -972,6 +976,7 @@ public class DbgManagerImpl implements DbgManager {
if (thread != null) {
getEventListeners().fire.threadSelected(thread, null, evt.getCause());
}
processEvent(new DbgPromptChangedEvent(getControl().getPromptText()));
break;
}
}
@@ -1033,6 +1038,10 @@ public class DbgManagerImpl implements DbgManager {
getEventListeners().fire.consoleOutput(evt.getInfo(), evt.getMask());
}
protected void processPromptChanged(DbgPromptChangedEvent evt, Void v) {
getEventListeners().fire.promptChanged(evt.getPrompt());
}
/**
* Handler for breakpoint-created event
*
@@ -1480,8 +1489,8 @@ public class DbgManagerImpl implements DbgManager {
@Override
public CompletableFuture<Void> console(String command) {
if (continuation != null) {
String prompt = command.equals("") ? DbgModelTargetInterpreter.DBG_PROMPT : ">>>";
getEventListeners().fire.promptChanged(prompt);
//String prompt = command.equals("") ? DbgModelTargetInterpreter.DBG_PROMPT : ">>>";
//getEventListeners().fire.promptChanged(prompt);
continuation.complete(command);
setContinuation(null);
return AsyncUtils.NIL;
@@ -50,7 +50,7 @@ jar {
task configureNodepJar {
doLast {
configurations.runtimeOnly.files.forEach {
configurations.default.files.forEach {
if (filterJar(it)) {
nodepJar.from(zipTree(it))
}
@@ -18,6 +18,7 @@ package agent.dbgmodel.dbgmodel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.*;
import java.util.*;
import org.junit.Before;
@@ -44,7 +45,7 @@ public class DbgModelSetContextMWETest extends AbstractGhidraHeadlessIntegration
}
@Test
public void testMWE() {
public void testMWE() throws IOException {
HostDataModelAccess access = DbgModel.debugCreate();
DebugClient client = access.getClient();
DebugControl control = client.getControl();
@@ -272,12 +273,6 @@ public class DbgModelSetContextMWETest extends AbstractGhidraHeadlessIntegration
DebugStatus status = super.exitThread(exitCode);
return status;
}
@Override
public DebugStatus changeSymbolState(BitmaskSet<ChangeSymbolState> flags,
long argument) {
return defaultStatus;
}
};
try (ProcMaker maker = new ProcMaker(client, "C:\\Software\\Winmine__XP.exe")) {
@@ -317,6 +312,17 @@ public class DbgModelSetContextMWETest extends AbstractGhidraHeadlessIntegration
}
cb.dumpFrame0ViaDX();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.err.print(control.getPromptText());
//control.prompt(BitmaskSet.of(), "Hello?>");
String cmd = in.readLine();
control.execute(cmd);
if (control.getExecutionStatus().shouldWait) {
control.waitForEvent();
}
}
/**
* TODO: Didn't finish because the SetContext failed issue turned out to be mixed and/or
* broken DLLs.
@@ -1124,4 +1124,9 @@ public class DbgModelTest extends AbstractGhidraHeadlessIntegrationTest {
}
}
*/
@Test
public void testPrompt() throws Exception {
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ jar {
task configureNodepJar {
doLast {
configurations.runtimeOnly.files.forEach {
configurations.default.files.forEach {
if (filterJar(it)) {
nodepJar.from(zipTree(it))
}
@@ -132,6 +132,7 @@ src/main/resources/images/breakpoint-set.png||GHIDRA||||END|
src/main/resources/images/breakpoints-clear-all.png||GHIDRA||||END|
src/main/resources/images/breakpoints-disable-all.png||GHIDRA||||END|
src/main/resources/images/breakpoints-enable-all.png||GHIDRA||||END|
src/main/resources/images/breakpoints-make-effective.png||GHIDRA||||END|
src/main/resources/images/breakpoints.png||GHIDRA||||END|
src/main/resources/images/closedFolder.png||Modified Nuvola Icons - LGPL 2.1||||END|
src/main/resources/images/connect.png||GHIDRA||||END|
@@ -179,6 +180,7 @@ src/main/svg/breakpoint-set.svg||GHIDRA||||END|
src/main/svg/breakpoints-clear-all.svg||GHIDRA||||END|
src/main/svg/breakpoints-disable-all.svg||GHIDRA||||END|
src/main/svg/breakpoints-enable-all.svg||GHIDRA||||END|
src/main/svg/breakpoints-make-effective.svg||GHIDRA||||END|
src/main/svg/breakpoints.svg||GHIDRA||||END|
src/main/svg/connect.svg||GHIDRA||||END|
src/main/svg/console.svg||GHIDRA||||END|
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

@@ -99,6 +99,8 @@ public interface DebuggerResources {
ResourceManager.loadImage("images/breakpoints-disable-all.png");
ImageIcon ICON_CLEAR_ALL_BREAKPOINTS =
ResourceManager.loadImage("images/breakpoints-clear-all.png");
ImageIcon ICON_MAKE_BREAKPOINTS_EFFECTIVE =
ResourceManager.loadImage("images/breakpoints-make-effective.png");
// TODO: Some overlay to indicate dynamic, or new icon altogether
ImageIcon ICON_LISTING = ResourceManager.loadImage("images/Browser.gif");
@@ -986,13 +988,29 @@ public interface DebuggerResources {
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuGroup(GROUP)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface OpenProgramAction {
String NAME = "Open Program";
Icon ICON = ICON_PROGRAM;
String DESCRIPTION = "Open the program";
String HELP_ANCHOR = "open_program";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
abstract class AbstractToggleBreakpointAction extends DockingAction {
public static final String NAME = "Toggle Breakpoint";
// TODO: A "toggle breakpoint" icon
@@ -1142,6 +1160,18 @@ public interface DebuggerResources {
}
}
abstract class AbstractMakeBreakpointsEffectiveAction extends DockingAction {
public static final String NAME = "Make Breakpoints Effective";
public static final Icon ICON = ICON_MAKE_BREAKPOINTS_EFFECTIVE;
public static final String HELP_ANCHOR = "make_breakpoints_effective";
public AbstractMakeBreakpointsEffectiveAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Place enabled but ineffective breakpoints where possible");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
}
}
interface MapModulesAction {
String NAME = "Map Modules";
String DESCRIPTION = "Map selected modules to program images";
@@ -22,7 +22,6 @@ 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 {
@@ -43,17 +42,18 @@ public class BreakpointLocationRow {
}
public void setEnabled(boolean enabled) {
// TODO: Make this toggle the individual location, if possible, not the whole spec.
TraceRecorder recorder = provider.modelService.getRecorder(loc.getTrace());
TargetBreakpointLocation bpt = recorder.getTargetBreakpoint(loc);
if (enabled) {
bpt.getSpecification().enable().exceptionally(ex -> {
Msg.showError(this, null, "Toggle breakpoint", "Could not enable breakpoint", ex);
provider.breakpointError("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);
provider.breakpointError("Toggle breakpoint", "Could not disable breakpoint", ex);
return null;
});
}
@@ -532,7 +532,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
if (bs == null || bs.isEmpty()) {
Set<TraceBreakpointKind> supported = getSupportedKindsFromContext(context);
if (supported.isEmpty()) {
Msg.showError(this, null, NAME,
breakpointError(NAME,
"It seems this target does not support breakpoints.");
return;
}
@@ -545,13 +545,13 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
Trace trace = getTraceFromContext(context); // OK if null - means all traces
if (en.enabled) {
breakpointService.disableAll(bs, trace).exceptionally(ex -> {
Msg.showError(this, null, NAME, "Could not disable breakpoints", ex);
breakpointError(NAME, "Could not disable breakpoints", ex);
return null;
});
}
else {
breakpointService.enableAll(bs, trace).exceptionally(ex -> {
Msg.showError(this, null, NAME, "Could not enable breakpoints", ex);
breakpointError(NAME, "Could not enable breakpoints", ex);
return null;
});
}
@@ -628,7 +628,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
ProgramLocation location = getLocationFromContext(context);
Set<LogicalBreakpoint> col = breakpointService.getBreakpointsAt(location);
breakpointService.enableAll(col, getTraceFromContext(context)).exceptionally(ex -> {
Msg.showError(this, null, NAME, "Could not enable breakpoint", ex);
breakpointError(NAME, "Could not enable breakpoint", ex);
return null;
});
}
@@ -665,7 +665,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
ProgramLocation location = getLocationFromContext(context);
Set<LogicalBreakpoint> col = breakpointService.getBreakpointsAt(location);
breakpointService.disableAll(col, getTraceFromContext(context)).exceptionally(ex -> {
Msg.showError(this, null, NAME, "Could not disable breakpoint", ex);
breakpointError(NAME, "Could not disable breakpoint", ex);
return null;
});
}
@@ -704,7 +704,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
ProgramLocation location = getLocationFromContext(context);
Set<LogicalBreakpoint> col = breakpointService.getBreakpointsAt(location);
breakpointService.deleteAll(col, getTraceFromContext(context)).exceptionally(ex -> {
Msg.showError(this, null, NAME, "Could not delete breakpoint", ex);
breakpointError(NAME, "Could not delete breakpoint", ex);
return null;
});
}
@@ -733,6 +733,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
private DebuggerStaticMappingService mappingService;
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed
private DebuggerConsoleService consoleService;
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
@@ -1084,4 +1086,21 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
}
}
}
protected void breakpointError(String title, String message) {
if (consoleService == null) {
Msg.showError(this, null, title, message);
return;
}
consoleService.log(DebuggerResources.ICON_LOG_ERROR, message);
}
protected void breakpointError(String title, String message, Throwable ex) {
if (consoleService == null) {
Msg.showError(this, null, title, message, ex);
return;
}
Msg.error(this, message, ex);
consoleService.log(DebuggerResources.ICON_LOG_ERROR, message + " (" + ex + ")");
}
}
@@ -25,19 +25,20 @@ import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@PluginInfo( //
shortDescription = "Debugger breakpoints manager", //
description = "GUI to manage breakpoints", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.RELEASED, //
servicesRequired = { //
DebuggerLogicalBreakpointService.class, //
DebuggerModelService.class, //
}, eventsConsumed = {
TraceOpenedPluginEvent.class, //
TraceClosedPluginEvent.class, //
TraceActivatedPluginEvent.class, //
} //
shortDescription = "Debugger breakpoints manager", //
description = "GUI to manage breakpoints", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.RELEASED, //
servicesRequired = { //
DebuggerLogicalBreakpointService.class, //
DebuggerModelService.class, //
},
eventsConsumed = {
TraceOpenedPluginEvent.class, //
TraceClosedPluginEvent.class, //
TraceActivatedPluginEvent.class, //
} //
)
public class DebuggerBreakpointsPlugin extends AbstractDebuggerPlugin {
protected DebuggerBreakpointsProvider provider;
@@ -54,6 +55,7 @@ public class DebuggerBreakpointsPlugin extends AbstractDebuggerPlugin {
@Override
protected void dispose() {
provider.dispose();
tool.removeComponentProvider(provider);
super.dispose();
}
@@ -254,8 +254,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
(DebuggerLogicalBreakpointsActionContext) context;
Collection<LogicalBreakpoint> sel = ctx.getSelection();
breakpointService.enableAll(sel, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Enable Breakpoints",
"Could not enable breakpoints", ex);
breakpointError("Enable Breakpoints", "Could not enable breakpoints", ex);
return null;
});
}
@@ -286,8 +285,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
public void actionPerformed(ActionContext context) {
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
breakpointService.enableAll(all, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Enable All Breakpoints",
"Could not enable breakpoints", ex);
breakpointError("Enable All Breakpoints", "Could not enable breakpoints", ex);
return null;
});
}
@@ -320,8 +318,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
(DebuggerLogicalBreakpointsActionContext) context;
Collection<LogicalBreakpoint> sel = ctx.getSelection();
breakpointService.disableAll(sel, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Disable Breakpoints",
"Could not disable breakpoints", ex);
breakpointError("Disable Breakpoints", "Could not disable breakpoints", ex);
return null;
});
}
@@ -352,8 +349,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
public void actionPerformed(ActionContext context) {
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
breakpointService.disableAll(all, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Disable All Breakpoints",
"Could not disable breakpoints", ex);
breakpointError("Disable All Breakpoints", "Could not disable breakpoints", ex);
return null;
});
}
@@ -365,7 +361,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
protected class ClearSelectedBreakpointsAction extends AbstractClearSelectedBreakpointsAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS + "Clear";
public ClearSelectedBreakpointsAction() {
super(plugin);
@@ -382,8 +378,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
(DebuggerLogicalBreakpointsActionContext) context;
Collection<LogicalBreakpoint> sel = ctx.getSelection();
breakpointService.deleteAll(sel, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Clear Breakpoints",
"Could not clear breakpoints", ex);
breakpointError("Clear Breakpoints", "Could not clear breakpoints", ex);
return null;
});
}
@@ -401,7 +396,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
protected class ClearAllBreakpointsAction extends AbstractClearAllBreakpointsAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS + "Clear";
public ClearAllBreakpointsAction() {
super(plugin);
@@ -414,8 +409,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
public void actionPerformed(ActionContext context) {
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
breakpointService.deleteAll(all, null).exceptionally(ex -> {
Msg.showError(this, getComponent(), "Clear All Breakpoints",
"Could not clear breakpoints", ex);
breakpointError("Clear All Breakpoints", "Could not clear breakpoints", ex);
return null;
});
}
@@ -426,6 +420,64 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
}
protected abstract class CommonMakeBreakpointsEffectiveAction
extends AbstractMakeBreakpointsEffectiveAction {
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
public CommonMakeBreakpointsEffectiveAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
}
@Override
public void actionPerformed(ActionContext context) {
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
for (LogicalBreakpoint lb : all) {
if (lb.computeEnablement() != Enablement.INEFFECTIVE_ENABLED) {
continue;
}
if (lb.getMappedTraces().isEmpty()) {
continue;
}
lb.enable();
}
}
}
protected class MakeBreakpointsEffectiveAction extends CommonMakeBreakpointsEffectiveAction {
public MakeBreakpointsEffectiveAction() {
super();
addLocalAction(this);
setEnabled(false);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (breakpointService == null) {
return false;
}
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
for (LogicalBreakpoint lb : all) {
if (lb.computeEnablement() != Enablement.INEFFECTIVE_ENABLED) {
continue;
}
if (lb.getMappedTraces().isEmpty()) {
continue;
}
return true;
}
return false;
}
}
protected class MakeBreakpointsEffectiveResolutionAction
extends CommonMakeBreakpointsEffectiveAction {
@Override
public boolean isValidContext(ActionContext context) {
return context instanceof DebuggerMakeBreakpointsEffectiveActionContext;
}
}
class LocationsBySelectedBreakpointsTableFilter implements TableFilter<BreakpointLocationRow> {
@Override
public boolean acceptsRow(BreakpointLocationRow locationRow) {
@@ -540,13 +592,15 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
// @AutoServiceConsumed via method
private DebuggerLogicalBreakpointService breakpointService;
// @AutoServiceConsumed via method
// @AutoServiceConsumed via method, package access for BreakpointLogicalRow
DebuggerModelService modelService;
@AutoServiceConsumed
private DebuggerListingService listingService;
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed
private DebuggerConsoleService consoleService;
@AutoServiceConsumed
private GoToService goToService;
@SuppressWarnings("unused")
private AutoService.Wiring autoServiceWiring;
@@ -571,13 +625,18 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
private ActionContext myActionContext;
private final DebuggerMakeBreakpointsEffectiveActionContext makeEffectiveResolutionContext =
new DebuggerMakeBreakpointsEffectiveActionContext();
// package access for testing
EnableSelectedBreakpointsAction actionEnableSelectedBreakpointsAction;
EnableAllBreakpointsAction actionEnableAllBreakpointsAction;
DisableSelectedBreakpointsAction actionDisableSelectedBreakpointsAction;
DisableAllBreakpointsAction actionDisableAllBreakpointsAction;
ClearSelectedBreakpointsAction actionClearSelectedBreakpointsAction;
ClearAllBreakpointsAction actionClearAllBreakpointsAction;
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
EnableAllBreakpointsAction actionEnableAllBreakpoints;
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
DisableAllBreakpointsAction actionDisableAllBreakpoints;
ClearSelectedBreakpointsAction actionClearSelectedBreakpoints;
ClearAllBreakpointsAction actionClearAllBreakpoints;
MakeBreakpointsEffectiveAction actionMakeBreakpointsEffective;
MakeBreakpointsEffectiveResolutionAction actionMakeBreakpointsEffectiveResolution;
ToggleDockingAction actionFilterByCurrentTrace;
ToggleDockingAction actionFilterLocationsByBreakpoints;
@@ -598,6 +657,35 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
createActions();
}
protected void dispose() {
if (consoleService != null) {
if (actionMakeBreakpointsEffectiveResolution != null) {
consoleService.removeResolutionAction(actionMakeBreakpointsEffectiveResolution);
}
}
}
@Override
public void contextChanged() {
super.contextChanged();
if (consoleService == null) {
return;
}
// TODO: This should probably check for its existence first
// Kind of a hack, but it works.
if (actionMakeBreakpointsEffective != null &&
actionMakeBreakpointsEffective.isEnabledForContext(myActionContext)) {
if (!consoleService.logContains(makeEffectiveResolutionContext)) {
consoleService.log(DebuggerResources.ICON_PROVIDER_BREAKPOINTS,
"There are ineffective breakpoints that can be placed",
makeEffectiveResolutionContext);
}
}
else {
consoleService.removeFromLog(makeEffectiveResolutionContext);
}
}
@AutoServiceConsumed
private void setBreakpointService(DebuggerLogicalBreakpointService breakpointService) {
if (this.breakpointService != null) {
@@ -623,6 +711,15 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
}
@AutoServiceConsumed
private void setConsoleService(DebuggerConsoleService consoleService) {
if (consoleService != null) {
if (actionMakeBreakpointsEffectiveResolution != null) {
consoleService.addResolutionAction(actionMakeBreakpointsEffectiveResolution);
}
}
}
protected void loadBreakpoints() {
Set<LogicalBreakpoint> all = breakpointService.getAllBreakpoints();
breakpointTableModel.addAllItems(all);
@@ -921,12 +1018,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
}
protected void createActions() {
actionEnableSelectedBreakpointsAction = new EnableSelectedBreakpointsAction();
actionEnableAllBreakpointsAction = new EnableAllBreakpointsAction();
actionDisableSelectedBreakpointsAction = new DisableSelectedBreakpointsAction();
actionDisableAllBreakpointsAction = new DisableAllBreakpointsAction();
actionClearSelectedBreakpointsAction = new ClearSelectedBreakpointsAction();
actionClearAllBreakpointsAction = new ClearAllBreakpointsAction();
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();
actionDisableAllBreakpoints = new DisableAllBreakpointsAction();
actionClearSelectedBreakpoints = new ClearSelectedBreakpointsAction();
actionClearAllBreakpoints = new ClearAllBreakpointsAction();
actionMakeBreakpointsEffective = new MakeBreakpointsEffectiveAction();
actionFilterByCurrentTrace = FilterAction.builder(plugin)
.toolBarIcon(DebuggerResources.ICON_TRACE)
.description("Filter locations to those in current trace")
@@ -938,6 +1036,8 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
.helpLocation(new HelpLocation(plugin.getName(), "filter_by_logical"))
.onAction(this::toggledFilterLocationsByBreakpoints)
.buildAndInstallLocal(this);
actionMakeBreakpointsEffectiveResolution = new MakeBreakpointsEffectiveResolutionAction();
}
private void toggledFilterByCurrentTrace(ActionContext ignored) {
@@ -979,4 +1079,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
DebuggerResources.setSelectedRows(sel, locationTableModel::getRow, locationTable,
locationTableModel, locationFilterPanel);
}
protected void breakpointError(String title, String message, Throwable ex) {
if (consoleService == null) {
Msg.showError(this, null, title, message, ex);
return;
}
Msg.error(this, message, ex);
consoleService.log(DebuggerResources.ICON_LOG_ERROR, message + " (" + ex + ")");
}
}
@@ -0,0 +1,23 @@
/* ###
* 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 docking.ActionContext;
// TODO: Any granularity, or just one suggested action on the global tool?
public class DebuggerMakeBreakpointsEffectiveActionContext extends ActionContext {
// Nothing to add
}
@@ -24,7 +24,6 @@ import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
import ghidra.util.Msg;
public class LogicalBreakpointRow {
private final DebuggerBreakpointsProvider provider;
@@ -64,7 +63,7 @@ public class LogicalBreakpointRow {
? lb.enableForTrace(provider.currentTrace)
: lb.enable();
future.exceptionally(ex -> {
Msg.showError(this, null, "Toggle Breakpoint", "Could not enable breakpoint", ex);
provider.breakpointError("Toggle Breakpoint", "Could not enable breakpoint", ex);
return null;
});
}
@@ -73,7 +72,7 @@ public class LogicalBreakpointRow {
? lb.disableForTrace(provider.currentTrace)
: lb.disable();
future.exceptionally(ex -> {
Msg.showError(this, null, "Toggle Breakpoint", "Could not disable breakpoint", ex);
provider.breakpointError("Toggle Breakpoint", "Could not disable breakpoint", ex);
return null;
});
}
@@ -29,6 +29,7 @@ import docking.ActionContext;
import docking.action.DockingActionIf;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow;
import ghidra.app.services.DebuggerConsoleService;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@@ -99,14 +100,24 @@ public class DebuggerConsolePlugin extends Plugin implements DebuggerConsoleServ
super.dispose();
}
@Override
public void log(Icon icon, String message) {
provider.log(icon, message);
}
@Override
public void log(Icon icon, String message, ActionContext context) {
provider.log(icon, message, context);
}
@Override
public void remove(ActionContext context) {
provider.remove(context);
public void removeFromLog(ActionContext context) {
provider.removeFromLog(context);
}
@Override
public boolean logContains(ActionContext context) {
return provider.logContains(context);
}
@Override
@@ -127,4 +138,14 @@ public class DebuggerConsolePlugin extends Plugin implements DebuggerConsoleServ
public long getRowCount(Class<? extends ActionContext> ctxCls) {
return provider.getRowCount(ctxCls);
}
/**
* For testing: to verify the contents of a message delivered to the console log
*
* @param ctx the context
* @return the the log entry
*/
public LogRow getLogRow(ActionContext ctx) {
return provider.getLogRow(ctx);
}
}
@@ -56,7 +56,7 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
static final int ACTION_BUTTON_SIZE = 32;
static final Dimension ACTION_BUTTON_DIM =
new Dimension(ACTION_BUTTON_SIZE, ACTION_BUTTON_SIZE);
static final int MAX_ROW_HEIGHT = 300;
static final int MIN_ROW_HEIGHT = 16;
protected enum LogTableColumns implements EnumeratedTableColumn<LogTableColumns, LogRow> {
LEVEL("Level", Icon.class, LogRow::getIcon, SortDirection.ASCENDING, false),
@@ -109,9 +109,15 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
}
}
protected static class BoundAction {
protected final DockingActionIf action;
protected final ActionContext context;
/**
* An action bound to a context
*
* <p>
* This class is public for access by test cases only.
*/
public static class BoundAction {
public final DockingActionIf action;
public final ActionContext context;
public BoundAction(DockingActionIf action, ActionContext context) {
this.action = action;
@@ -144,10 +150,22 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
}
}
protected static class ActionList extends ArrayList<BoundAction> {
/**
* A list of bound actions
*
* <p>
* This class is public for access by test cases only.
*/
public static class ActionList extends ArrayList<BoundAction> {
}
protected static class LogRow {
/**
* An entry in the console's log
*
* <p>
* This class is public for access by test cases only.
*/
public static class LogRow {
private final Icon icon;
private final String message;
private final Date date;
@@ -224,7 +242,7 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
int rows = model.getRowCount();
int cols = getColumnCount();
for (int r = 0; r < rows; r++) {
int height = 0;
int height = MIN_ROW_HEIGHT;
for (int c = 0; c < cols; c++) {
height = Math.max(height, computePreferredHeight(r, c));
}
@@ -382,6 +400,10 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
}
}
protected void log(Icon icon, String message) {
log(icon, message, new LogRowConsoleActionContext());
}
protected void log(Icon icon, String message, ActionContext context) {
logRow(new LogRow(icon, message, new Date(), context, computeToolbarActions(context)));
}
@@ -419,13 +441,19 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
new Date(event.getTimeMillis()), context, computeToolbarActions(context)));
}
protected void remove(ActionContext context) {
protected void removeFromLog(ActionContext context) {
synchronized (buffer) {
LogRow r = logTableModel.deleteKey(context);
buffer.remove(r);
}
}
protected boolean logContains(ActionContext context) {
synchronized (buffer) {
return logTableModel.getMap().containsKey(context);
}
}
protected void addResolutionAction(DockingActionIf action) {
DockingActionIf replaced =
actionsByOwnerThenName.computeIfAbsent(action.getOwner(), o -> new LinkedHashMap<>())
@@ -479,9 +507,17 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
}
protected long getRowCount(Class<? extends ActionContext> ctxCls) {
return logTableModel.getModelData()
.stream()
.filter(r -> ctxCls.isInstance(r.context))
.count();
synchronized (buffer) {
return logTableModel.getModelData()
.stream()
.filter(r -> ctxCls.isInstance(r.context))
.count();
}
}
public LogRow getLogRow(ActionContext ctx) {
synchronized (buffer) {
return logTableModel.getMap().get(ctx);
}
}
}
@@ -65,6 +65,7 @@ import utilities.util.SuppressableCallback.Suppression;
eventsConsumed = {
// ProgramSelectionPluginEvent.class, // TODO: Later or remove
// ProgramHighlightPluginEvent.class, // TODO: Later or remove
ProgramOpenedPluginEvent.class, // For auto-open log cleanup
ProgramClosedPluginEvent.class, // For marker set cleanup
ProgramLocationPluginEvent.class, // For static listing sync
TraceActivatedPluginEvent.class, // Trace/thread activation and register tracking
@@ -275,6 +276,10 @@ public class DebuggerListingPlugin extends CodeBrowserPlugin implements Debugger
}
});
}
if (event instanceof ProgramOpenedPluginEvent) {
ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
allProviders(p -> p.programOpened(ev.getProgram()));
}
if (event instanceof ProgramClosedPluginEvent) {
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
allProviders(p -> p.programClosed(ev.getProgram()));
@@ -50,8 +50,7 @@ import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec.TrackingSpecConfigFieldCodec;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.plugin.core.debug.utils.*;
import ghidra.app.plugin.core.exporter.ExporterDialog;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.*;
@@ -86,6 +85,9 @@ import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.*;
import utilities.util.SuppressableCallback;
import utilities.util.SuppressableCallback.Suppression;
@@ -353,6 +355,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionExportView;
protected DockingAction actionOpenProgram;
protected final DebuggerGoToDialog goToDialog;
@@ -586,6 +589,15 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
}
}
@AutoServiceConsumed
private void setConsoleService(DebuggerConsoleService consoleService) {
if (consoleService != null) {
if (actionOpenProgram != null) {
consoleService.addResolutionAction(actionOpenProgram);
}
}
}
protected void markTrackedStaticLocation(ProgramLocation location) {
Swing.runIfSwingOrRunLater(() -> {
if (location == null) {
@@ -607,6 +619,17 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
});
}
public void programOpened(Program program) {
if (!isMainListing()) {
return;
}
DomainFile df = program.getDomainFile();
DebuggerOpenProgramActionContext ctx = new DebuggerOpenProgramActionContext(df);
if (consoleService != null) {
consoleService.removeFromLog(ctx);
}
}
public void programClosed(Program program) {
if (program == markedProgram) {
removeOldStaticTrackingMarker();
@@ -784,6 +807,11 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
.onAction(this::activatedExportView)
.buildAndInstallLocal(this);
actionOpenProgram = OpenProgramAction.builder(plugin)
.withContext(DebuggerOpenProgramActionContext.class)
.onAction(this::activatedOpenProgram)
.build();
contextChanged();
}
@@ -813,6 +841,11 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
tool.showDialog(dialog);
}
private void activatedOpenProgram(DebuggerOpenProgramActionContext context) {
programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT);
}
protected void activatedLocationTracking(ActionContext ctx) {
doTrackSpec();
}
@@ -901,7 +934,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
public void programLocationChanged(ProgramLocation location, EventTrigger trigger) {
updateLocationLabel();
if (traceManager != null) {
location = traceManager.fixLocation(location, false);
location = ProgramLocationUtils.fixLocation(location, false);
}
super.programLocationChanged(location, trigger);
if (trigger == EventTrigger.GUI_ACTION) {
@@ -935,7 +968,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
}
protected void doSyncToStatic(ProgramLocation location) {
if (syncToStaticListing && location != null) {
if (isSyncToStaticListing() && location != null) {
ProgramLocation staticLoc = mappingService.getStaticLocationFromDynamic(location);
if (staticLoc != null) {
Swing.runIfSwingOrRunLater(() -> plugin.fireStaticLocationEvent(staticLoc));
@@ -943,8 +976,58 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
}
}
protected void doTryOpenProgram(DomainFile df, int version, int state) {
DebuggerOpenProgramActionContext ctx = new DebuggerOpenProgramActionContext(df);
if (consoleService != null && consoleService.logContains(ctx)) {
return;
}
if (df.canRecover()) {
if (consoleService != null) {
consoleService.log(DebuggerResources.ICON_MODULES, "<html>Program <b>" +
HTMLUtilities.escapeHTML(df.getPathname()) +
"</b> has recovery data. It must be opened manually.</html>", ctx);
}
return;
}
new TaskLauncher(new Task("Open " + df, true, false, false) {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
Program program = null;
try {
program = (Program) df.getDomainObject(this, false, false, monitor);
programManager.openProgram(program, state);
}
catch (VersionException e) {
if (consoleService != null) {
consoleService.log(DebuggerResources.ICON_MODULES, "<html>Program <b>" +
HTMLUtilities.escapeHTML(df.getPathname()) +
"</b> was created with a different version of Ghidra." +
" It must be opened manually.</html>", ctx);
}
return;
}
catch (Exception e) {
if (consoleService != null) {
consoleService.log(DebuggerResources.ICON_LOG_ERROR, "<html>Program <b>" +
HTMLUtilities.escapeHTML(df.getPathname()) +
"</b> could not be opened: " + e + ". Try opening it manually.</html>",
ctx);
}
return;
}
finally {
if (program != null) {
program.release(this);
}
}
}
}, tool.getToolFrame());
}
protected void doCheckCurrentModuleMissing() {
if (importerService == null || consoleService == null) {
// Is there any reason to try to open the module if we're not syncing listings?
// I don't think so.
if (!isSyncToStaticListing()) {
return;
}
Trace trace = current.getTrace();
@@ -968,9 +1051,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
DomainFile df = ProgramURLUtils.getFileForHackedUpGhidraURL(tool.getProject(),
mapping.getStaticProgramURL());
if (df != null) {
// We're almost certainly preparing to goTo, so make it current
programManager.openProgram(df, DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT);
doTryOpenProgram(df, DomainFile.DEFAULT_VERSION, ProgramManager.OPEN_CURRENT);
}
}
@@ -996,14 +1077,19 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
if (programManager != null && !toOpen.isEmpty()) {
for (DomainFile df : toOpen) {
// Do not presume a goTo is about to happen. There are no mappings, yet.
programManager.openProgram(df, DomainFile.DEFAULT_VERSION,
doTryOpenProgram(df, DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_VISIBLE);
}
}
if (importerService == null || consoleService == null) {
return;
}
for (TraceModule mod : missing) {
consoleService.log(DebuggerResources.ICON_LOG_ERROR,
"<html>The module <b><tt>" + HTMLUtilities.escapeHTML(mod.getName()) +
"</tt></b> was not found in the project",
"</tt></b> was not found in the project</html>",
new DebuggerMissingModuleActionContext(mod));
}
/**
@@ -1145,6 +1231,11 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
public void dispose() {
super.dispose();
removeOldListeners();
if (consoleService != null) {
if (actionOpenProgram != null) {
consoleService.removeResolutionAction(actionOpenProgram);
}
}
}
@Override
@@ -0,0 +1,55 @@
/* ###
* 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.listing;
import java.util.Objects;
import docking.ActionContext;
import ghidra.framework.model.DomainFile;
public class DebuggerOpenProgramActionContext extends ActionContext {
private final DomainFile df;
private final int hashCode;
public DebuggerOpenProgramActionContext(DomainFile df) {
this.df = df;
this.hashCode = Objects.hash(getClass(), df);
}
public DomainFile getDomainFile() {
return df;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DebuggerOpenProgramActionContext)) {
return false;
}
DebuggerOpenProgramActionContext that = (DebuggerOpenProgramActionContext) obj;
if (!this.df.equals(that.df)) {
return false;
}
return true;
}
}
@@ -15,6 +15,7 @@
*/
package ghidra.app.plugin.core.debug.gui.modules;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
@@ -34,18 +35,17 @@ import ghidra.util.Swing;
public class DebuggerModuleMapProposalDialog
extends AbstractDebuggerMapProposalDialog<ModuleMapEntry> {
static final String BLANK = "";
static final int BUTTON_SIZE = 32;
protected enum ModuleMapTableColumns
implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> {
REMOVE("Remove", String.class, e -> BLANK, (e, v) -> nop()),
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getModule().getBase()),
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
PROGRAM_NAME("Program", String.class, e -> e.getProgram().getName()),
STATIC_BASE("Static Base", Address.class, e -> e.getProgram().getImageBase()),
SIZE("Size", Long.class, e -> e.getModuleRange().getLength()),
CHOOSE("Choose", String.class, e -> BLANK, (e, v) -> nop());
SIZE("Size", Long.class, e -> e.getModuleRange().getLength());
private final String header;
private final Class<?> cls;
@@ -94,6 +94,19 @@ public class DebuggerModuleMapProposalDialog
}
}
protected static class ModuleMapPropsalTableModel extends
DefaultEnumeratedColumnTableModel<ModuleMapTableColumns, ModuleMapEntry> {
public ModuleMapPropsalTableModel() {
super("Module Map", ModuleMapTableColumns.class);
}
@Override
public List<ModuleMapTableColumns> defaultSortOrder() {
return List.of(ModuleMapTableColumns.MODULE_NAME);
}
}
private final DebuggerModulesProvider provider;
protected DebuggerModuleMapProposalDialog(DebuggerModulesProvider provider) {
@@ -102,8 +115,8 @@ public class DebuggerModuleMapProposalDialog
}
@Override
protected EnumeratedColumnTableModel<ModuleMapEntry> createTableModel() {
return new DefaultEnumeratedColumnTableModel<>("Module Map", ModuleMapTableColumns.class);
protected ModuleMapPropsalTableModel createTableModel() {
return new ModuleMapPropsalTableModel();
}
@Override
@@ -117,9 +130,19 @@ public class DebuggerModuleMapProposalDialog
CellEditorUtils.installButton(table, filterPanel, removeCol,
DebuggerResources.ICON_DELETE, BUTTON_SIZE, this::removeEntry);
TableColumn dynBaseCol =
columnModel.getColumn(ModuleMapTableColumns.DYNAMIC_BASE.ordinal());
dynBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn chooseCol = columnModel.getColumn(ModuleMapTableColumns.CHOOSE.ordinal());
CellEditorUtils.installButton(table, filterPanel, chooseCol,
DebuggerResources.ICON_PROGRAM, BUTTON_SIZE, this::chooseAndSetProgram);
TableColumn stBaseCol = columnModel.getColumn(ModuleMapTableColumns.STATIC_BASE.ordinal());
stBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn sizeCol = columnModel.getColumn(ModuleMapTableColumns.SIZE.ordinal());
sizeCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX);
}
private void chooseAndSetProgram(ModuleMapEntry entry) {
@@ -867,12 +867,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
Msg.error(this, "Import service is not present");
}
importModuleFromFileSystem(context.getModule());
consoleService.remove(context); // TODO: Should remove when mapping is created
consoleService.removeFromLog(context); // TODO: Should remove when mapping is created
}
private void activatedMapMissingModule(DebuggerMissingModuleActionContext context) {
mapModuleTo(context.getModule());
consoleService.remove(context); // TODO: Should remove when mapping is created
consoleService.removeFromLog(context); // TODO: Should remove when mapping is created
}
private void toggledFilter(ActionContext ignored) {
@@ -972,12 +972,19 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
protected void promptModuleProposal(Collection<ModuleMapEntry> proposal) {
if (proposal.isEmpty()) {
Msg.showInfo(this, getComponent(), "Map Modules",
"Could not formulate a proposal for any selected module." +
" You may need to import and/or open the destination images first.");
return;
}
Collection<ModuleMapEntry> adjusted =
moduleProposalDialog.adjustCollection(getTool(), proposal);
if (adjusted != null && staticMappingService != null) {
tool.executeBackgroundCommand(
new MapModulesBackgroundCommand(staticMappingService, adjusted), currentTrace);
if (adjusted == null || staticMappingService == null) {
return;
}
tool.executeBackgroundCommand(
new MapModulesBackgroundCommand(staticMappingService, adjusted), currentTrace);
}
protected void mapModules(Set<TraceModule> modules) {
@@ -1004,12 +1011,19 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
protected void promptSectionProposal(Collection<SectionMapEntry> proposal) {
if (proposal.isEmpty()) {
Msg.showInfo(this, getComponent(), "Map Sections",
"Could not formulate a proposal for any selected section." +
" You may need to import and/or open the destination images first.");
return;
}
Collection<SectionMapEntry> adjusted =
sectionProposalDialog.adjustCollection(getTool(), proposal);
if (adjusted != null && staticMappingService != null) {
tool.executeBackgroundCommand(
new MapSectionsBackgroundCommand(staticMappingService, adjusted), currentTrace);
if (adjusted == null || staticMappingService == null) {
return;
}
tool.executeBackgroundCommand(
new MapSectionsBackgroundCommand(staticMappingService, adjusted), currentTrace);
}
protected void mapSections(Set<TraceSection> sections) {
@@ -15,6 +15,7 @@
*/
package ghidra.app.plugin.core.debug.gui.modules;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
@@ -35,20 +36,19 @@ import ghidra.util.Swing;
public class DebuggerSectionMapProposalDialog
extends AbstractDebuggerMapProposalDialog<SectionMapEntry> {
static final String BLANK = "";
static final int BUTTON_SIZE = 32;
protected enum SectionMapTableColumns
implements EnumeratedTableColumn<SectionMapTableColumns, SectionMapEntry> {
REMOVE("Remove", String.class, e -> BLANK, (e, v) -> nop()),
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
SECTION_NAME("Section", String.class, e -> e.getSection().getName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getSection().getStart()),
CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()),
PROGRAM_NAME("Program", String.class, e -> e.getProgram().getName()),
BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()),
STATIC_BASE("Static Base", Address.class, e -> e.getBlock().getStart()),
SIZE("Size", Long.class, e -> e.getLength()),
CHOOSE("Choose", String.class, e -> BLANK, (e, s) -> nop());
SIZE("Size", Long.class, e -> e.getLength());
private final String header;
private final Class<?> cls;
@@ -98,6 +98,19 @@ public class DebuggerSectionMapProposalDialog
}
}
protected static class SectionMapPropsalTableModel extends
DefaultEnumeratedColumnTableModel<SectionMapTableColumns, SectionMapEntry> {
public SectionMapPropsalTableModel() {
super("Section Map", SectionMapTableColumns.class);
}
@Override
public List<SectionMapTableColumns> defaultSortOrder() {
return List.of(SectionMapTableColumns.MODULE_NAME, SectionMapTableColumns.SECTION_NAME);
}
}
private final DebuggerModulesProvider provider;
public DebuggerSectionMapProposalDialog(DebuggerModulesProvider provider) {
@@ -106,8 +119,8 @@ public class DebuggerSectionMapProposalDialog
}
@Override
protected EnumeratedColumnTableModel<SectionMapEntry> createTableModel() {
return new DefaultEnumeratedColumnTableModel<>("Section Map", SectionMapTableColumns.class);
protected SectionMapPropsalTableModel createTableModel() {
return new SectionMapPropsalTableModel();
}
@Override
@@ -121,9 +134,19 @@ public class DebuggerSectionMapProposalDialog
CellEditorUtils.installButton(table, filterPanel, removeCol,
DebuggerResources.ICON_DELETE, BUTTON_SIZE, this::removeEntry);
TableColumn dynBaseCol =
columnModel.getColumn(SectionMapTableColumns.DYNAMIC_BASE.ordinal());
dynBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn chooseCol = columnModel.getColumn(SectionMapTableColumns.CHOOSE.ordinal());
CellEditorUtils.installButton(table, filterPanel, chooseCol, DebuggerResources.ICON_PROGRAM,
BUTTON_SIZE, this::chooseAndSetBlock);
TableColumn stBaseCol = columnModel.getColumn(SectionMapTableColumns.STATIC_BASE.ordinal());
stBaseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn sizeCol = columnModel.getColumn(SectionMapTableColumns.SIZE.ordinal());
sizeCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX);
}
private void chooseAndSetBlock(SectionMapEntry entry) {
@@ -1118,7 +1118,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
@Override
public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
loc = traceManager.fixLocation(loc, true);
loc = ProgramLocationUtils.fixLocation(loc, true);
TraceProgramView view = (TraceProgramView) loc.getProgram();
Trace trace = view.getTrace();
TraceLocation tloc = new DefaultTraceLocation(trace, null,
@@ -31,7 +31,6 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
import ghidra.app.services.*;
import ghidra.async.AsyncConfigFieldCodec.BooleanAsyncConfigFieldCodec;
import ghidra.async.AsyncReference;
@@ -47,8 +46,6 @@ import ghidra.framework.plugintool.annotation.AutoConfigStateField;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.lifecycle.Internal;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceThreadChangeType;
import ghidra.trace.model.TraceDomainObjectListener;
@@ -1147,23 +1144,6 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
autoCloseOnTerminate.removeChangeListener(listener);
}
@Override
// TODO: Move this into some static util, now that canonical view is a trace concept
public ProgramLocation fixLocation(ProgramLocation location, boolean matchSnap) {
Program program = location.getProgram();
if (!(program instanceof TraceProgramView)) {
return location;
}
TraceProgramView itsView = (TraceProgramView) program;
Trace trace = itsView.getTrace();
TraceProgramView canonicalView = trace.getProgramView();
if (canonicalView == itsView ||
(matchSnap && canonicalView.getSnap() != itsView.getSnap())) {
return location;
}
return ProgramLocationUtils.replaceProgram(location, canonicalView);
}
@Override
public boolean canClose() {
if (isSaveTracesByDefault()) {
@@ -18,20 +18,48 @@ package ghidra.app.plugin.core.debug.utils;
import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.util.BytesFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
public enum ProgramLocationUtils {
;
public static ProgramLocation replaceAddress(ProgramLocation loc, Program program,
Address address) {
// HACK: ... and a half
SaveState state = new SaveState("LOC");
loc.saveState(state);
state.putString("_ADDRESS", address.toString());
state.putString("_BYTE_ADDR",
address.add(loc.getByteAddress().subtract(loc.getAddress())).toString());
return ProgramLocation.getLocation(program, state);
// Outside of byte fields, I really don't care
if (loc instanceof BytesFieldLocation) {
return new BytesFieldLocation(program, address);
}
return new ProgramLocation(program, address);
}
/**
* Swap out the trace view of a {@link ProgramLocation} if it is not the canonical view
*
* <p>
* If the program location is not associated with a trace, the same location is returned.
* Otherwise, this ensures that the given view is the canonical one for the same trace. If
* matchSnap is true, the view is only replaced when the replacement shares the same snap.
*
* @param location a location possibly in a trace view
* @param matchSnap true to only replace is snap matches, false to always replace
* @return the adjusted location
*/
public static ProgramLocation fixLocation(ProgramLocation loc, boolean matchSnap) {
Program program = loc.getProgram();
if (!(program instanceof TraceProgramView)) {
return loc;
}
TraceProgramView itsView = (TraceProgramView) program;
Trace trace = itsView.getTrace();
TraceProgramView canonicalView = trace.getProgramView();
if (canonicalView == itsView ||
(matchSnap && canonicalView.getSnap() != itsView.getSnap())) {
return loc;
}
return replaceProgram(loc, canonicalView);
}
public static ProgramLocation replaceProgram(ProgramLocation loc, Program program) {
@@ -27,6 +27,17 @@ import ghidra.util.HTMLUtilities;
@ServiceInfo(defaultProvider = DebuggerConsolePlugin.class)
public interface DebuggerConsoleService extends DebuggerConsoleLogger {
/**
* Log a message to the console
*
* <p>
* <b>WARNING:</b> See {@link #log(Icon, String, ActionContext)} regarding HTML.
*
* @param icon an icon for the message
* @param message the HTML-formatted message
*/
void log(Icon icon, String message);
/**
* Log an actionable message to the console
*
@@ -51,7 +62,15 @@ public interface DebuggerConsoleService extends DebuggerConsoleLogger {
*
* @param context the context of the entry to remove
*/
void remove(ActionContext context);
void removeFromLog(ActionContext context);
/**
* Check if the console contains an actionable message for the given context
*
* @param context the context to check for
* @return true if present, false if absent
*/
boolean logContains(ActionContext context);
/**
* Add an action which might be applied to an actionable log message
@@ -22,7 +22,6 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.framework.model.DomainFile;
import ghidra.framework.plugintool.ServiceInfo;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
@@ -195,21 +194,6 @@ public interface DebuggerTraceManagerService {
void removeAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener);
/**
* Swap out the trace view of a {@link ProgramLocation} if it is not the debugger's view
*
* <p>
* If the program location is not associated with a trace, the same location is returned.
* Otherwise, this ensures that the given view is the one found in the debugger plugin for the
* same trace. If matchSnap is true, the view is only replaced when the replacement shares the
* same snap.
*
* @param location a location possibly in a trace view
* @param matchSnap true to only replace is snap matches, false to always replace
* @return the adjusted location
*/
ProgramLocation fixLocation(ProgramLocation location, boolean matchSnap);
/**
* Fill in an incomplete coordinate specification, using the manager's "best judgement"
*
Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

@@ -0,0 +1,64 @@
<?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"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16px"
height="16px"
viewBox="0 0 16 16"
version="1.1"
id="SVGRoot"
sodipodi:docname="breakpoints-make-effective.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="757"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="1405"
inkscape:window-y="650"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
<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:#aa0000;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path821"
cx="11"
cy="11.000002"
r="4" />
<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 5,1 C 2.790861,1 1,2.790861 1,5 1,7.209139 2.790861,9 5,9 7.209139,9 9,7.209139 9,5 9,2.790861 7.209139,1 5,1 Z"
id="path821-7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -31,6 +31,7 @@ import generic.Unique;
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.plugin.core.debug.gui.console.DebuggerConsolePlugin;
import ghidra.app.services.*;
import ghidra.app.services.LogicalBreakpoint.Enablement;
import ghidra.async.AsyncTestUtils;
@@ -260,12 +261,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
LogicalBreakpointRow row =
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
@@ -274,28 +275,28 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
waitForSwing();
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
performAction(breakpointsProvider.actionEnableSelectedBreakpointsAction);
performAction(breakpointsProvider.actionEnableSelectedBreakpoints);
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointTable.clearSelection();
waitForSwing();
assertFalse(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
waitForSwing();
assertTrue(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
// Bookmark part should actually be synchronous.
waitOn(row.getLogicalBreakpoint().delete());
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionEnableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled());
}
@Test
@@ -304,12 +305,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableAllBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertTrue(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled());
LogicalBreakpointRow row =
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
@@ -317,18 +318,18 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
waitForSwing();
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled());
performAction(breakpointsProvider.actionEnableAllBreakpointsAction);
performAction(breakpointsProvider.actionEnableAllBreakpoints);
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled());
// Bookmark part should actually be synchronous.
row.getLogicalBreakpoint().delete().get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionEnableAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionEnableAllBreakpoints.isEnabled());
}
@Test
@@ -337,12 +338,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
LogicalBreakpointRow row =
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
@@ -350,28 +351,28 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
waitForSwing();
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
performAction(breakpointsProvider.actionDisableSelectedBreakpointsAction);
performAction(breakpointsProvider.actionDisableSelectedBreakpoints);
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointTable.clearSelection();
waitForSwing();
assertFalse(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
waitForSwing();
assertTrue(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
// Bookmark part should actually be synchronous.
waitOn(row.getLogicalBreakpoint().delete());
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionDisableSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled());
}
@Test
@@ -380,27 +381,27 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableAllBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled());
LogicalBreakpointRow row =
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled());
performAction(breakpointsProvider.actionDisableAllBreakpointsAction);
performAction(breakpointsProvider.actionDisableAllBreakpoints);
assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement());
assertTrue(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled());
// Bookmark part should actually be synchronous.
row.getLogicalBreakpoint().delete().get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionDisableAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionDisableAllBreakpoints.isEnabled());
}
@Test
@@ -409,34 +410,34 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertFalse(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
LogicalBreakpointRow row =
Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData());
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
waitForSwing();
assertTrue(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointTable.clearSelection();
waitForSwing();
assertFalse(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
breakpointsProvider.breakpointFilterPanel.setSelectedItem(row);
waitForSwing();
assertTrue(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
performAction(breakpointsProvider.actionClearSelectedBreakpointsAction);
performAction(breakpointsProvider.actionClearSelectedBreakpoints);
assertProviderEmpty();
assertFalse(breakpointsProvider.actionClearSelectedBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearSelectedBreakpoints.isEnabled());
}
@Test
@@ -445,17 +446,54 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
programManager.openProgram(program);
waitForSwing();
assertFalse(breakpointsProvider.actionClearAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearAllBreakpoints.isEnabled());
addStaticMemoryAndBreakpoint();
waitForDomainObject(program);
assertTrue(breakpointsProvider.actionClearAllBreakpointsAction.isEnabled());
assertTrue(breakpointsProvider.actionClearAllBreakpoints.isEnabled());
performAction(breakpointsProvider.actionClearAllBreakpointsAction);
performAction(breakpointsProvider.actionClearAllBreakpoints);
assertProviderEmpty();
assertFalse(breakpointsProvider.actionClearAllBreakpointsAction.isEnabled());
assertFalse(breakpointsProvider.actionClearAllBreakpoints.isEnabled());
}
@Test
public void testActionMakeBreakpointsEffective() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace();
createProgramFromTrace(trace);
intoProject(trace);
intoProject(program);
assertFalse(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
programManager.openProgram(program);
assertFalse(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
traceManager.openTrace(trace);
assertFalse(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
addStaticMemoryAndBreakpoint();
assertFalse(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
addMapping(trace, program);
waitForPass(() -> {
assertTrue(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
assertEquals(1,
consolePlugin.getRowCount(DebuggerMakeBreakpointsEffectiveActionContext.class));
});
performAction(breakpointsProvider.actionMakeBreakpointsEffective);
waitForPass(() -> {
assertFalse(breakpointsProvider.actionMakeBreakpointsEffective.isEnabled());
assertEquals(0,
consolePlugin.getRowCount(DebuggerMakeBreakpointsEffectiveActionContext.class));
});
}
@Test
@@ -19,6 +19,7 @@ import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*;
import java.awt.*;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -37,10 +38,13 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractFollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.BoundAction;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.SwingExecutorService;
import ghidra.framework.model.*;
import ghidra.plugin.importer.ImporterPlugin;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
@@ -57,6 +61,9 @@ import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUITest {
static LocationTrackingSpec getLocationTrackingSpec(String name) {
@@ -1351,4 +1358,127 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress());
}
@Test
public void testSyncToStaticListingOpensModule() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
program.getMemory()
.createInitializedBlock(".text", ss.getAddress(0x00600000), 0x10000, (byte) 0,
monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
mappingService.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
programManager.closeAllPrograms(true);
waitForPass(() -> assertEquals(0, programManager.getAllOpenPrograms().length));
traceManager.activateTrace(tb.trace);
waitForSwing();
listingProvider.getListingPanel()
.setCursorPosition(
new ProgramLocation(tb.trace.getProgramView(), tb.addr(0x00401234)),
EventTrigger.GUI_ACTION);
waitForSwing();
waitForPass(() -> assertEquals(1, programManager.getAllOpenPrograms().length));
assertTrue(java.util.List.of(programManager.getAllOpenPrograms()).contains(program));
assertFalse(consolePlugin
.logContains(new DebuggerOpenProgramActionContext(program.getDomainFile())));
}
@Test
public void testSyncToStaticLogsRecoverableProgram() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
TestDummyDomainFolder root = new TestDummyDomainFolder(null, "root");
DomainFile df = new TestDummyDomainFile(root, "dummyFile") {
@Override
public boolean canRecover() {
return true;
}
};
listingProvider.doTryOpenProgram(df, DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT);
waitForSwing();
DebuggerOpenProgramActionContext ctx = new DebuggerOpenProgramActionContext(df);
waitForPass(() -> assertTrue(consolePlugin.logContains(ctx)));
assertTrue(consolePlugin.getLogRow(ctx).getMessage().contains("recovery"));
}
@Test
public void testSyncToStaticLogsUpgradeableProgram() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
TestDummyDomainFolder root = new TestDummyDomainFolder(null, "root");
DomainFile df = new TestDummyDomainFile(root, "dummyFile") {
@Override
public boolean canRecover() {
return false;
}
@Override
public DomainObject getDomainObject(Object consumer, boolean okToUpgrade,
boolean okToRecover, TaskMonitor monitor)
throws VersionException, IOException, CancelledException {
throw new VersionException();
}
};
listingProvider.doTryOpenProgram(df, DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT);
waitForSwing();
DebuggerOpenProgramActionContext ctx = new DebuggerOpenProgramActionContext(df);
waitForPass(() -> assertTrue(consolePlugin.logContains(ctx)));
assertTrue(consolePlugin.getLogRow(ctx).getMessage().contains("version"));
}
@Test
public void testActionOpenProgram() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
createProgram();
intoProject(program);
assertEquals(0, programManager.getAllOpenPrograms().length);
DebuggerOpenProgramActionContext ctx =
new DebuggerOpenProgramActionContext(program.getDomainFile());
consolePlugin.log(DebuggerResources.ICON_MODULES, "Test resolution", ctx);
waitForSwing();
LogRow row = consolePlugin.getLogRow(ctx);
assertEquals(1, row.getActions().size());
BoundAction boundAction = row.getActions().get(0);
assertEquals(listingProvider.actionOpenProgram, boundAction.action);
boundAction.perform();
waitForSwing();
waitForPass(() -> assertEquals(1, programManager.getAllOpenPrograms().length));
assertTrue(java.util.List.of(programManager.getAllOpenPrograms()).contains(program));
// TODO: Test this independent of this particular action?
assertNull(consolePlugin.getLogRow(ctx));
}
}
@@ -51,6 +51,7 @@ public class IconButtonTableCellEditor<R> extends AbstractCellEditor
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int row, int column) {
this.row = filterPanel.getRowObject(row);
button.setToolTipText(value.toString());
return button;
}
@@ -15,27 +15,31 @@
*/
package docking.widgets.table;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.*;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.*;
import ghidra.docking.settings.Settings;
import ghidra.util.table.column.AbstractGhidraColumnRenderer;
public class IconButtonTableCellRenderer
extends AbstractGhidraColumnRenderer<String> {
protected final JPanel panel = new JPanel();
protected final JButton button = new JButton("");
public IconButtonTableCellRenderer(Icon icon, int buttonSize) {
button.setIcon(icon);
button.setMinimumSize(new Dimension(buttonSize, buttonSize));
panel.setMinimumSize(new Dimension(buttonSize, buttonSize));
panel.setLayout(new BorderLayout());
panel.add(button);
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
return button;
super.getTableCellRendererComponent(data); // Waste, but sets background
panel.setBackground(getBackground());
return panel;
}
@Override
@@ -0,0 +1,94 @@
/* ###
* 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.util;
import java.nio.ByteBuffer;
/**
* Some utilities for manipulating a {@link ByteBuffer}
*/
public interface ByteBufferUtils {
/**
* Resize a write-mode buffer
*
* <p>
* This preserves the buffer contents
*
* @param buf the buffer
* @param capacity the new capacity, greater or equal to the buffer's limit
* @return the new buffer
*/
public static ByteBuffer resize(ByteBuffer buf, int capacity) {
if (capacity < buf.limit()) {
throw new IllegalArgumentException("New capacity must fit current contents");
}
buf.flip();
ByteBuffer resized = ByteBuffer.allocate(capacity);
resized.put(buf);
return resized;
}
/**
* Resize a write-mode buffer to twice its current capacity
*
* <p>
* This preserves the buffer contents
*
* @param buf the buffer
* @return the new buffer
*/
public static ByteBuffer upsize(ByteBuffer buf) {
return resize(buf, buf.capacity() * 2);
}
/**
* Checks for equality, with a mask applied
*
* <p>
* This considers the entire contents of both buffers without regard for position or limit. Both
* buffers must have equal capacities to be considered equal. The mask, if given, must have
* capacity equal to that of the first buffer {@code a} or an exception is thrown.
*
* @param mask a buffer containing the mask, or null to match all bytes exactly
* @param a the first buffer
* @param b the second buffer
* @return true if matches, false otherwise
* @throws IllegalArgumentException if {@code mask} and {@code a} have unequal capacities
*/
public static boolean maskedEquals(ByteBuffer mask, ByteBuffer a, ByteBuffer b) {
int len = a.capacity();
if (mask != null && mask.capacity() != len) {
throw new IllegalArgumentException("mask and a must have equal capacities");
}
if (len != a.capacity()) {
return false;
}
if (mask != null) {
for (int i = 0; i < len; i++) {
if ((a.get(i) & mask.get(i)) != (b.get(i) & mask.get(i))) {
return false;
}
}
return true;
}
for (int i = 0; i < len; i++) {
if (a.get(i) != b.get(i)) {
return false;
}
}
return true;
}
}