mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4144: Many fixes, esp., for dbgeng Trace RMI.
This commit is contained in:
parent
1281fb979b
commit
a6549947ab
30 changed files with 1526 additions and 725 deletions
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 trace is active</em>";
|
||||
}
|
||||
TraceObject root = trace.getObjectManager().getRootObject();
|
||||
if (root == null) {
|
||||
return "<html><em>Trace has no model</em>";
|
||||
return "<html><em>Trace has no 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) + ": <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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue