mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-24 03:09:36 +08:00
Merge remote-tracking branch 'origin/debugger'
This commit is contained in:
@@ -54,7 +54,7 @@ jar {
|
||||
|
||||
task configureNodepJar {
|
||||
doLast {
|
||||
configurations.runtimeOnly.files.forEach {
|
||||
configurations.default.files.forEach {
|
||||
if (filterJar(it)) {
|
||||
nodepJar.from(zipTree(it))
|
||||
}
|
||||
|
||||
+5
-3
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -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();
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -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))
|
||||
}
|
||||
|
||||
+13
-7
@@ -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.
|
||||
|
||||
+5
@@ -1124,4 +1124,9 @@ public class DbgModelTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testPrompt() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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|
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
+31
-1
@@ -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";
|
||||
|
||||
+3
-3
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
+25
-6
@@ -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 + ")");
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -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();
|
||||
}
|
||||
|
||||
+136
-27
@@ -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 + ")");
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -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
|
||||
}
|
||||
+2
-3
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
+23
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-12
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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()));
|
||||
|
||||
+101
-10
@@ -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
|
||||
|
||||
+55
@@ -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;
|
||||
}
|
||||
}
|
||||
+29
-6
@@ -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) {
|
||||
|
||||
+22
-8
@@ -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) {
|
||||
|
||||
+29
-6
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
-20
@@ -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()) {
|
||||
|
||||
+35
-7
@@ -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) {
|
||||
|
||||
+20
-1
@@ -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
|
||||
|
||||
-16
@@ -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 |
+77
-39
@@ -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
|
||||
|
||||
+130
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user