GP-4144: Many fixes, esp., for dbgeng Trace RMI.

This commit is contained in:
Dan 2024-01-29 14:56:28 -05:00
parent 1281fb979b
commit a6549947ab
30 changed files with 1526 additions and 725 deletions

View file

@ -18,67 +18,37 @@ package ghidra.app.plugin.core.debug.disassemble;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin.Reqs;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingActionContext;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.debug.api.platform.DisassemblyResult;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.cmd.TypedBackgroundCommand;
import ghidra.app.plugin.core.debug.disassemble.CurrentPlatformTraceDisassembleCommand.Reqs;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor;
public class CurrentPlatformTraceDisassembleAction extends DockingAction {
private static final String NAME = "Disassemble";
private static final String MENU_GROUP = "Disassembly";
private static final KeyBindingData KEY_BINDING = new KeyBindingData("D");
private final DebuggerDisassemblerPlugin plugin;
private final PluginTool tool;
public CurrentPlatformTraceDisassembleAction(DebuggerDisassemblerPlugin plugin) {
super(NAME, plugin.getName());
this.plugin = plugin;
this.tool = plugin.getTool();
setPopupMenuData(new MenuData(new String[] { NAME }, MENU_GROUP));
setKeyBindingData(KEY_BINDING);
setHelpLocation(new HelpLocation(plugin.getName(), "disassemble"));
}
protected Reqs getReqs(ActionContext context) {
if (plugin.platformService == null) {
return null;
}
if (!(context instanceof DebuggerListingActionContext lac)) {
return null;
}
TraceProgramView view = lac.getProgram();
Trace trace = view.getTrace();
DebuggerCoordinates current = plugin.traceManager == null ? DebuggerCoordinates.NOWHERE
: plugin.traceManager.getCurrentFor(trace);
TraceThread thread = current.getThread();
TraceObject object = current.getObject();
DebuggerPlatformMapper mapper =
plugin.platformService.getMapper(trace, object, view.getSnap());
if (mapper == null) {
return null;
}
return new Reqs(mapper, thread, object, view);
}
@Override
public boolean isAddToPopup(ActionContext context) {
Reqs reqs = getReqs(context);
Reqs reqs = Reqs.fromContext(tool, context);
return reqs != null;
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Reqs reqs = getReqs(context);
Reqs reqs = Reqs.fromContext(tool, context);
if (reqs == null) {
return false;
}
@ -87,7 +57,7 @@ public class CurrentPlatformTraceDisassembleAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
Reqs reqs = getReqs(context);
Reqs reqs = Reqs.fromContext(tool, context);
if (reqs == null) {
return;
}
@ -100,21 +70,12 @@ public class CurrentPlatformTraceDisassembleAction extends DockingAction {
set = selection;
}
else {
set = reqs.view.getAddressFactory()
set = reqs.view()
.getAddressFactory()
.getAddressSet(space.getMinAddress(), space.getMaxAddress());
}
TypedBackgroundCommand<TraceProgramView> cmd =
new TypedBackgroundCommand<>(NAME, true, true, false) {
@Override
public boolean applyToTyped(TraceProgramView view, TaskMonitor monitor) {
DisassemblyResult result = reqs.mapper.disassemble(
reqs.thread, reqs.object, address, set, view.getSnap(), monitor);
if (!result.isSuccess()) {
plugin.getTool().setStatusInfo(result.getErrorMessage(), true);
}
return true;
}
};
cmd.run(plugin.getTool(), reqs.view);
CurrentPlatformTraceDisassembleCommand cmd =
new CurrentPlatformTraceDisassembleCommand(tool, set, reqs, address);
cmd.run(tool, reqs.view());
}
}

View file

@ -0,0 +1,95 @@
/* ###
* 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.disassemble;
import docking.ActionContext;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingActionContext;
import ghidra.app.services.DebuggerPlatformService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.debug.api.platform.DisassemblyResult;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.cmd.TypedBackgroundCommand;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.task.TaskMonitor;
public final class CurrentPlatformTraceDisassembleCommand
extends TypedBackgroundCommand<TraceProgramView> {
public static final String NAME = "Disassemble";
public record Reqs(DebuggerPlatformMapper mapper, TraceThread thread, TraceObject object,
TraceProgramView view) {
public static Reqs fromView(PluginTool tool, TraceProgramView view) {
DebuggerTraceManagerService traceManager =
tool.getService(DebuggerTraceManagerService.class);
DebuggerPlatformService platformService =
tool.getService(DebuggerPlatformService.class);
if (platformService == null) {
return null;
}
Trace trace = view.getTrace();
DebuggerCoordinates current = traceManager == null
? DebuggerCoordinates.NOWHERE
: traceManager.getCurrentFor(trace);
TraceThread thread = current.getThread();
TraceObject object = current.getObject();
DebuggerPlatformMapper mapper =
platformService.getMapper(trace, object, view.getSnap());
if (mapper == null) {
return null;
}
return new Reqs(mapper, thread, object, view);
}
public static Reqs fromContext(PluginTool tool, ActionContext context) {
if (!(context instanceof DebuggerListingActionContext lac)) {
return null;
}
return fromView(tool, lac.getProgram());
}
}
private final PluginTool tool;
private final AddressSetView set;
private final Reqs reqs;
private final Address address;
public CurrentPlatformTraceDisassembleCommand(PluginTool tool, AddressSetView set,
Reqs reqs, Address address) {
super(NAME, true, true, false);
this.tool = tool;
this.set = set;
this.reqs = reqs;
this.address = address;
}
@Override
public boolean applyToTyped(TraceProgramView view, TaskMonitor monitor) {
DisassemblyResult result = reqs.mapper.disassemble(
reqs.thread, reqs.object, address, set, view.getSnap(), monitor);
if (!result.isSuccess()) {
tool.setStatusInfo(result.getErrorMessage(), true);
}
return true;
}
}

View file

@ -27,10 +27,7 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingActionContext;
import ghidra.app.services.DebuggerPlatformService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.AutoService.Wiring;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
@ -40,8 +37,6 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
@PluginInfo(
shortDescription = "Disassemble trace bytes in the debugger",
@ -61,21 +56,6 @@ import ghidra.trace.model.thread.TraceThread;
})
public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionProvider {
protected static class Reqs {
final DebuggerPlatformMapper mapper;
final TraceThread thread;
final TraceObject object;
final TraceProgramView view;
public Reqs(DebuggerPlatformMapper mapper, TraceThread thread, TraceObject object,
TraceProgramView view) {
this.mapper = mapper;
this.thread = thread;
this.object = object;
this.view = view;
}
}
public static RegisterValue deriveAlternativeDefaultContext(Language language,
LanguageID alternative, Address address) {
LanguageService langServ = DefaultLanguageService.getLanguageService();
@ -100,19 +80,11 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
return result;
}
@AutoServiceConsumed
DebuggerTraceManagerService traceManager;
@AutoServiceConsumed
DebuggerPlatformService platformService;
@SuppressWarnings("unused")
private final Wiring autoServiceWiring;
CurrentPlatformTraceDisassembleAction actionDisassemble;
CurrentPlatformTracePatchInstructionAction actionPatchInstruction;
public DebuggerDisassemblerPlugin(PluginTool tool) {
super(tool);
this.autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this);
}
@Override

View file

@ -33,6 +33,7 @@ import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoConfigStateField;
@ -100,10 +101,16 @@ public abstract class DebuggerReadsMemoryTrait {
protected class ForReadsTraceListener extends TraceDomainObjectListener {
public ForReadsTraceListener() {
listenForUntyped(DomainObject.DO_OBJECT_RESTORED, this::objectRestored);
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged);
}
private void objectRestored(DomainObjectChangeRecord rec) {
actionRefreshSelected.updateEnabled(null);
doAutoRead();
}
private void snapshotAdded(TraceSnapshot snapshot) {
actionRefreshSelected.updateEnabled(null);
}

View file

@ -56,7 +56,7 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
AddressSetView alreadyKnown = mm.getAddressesWithState(coordinates.getSnap(), visible,
s -> s == TraceMemoryState.KNOWN);
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {

View file

@ -57,7 +57,7 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
AddressSetView alreadyKnown = mm.getAddressesWithState(coordinates.getSnap(), visible,
s -> s == TraceMemoryState.KNOWN);
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {

View file

@ -48,7 +48,8 @@ import ghidra.app.nav.ListingPanelContainer;
import ghidra.app.plugin.core.clipboard.CodeBrowserClipboardProvider;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.plugin.core.debug.disassemble.CurrentPlatformTraceDisassembleCommand;
import ghidra.app.plugin.core.debug.disassemble.CurrentPlatformTraceDisassembleCommand.Reqs;
import ghidra.app.plugin.core.debug.gui.DebuggerLocationLabel;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
@ -180,10 +181,10 @@ public class DebuggerListingProvider extends CodeViewerProvider {
if (codeViewer.isEmpty()) {
return;
}
Swing.runIfSwingOrRunLater(
() -> codeViewer.get()
.getListingPanel()
.scrollTo(new ProgramLocation(program, selection.getMinAddress())));
ListingPanel listingPanel = codeViewer.get().getListingPanel();
Swing.runIfSwingOrRunLater(() -> {
listingPanel.scrollTo(new ProgramLocation(program, selection.getMinAddress()));
});
}
@Override
@ -1203,8 +1204,10 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
AddressSpace space = start.getAddressSpace();
AddressSet set = new AddressSet(space.getMinAddress(), space.getMaxAddress());
TraceDisassembleCommand dis =
new TraceDisassembleCommand(current.getPlatform(), start, set);
Reqs reqs = Reqs.fromView(tool, view);
CurrentPlatformTraceDisassembleCommand dis =
new CurrentPlatformTraceDisassembleCommand(tool, set, reqs, start);
dis.run(tool, view);
}

View file

@ -210,7 +210,7 @@ public class ObjectTreeModel implements DisplaysModified {
/**
* Our nodes are re-usable. They're cached so that as an item comes and goes, its
* corresponding node can also come and go without being re-instantiated each time.
* Furthermore, it's like to have all the same children as before, too. For now, we'll
* Furthermore, it's likely to have all the same children as before, too. For now, we'll
* just ignore dispose. If there's too many unexpected behaviors resulting from this,
* then perhaps we should just have dispose also remove itself from the node cache.
*/
@ -219,12 +219,25 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
public int compareTo(GTreeNode node) {
return TargetObjectKeyComparator.CHILD.compare(this.getName(), node.getName());
if (!(node instanceof AbstractNode that)) {
return -1;
}
int c;
c = TargetObjectKeyComparator.CHILD.compare(this.getValue().getEntryKey(),
that.getValue().getEntryKey());
if (c != 0) {
return c;
}
c = Lifespan.DOMAIN.compare(this.getValue().getMinSnap(), that.getValue().getMinSnap());
if (c != 0) {
return c;
}
return 0;
}
@Override
public String getName() {
return getValue().getEntryKey() + "@" + getValue().getMinSnap();
return getValue().getEntryKey() + "@" + System.identityHashCode(getValue());
}
@Override
@ -340,14 +353,14 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
public String getDisplayText() {
if (trace == null) {
return "<html><em>No trace is active</em>";
return "<html><em>No&nbsp;trace&nbsp;is&nbsp;active</em>";
}
TraceObject root = trace.getObjectManager().getRootObject();
if (root == null) {
return "<html><em>Trace has no model</em>";
return "<html><em>Trace&nbsp;has&nbsp;no&nbsp;model</em>";
}
return "<html>" +
HTMLUtilities.escapeHTML(display.getObjectDisplay(root.getCanonicalParent(0)));
return "<html>" + HTMLUtilities
.escapeHTML(display.getObjectDisplay(root.getCanonicalParent(0)), true);
}
@Override
@ -424,7 +437,8 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
public String getDisplayText() {
String html = HTMLUtilities.escapeHTML(
value.getEntryKey() + ": " + display.getPrimitiveValueDisplay(value.getValue()));
value.getEntryKey() + ": " + display.getPrimitiveValueDisplay(value.getValue()),
true);
return "<html>" + html;
}
@ -471,8 +485,8 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
public String getDisplayText() {
return "<html>" + HTMLUtilities.escapeHTML(value.getEntryKey()) + ": <em>" +
HTMLUtilities.escapeHTML(display.getObjectLinkDisplay(value)) + "</em>";
return "<html>" + HTMLUtilities.escapeHTML(value.getEntryKey(), true) + ":&nbsp;<em>" +
HTMLUtilities.escapeHTML(display.getObjectLinkDisplay(value), true) + "</em>";
}
@Override
@ -513,7 +527,7 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
public String getDisplayText() {
return "<html>" + HTMLUtilities.escapeHTML(display.getObjectDisplay(value));
return "<html>" + HTMLUtilities.escapeHTML(display.getObjectDisplay(value), true);
}
@Override

View file

@ -262,6 +262,20 @@ public class ObjectsTreePanel extends JPanel {
}
//tree.filterChanged();
// Repaint for bold current path is already going to happen
// Repaint is not enough, as node sizes may change
for (TraceObjectKeyPath path = current.getPath(); path != null; path = path.parent()) {
AbstractNode node = treeModel.getNode(path);
if (node != null) {
node.fireNodeChanged();
}
}
for (TraceObjectKeyPath path = previous.getPath(); path != null; path = path.parent()) {
AbstractNode node = treeModel.getNode(path);
if (node != null) {
node.fireNodeChanged();
}
}
}
}