diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java new file mode 100644 index 0000000000..5393c02e0d --- /dev/null +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java @@ -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.debug.api.modules; + +import java.net.URL; +import java.util.*; + +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; +import ghidra.trace.model.*; +import ghidra.trace.model.program.TraceProgramView; + +public class IdentityDebuggerAddressTranslator implements DebuggerAddressTranslator { + private final Trace trace; + private final Program program; + + public IdentityDebuggerAddressTranslator(Trace trace, Program program) { + this.trace = trace; + this.program = program; + } + + @Override + public Set getOpenMappedProgramsAtSnap(Trace trace, long snap) { + return Set.of(program); + } + + @Override + public ProgramLocation getOpenMappedLocation(TraceLocation loc) { + return new ProgramLocation(program, loc.getAddress()); + } + + @Override + public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) { + return new ProgramLocation(program, loc.getAddress()); + } + + @Override + public Set getOpenMappedLocations(ProgramLocation loc) { + return Set.of(new DefaultTraceLocation(trace, null, Lifespan.ALL, loc.getAddress())); + } + + @Override + public TraceLocation getOpenMappedLocation(Trace trace, ProgramLocation loc, long snap) { + if (trace != this.trace) { + return null; + } + return new DefaultTraceLocation(trace, null, Lifespan.ALL, loc.getAddress()); + } + + @Override + public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view, + ProgramLocation loc) { + if (view.getTrace() != this.trace) { + return null; + } + return new ProgramLocation(view, loc.getAddress()); + } + + @Override + public Map> getOpenMappedViews(Trace trace, + AddressSetView set, long snap) { + return Map.ofEntries( + Map.entry(program, set.stream().map(r -> new MappedAddressRange(r, r)).toList())); + } + + @Override + public Map> getOpenMappedViews(Program program, + AddressSetView set) { + if (program != this.program) { + return Map.of(); + } + return Map.ofEntries(Map.entry(new DefaultTraceSpan(trace, Lifespan.ALL), + set.stream().map(r -> new MappedAddressRange(r, r)).toList())); + } + + @Override + public Set getMappedProgramUrlsInView(Trace trace, AddressSetView set, long snap) { + // This is not necessary here + return Set.of(); + } +} diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java index 515a7306f6..ecc170c747 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java @@ -239,6 +239,10 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter createActions(); } + public void cleanConnectionNode(TraceRmiConnectionNode node) { + rootNode.cleanConnectionNode(node); + } + private void buildMainPanel() { mainPanel = new JPanel(new BorderLayout()); diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java index 5f6a5f61e3..e98c2d0f0b 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java @@ -42,7 +42,13 @@ public class TraceRmiConnectionNode extends AbstractTraceRmiManagerNode { @Override public String getDisplayText() { - return connection.getDescription() + " at " + connection.getRemoteAddress(); + try { + return connection.getDescription() + " at " + connection.getRemoteAddress(); + } + catch (Exception e) { + provider.cleanConnectionNode(this); + return "Error: " + e; + } } @Override @@ -52,8 +58,13 @@ public class TraceRmiConnectionNode extends AbstractTraceRmiManagerNode { @Override public String getToolTip() { - return "Trace RMI Connection to " + connection.getDescription() + " at " + - connection.getRemoteAddress(); + try { + return "Trace RMI Connection to " + connection.getDescription() + " at " + + connection.getRemoteAddress(); + } + catch (Exception e) { + return "Error: " + e; + } } @Override diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java index db3539667e..97045868eb 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java @@ -25,6 +25,7 @@ import ghidra.debug.api.target.Target; import ghidra.debug.api.target.TargetPublicationListener; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracermi.*; +import ghidra.lifecycle.Internal; import ghidra.util.Msg; import ghidra.util.Swing; @@ -83,6 +84,15 @@ public class TraceRmiServiceNode extends AbstractTraceRmiManagerNode removeNode(node); } + @Internal + public void cleanConnectionNode(TraceRmiConnectionNode node) { + TraceRmiConnection connection = node.getConnection(); + synchronized (connectionNodes) { + connectionNodes.remove(connection); + } + removeNode(node); + } + private TraceRmiAcceptorNode newAcceptorNode(TraceRmiAcceptor acceptor) { return new TraceRmiAcceptorNode(provider, acceptor); } diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest index 96169f2e80..c25baeb7ac 100644 --- a/Ghidra/Debug/Debugger/certification.manifest +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -26,6 +26,7 @@ src/main/help/help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.ht src/main/help/help/topics/DebuggerCopyActionsPlugin/images/DebuggerCopyIntoProgramDialog.png||GHIDRA||||END| src/main/help/help/topics/DebuggerDisassemblerPlugin/DebuggerDisassemblerPlugin.html||GHIDRA||||END| src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png||GHIDRA||||END| src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END| src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END| src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java b/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java new file mode 100644 index 0000000000..70d13dab19 --- /dev/null +++ b/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java @@ -0,0 +1,38 @@ +/* ### + * 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. + */ +import ghidra.app.script.GhidraScript; +import ghidra.debug.flatapi.FlatDebuggerAPI; +import ghidra.program.model.address.AddressSetView; +import ghidra.trace.model.Trace; +import ghidra.trace.model.memory.TraceMemoryOperations.StatePredicate; + +//List all address ranges that are "known" at the current snapshot +//This script must be run from the Debugger tool, or another tool with the required plugins. +//@category Debugger +//@keybinding +//@menupath +//@toolbar +public class ListAllKnownMemoryScript extends GhidraScript implements FlatDebuggerAPI { + @Override + protected void run() throws Exception { + Trace trace = requireCurrentTrace(); + long snap = getCurrentSnap(); + + AddressSetView addresses = + trace.getMemoryManager().getAddressesWithState(snap, StatePredicate.IS_KNOWN); + show("Known Memory at %d".formatted(snap), addresses); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html index e8ea74094e..8651258391 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html @@ -48,6 +48,12 @@ Optionally, other registers can be initialized via the UI or a script. The new thread is activated so that control actions will affect it by default.

+

Emulate Function

+ +

This action is available whenever the cursor is within a function in the Static Listing. It + displays a dialog for harnessing and emulating the + current function.

+

Configure Emulator

This action is always available. It lists emulators available for configuration. Selecting @@ -62,5 +68,188 @@ emulated breakpoint, or when you patch the trace database. If you do not invalidate the cache, the effects of your change may not appear, since the trace manager may recall a cached snapshot instead of actually emulating.

+ +

Emulate Function Dialog

+ +

This dialog provides a means of harnessing and emulating a target function. Depending on the + size and scope of the function and the configuration of the emulator, the emulation may or may + not complete. See further below for options and actions in the dialog.

+ +
+ +
+ +

The dialog supports the addition of vararg arguments, custom initializations, and heap + initializations. All of these inputs are handled in the top table. The "Run" button will + perform the emulation and capture the outputs into the bottom table. Analogous to the inputs, + the Outputs table can be configured with custom variables and pointer dereferences. The + emulation session can optionally be captured into a trace, which is opened automatically in + your tool, usually the Debugger or Emulator. Multiple sessions can be run from the same dialog, + allowing trial-and-error runs while also iterating on the target function's markup.

+ +

Option: Ending Return Address

+ +

In order to detect the proper completion of the target function, the harness places a + breakpoint at a "sentinel" address. That sentinel address must be placed where the function + expects the return address. This option allows specification of the sentinel address. + Generally, anything easily recognizable that does not conflict with a real address should work. + NOTE: You cannot specify the location of the return address. This is determined + automatically through static analysis, but you can place the sentinel address through a custom + input, if the analysis fails or is incorrect.

+ +

Option: Snapshot Period

+ +

By default, the harness captures a snapshot of the emulator's state into a trace after every + instruction step. This feature can be disabled entirely by setting this option to 0 (zero), in + which case no trace is captured or opened in your tool. Only the outputs are captured. + Otherwise, the period indicates how many instructions are executed between consecutive + snapshots. If only a starting and ending snapshot are desired, set this to a large number. The + initial and final snapshots are always captured, no matter the (non-zero) period.

+ +

Option: Next Allocation

+ +

Set this to the location of the heap, if applicable. Any address not already used by the + program with plenty of space above should suffice. It may help if the upper digits of the + address are easily recognizable. Several of the below actions on the Inputs table will + automatically initialize pointers to the start of an "allocated block". This field is then + automatically incremented by the size of that allocation. You may adjust this field at any time + to "undo" allocations, establish a second heap, etc.

+ +

Inputs table

+ +

The Inputs table lists of all the configured inputs. The types and values of the inputs are + specified in this table. NOTE: No actual initializations are performed until right + before emulation. If two inputs happen to be at the same address, that conflict will not be + discovered until clicking "Run." No two inputs can have the same name. Adding an input with a + duplicate name replaces the existing one.

+ +

The default inputs are derived from the target function's parameters. Adjusting the type of + an input does not edit or update the target function in the program database. The + columns are:

+ +
    +
  • Name — Provides an informal name for the input. For parameters, this is the name of + the parameter. For custom inputs, this is "Custom 1" counting up in the order added. For + vararg arguments, this is, e.g., "Vararg 3" couting up in the order added, starting after the + number of fixed arguments. For allocated inputs, this is a C-like expression derived from the + pointer to it and its type. This field cannot be edited.
  • + +
  • Storage — Provides a formal Sleigh expression describing the location of the input. + For custom inputs, this is specified by the user when added. For parameter and vararg inputs, + this is derived from the target function's signature, custom storage, and/or calling + convention. For pointer allocations, this is derived from the pointer to it and its type. + This field cannot be edited.
  • + +
  • Value — Controls the raw initial value of the input variable. This can be specified + as a positive integer, e.g., 0x1234, so long as it fits in the storage, or as a + byte sequence, e.g., { 34 12 }, so long as its length matches that of the + storage.
  • + +
  • Type — Controls the type of the input variable. This has no direct impact on the + emulation nor on the program database (except to resolve the type), but permits the initial + value to be specified using a representation suitable to that type.
  • + +
  • Repr — Controls the initial value of the input variable as represented by the + chosen type. NOTE: some types, particularly structure types, do not have a string + representation, and so this box may appear empty. This field can be edited only if the chosen + type implements an encoder.
    +
    +
  • +
+ +

Action: Remove Input

+ +

This action removes the selected input(s) from the table. A removed input is no longer + initialized. NOTE: Inputs can have dependencies, e.g., an allocated block depends on its + pointer, because the storage location of that block is computed from the value of the pointer. + Removing the pointer will leave it uninitialized, likely resulting in the block's location + being 0 (zero). This will generally still work, but is usually not desired. Re-adding the + relevant pointer can fix this. So long as the old and new names match, the dependency + relationship is restored, too.

+ +

Action: Clear Inputs

+ +

For when things have gone so far south you need to start over. Typically this is followed by + a Refresh, too.

+ +

Action: Add Custom Input

+ +

This action prompts the user for a Sleigh expression of a variable's storage and adds it as + a Custom input. Typically, this is a register, e.g., RAX, or a fixed address and + size, e.g., *:8 0x00401234.

+ +

Action: Add Vararg Input

+ +

This action is only enabled if the target function has variable arguments. It prompts the + user for a type, derives the storage from the target function's signature and calling convetion + and adds it as a Vararg input. NOTE: Adjustments to this row's type will not + automatically update the argument's storage. Consider deleting and re-adding an argument along + with all its subsequent arguments if consistent storage is desired.

+ +

Action: Allocate and Add Pointer Inputs

+ +

This action is available when the selected input has a pointer type. (Multiple selections + are supported.) The size of the pointed-to type is calculated; the pointer's value is + initialized to the Next Allocation, which is then incremented by the + size of the pointed-to type. A new input row is then added and selected, describing the + variable pointed to by the formerly-selected input. Under normal operation, this allocates a + single instance of the pointed-to type. To instead allocate an array of the pointed-to type, + hold <Shift> when clicking the button. If the pointed-to type is a composite + (struct or union), a separate row is generated for each field. For the array case, yes, this + results in an n-by-m set of new rows. If the pointed-to type is a string (or + char), hold <Ctrl> when clicking the button. This will prompt the + user for string data type settings and then for the initial string value. It encodes it and + allocates sufficient space for the encoding. NOTE: So long as edits to the Repr column + result in encodings of smaller or equal size, no re-allocation is necessary. The table will + still permit the edit, but it may result in conflicts. Either update the pointer values + manually — this will automatically adjust the storage location of the pointed-to variable + — or delete and re-allocate the string input.

+ +

Action: Refresh Inputs

+ +

This re-adds (likely replacing) all the inputs derived from the function's parameters. This + is especially useful when trying to derive a function's signature by trial and error. Clicking + this button after editing a function's signature will update the Parameter inputs accordingly. + NOTE: This will not remove any Parameter inputs.

+ +

Action: Type Settings

+ +

This action is available in the right-click context menu on rows with an assigned type. It + controls the settings on the chosen datatype, e.g., radix for integer types, or encodings for + string types.

+ +

Outputs table

+ +

The Outputs table is analogous to the Inputs table, except that it displays values captured + from the last successful emulation. The default output is derived from the target function's + return type. It has the same columns and similar actions with the following exceptions:

+ +
    +
  • There is no Add Vararg Output action.
  • + +
  • Only the Type column can be edited.
  • + +
  • Adding Pointed-to outputs does not "allocate," since the pointer is expected to be set by + the target function.
  • + +
  • When adding pointed-to strings, the prompt is for the number of bytes to capture. The + string decoder should only use what it needs, so just pick a reasonable maximum expected + length. Adjust the type and settings using other actions afterward.
  • +
+ +

Probe Outputs

+ +

The Outputs table also supports "Probe" outputs. These are outputs automatically generated + during the emulation of the target function via the emu_probe userop. These are + configured using the Set + Injection action on a breakpoint. The breakpoint need only be enabled. Even if + "ineffective,"" it will be installed by the harness. The emu_probe userop accepts + exactly one argument. All Probe outputs are cleared at the start of emulation. Each time the + userop is executed, its argument is captured with its location and value at the time of + invocation. These are then added as Probe outputs, counting 1-up by invocation. If the + userop is invoked within a loop of the target code, each run-time invocation gets a distinct + row. Types can be applied as usual. NOTE: If the userop argument is anything other than + a simple varnode, the location will be "$Unique."

diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png new file mode 100644 index 0000000000..1de2c1998a Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png differ diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html index 842597dcd8..41ab8e0c50 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html @@ -191,7 +191,7 @@ debugger. This failure can be overcome by enabling Force Full View.

-

Some examples (in Hex mode):

+

Some examples (in Hex mode):

  • 00401234 — A constant address
  • diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/BasicAutoReadMemorySpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/BasicAutoReadMemorySpec.java index 4deced1017..5e424a962f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/BasicAutoReadMemorySpec.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/BasicAutoReadMemorySpec.java @@ -172,7 +172,7 @@ public enum BasicAutoReadMemorySpec implements AutoReadMemorySpec { AddressSet toRead = new AddressSet(quantize(12, visible)); for (Lifespan span : coordinates.getView().getViewport().getOrderedSpans()) { AddressSetView alreadyKnown = - mm.getAddressesWithState(span.lmin(), visible, StatePredicate.IS_KNOWN); + mm.getAddressesWithState(span, toRead, StatePredicate.IS_KNOWN); toRead.delete(alreadyKnown); if (span.lmax() != span.lmin() || toRead.isEmpty()) { break; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java index 426f75fac7..ca4d04dfc9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java @@ -1339,38 +1339,16 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter return null; } - private boolean isAllInvolvedTracesUsingEmulatedBreakpoints(ActionContext ctx) { - if (controlService == null) { - return false; - } - Set traces = new HashSet<>(); + private boolean isAtLeastOneBreakpoint(ActionContext ctx) { Collection breakpoints = getLogicalBreakpoints(ctx); - if (breakpoints != null) { - if (breakpoints.isEmpty()) { - return false; - } - for (LogicalBreakpoint lb : breakpoints) { - traces.addAll(lb.getParticipatingTraces()); - } + if (breakpoints != null && !breakpoints.isEmpty()) { + return true; } - else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) { - Collection locations = locCtx.getLocations(); - if (locations.isEmpty()) { - return false; - } - for (TraceBreakpointLocation loc : locations) { - traces.add(loc.getTrace()); - } + if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx && + !locCtx.getLocations().isEmpty()) { + return true; } - else { - return false; - } - for (Trace trace : traces) { - if (!controlService.getCurrentMode(trace).useEmulatedBreakpoints()) { - return false; - } - } - return true; + return false; } private static final Set EXECUTE_KINDS = @@ -1400,11 +1378,11 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter } private boolean isPopupSetCondition(ActionContext ctx) { - return isAllInvolvedTracesUsingEmulatedBreakpoints(ctx) && isAllBreakpointsExecution(ctx); + return isAtLeastOneBreakpoint(ctx) && isAllBreakpointsExecution(ctx); } private boolean isPopupSetInjection(ActionContext ctx) { - return isAllInvolvedTracesUsingEmulatedBreakpoints(ctx) && isAllBreakpointsExecution(ctx); + return isAtLeastOneBreakpoint(ctx) && isAllBreakpointsExecution(ctx); } private String deriveCurrentSleigh(ActionContext ctx) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/AddressInputVerifier.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/AddressInputVerifier.java new file mode 100644 index 0000000000..2448ced76a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/AddressInputVerifier.java @@ -0,0 +1,58 @@ +/* ### + * 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.emulation; + +import javax.swing.*; + +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFactory; + +public abstract class AddressInputVerifier extends InputVerifier { + private AddressFactory factory; + + public AddressInputVerifier(AddressFactory factory) { + this.factory = factory; + } + + @Override + public boolean verify(JComponent input) { + if (!(input instanceof JTextField text)) { + throw new IllegalArgumentException( + "Only JTextField is supported. Got %s".formatted(input.getClass())); + } + String str = text.getText(); + try { + Address address = factory.getAddress(str); + if (address == null) { + reject("Invalid address: %s".formatted(str)); + return false; + } + if (!verifyAddress(address)) { + reject("Invalid address: %s".formatted(str)); + return false; + } + return true; + } + catch (Exception e) { + reject("%s while parsing '%s'".formatted(e.getMessage(), str)); + return false; + } + } + + protected abstract boolean verifyAddress(Address address); + + protected abstract void reject(String message); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialog.java new file mode 100644 index 0000000000..d7decc1230 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialog.java @@ -0,0 +1,1148 @@ +/* ### + * 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.emulation; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.stream.Collectors; + +import javax.swing.*; + +import db.Transaction; +import docking.*; +import docking.action.ActionContextProvider; +import docking.action.DockingActionIf; +import docking.action.builder.ActionBuilder; +import docking.menu.DialogToolbarButton; +import docking.widgets.table.*; +import ghidra.app.plugin.core.data.AbstractSettingsDialog; +import ghidra.app.plugin.core.debug.gui.DebuggerResources; +import ghidra.app.plugin.core.debug.gui.emulation.FunctionEmulationHarness.*; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.app.services.*; +import ghidra.debug.api.control.ControlMode; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.docking.settings.*; +import ghidra.framework.model.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.pcode.utils.Utils; +import ghidra.program.database.data.DataTypeUtilities; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.ByteMemBufferImpl; +import ghidra.program.model.pcode.Varnode; +import ghidra.program.util.FunctionChangeRecord; +import ghidra.program.util.ProgramEvent; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.table.GhidraTableFilterPanel; +import ghidra.util.task.Task; +import ghidra.util.task.TaskMonitor; + +public class DebuggerEmulateFunctionDialog extends DialogComponentProvider { + static final int GAP = 5; + static final String FMT_VA = "Vararg %d"; + static final String FMT_CUSTOM = "Custom %d"; + static final String PREFIX_PROBE = "Probe "; + static final String FMT_PROBE = PREFIX_PROBE + "%d"; + + protected static class VarActionContext extends DefaultActionContext { + private final List selection; + + public VarActionContext(List selection) { + this.selection = selection; + } + + public List getSelection() { + return selection; + } + } + + protected static class InputActionContext extends VarActionContext { + public InputActionContext(List selection) { + super(selection); + } + } + + protected static class OutputActionContext extends VarActionContext { + public OutputActionContext(List selection) { + super(selection); + } + } + + protected abstract static class ActionContainer implements ActionContextProvider { + protected final JComponent component; + private final List actions = new ArrayList<>(); + private boolean valid; + + public ActionContainer(JComponent container) { + this.component = container; + } + + protected abstract void addGroupSeparator(); + + public void addAction(DockingActionIf action) { + if (actions.add(action)) { + valid = false; + } + } + + private void revalidate() { + if (valid) { + return; + } + component.removeAll(); + + Map> byGroup = actions.stream() + .collect(Collectors.groupingBy(a -> a.getToolBarData().getToolBarGroup())); + boolean first = true; + for (Map.Entry> ent : byGroup.entrySet()) { + List actions = ent.getValue(); + actions.sort(Comparator.comparing(a -> a.getToolBarData().getToolBarSubGroup())); + if (!first) { + addGroupSeparator(); + } + for (DockingActionIf action : actions) { + component.add(new DialogToolbarButton(action, this)); + } + first = false; + } + valid = true; + } + + public void contextChanged() { + revalidate(); + ActionContext ctx = getActionContext(null); + for (DockingActionIf action : actions) { + action.setEnabled(action.isEnabledForContext(ctx)); + } + } + + /* Testing */ DockingActionIf byName(String name) { + return actions.stream().filter(a -> name.equals(a.getName())).findFirst().orElseThrow(); + } + } + + protected abstract static class HorizontalBoxActionContainer extends ActionContainer { + public HorizontalBoxActionContainer() { + super(Box.createHorizontalBox()); + } + + @Override + protected void addGroupSeparator() { + component.add(Box.createHorizontalStrut(5)); + component.add(DockingUtils.createToolbarSeparator()); + component.add(Box.createHorizontalStrut(5)); + } + } + + protected final PluginTool tool; + protected final Function function; + protected final SleighLanguage language; + + protected JTextField textSentinel; + protected JTextField textSnapshotPeriod; + protected JTextField textNextAlloc; + protected Address sentinel; + protected long snapshotPeriod; + protected Address nextAlloc; + + protected final InputsTableModel inputsTableModel; + protected GTable inputsTable; + protected GhidraTableFilterPanel inputsFilterPanel; + + protected final OutputsTableModel outputsTableModel; + protected GTable outputsTable; + protected GhidraTableFilterPanel outputsFilterPanel; + + protected final ActionContainer inputActions = new HorizontalBoxActionContainer() { + @Override + public ActionContext getActionContext(MouseEvent e) { + return new InputActionContext(inputsFilterPanel.getSelectedItems()); + } + }; + protected final ActionContainer outputActions = new HorizontalBoxActionContainer() { + @Override + public ActionContext getActionContext(MouseEvent e) { + return new OutputActionContext(outputsFilterPanel.getSelectedItems()); + } + }; + + protected final DomainObjectListener listenerForFunction = new DomainObjectListenerBuilder(this) + .any(DomainObjectEvent.RESTORED) + .terminate(inputActions::contextChanged) + .with(FunctionChangeRecord.class) + .each(ProgramEvent.FUNCTION_CHANGED) + .call(this::functionChanged) + .build(); + + public DebuggerEmulateFunctionDialog(PluginTool tool, Function function) { + super("Emulate %s of %s".formatted(function.getName(), function.getProgram().getName()), + false, true, true, true); + if (!(function.getProgram().getLanguage() instanceof SleighLanguage language)) { + throw new IllegalArgumentException("Sleigh language required"); + } + this.tool = tool; + this.function = function; + this.language = language; + + inputsTableModel = new InputsTableModel(tool, this); + outputsTableModel = new OutputsTableModel(tool, this); + + inputsTableModel.addTableModelListener(evt -> { + inputActions.contextChanged(); + }); + outputsTableModel.addTableModelListener(evt -> { + outputActions.contextChanged(); + }); + + sentinel = language.getDefaultSpace().getAddress(0xdeadbeef); + snapshotPeriod = 1; + nextAlloc = language.getDefaultDataSpace().getAddress(0x7beef000); + + populateComponents(); + createActions(); + + initializeVarTables(); + inputActions.contextChanged(); + outputActions.contextChanged(); + + function.getProgram().addListener(listenerForFunction); + + } + + protected void populateComponents() { + JPanel panel = new JPanel(new BorderLayout()); + + { + JPanel opts = new JPanel(); + opts.setLayout(new BoxLayout(opts, BoxLayout.Y_AXIS)); + opts.getAccessibleContext().setAccessibleName("Options"); + { + Box box = Box.createHorizontalBox(); + box.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); + JLabel label = new JLabel("Ending Return Address:"); + label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, GAP)); + box.add(label); + textSentinel = new JTextField("deadbeef"); + box.add(textSentinel); + opts.add(box); + } + { + Box box = Box.createHorizontalBox(); + box.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); + JLabel label = new JLabel("Snapshot Period (0 for none):"); + label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, GAP)); + box.add(label); + textSnapshotPeriod = new JTextField(Long.toString(snapshotPeriod)); + box.add(textSnapshotPeriod); + opts.add(box); + } + { + Box box = Box.createHorizontalBox(); + box.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); + JLabel label = new JLabel("Next Allocation:"); + label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, GAP)); + box.add(label); + textNextAlloc = new JTextField(nextAlloc.toString()); + box.add(textNextAlloc); + opts.add(box); + } + panel.add(opts, BorderLayout.NORTH); + } + { + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + { + JPanel inputsTablePanel = new JPanel(new BorderLayout()); + JPanel topBar = new JPanel(new BorderLayout()); + topBar.add(new JLabel("Inputs:"), BorderLayout.WEST); + topBar.add(inputActions.component, BorderLayout.EAST); + topBar.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.NORTH); + inputsTablePanel.add(topBar, BorderLayout.NORTH); + + inputsTable = new GTable(inputsTableModel); + inputsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + inputsTablePanel.add(new JScrollPane(inputsTable)); + inputsFilterPanel = new GhidraTableFilterPanel<>(inputsTable, inputsTableModel); + inputsTablePanel.add(inputsFilterPanel, BorderLayout.SOUTH); + inputsTablePanel.getAccessibleContext().setAccessibleName("Inputs Filters"); + splitPane.setTopComponent(inputsTablePanel); + } + { + JPanel outputsTablePanel = new JPanel(new BorderLayout()); + JPanel topBar = new JPanel(new BorderLayout()); + topBar.add(new JLabel("Outputs:"), BorderLayout.WEST); + topBar.add(outputActions.component, BorderLayout.EAST); + topBar.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.NORTH); + outputsTablePanel.add(topBar, BorderLayout.NORTH); + + outputsTable = new GTable(outputsTableModel); + outputsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + outputsTablePanel.add(new JScrollPane(outputsTable)); + outputsFilterPanel = new GhidraTableFilterPanel<>(outputsTable, outputsTableModel); + outputsTablePanel.add(outputsFilterPanel, BorderLayout.SOUTH); + outputsTablePanel.getAccessibleContext().setAccessibleName("Outputs Filters"); + splitPane.setBottomComponent(outputsTablePanel); + } + panel.add(splitPane); + } + + panel.getAccessibleContext().setAccessibleName("Emulate " + function.getName()); + addWorkPanel(panel); + + addOKButton(); + okButton.setText("Run"); + addDismissButton(); + + inputsTable.getSelectionModel().addListSelectionListener(evt -> { + inputActions.contextChanged(); + }); + outputsTable.getSelectionModel().addListSelectionListener(evt -> { + outputActions.contextChanged(); + }); + + textSentinel.setInputVerifier(new AddressInputVerifier(language.getAddressFactory()) { + @Override + protected boolean verifyAddress(Address address) { + sentinel = address; + return true; + } + + @Override + protected void reject(String message) { + setStatusText(message, MessageType.ERROR); + } + }); + textSnapshotPeriod.setInputVerifier(new LongInputVerifier() { + @Override + protected boolean verifyLong(long value) { + snapshotPeriod = value; + return true; + } + + @Override + protected void reject(String message) { + setStatusText(message, MessageType.ERROR); + } + }); + textNextAlloc.setInputVerifier(new AddressInputVerifier(language.getAddressFactory()) { + @Override + protected boolean verifyAddress(Address address) { + nextAlloc = address; + return true; + } + + @Override + protected void reject(String message) { + setStatusText(message, MessageType.ERROR); + } + }); + } + + @Override + protected void dialogClosed() { + function.getProgram().removeListener(listenerForFunction); + super.dialogClosed(); + } + + protected void createActions() { + inputActions.addAction(new ActionBuilder("Add Custom Input", getTitle()) + .toolBarIcon(DebuggerResources.ICON_ADD) + .toolBarGroup("Add", "1") + .onAction(this::addCustomInputActivated) + .build()); + inputActions.addAction(new ActionBuilder("Add Vararg Input", getTitle()) + // TODO: ICON + .toolBarIcon(DebuggerResources.ICON_OBJECT_POPULATED) + .toolBarGroup("Add", "2") + .enabledWhen(this::addVarargEnabled) + .onAction(this::addVarargActivated) + .build()); + inputActions.addAction(new ActionBuilder("Allocate and Add Pointer Inputs", getTitle()) + // TODO: ICON + .toolBarIcon(DebuggerResources.ICON_REGIONS) + .toolBarGroup("Add", "3") + .description(""" + Allocate and add input variables for pointed-to types. \ + Hold SHIFT for arrays. Hold CTRL for strings.""") + .withContext(InputActionContext.class) + .enabledWhen(this::addPointerVarsEnabled) + .onAction(this::addPointerInputsActivated) + .build()); + inputActions.addAction(new ActionBuilder("Remove Input", getTitle()) + .toolBarIcon(DebuggerResources.ICON_DELETE) + .toolBarGroup("Delete", "1") + .withContext(InputActionContext.class) + .enabledWhen(this::removeInputEnabled) + .onAction(this::removeInputActivated) + .build()); + inputActions.addAction(new ActionBuilder("Clear Inputs", getTitle()) + .toolBarIcon(DebuggerResources.ICON_CLEAR) + .toolBarGroup("Delete", "2") + .onAction(this::clearInputsActivated) + .build()); + inputActions.addAction(new ActionBuilder("Refresh Parameter Inputs", getTitle()) + .toolBarIcon(DebuggerResources.ICON_REFRESH) + .toolBarGroup("Util") + .onAction(this::refreshInputsActivated) + .build()); + + outputActions.addAction(new ActionBuilder("Add Custom Output", getTitle()) + .toolBarIcon(DebuggerResources.ICON_ADD) + .toolBarGroup("Add", "1") + .onAction(this::addOutputActivated) + .build()); + outputActions.addAction(new ActionBuilder("Add Pointer Outputs", getTitle()) + // TODO: ICON + .toolBarIcon(DebuggerResources.ICON_REGIONS) + .toolBarGroup("Add", "2") + .description(""" + Add output variables for pointed-to types. \ + Hold SHIFT for arrays. Hold CTRL for strings.""") + .withContext(OutputActionContext.class) + .enabledWhen(this::addPointerVarsEnabled) + .onAction(this::addPointerOutputsActivated) + .build()); + outputActions.addAction(new ActionBuilder("Remove Output", getTitle()) + .toolBarIcon(DebuggerResources.ICON_DELETE) + .toolBarGroup("Delete", "1") + .withContext(OutputActionContext.class) + .enabledWhen(this::removeOutputEnabled) + .onAction(this::removeOutputActivated) + .build()); + outputActions.addAction(new ActionBuilder("Clear Outputs", getTitle()) + .toolBarIcon(DebuggerResources.ICON_CLEAR) + .toolBarGroup("Delete", "2") + .onAction(this::clearOutputsActivated) + .build()); + outputActions.addAction(new ActionBuilder("Refresh Return Outputs", getTitle()) + .toolBarIcon(DebuggerResources.ICON_REFRESH) + .toolBarGroup("Util") + .onAction(this::refreshOutputsActivated) + .build()); + + new ActionBuilder("Type Settings", getTitle()) + .popupMenuPath("Type Settings") + .withContext(VarActionContext.class) + .enabledWhen(this::typeSettingsEnabled) + .onAction(this::typeSettingsActivated) + .buildAndInstallLocal(this); + } + + private void functionChanged(FunctionChangeRecord rec) { + if (rec.getFunction() == function) { + inputActions.contextChanged(); + } + } + + Optional findRow(RowObjectTableModel model, String name) { + return model.getModelData().stream().filter(v -> name.equals(v.getName())).findAny(); + } + + private DataType findParameterType(int i) { + Parameter parameter = function.getParameter(i); + if (parameter == null) { + throw new IllegalArgumentException("No such parameter %d".formatted(i)); + } + Optional row = findRow(inputsTableModel, parameter.getName()); + if (row.isPresent() && row.get().getType() != null) { + return row.get().getType(); + } + return parameter.getDataType(); + } + + private Optional findVarargType(int i) { + return findRow(inputsTableModel, FMT_VA.formatted(i)).map(input -> input.getType()); + } + + private boolean conflictsInputName(RowObjectTableModel model, String name) { + return findRow(model, name).isPresent(); + } + + private String chooseCustomName(RowObjectTableModel model) { + for (int i = 1;; i++) { + String name = FMT_CUSTOM.formatted(i); + if (!conflictsInputName(model, name)) { + return name; + } + } + } + + private byte[] promptString(String title, String message, DataType type, Settings settings) { + while (true) { + Object response = JOptionPane.showInputDialog(rootPanel, message, title, + JOptionPane.QUESTION_MESSAGE, DebuggerResources.ICON_REGIONS, null, null); + if (response == null) { + return null; + } + try { + String str = response.toString(); + return type.encodeValue(str, + new ByteMemBufferImpl(language.getDefaultDataSpace().getAddress(0), + new byte[] {}, language.isBigEndian()), + settings, -1); + } + catch (Exception e) { + setStatusText(e.getMessage(), MessageType.ERROR); + } + } + } + + private int promptIntGt1(String title, String message) { + while (true) { + Object response = JOptionPane.showInputDialog(rootPanel, message, title, + JOptionPane.QUESTION_MESSAGE, DebuggerResources.ICON_REGIONS, null, null); + clearStatusText(); + if (response == null) { + return -1; + } + try { + int count = Integer.parseInt(response.toString()); + if (count >= 1) { + return count; + } + setStatusText("Must be 1 or greater", MessageType.ERROR); + } + catch (Exception e) { + setStatusText(e.getMessage(), MessageType.ERROR); + } + } + } + + private VarStorage promptStorage(String title) { + while (true) { + Object expr = JOptionPane.showInputDialog(rootPanel, "Expression", title, + JOptionPane.QUESTION_MESSAGE, DebuggerResources.ICON_ADD, null, null); + clearStatusText(); + if (expr == null) { + return null; + } + try { + return VarStorage.fromExpression(language, expr.toString()); + } + catch (Exception e) { + setStatusText(e.getMessage(), MessageType.ERROR); + } + } + } + + private void addCustomInputActivated(ActionContext ctx) { + VarStorage storage = promptStorage("Add Custom Input"); + if (storage == null) { + return; + } + inputsTableModel.add(new InputRow(language, chooseCustomName(inputsTableModel), + storage, null)); + } + + private boolean removeInputEnabled(InputActionContext ctx) { + return !ctx.getSelection().isEmpty(); + } + + private void removeInputActivated(InputActionContext ctx) { + for (InputRow row : ctx.getSelection()) { + inputsTableModel.delete(row); + } + } + + private boolean addVarargEnabled(ActionContext ctx) { + DataTypeManagerService dtms = tool.getService(DataTypeManagerService.class); + return dtms != null && function.hasVarArgs(); + } + + private static DataType getBaseDataType(DataType dt) { + return dt instanceof TypeDef tdef ? tdef.getBaseDataType() : dt; + } + + private boolean addPointerVarsEnabled(VarActionContext ctx) { + List sel = ctx.getSelection(); + if (sel.isEmpty()) { + return false; + } + for (VarRow input : sel) { + DataType baseType = getBaseDataType(input.getType()); + if (!(baseType instanceof Pointer)) { + return false; + } + } + return true; + } + + private Varnode doAllocate(int length) { + Address addr = nextAlloc; + nextAlloc = nextAlloc.add(length); + textNextAlloc.setText(nextAlloc.toString()); + return new Varnode(addr, length); + } + + interface Allocator { + void allocate(int length); + } + + interface RowConstructor { + T create(String name, VarStorage storage, DataType type); + } + + abstract class VarAdder { + protected final T from; + protected final DefaultEnumeratedColumnTableModel into; + protected final List added; + + public VarAdder(T from, DefaultEnumeratedColumnTableModel into, List added) { + this.from = from; + this.into = into; + this.added = added; + } + + byte[] addrToBytes(Address address, int length) { + return Utils.longToBytes(address.getOffset(), length, language.isBigEndian()); + } + + void allocate(int length) { + // Extension point + } + + abstract T newRow(String name, VarStorage storage, DataType type); + + public void addPointerVars(int count) { + if (!(getBaseDataType(from.getType()) instanceof Pointer ptr)) { + return; + } + DataType dest = ptr.getDataType(); + int length = dest.getLength(); + int alignedLen = dest.getAlignedLength(); + if (length == -1) { + throw new IllegalArgumentException( + "DataType %s has dynamic length".formatted(dest)); + } + allocate((alignedLen * (count - 1)) + length); + + AddressSpace space = function.getProgram().getLanguage().getDefaultDataSpace(); + int offset = 0; + + if (!(DataTypeUtilities.getBaseDataType(dest) instanceof Composite composite)) { + for (int i = 0; i < count; i++) { + VarStorage deref = from.getStorage().deref(language, space, offset, length); + String name = count == 1 // LATER: Cull unnecessary ()s? + ? "*(%s)".formatted(from.name) + : "(%s)[%d]".formatted(from.name, i); + findRow(into, name).ifPresent(into::delete); + T row = newRow(name, deref, dest); + into.add(row); + added.add(row); + + offset += alignedLen; + } + return; + } + + for (int i = 0; i < count; i++) { + for (DataTypeComponent comp : composite.getDefinedComponents()) { + VarStorage deref = from.getStorage() + .deref(language, space, offset + comp.getOffset(), comp.getLength()); + String fieldName = comp.getFieldName(); + if (fieldName == null) { + fieldName = comp.getDefaultFieldName(); + } + String name = count == 1 + ? "(%s)->%s".formatted(from.name, fieldName) + : "(%s)[%d].%s".formatted(from.name, i, fieldName); + findRow(into, name).ifPresent(into::delete); + T row = newRow(name, deref, comp.getDataType()); + into.add(row); + added.add(row); + } + + offset += alignedLen; + } + } + + record SettingsAndValue(Settings settings, byte[] encoded) {} + + abstract SettingsAndValue defaultStringValue(String name, DataType type); + + public void addStringVar() { + AddressSpace space = function.getProgram().getLanguage().getDefaultDataSpace(); + + DataType strType = deriveStringType(from.type); + if (strType == null) { + return; + } + SettingsAndValue value = defaultStringValue(from.name, strType); + Varnode alloc = doAllocate(value.encoded.length); + from.setValue(addrToBytes(alloc.getAddress(), from.length)); + VarStorage deref = from.getStorage().deref(language, space, alloc.getSize()); + T row = newRow("*(%s)".formatted(from.name), deref, strType); + copySettings(value.settings, row.getSettings(), strType.getSettingsDefinitions()); + row.setValue(value.encoded); + added.add(row); + into.add(row); + } + } + + class InputAdder extends VarAdder { + private final Map settingsMap; + + public InputAdder(InputRow from, List added, + Map settingsMap) { + super(from, inputsTableModel, added); + this.settingsMap = settingsMap; + } + + @Override + SettingsAndValue defaultStringValue(String name, DataType type) { + Settings settings = settingsMap.computeIfAbsent(type, t -> { + Settings s = new SettingsImpl(); + tool.showDialog(new VarDataSettingsDialog(type, s)); + return s; + }); + byte[] encoded = + promptString("Allocate String", "Value of %s".formatted(name), type, settings); + return new SettingsAndValue(settings, encoded); + } + + @Override + void allocate(int length) { + Varnode alloc = doAllocate(length); + from.setValue(addrToBytes(alloc.getAddress(), from.length)); + } + + @Override + InputRow newRow(String name, VarStorage storage, DataType type) { + return new InputRow(language, name, storage, type, Set.of(from.name)); + } + } + + class OutputAdder extends VarAdder { + public OutputAdder(OutputRow from, List added) { + super(from, outputsTableModel, added); + } + + @Override + SettingsAndValue defaultStringValue(String name, DataType type) { + int length = promptIntGt1("Add String", "Length of %s".formatted(name)); + if (length < -1) { + return null; + } + return new SettingsAndValue(new SettingsImpl(), new byte[length]); + } + + @Override + OutputRow newRow(String name, VarStorage storage, DataType type) { + return new OutputRow(language, name, storage, type); + } + } + + AbstractStringDataType deriveStringType(DataType dt) { + if (!(dt instanceof Pointer ptr)) { + return null; + } + return switch (ptr.getDataType()) { + case AbstractStringDataType strType -> strType; + case CharDataType charType -> TerminatedStringDataType.dataType; + default -> null; + }; + } + + private int getCount(ActionContext ctx) { + if ((ctx.getEventClickModifiers() & ActionEvent.SHIFT_MASK) == 0) { + return 1; + } + return promptIntGt1("Allocate Pointer Array Inputs", "Count"); + } + + private void addPointerInputsActivated(InputActionContext ctx) { + List added = new ArrayList<>(); + Map settingsMap = new HashMap<>(); + if ((ctx.getEventClickModifiers() & ActionEvent.CTRL_MASK) != 0) { + for (InputRow input : ctx.getSelection()) { + new InputAdder(input, added, settingsMap).addStringVar(); + } + } + else { + int count = getCount(ctx); + if (count < 0) { + return; + } + for (InputRow input : ctx.getSelection()) { + new InputAdder(input, added, settingsMap).addPointerVars(count); + } + } + if (!added.isEmpty()) { + inputsFilterPanel.setSelectedItems(added); + } + } + + private void addPointerOutputsActivated(OutputActionContext ctx) { + List added = new ArrayList<>(); + if ((ctx.getEventClickModifiers() & ActionEvent.CTRL_MASK) != 0) { + for (OutputRow output : ctx.getSelection()) { + new OutputAdder(output, added).addStringVar(); + } + } + else { + int count = getCount(ctx); + if (count < 0) { + return; + } + for (OutputRow output : ctx.getSelection()) { + new OutputAdder(output, added).addPointerVars(count); + } + if (!added.isEmpty()) { + outputsFilterPanel.setSelectedItems(added); + } + } + } + + private void addVarargActivated(ActionContext ctx) { + DataTypeManagerService dtms = tool.getService(DataTypeManagerService.class); + if (dtms == null) { + return; + } + DataType chosenDt = dtms.promptForDataType(null); + if (chosenDt == null) { + return; + } + Program program = function.getProgram(); + DataTypeManager dtm = program.getDataTypeManager(); + try (Transaction tx = program.openTransaction("Resolve data type")) { + chosenDt = dtm.resolve(chosenDt, DataTypeConflictHandler.DEFAULT_HANDLER); + } + + PrototypeModel cc = function.getCallingConvention(); + PrototypePieces proto = new PrototypePieces(cc, function.getReturnType()); + + int paramCount = function.getParameterCount(); + for (int i = 0; i < paramCount; i++) { + proto.intypes.add(findParameterType(i)); + } + for (int i = 1;; i++) { + Optional dt = findVarargType(i); + if (dt.isEmpty()) { + break; + } + proto.intypes.add(dt.get()); + } + proto.intypes.add(chosenDt); + + ArrayList result = new ArrayList<>(); + cc.assignParameterStorage(proto, dtm, result, true); + CompilerSpec cSpec = program.getCompilerSpec(); + String name = FMT_VA.formatted(result.size() - 1); // -1 to account for "return" param + ParameterPieces last = result.getLast(); + InputRow input = new InputRow(language, name, + VarStorage.fromVariableStorage(last.getVariableStorage(program), cSpec), last.type); + inputsTableModel.add(input); + } + + private void refreshInputsActivated(ActionContext ctx) { + doRefreshInputs(); + } + + private void clearInputsActivated(ActionContext ctx) { + inputsTableModel.clear(); + } + + private void addOutputActivated(ActionContext ctx) { + outputsTableModel.add(new OutputRow(language, chooseCustomName(outputsTableModel), + promptStorage("Add Output"), null)); + } + + private boolean removeOutputEnabled(OutputActionContext ctx) { + return !ctx.getSelection().isEmpty(); + } + + private void removeOutputActivated(OutputActionContext ctx) { + for (OutputRow row : ctx.getSelection()) { + outputsTableModel.delete(row); + } + } + + private void refreshOutputsActivated(ActionContext ctx) { + doRefreshOutputs(); + } + + private void clearOutputsActivated(ActionContext ctx) { + outputsTableModel.clear(); + } + + private void doRefreshInputs() { + CompilerSpec cSpec = function.getProgram().getCompilerSpec(); + for (Parameter parameter : function.getParameters()) { + findRow(inputsTableModel, parameter.getName()).ifPresent(inputsTableModel::delete); + inputsTableModel.add(InputRow.fromVariable(parameter, cSpec)); + } + } + + private void doRefreshOutputs() { + CompilerSpec cSpec = function.getProgram().getCompilerSpec(); + Parameter ret = function.getReturn(); + findRow(outputsTableModel, ret.getName()).ifPresent(outputsTableModel::delete); + if (!(ret.getDataType() instanceof VoidDataType) && + !ret.getVariableStorage().isUnassignedStorage()) { + outputsTableModel.add(OutputRow.fromVariable(ret, cSpec)); + } + } + + private boolean typeSettingsEnabled(VarActionContext ctx) { + return ctx != null && ctx.getSelection().size() == 1; + } + + protected static void copySettings(Settings src, Settings dst, SettingsDefinition[] defs) { + for (SettingsDefinition sd : defs) { + sd.copySetting(src, dst); + } + } + + protected class VarDataSettingsDialog extends AbstractSettingsDialog { + private final Settings mySettings; + + public VarDataSettingsDialog(DataType type, Settings mySettings) { + super("Settings for %s".formatted(type), type.getSettingsDefinitions(), mySettings); + this.mySettings = mySettings; + } + + @Override + protected Settings getSettings() { + return super.getSettings(); + } + + @Override + protected void okCallback() { + super.okCallback(); + } + + @Override + protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) { + if (!settingsDefinition.supportsSuggestedValues()) { + return null; + } + return settingsDefinition.getSuggestedValues(mySettings); + } + + @Override + protected void applySettings() throws CancelledException { + copySettings(getSettings(), mySettings, getSettingsDefinitions()); + } + } + + private void typeSettingsActivated(VarActionContext ctx) { + VarRow row = ctx.getSelection().getFirst(); + DataType type = row.getType(); + if (type == null) { + return; + } + tool.showDialog(new VarDataSettingsDialog(row.getType(), row.getSettings())); + // It is modal + row.settingsChanged(); + if (row instanceof InputRow) { + inputsTableModel.fireTableDataChanged(); + } + else { + outputsTableModel.fireTableDataChanged(); + } + } + + protected void initializeVarTables() { + doRefreshInputs(); + doRefreshOutputs(); + } + + protected GTable containingTable(Component c) { + for (Component p = c; p != null; p = p.getParent()) { + if (p == inputsTable) { + return inputsTable; + } + if (p == outputsTable) { + return outputsTable; + } + } + return null; + } + + @Override + public ActionContext getActionContext(MouseEvent event) { + if (event == null) { + return super.getActionContext(event); + } + GTable table = containingTable(event.getComponent()); + if (table == inputsTable) { + return inputActions.getActionContext(event); + } + else if (table == outputsTable) { + return outputActions.getActionContext(event); + } + return super.getActionContext(event); + } + + @Override + protected void okCallback() { + super.okCallback(); + + clearStatusText(); + + DebuggerTraceManagerService traceManager = + tool.getService(DebuggerTraceManagerService.class); + DebuggerControlService controlService = tool.getService(DebuggerControlService.class); + + executeProgressTask(new Task("Emulate", true, false, false, false) { + @Override + public void run(TaskMonitor monitor) throws CancelledException { + monitor.setMessage("Starting"); + try (FunctionEmulationHarness harness = + FunctionEmulationHarness.start(tool, function, monitor)) { + /** + * Sentinel placement precedes input initialization, because 1) The user's say + * should override anything we do automatically, though it may be a foot gun, + * and 2) input initialization commits the state to the trace. We'd have to + * commit again were we to place the sentinel after. + */ + monitor.setMessage("Placing end breakpoint"); + harness.placeSentinel(sentinel); + monitor.setMessage("Initializing inputs"); + initializeInputs(harness.init()); + monitor.setMessage("Installing injects"); + harness.installInjects(); + monitor.setMessage("Emulating"); + EmulationResult result = harness.run(snapshotPeriod); + if (result.error() != null) { + setStatusText("Emulation Error: %s".formatted(result.error().getMessage()), + MessageType.ERROR); + } + else if (!sentinel.equals(harness.emuThread.getCounter())) { + setStatusText("Emulator interrupted before completion", MessageType.ERROR); + } + monitor.setMessage("Capturing Outputs"); + captureOutputs(harness.eval(result.snap()), harness.getProbesOut()); + + if (snapshotPeriod != 0) { + if (traceManager != null) { + monitor.setMessage("Opening Trace"); + traceManager.openTrace(harness.trace); + traceManager.activate(DebuggerCoordinates.NOWHERE + .trace(harness.trace) + .snap(result.defaultSnap())); + } + + if (controlService != null) { + controlService.setCurrentMode(harness.trace, ControlMode.RW_EMULATOR); + } + } + } + catch (Exception e) { + setStatusText(e.getMessage(), MessageType.ERROR); + Msg.error(this, "Emulation Setup Error", e); + } + } + }, 500); + } + + class InputInitializer { + final Eval eval; + final Collection inputs; + final Map remains; + + public InputInitializer(Eval eval, Collection inputs) { + this.eval = eval; + this.inputs = inputs; + this.remains = inputs + .stream() + .collect(Collectors.toMap(InputRow::getName, + java.util.function.Function.identity())); + } + + public void initializeInputs() { + for (InputRow input : inputs) { + initializeInput(input); + } + } + + public void initializeInput(InputRow input) { + if (remains.remove(input.name) == null) { + return; + } + doInitialize(input); + } + + public void doInitialize(InputRow input) { + initializeDependencies(input); + eval.writeVariable(input.storage, input.value); + } + + public void initializeDependency(String name) { + InputRow input = remains.remove(name); + if (input == null) { + return; + } + doInitialize(input); + } + + public void initializeDependencies(InputRow input) { + for (String dep : input.depsByName) { + initializeDependency(dep); + } + } + + public String toHex(byte[] value, int length) { + return Utils.bytesToBigInteger(value, length, language.isBigEndian(), false) + .toString(16); + } + + public void checkConflicts() { + for (InputRow r : inputs) { + LocAndVal value = eval.readVariable(r.storage); + if (!(Arrays.equals(value.value(), r.value))) { + throw new IllegalStateException("Input %s conflicts: 0x%s != 0x%s".formatted( + r.storage, toHex(r.value, r.length), toHex(value.value(), r.length))); + } + } + } + } + + protected void initializeInputs(Eval eval) { + InputInitializer initializer = new InputInitializer(eval, inputsTableModel.getModelData()); + initializer.initializeInputs(); + initializer.checkConflicts(); + eval.commit(); + } + + protected void captureOutputs(Eval eval, List probesOut) { + for (OutputRow r : outputsTableModel.copyModelData()) { + if (r.name.startsWith(PREFIX_PROBE)) { + outputsTableModel.delete(r); + } + else { + LocAndVal value = eval.readVariable(r.storage); + r.setValue(value.value()); + } + } + CompilerSpec cSpec = function.getProgram().getCompilerSpec(); + for (int i = 0; i < probesOut.size(); i++) { + ProbeOut po = probesOut.get(i); + OutputRow row = new OutputRow(language, FMT_PROBE.formatted(i + 1), + VarStorage.fromPieces(new Varnode[] { po.vn() }, cSpec), null); + row.setValue(po.value()); + outputsTableModel.add(row); + } + Swing.runLater(outputsTableModel::fireTableDataChanged); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/FunctionEmulationHarness.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/FunctionEmulationHarness.java new file mode 100644 index 0000000000..f4f9c67558 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/FunctionEmulationHarness.java @@ -0,0 +1,346 @@ +/* ### + * 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.emulation; + +import java.io.IOException; +import java.util.*; + +import db.Transaction; +import generic.Unique; +import ghidra.app.plugin.core.debug.gui.emulation.LocAndVal.WriterAndExecutor; +import ghidra.app.plugin.core.debug.service.breakpoint.ProgramBreakpoint; +import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationIntegration; +import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils; +import ghidra.app.plugin.core.debug.service.emulation.data.TranslatedPcodeDebuggerAccess; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector; +import ghidra.app.plugin.core.debug.stack.*; +import ghidra.app.plugin.core.debug.utils.ManagedDomainObject; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.debug.api.breakpoint.LogicalBreakpoint; +import ghidra.debug.api.emulation.PcodeDebuggerAccess; +import ghidra.debug.api.modules.DebuggerAddressTranslator; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.framework.plugintool.PluginTool; +import ghidra.pcode.emu.*; +import ghidra.pcode.eval.ArithmeticVarnodeEvaluator; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; +import ghidra.pcode.utils.Utils; +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.pcode.PcodeOp; +import ghidra.program.model.pcode.Varnode; +import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.model.time.TraceSnapshot; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; +import ghidra.util.task.TaskMonitor; + +public class FunctionEmulationHarness implements AutoCloseable { + + public record ProbeOut(Varnode vn, byte[] value) { + public String toString(Language language) { + return "%s = %s (%s)".formatted(vn.toString(language), + NumericUtilities.convertBytesToString(value, ":"), + Utils.bytesToBigInteger(value, vn.getSize(), language.isBigEndian(), false)); + } + } + + public record ReturnAddressInfo(Address location, long mask) { + public Address computePhysicalLocation(Eval eval, CompilerSpec cSpec) { + if (location.isMemoryAddress() || location.isRegisterAddress()) { + return location; + } + if (location.isStackAddress()) { + Register sp = cSpec.getStackPointer(); + LocAndVal spVal = eval.state.getVar(sp, Reason.INSPECT); + Address stackBase = eval.state.getArithmetic() + .toAddress(spVal, cSpec.getStackBaseSpace(), Purpose.INSPECT); + return stackBase.add(location.getOffset()); + } + throw new IllegalStateException("Unknown space for return address location"); + } + } + + public ReturnAddressInfo locateReturnAddress() { + UnwindAnalysis unwindAnalysis = new UnwindAnalysis(program); + try { + UnwindInfo unwindInfo = unwindAnalysis.getUnwindInfo(function.getEntryPoint(), monitor); + return new ReturnAddressInfo(unwindInfo.ofReturn(), unwindInfo.maskOfReturn()); + } + catch (UnwindException e) { + return null; + } + } + + public static FunctionEmulationHarness start(PluginTool tool, Function function, + TaskMonitor monitor) throws IOException { + ManagedDomainObject mt = new ManagedDomainObject(mdo -> ProgramEmulationUtils + .launchEmulationTrace(function.getProgram(), function.getEntryPoint(), mdo)); + return new FunctionEmulationHarness(tool, mt, function, monitor); + } + + public class Eval { + public final long snap; + public final Writer writer; + public final PcodeExecutor exec; + public final PcodeExecutorState state; + + private Eval(long snap) { + this.snap = snap; + WriterAndExecutor we = LocAndVal.buildExecutor(tool, start.snap(snap)); + this.writer = we.writer(); + this.exec = we.executor(); + this.state = exec.getState(); + } + + public void writeNode(VarStorageNode node, LocAndVal value) { + LocAndVal cur = node.compile(language).evaluate(exec); + exec.getState().setVar(cur.loc().getAddress(), node.size(), false, value); + } + + public void writeVariable(VarStorage storage, LocAndVal value) { + int shift = 0; + for (VarStorageNode n : storage.nodes()) { + LocAndVal piece = lvArith.binaryOp(PcodeOp.INT_RIGHT, n.size(), + value.value().length, value, 4, lvArith.fromConst(shift, 4)); + shift += n.size(); + writeNode(n, piece); + } + } + + public void writeVariable(VarStorage storage, byte[] value) { + writeVariable(storage, lvArith.fromConst(value)); + } + + public void commit() { + writer.writeDown(snap); + } + + public LocAndVal readNode(VarStorageNode node) { + return node.compile(language).evaluate(exec); + } + + public LocAndVal readVariable(VarStorage storage) { + int total = storage.size(); + LocAndVal value = lvArith.fromConst(0, total); + for (VarStorageNode n : storage.nodes()) { + LocAndVal piece = readNode(n); + value = ArithmeticVarnodeEvaluator.catenate(lvArith, total, value, piece, n.size()); + } + return value; + } + } + + class MonitoredCallbacks extends ComposedPcodeEmulationCallbacks { + public MonitoredCallbacks(PcodeEmulationCallbacks cb) { + super(cb); + } + + @Override + public void beforeStepOp(PcodeThread thread, PcodeOp op, PcodeFrame frame) { + if (monitor.isCancelled()) { + throw new InterruptPcodeExecutionException(frame, null); + } + } + } + + public class OverrideProbeUseropLibrary extends AnnotatedPcodeUseropLibrary { + @PcodeUserop + public void emu_probe(@OpExecutor PcodeExecutor exec, Varnode in) { + probesOut.add(new ProbeOut(in, exec.getState().getVar(in, Reason.INSPECT))); + } + } + + class MonitoredEmulator extends PcodeEmulator { + public MonitoredEmulator(Language language, PcodeEmulationCallbacks cb) { + super(language, new MonitoredCallbacks(cb)); + } + + @Override + protected PcodeUseropLibrary createUseropLibrary() { + return super.createUseropLibrary().compose(new OverrideProbeUseropLibrary(), true); + } + } + + final PluginTool tool; + final ManagedDomainObject mt; + final Transaction tx; + public final Trace trace; + final TraceThread thread; + final DebuggerCoordinates start; + final Eval initEval; + public final PcodeArithmetic lvArith; + final SleighLanguage language; + final Function function; + final Program program; + final DebuggerAddressTranslator translator; + final TaskMonitor monitor; + + final Writer writer; + final PcodeEmulator emulator; + final PcodeThread emuThread; + + final List probesOut = new ArrayList<>(); + + private FunctionEmulationHarness(PluginTool tool, ManagedDomainObject mt, + Function function, TaskMonitor monitor) { + this.tool = tool; + this.mt = mt; + this.trace = mt.get(); + this.tx = trace.openTransaction("Emulate"); + this.thread = Unique.assertOne(trace.getThreadManager().getAllThreads()); + this.start = DebuggerCoordinates.NOWHERE.thread(thread); + this.initEval = new Eval(0); + this.lvArith = initEval.exec.getArithmetic(); + this.language = initEval.exec.getLanguage(); + this.function = function; + this.program = function.getProgram(); + this.monitor = monitor; + + TracePlatform host = trace.getPlatformManager().getHostPlatform(); + this.translator = getTranslator(); + PcodeDebuggerAccess access = new TranslatedPcodeDebuggerAccess(null, host, 0) { + @Override + public DebuggerAddressTranslator getAddressTranslator() { + return translator; + } + }; + writer = DebuggerEmulationIntegration.bytesDelayedWriteTrace(access); + emulator = new MonitoredEmulator(language, writer.callbacks()); + emuThread = emulator.newThread(thread.getPath()); + } + + private DebuggerAddressTranslator getTranslator() { + // Don't use the service. It requires the trace and program to be opened in the tool + DebuggerStaticMappingContext mapper = new DebuggerStaticMappingContext(); + try (ChangeCollector cc = mapper.collectChanges()) { + mapper.addProgram(cc, program); + mapper.addTrace(cc, trace); + } + return mapper; + } + + public Eval init() { + return initEval; + } + + public Eval eval(long snap) { + return new Eval(snap); + } + + public void placeSentinel(Address sentinel) { + ReturnAddressInfo retInfo = locateReturnAddress(); + if (retInfo == null) { + Address zero = language.getDefaultSpace().getAddress(0); + Msg.warn(this, + "Could not locate return address. Placing end break at %s".formatted(zero)); + emulator.addBreakpoint(zero, SleighUtils.CONDITION_ALWAYS); + } + else { + initEval.state.setVar( + retInfo.computePhysicalLocation(initEval, program.getCompilerSpec()), + language.getProgramCounter().getNumBytes(), false, lvArith.fromConst(sentinel)); + emulator.addBreakpoint(sentinel.getNewAddress(sentinel.getOffset() & retInfo.mask), + SleighUtils.CONDITION_ALWAYS); + } + } + + public void installInjects() { + Iterator bit = program.getBookmarkManager() + .getBookmarksIterator(LogicalBreakpoint.ENABLED_BOOKMARK_TYPE); + while (bit.hasNext()) { + ProgramBreakpoint brk = ProgramBreakpoint.fromBookmark(program, bit.next()); + String sleigh = brk.getEmuSleigh(); + if (sleigh == null || SleighUtils.UNCONDITIONAL_BREAK.equals(sleigh)) { + continue; + } + emulator.inject(brk.getLocation().getAddress(), sleigh); + } + } + + public void createSnapshot(long snap) { + TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, true); + snapshot.setDescription("Emulated"); + snapshot.setEventThread(thread); + writer.writeDown(snap); + } + + public record EmulationResult(long snap, Throwable error) { + /** + * In the case we terminate normally, we should probably ensure the {@code return} + * instruction highlighted. This can be accomplished by subtracting one from the last snap. + * For abnormal termination, I think we should just go to the last snap and let whatever + * problem(s) get displayed. + * + * @return the last snap - 1 if successful, otherwise just the last snap. + */ + public long defaultSnap() { + if (error == null) { + return snap - 1; + } + return snap; + } + } + + public EmulationResult run(long snapshotPeriod) { + long snap = 1; + boolean makeFinalSnap = false; + Throwable error = null; + try { + if (snapshotPeriod == 0) { + makeFinalSnap = true; + emuThread.run(); + throw new AssertionError("Shouldn't happen"); + } + for (;; snap += 1) { + emuThread.stepInstruction(); + makeFinalSnap = true; + emuThread.stepInstruction(snapshotPeriod - 1); + createSnapshot(snap); + makeFinalSnap = false; + } + } + catch (InterruptPcodeExecutionException e) { + // Expected, but snap is now one ahead of the last snapshot + snap--; + } + catch (Throwable t) { + error = t; + snap--; + } + if (makeFinalSnap) { + createSnapshot(++snap); + } + return new EmulationResult(snap, error); + } + + public List getProbesOut() { + return probesOut; + } + + @Override + public void close() { + tx.close(); + mt.close(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputColumn.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputColumn.java new file mode 100644 index 0000000000..532c3e7cdd --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputColumn.java @@ -0,0 +1,92 @@ +/* ### + * 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.emulation; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import javax.swing.table.TableCellEditor; + +import docking.widgets.table.CustomToStringCellRenderer; +import docking.widgets.table.EnumeratedTableColumn; +import ghidra.program.model.data.DataType; +import ghidra.util.table.column.GColumnRenderer; + +enum InputColumn implements EnumeratedTableColumn { + NAME("Name", String.class, InputRow::getName), + STORAGE("Storage", VarStorage.class, InputRow::getStorage), + VALUE("Value", String.class, InputRow::getValueStr, InputRow::setValueStr) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + TYPE("Type", DataType.class, InputRow::getType, InputRow::setType) { + @Override + public TableCellEditor getEditor() { + return VarDataTypeEditor.INSTANCE; + } + }, + REPR("Repr", String.class, InputRow::getRepr, InputRow::setRepr) { + @Override + public boolean isEditable(InputRow row) { + return row.isReprEditable(); + } + }; + + final String header; + final Class cls; + final Function getter; + final BiConsumer setter; + + @SuppressWarnings("unchecked") + private InputColumn(String header, Class cls, Function getter, + BiConsumer setter) { + this.header = header; + this.cls = cls; + this.getter = (Function) getter; + this.setter = (BiConsumer) setter; + } + + private InputColumn(String header, Class cls, Function getter) { + this(header, cls, getter, null); + } + + @Override + public Class getValueClass() { + return cls; + } + + @Override + public Object getValueOf(InputRow row) { + return getter.apply(row); + } + + @Override + public boolean isEditable(InputRow row) { + return setter != null; + } + + @Override + public void setValueOf(InputRow row, Object value) { + setter.accept(row, value); + } + + @Override + public String getHeader() { + return header; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputRow.java new file mode 100644 index 0000000000..de70aecdcb --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputRow.java @@ -0,0 +1,81 @@ +/* ### + * 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.emulation; + +import java.util.Arrays; +import java.util.Set; + +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeEncodeException; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; +import ghidra.program.model.listing.Variable; +import ghidra.program.model.mem.ByteMemBufferImpl; +import ghidra.program.model.mem.MemBuffer; + +class InputRow extends VarRow { + static InputRow fromVariable(Variable v, CompilerSpec cSpec) { + return VarRow.fromVariable(InputRow::new, v, cSpec); + } + + final Set depsByName; // Use names since the actual row can be replaced by refresh + + InputRow(Language language, String name, VarStorage storage, DataType type, + Set depsByName) { + super(language, name, storage, type); + this.depsByName = depsByName; + } + + InputRow(Language language, String name, VarStorage storage, DataType type) { + this(language, name, storage, type, Set.of()); + } + + private void encodeRepr() throws DataTypeEncodeException { + MemBuffer buf = new ByteMemBufferImpl(address, value, language.isBigEndian()); + byte[] data = type.encodeRepresentation(repr, buf, settings, length); + if (data.length == length) { + value = data; + } + else { + value = Arrays.copyOf(data, length); + } + } + + void setValueStr(String string) { + RawStyle style = RawStyle.fromString(string); + byte[] value = style.fromString(string, length, language); + this.style = style; + this.value = value; + decodeValue(); + } + + void setRepr(String repr) { + String oldRepr = this.repr; + this.repr = repr; + try { + encodeRepr(); + decodeValue(); + } + catch (DataTypeEncodeException e) { + this.repr = oldRepr; + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + boolean isReprEditable() { + return repr != null && type.isEncodable(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputsTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputsTableModel.java new file mode 100644 index 0000000000..5dc44d4bde --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputsTableModel.java @@ -0,0 +1,24 @@ +/* ### + * 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.emulation; + +import ghidra.framework.plugintool.ServiceProvider; + +class InputsTableModel extends VarTableModel { + public InputsTableModel(ServiceProvider tool, DebuggerEmulateFunctionDialog dialog) { + super(tool, "Inputs", InputColumn.class, dialog); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LocAndVal.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LocAndVal.java new file mode 100644 index 0000000000..09a51e4639 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LocAndVal.java @@ -0,0 +1,266 @@ +/* ### + * 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.emulation; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Stream; + +import ghidra.app.plugin.core.debug.service.emulation.Mode; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; +import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess; +import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.*; +import ghidra.program.model.mem.MemBuffer; +import ghidra.trace.model.guest.TracePlatform; + +public record LocAndVal(byte[] value, ValueLocation loc) { + public enum LocAndValPcodeArithmetic implements PcodeArithmetic { + BIG_ENDIAN(BytesPcodeArithmetic.BIG_ENDIAN, LocationPcodeArithmetic.BIG_ENDIAN), + LITTLE_ENDIAN(BytesPcodeArithmetic.LITTLE_ENDIAN, LocationPcodeArithmetic.BIG_ENDIAN); + + public static LocAndValPcodeArithmetic forEndian(boolean isBigEndian) { + return isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN; + } + + public static LocAndValPcodeArithmetic forLanguage(Language language) { + return forEndian(language.isBigEndian()); + } + + private final BytesPcodeArithmetic bytes; + private final LocationPcodeArithmetic location; + + private LocAndValPcodeArithmetic(BytesPcodeArithmetic bytes, + LocationPcodeArithmetic location) { + this.bytes = bytes; + this.location = location; + } + + @Override + public Class getDomain() { + return LocAndVal.class; + } + + @Override + public Endian getEndian() { + return bytes.getEndian(); + } + + @Override + public LocAndVal unaryOp(int opcode, int sizeout, int sizein1, LocAndVal in1) { + return new LocAndVal( + bytes.unaryOp(opcode, sizeout, sizein1, in1.value), + location.unaryOp(opcode, sizeout, sizein1, in1.loc)); + } + + @Override + public LocAndVal binaryOp(int opcode, int sizeout, int sizein1, LocAndVal in1, int sizein2, + LocAndVal in2) { + return new LocAndVal( + bytes.binaryOp(opcode, sizeout, sizein1, in1.value, sizein2, in2.value), + location.binaryOp(opcode, sizeout, sizein1, in1.loc, sizein2, in2.loc)); + } + + @Override + public LocAndVal modBeforeStore(int sizeinOffset, AddressSpace space, LocAndVal inOffset, + int sizeinValue, LocAndVal inValue) { + return new LocAndVal( + bytes.modBeforeStore(sizeinOffset, space, inOffset.value, sizeinValue, + inValue.value), + location.modBeforeStore(sizeinOffset, space, inOffset.loc, sizeinValue, + inValue.loc)); + } + + @Override + public LocAndVal modAfterLoad(int sizeinOffset, AddressSpace space, LocAndVal inOffset, + int sizeinValue, LocAndVal inValue) { + return new LocAndVal( + bytes.modAfterLoad(sizeinOffset, space, inOffset.value, sizeinValue, inValue.value), + location.modAfterLoad(sizeinOffset, space, inOffset.loc, sizeinValue, inValue.loc)); + } + + @Override + public LocAndVal fromConst(byte[] value) { + return new LocAndVal( + bytes.fromConst(value), + location.fromConst(value)); + } + + @Override + public byte[] toConcrete(LocAndVal value, Purpose purpose) { + return bytes.toConcrete(value.value, purpose); + } + + @Override + public long sizeOf(LocAndVal value) { + return bytes.sizeOf(value.value); + } + } + + public static class LocAndValPcodeExecutorStatePiece + implements PcodeExecutorStatePiece { + private final PcodeExecutorStatePiece bytesPiece; + private final PcodeExecutorStatePiece locationPiece; + + private final PcodeArithmetic arithmetic; + + public LocAndValPcodeExecutorStatePiece( + PcodeExecutorStatePiece bytesPiece, + PcodeExecutorStatePiece locationPiece) { + this.bytesPiece = bytesPiece; + this.locationPiece = locationPiece; + this.arithmetic = LocAndValPcodeArithmetic.forLanguage(bytesPiece.getLanguage()); + } + + @Override + public Language getLanguage() { + return bytesPiece.getLanguage(); + } + + @Override + public PcodeArithmetic getAddressArithmetic() { + return bytesPiece.getAddressArithmetic(); + } + + @Override + public PcodeArithmetic getArithmetic() { + return arithmetic; + } + + @Override + public Stream> streamPieces() { + return Stream.of(bytesPiece, locationPiece); + } + + @Override + public LocAndValPcodeExecutorStatePiece fork(PcodeStateCallbacks cb) { + return new LocAndValPcodeExecutorStatePiece( + bytesPiece.fork(cb), + locationPiece.fork(cb)); + } + + @Override + public void setVarInternal(AddressSpace space, byte[] offset, int size, LocAndVal val) { + bytesPiece.setVarInternal(space, offset, size, val.value); + locationPiece.setVarInternal(space, offset, size, val.loc); + } + + @Override + public void setVar(AddressSpace space, byte[] offset, int size, boolean quantize, + LocAndVal val) { + bytesPiece.setVar(space, offset, size, quantize, val.value); + locationPiece.setVar(space, offset, size, quantize, val.loc); + } + + @Override + public LocAndVal getVar(AddressSpace space, byte[] offset, int size, boolean quantize, + Reason reason) { + return new LocAndVal( + bytesPiece.getVar(space, offset, size, quantize, reason), + locationPiece.getVar(space, offset, size, quantize, reason)); + } + + @Override + public LocAndVal getVarInternal(AddressSpace space, byte[] offset, int size, + Reason reason) { + return new LocAndVal( + bytesPiece.getVarInternal(space, offset, size, reason), + locationPiece.getVarInternal(space, offset, size, reason)); + } + + @Override + public Map getRegisterValues() { + Map result = new HashMap<>(); + for (Entry entry : bytesPiece.getRegisterValues().entrySet()) { + Register reg = entry.getKey(); + AddressSpace space = reg.getAddressSpace(); + long offset = reg.getAddress().getOffset(); + int size = reg.getNumBytes(); + result.put(reg, new LocAndVal( + entry.getValue(), + locationPiece.getVar(space, offset, size, false, Reason.INSPECT))); + } + return result; + } + + @Override + public MemBuffer getConcreteBuffer(Address address, Purpose purpose) { + return bytesPiece.getConcreteBuffer(address, purpose); + } + + @Override + public void clear() { + bytesPiece.clear(); + locationPiece.clear(); + } + } + + public static class LocAndValPcodeExecutorState + extends AbstractPcodeExecutorState { + + public LocAndValPcodeExecutorState(PcodeExecutorStatePiece piece) { + super(piece); + } + + @Override + protected byte[] extractAddress(LocAndVal value) { + return value.value; + } + + @Override + public LocAndValPcodeExecutorState fork(PcodeStateCallbacks cb) { + return new LocAndValPcodeExecutorState(piece.fork(cb)); + } + } + + public record WriterAndState(Writer writer, LocAndValPcodeExecutorState state) {} + + public static WriterAndState buildState(ServiceProvider provider, + DebuggerCoordinates coordinates) { + PcodeTraceDataAccess data = new DefaultPcodeTraceAccess(coordinates.getPlatform(), + coordinates.getViewSnap(), coordinates.getSnap()) + .getDataForThreadState(coordinates.getThread(), coordinates.getFrame()); + // Seems weird, but RO is in terms of the Target. RO writes the Trace. + DebuggerPcodeUtils.WriterAndState ws = + DebuggerPcodeUtils.executorStateForCoordinates(provider, coordinates, Mode.RO); + return new WriterAndState(ws.writer(), + new LocAndValPcodeExecutorState(new LocAndValPcodeExecutorStatePiece(ws.state(), + new LocationPcodeExecutorStatePiece(data.getLanguage())))); + } + + public record WriterAndExecutor(Writer writer, PcodeExecutor executor) {} + + public static WriterAndExecutor buildExecutor(ServiceProvider provider, + DebuggerCoordinates coordinates) { + TracePlatform platform = coordinates.getPlatform(); + Language language = platform.getLanguage(); + if (!(language instanceof SleighLanguage slang)) { + throw new IllegalArgumentException("Emulation requires a Sleigh language"); + } + WriterAndState ws = buildState(provider, coordinates); + return new WriterAndExecutor(ws.writer, + new PcodeExecutor<>(slang, ws.state.getArithmetic(), ws.state, Reason.INSPECT)); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LongInputVerifier.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LongInputVerifier.java new file mode 100644 index 0000000000..22ffc21507 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LongInputVerifier.java @@ -0,0 +1,48 @@ +/* ### + * 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.emulation; + +import java.math.BigInteger; + +import javax.swing.*; + +public abstract class LongInputVerifier extends InputVerifier { + @Override + public boolean verify(JComponent input) { + if (!(input instanceof JTextField text)) { + throw new IllegalArgumentException( + "Only JTextField is supported. Got %s".formatted(input.getClass())); + } + String str = text.getText(); + try { + BigInteger value = new BigInteger(str); + long l = value.longValueExact(); + if (!verifyLong(l)) { + reject("Invalid value: %s".formatted(str)); + return false; + } + return true; + } + catch (Exception e) { + reject("%s while parsing '%s'".formatted(e.getMessage(), str)); + return false; + } + } + + protected abstract boolean verifyLong(long value); + + protected abstract void reject(String message); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputColumn.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputColumn.java new file mode 100644 index 0000000000..73130d2d5b --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputColumn.java @@ -0,0 +1,87 @@ +/* ### + * 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.emulation; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import javax.swing.table.TableCellEditor; + +import docking.widgets.table.CustomToStringCellRenderer; +import docking.widgets.table.EnumeratedTableColumn; +import ghidra.program.model.data.DataType; +import ghidra.util.table.column.GColumnRenderer; + +enum OutputColumn implements EnumeratedTableColumn { + NAME("Name", String.class, OutputRow::getName), + STORAGE("Storage", VarStorage.class, OutputRow::getStorage), + VALUE("Value", String.class, OutputRow::getValueStr) { + @Override + public GColumnRenderer getRenderer() { + return CustomToStringCellRenderer.MONO_OBJECT; + } + }, + TYPE("Type", DataType.class, OutputRow::getType, OutputRow::setType) { + @Override + public TableCellEditor getEditor() { + return VarDataTypeEditor.INSTANCE; + } + }, + REPR("Repr", String.class, OutputRow::getRepr); + + final String header; + final Class cls; + final Function getter; + final BiConsumer setter; + + @SuppressWarnings("unchecked") + private OutputColumn(String header, Class cls, Function getter, + BiConsumer setter) { + this.header = header; + this.cls = cls; + this.getter = (Function) getter; + this.setter = (BiConsumer) setter; + } + + private OutputColumn(String header, Class cls, Function getter) { + this(header, cls, getter, null); + } + + @Override + public Class getValueClass() { + return cls; + } + + @Override + public Object getValueOf(OutputRow row) { + return getter.apply(row); + } + + @Override + public boolean isEditable(OutputRow row) { + return setter != null; + } + + @Override + public void setValueOf(OutputRow row, Object value) { + setter.accept(row, value); + } + + @Override + public String getHeader() { + return header; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputRow.java new file mode 100644 index 0000000000..6e5bc06bed --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputRow.java @@ -0,0 +1,31 @@ +/* ### + * 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.emulation; + +import ghidra.program.model.data.DataType; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; +import ghidra.program.model.listing.Variable; + +class OutputRow extends VarRow { + static OutputRow fromVariable(Variable v, CompilerSpec cSpec) { + return VarRow.fromVariable(OutputRow::new, v, cSpec); + } + + OutputRow(Language language, String name, VarStorage storage, DataType type) { + super(language, name, storage, type); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputsTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputsTableModel.java new file mode 100644 index 0000000000..084e24ffef --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputsTableModel.java @@ -0,0 +1,24 @@ +/* ### + * 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.emulation; + +import ghidra.framework.plugintool.ServiceProvider; + +public class OutputsTableModel extends VarTableModel { + public OutputsTableModel(ServiceProvider tool, DebuggerEmulateFunctionDialog dialog) { + super(tool, "Outputs", OutputColumn.class, dialog); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/RawStyle.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/RawStyle.java new file mode 100644 index 0000000000..e53d80ded8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/RawStyle.java @@ -0,0 +1,83 @@ +/* ### + * 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.emulation; + +import java.math.BigInteger; +import java.util.Arrays; + +import ghidra.pcode.utils.Utils; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.Language; +import ghidra.util.NumericUtilities; + +// LATER: Move this into some utilities and have DefaultWatchRow use it, too? +enum RawStyle { + INTEGER { + @Override + String toString(byte[] value, Language language) { + if (value == null) { + return ""; + } + BigInteger asInt = + Utils.bytesToBigInteger(value, value.length, language.isBigEndian(), false); + return "0x" + asInt.toString(16); + } + + @Override + byte[] fromString(String string, int length, Language language) { + string = string.trim(); + final BigInteger asInt = string.startsWith("0x") + ? new BigInteger(string.substring(2), 16) + : new BigInteger(string, 10); + return Utils.bigIntegerToBytes(asInt, length, language.isBigEndian()); + } + }, + BYTES { + @Override + String toString(byte[] value, Language language) { + if (value == null) { + return ""; + } + return "{ %s }".formatted(NumericUtilities.convertBytesToString(value, " ")); + } + + @Override + byte[] fromString(String string, int length, Language language) { + string = string.trim(); + if (!string.startsWith("{") && string.endsWith("}")) { + throw new IllegalArgumentException(string); + } + string = string.substring(1, string.length() - 1); + byte[] data = NumericUtilities.convertStringToBytes(string); + if (data.length == length) { + return data; + } + return Arrays.copyOf(data, length); + } + }; + + static RawStyle defaultForSpace(AddressSpace space) { + return space.isMemorySpace() ? BYTES : INTEGER; + } + + static RawStyle fromString(String string) { + return string.trim().startsWith("{") ? BYTES : INTEGER; + } + + abstract String toString(byte[] value, Language language); + + abstract byte[] fromString(String string, int length, Language language); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarDataTypeEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarDataTypeEditor.java new file mode 100644 index 0000000000..d8341c4c87 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarDataTypeEditor.java @@ -0,0 +1,67 @@ +/* ### + * 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.emulation; + +import javax.swing.table.TableModel; + +import db.Transaction; +import ghidra.app.services.DataTypeManagerService; +import ghidra.base.widgets.table.AbstractDataTypeTableCellEditor; +import ghidra.program.model.data.DataType; +import ghidra.program.model.listing.Program; + +class VarDataTypeEditor extends AbstractDataTypeTableCellEditor { + static final VarDataTypeEditor INSTANCE = new VarDataTypeEditor(); + + @Override + protected boolean validateSelection(DataType dataType, TableModel model) { + if (!(model instanceof VarTableModel vModel)) { + return false; + } + + VarRow row = vModel.getModelData().get(table.getEditingRow()); + if (row == null) { + return false; + } + int dtLength = dataType.getLength(); + if (dtLength != -1 && dtLength != row.length) { + vModel.getDialog() + .setStatusText("Invalid DataType %s. Length must be %d.".formatted(dataType, + row.length)); + return false; + } + return true; + } + + @Override + protected DataTypeManagerService getService(TableModel model) { + if (!(model instanceof VarTableModel vModel)) { + return null; + } + return vModel.getServiceProvider().getService(DataTypeManagerService.class); + } + + @Override + protected DataType resolveSelection(DataType dataType, TableModel model) { + if (dataType == null || !(model instanceof VarTableModel vModel)) { + return null; + } + Program program = vModel.getProgram(); + try (Transaction tx = program.openTransaction("Resolve DataTye")) { + return program.getDataTypeManager().resolve(dataType, null); + } + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarRow.java new file mode 100644 index 0000000000..b3ed488342 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarRow.java @@ -0,0 +1,134 @@ +/* ### + * 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.emulation; + +import java.util.Objects; + +import ghidra.docking.settings.SettingsImpl; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; +import ghidra.program.model.listing.Variable; +import ghidra.program.model.listing.VariableStorage; +import ghidra.program.model.mem.ByteMemBufferImpl; +import ghidra.program.model.mem.MemBuffer; +import ghidra.util.Msg; + +abstract class VarRow { + interface VarRowFactory { + T create(Language language, String name, VarStorage storage, DataType type); + } + + static T fromVariable(VarRowFactory cons, Variable v, + CompilerSpec cSpec) { + VariableStorage storage = v.getVariableStorage(); + return cons.create(cSpec.getLanguage(), v.getName(), + VarStorage.fromVariableStorage(storage, cSpec), v.getDataType()); + } + + final Language language; + final String name; + final VarStorage storage; + final int length; + final Address address; + final SettingsImpl settings = new SettingsImpl(); + + byte[] value; + String repr; + DataType type; + + RawStyle style; + + VarRow(Language language, String name, VarStorage storage, DataType type) { + this.language = language; + this.name = name; + this.storage = storage; + this.length = storage.size(); + this.address = storage.address(); + + this.value = new byte[storage.size()]; + this.type = type; + this.style = RawStyle.defaultForSpace(address.getAddressSpace()); + + decodeValue(); + } + + void decodeValue() { + try { + if (type == null) { + repr = null; + return; + } + MemBuffer buf = new ByteMemBufferImpl(address, value, language.isBigEndian()) { + @Override + public Language getLanguage() { + return language; + } + }; + repr = type.getRepresentation(buf, settings, length); + } + catch (Exception e) { + Msg.error(this, e.getMessage()); + repr = null; + } + } + + String getName() { + return name; + } + + VarStorage getStorage() { + return storage; + } + + void setType(DataType type) { + this.type = type; + decodeValue(); + } + + DataType getType() { + return type; + } + + SettingsImpl getSettings() { + return settings; + } + + void settingsChanged() { + decodeValue(); + } + + void setValue(byte[] value) { + if (value.length != storage.size()) { + throw new IllegalArgumentException("Length mismatch"); + } + this.value = value; + decodeValue(); + } + + byte[] getValue() { + return value; + } + + String getValueStr() { + return style.toString(value, language); + } + + String getRepr() { + return Objects.requireNonNullElse(repr, ""); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorage.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorage.java new file mode 100644 index 0000000000..7cc11b0968 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorage.java @@ -0,0 +1,77 @@ +/* ### + * 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.emulation; + +import java.util.List; +import java.util.stream.Stream; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.listing.VariableStorage; +import ghidra.program.model.pcode.Varnode; + +record VarStorage(List nodes, int size) { + static VarStorage fromPieces(Varnode[] pieces, CompilerSpec cSpec) { + return new VarStorage(Stream.of(pieces) + .map(vn -> VarStorageNode.fromVarnode(vn, cSpec)) + .toList()); + } + + static VarStorage fromVariableStorage(VariableStorage vs, CompilerSpec cSpec) { + return fromPieces(vs.getVarnodes(), cSpec); + } + + static VarStorage fromExpression(SleighLanguage language, String expression) { + // TODO: Could break ValueLocation down into its Varnodes, but what's that buy? + return new VarStorage(List.of(VarStorageNode.fromExpression(language, expression))); + } + + VarStorage(List nodes) { + this(nodes, nodes.stream().mapToInt(VarStorageNode::size).sum()); + } + + Address address() { + return nodes.getFirst().address(); + } + + @Override + public final String toString() { + if (nodes.size() == 1) { + return nodes.getFirst().toString(); + } + return nodes.toString(); + } + + public VarStorage deref(SleighLanguage language, AddressSpace space, int length) { + if (nodes.size() != 1) { + throw new UnsupportedOperationException("Deref of multi-node storage not supported"); + } + return new VarStorage(List.of(nodes.getFirst().deref(language, space, length)), length); + } + + public VarStorage deref(SleighLanguage language, AddressSpace space, int offset, int length) { + if (offset == 0) { + return deref(language, space, length); + } + if (nodes.size() != 1) { + throw new UnsupportedOperationException("Deref of multi-node storage not supported"); + } + return new VarStorage(List.of(nodes.getFirst().deref(language, space, offset, length)), + length); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorageNode.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorageNode.java new file mode 100644 index 0000000000..f3c9118535 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorageNode.java @@ -0,0 +1,85 @@ +/* ### + * 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.emulation; + +import ghidra.app.plugin.core.debug.gui.emulation.LocAndVal.LocAndValPcodeExecutorState; +import ghidra.app.plugin.core.debug.gui.emulation.LocAndVal.LocAndValPcodeExecutorStatePiece; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.pcode.Varnode; + +// TODO: Consider using Varnode with defining op +// TODO: Consider compiling in the constructor? Would need language. +record VarStorageNode(String expr, Address address, int size) { + static VarStorageNode fromVarnode(Varnode vn, CompilerSpec cSpec) { + Address address = vn.getAddress(); + if (address.isStackAddress()) { + return new VarStorageNode("*:%d (%s + 0x%x)".formatted(vn.getSize(), + cSpec.getStackPointer(), address.getOffset()), + cSpec.getStackBaseSpace().getAddress(0), vn.getSize()); + } + if (address.isMemoryAddress()) { + return new VarStorageNode("*:%d 0x%08x".formatted(vn.getSize(), vn.getOffset()), + address, vn.getSize()); + } + if (address.isRegisterAddress()) { + return new VarStorageNode(vn.toString(cSpec.getLanguage()), address, vn.getSize()); + } + if (address.isUniqueAddress()) { + return new VarStorageNode("$Unique", address, vn.getSize()); + } + throw new AssertionError(); + } + + static VarStorageNode fromExpression(SleighLanguage language, String expression) { + PcodeExpression expr = SleighProgramCompiler.compileExpression(language, expression); + LocAndValPcodeExecutorState state = + new LocAndValPcodeExecutorState(new LocAndValPcodeExecutorStatePiece( + new BytesPcodeExecutorState(language, PcodeStateCallbacks.NONE), + new LocationPcodeExecutorStatePiece(language))); + PcodeExecutor exec = new PcodeExecutor(state, Reason.INSPECT); + ValueLocation loc = expr.evaluate(exec).loc(); + return new VarStorageNode(expression, loc.getAddress(), loc.size()); + } + + PcodeExpression compile(SleighLanguage language) { + return SleighProgramCompiler.compileExpression(language, expr); + } + + @Override + public final String toString() { + return expr; + } + + public VarStorageNode deref(SleighLanguage language, AddressSpace space, int length) { + String derefExpr = space == language.getDefaultDataSpace() + ? "*:%d (%s)".formatted(length, expr) + : "*[%s]:%d (%s)".formatted(space.getName(), length, expr); + return new VarStorageNode(derefExpr, space.getAddress(0), length); + } + + public VarStorageNode deref(SleighLanguage language, AddressSpace space, int offset, + int length) { + String derefExpr = space == language.getDefaultDataSpace() + ? "*:%d ((%s)+%d)".formatted(length, expr, offset) + : "*[%s]:%d ((%s)+%d)".formatted(space.getName(), length, expr, offset); + return new VarStorageNode(derefExpr, space.getAddress(0), length); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarTableModel.java new file mode 100644 index 0000000000..9d37210136 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarTableModel.java @@ -0,0 +1,69 @@ +/* ### + * 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.emulation; + +import docking.widgets.table.DefaultEnumeratedColumnTableModel; +import docking.widgets.table.EnumeratedTableColumn; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.util.MessageType; + +class VarTableModel & EnumeratedTableColumn, R extends VarRow> + extends DefaultEnumeratedColumnTableModel { + + protected final DebuggerEmulateFunctionDialog dialog; + protected final Function function; + + public VarTableModel(ServiceProvider tool, String name, Class colType, + DebuggerEmulateFunctionDialog dialog) { + super(tool, name, colType); + this.dialog = dialog; + this.function = dialog.function; + } + + ServiceProvider getServiceProvider() { + return serviceProvider; + } + + Function getFunction() { + return function; + } + + Program getProgram() { + return function.getProgram(); + } + + DataTypeManager getDataTypeManager() { + return function.getProgram().getDataTypeManager(); + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + try { + super.setValueAt(aValue, rowIndex, columnIndex); + dialog.clearStatusText(); + } + catch (Exception e) { + dialog.setStatusText(e.getMessage(), MessageType.ERROR); + } + } + + DebuggerEmulateFunctionDialog getDialog() { + return dialog; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DefaultWatchRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DefaultWatchRow.java index 32454897d5..c557659d3d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DefaultWatchRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DefaultWatchRow.java @@ -61,7 +61,7 @@ public class DefaultWatchRow implements WatchRow { private String expression; private String typePath; private DataType dataType; - private SettingsImpl settings = new SettingsImpl(); + private final SettingsImpl settings = new SettingsImpl(); private SavedSettings savedSettings = new SavedSettings(settings); private String comment; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/ProgramBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/ProgramBreakpoint.java index 809e7af3d6..2a03ac3cb7 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/ProgramBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/ProgramBreakpoint.java @@ -48,7 +48,7 @@ public class ProgramBreakpoint { private static final Gson GSON = new GsonBuilder().create(); /** - * A class for (de)serializing breakoint properties in the bookmark's comments + * A class for (de)serializing breakpoint properties in the bookmark's comments */ static class BreakpointProperties { public String name; @@ -106,6 +106,25 @@ public class ProgramBreakpoint { } } + /** + * Produce a breakpoint from its bookmark (utility). + *

    + * The breakpoint manager does not ordinarily use this, as the path to total construction spans + * discovering and indexing of logical breakpoints, and then adding into them information + * available from program bookmarks and trace breakpoints. This utility eases the processing of + * program-specified bookmarks without depending on the breakpoint service. + * + * @param program the program containing the bookmark + * @param bm the bookmark describing the breakpoint + * @return the breakpoint + */ + public static ProgramBreakpoint fromBookmark(Program program, Bookmark bm) { + ProgramBreakpoint brk = new ProgramBreakpoint(program, + bm.getAddress(), lengthFromBookmark(bm), kindsFromBookmark(bm)); + brk.add(bm); + return brk; + } + private final Program program; private final Address address; private final ProgramLocation location; @@ -399,10 +418,14 @@ public class ProgramBreakpoint { */ public Bookmark getBookmark() { Bookmark eBookmark = this.eBookmark; - if (eBookmark != null) { + if (eBookmark != null && !eBookmark.isDeleted()) { return eBookmark; } - return dBookmark; + Bookmark dBookmark = this.dBookmark; + if (dBookmark != null && !dBookmark.isDeleted()) { + return dBookmark; + } + return null; } protected String getComment() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationIntegration.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationIntegration.java index a50b26e1a3..636b7b8981 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationIntegration.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationIntegration.java @@ -23,7 +23,8 @@ import ghidra.pcode.emu.PcodeThread; import ghidra.pcode.exec.*; import ghidra.pcode.exec.trace.TraceEmulationIntegration; import ghidra.pcode.exec.trace.TraceEmulationIntegration.*; -import ghidra.pcode.exec.trace.data.*; +import ghidra.pcode.exec.trace.data.PcodeTraceAccess; +import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess; import ghidra.program.model.address.*; import ghidra.trace.model.thread.TraceThread; @@ -73,13 +74,41 @@ public enum DebuggerEmulationIntegration { } /** - * Create state callbacks that lazily load data and immediately write changes to the given - * access shim. + * Create state callbacks that lazily load data and writes changes to the given access shim. + *

    + * Reads may be redirected to the target. + *

    + * Use this instead of {@link #bytesImmediateWriteTarget(PcodeDebuggerAccess)} when interfacing + * directly with a {@link PcodeExecutorState} vice a {@link PcodeEmulator}. * + * @see TraceEmulationIntegration#bytesImmediateWrite(PcodeTraceAccess, TraceThread, int) + * @param access the access shim for loads and stores + * @param thread the trace thread for register accesses + * @param frame the frame for register accesses, usually 0 + * @param mode determines whether or not writes affect the target + * @return the callbacks + */ + public static Writer bytesWriteMode(PcodeDebuggerAccess access, + TraceThread thread, int frame, Mode mode) { + Writer writer = new TraceWriter(access) { + @Override + protected PcodeTraceDataAccess getDataAccess(PcodeTraceAccess access, + AddressSpace space, PcodeThread ignored) { + return space.isRegisterSpace() + ? access.getDataForLocalState(thread, frame) + : access.getDataForSharedState(); + } + }; + writer.putHandler(new TargetBytesPieceHandler(mode)); + return writer; + } + + /** + * Create state callbacks that lazily load data and immediately writes changes to the given + * access shim. *

    * Reads may be redirected to the target. If redirected, writes are immediately sent to the * target and presumably stored into the trace at the same snapshot as state is sourced. - * *

    * Use this instead of {@link #bytesImmediateWriteTarget(PcodeDebuggerAccess)} when interfacing * directly with a {@link PcodeExecutorState} vice a {@link PcodeEmulator}. @@ -92,15 +121,7 @@ public enum DebuggerEmulationIntegration { */ public static PcodeStateCallbacks bytesImmediateWriteTarget(PcodeDebuggerAccess access, TraceThread thread, int frame) { - PcodeDebuggerRegistersAccess regAcc = access.getDataForLocalState(thread, frame); - Writer writer = new TraceWriter(access) { - @Override - protected PcodeTraceRegistersAccess getRegAccess(PcodeThread ignored) { - return regAcc; - } - }; - writer.putHandler(new TargetBytesPieceHandler(Mode.RW)); - return writer.wrapFor(null); + return bytesWriteMode(access, thread, frame, Mode.RW).wrapFor(null); } protected static T waitTimeout(CompletableFuture future) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java index 028826d517..2fef0fda7a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java @@ -41,6 +41,7 @@ import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; import ghidra.app.plugin.core.debug.gui.DebuggerResources; +import ghidra.app.plugin.core.debug.gui.emulation.DebuggerEmulateFunctionDialog; import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess; import ghidra.app.services.*; import ghidra.async.AsyncLazyMap; @@ -62,6 +63,7 @@ import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess; import ghidra.pcode.exec.trace.data.PcodeTraceAccess; import ghidra.program.model.address.*; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; import ghidra.trace.model.*; @@ -130,6 +132,27 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm } } + interface EmulateFunctionAction { + String NAME = "Emulate Function"; + String DESCRIPTION = "Emulate the current function in a new trace"; + Icon ICON = DebuggerResources.ICON_EMULATE; + String GROUP = DebuggerResources.GROUP_GENERAL; + String HELP_ANCHOR = "emulate_function"; + + static ActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ActionBuilder(NAME, ownerName) + .description(DESCRIPTION) + .menuPath(DebuggerPluginPackage.NAME, NAME) + .menuIcon(ICON) + .menuGroup(GROUP) + .popupMenuPath(NAME) + .popupMenuIcon(ICON) + .popupMenuGroup(GROUP) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + interface EmulateAddThreadAction { String NAME = "Add Emulated Thread to Trace"; String DESCRIPTION = "Add an emulated thread to the current trace starting here"; @@ -374,6 +397,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm private AutoService.Wiring autoServiceWiring; DockingAction actionEmulateProgram; + DockingAction actionEmulateFunction; DockingAction actionEmulateAddThread; DockingAction actionInvalidateCache; Map, ToggleDockingAction> actionsChooseEmulatorFactory = @@ -399,6 +423,12 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm .popupWhen(this::emulateProgramEnabled) .onAction(this::emulateProgramActivated) .buildAndInstall(tool); + actionEmulateFunction = EmulateFunctionAction.builder(this) + .withContext(ProgramLocationActionContext.class) + .enabledWhen(this::emulateFunctionEnabled) + .popupWhen(this::emulateFunctionEnabled) + .onAction(this::emulateFunctionActivated) + .buildAndInstall(tool); actionEmulateAddThread = EmulateAddThreadAction.builder(this) .withContext(ProgramLocationActionContext.class) .enabledWhen(this::emulateAddThreadEnabled) @@ -487,6 +517,30 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm } } + private boolean emulateFunctionEnabled(ProgramLocationActionContext ctx) { + Program program = ctx.getProgram(); + if (program == null || program instanceof TraceProgramView) { + return false; + } + Function function = program.getFunctionManager().getFunctionContaining(ctx.getAddress()); + if (function == null) { + return false; + } + return true; + } + + private void emulateFunctionActivated(ProgramLocationActionContext ctx) { + Program program = ctx.getProgram(); + if (program == null) { + return; + } + Function function = program.getFunctionManager().getFunctionContaining(ctx.getAddress()); + if (function == null) { + return; + } + tool.showDialog(new DebuggerEmulateFunctionDialog(tool, function)); + } + private boolean emulateAddThreadEnabled(ProgramLocationActionContext ctx) { Program programOrView = ctx.getProgram(); if (programOrView instanceof TraceProgramView view) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/AbstractPcodeDebuggerAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/AbstractPcodeDebuggerAccess.java index d16a9e4448..1d616af248 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/AbstractPcodeDebuggerAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/AbstractPcodeDebuggerAccess.java @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.debug.service.emulation.data; import ghidra.debug.api.emulation.*; import ghidra.debug.api.target.Target; -import ghidra.framework.plugintool.ServiceProvider; import ghidra.pcode.exec.trace.data.AbstractPcodeTraceAccess; import ghidra.trace.model.guest.TracePlatform; @@ -27,41 +26,35 @@ import ghidra.trace.model.guest.TracePlatform; * @param the type of shared data-access shims provided * @param the type of thread-local data-access shims provided */ -public abstract class AbstractPcodeDebuggerAccess - extends AbstractPcodeTraceAccess +public abstract class AbstractPcodeDebuggerAccess extends AbstractPcodeTraceAccess implements PcodeDebuggerAccess { - protected final ServiceProvider provider; protected final Target target; /** * Construct a shim * - * @param provider the service provider (usually the tool) * @param target the target * @param platform the associated platform, having the same trace as the recorder * @param snap the associated snap */ - public AbstractPcodeDebuggerAccess(ServiceProvider provider, Target target, - TracePlatform platform, long snap) { + public AbstractPcodeDebuggerAccess(Target target, TracePlatform platform, long snap) { super(platform, snap); - this.provider = provider; this.target = target; } /** * Construct a shim * - * @param provider the service provider (usually the tool) * @param target the target * @param platform the associated platform, having the same trace as the recorder * @param snap the associated snap * @param threadsSnap the snap to use when finding associated threads between trace and emulator */ - public AbstractPcodeDebuggerAccess(ServiceProvider provider, Target target, - TracePlatform platform, long snap, long threadsSnap) { + public AbstractPcodeDebuggerAccess(Target target, TracePlatform platform, long snap, + long threadsSnap) { super(platform, snap, threadsSnap); - this.provider = provider; this.target = target; } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerAccess.java index 4922982513..09c4d73326 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerAccess.java @@ -25,9 +25,10 @@ import ghidra.trace.model.thread.TraceThread; /** * The default target-and-trace access shim for a session */ -public class DefaultPcodeDebuggerAccess extends - AbstractPcodeDebuggerAccess // - { +public class DefaultPcodeDebuggerAccess extends AbstractPcodeDebuggerAccess< + DefaultPcodeDebuggerMemoryAccess, DefaultPcodeDebuggerRegistersAccess> { + + protected final ServiceProvider provider; /** * Construct a shim @@ -39,7 +40,8 @@ public class DefaultPcodeDebuggerAccess extends */ public DefaultPcodeDebuggerAccess(ServiceProvider provider, Target target, TracePlatform platform, long snap) { - super(provider, target, platform, snap); + super(target, platform, snap); + this.provider = provider; } /** @@ -53,7 +55,8 @@ public class DefaultPcodeDebuggerAccess extends */ public DefaultPcodeDebuggerAccess(ServiceProvider provider, Target target, TracePlatform platform, long snap, long threadsSnap) { - super(provider, target, platform, snap, threadsSnap); + super(target, platform, snap, threadsSnap); + this.provider = provider; } /** @@ -75,7 +78,7 @@ public class DefaultPcodeDebuggerAccess extends @Override protected DefaultPcodeDebuggerRegistersAccess newDataForLocalState(TraceThread thread, int frame) { - return new DefaultPcodeDebuggerRegistersAccess(provider, target, platform, snap, thread, - frame, viewport); + return new DefaultPcodeDebuggerRegistersAccess(target, platform, snap, thread, frame, + viewport); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java index e2d1ade782..e53d8d6697 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java @@ -16,39 +16,25 @@ package ghidra.app.plugin.core.debug.service.emulation.data; import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor; import ghidra.app.services.DebuggerStaticMappingService; -import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess; -import ghidra.debug.api.modules.MappedAddressRange; +import ghidra.debug.api.modules.DebuggerAddressTranslator; import ghidra.debug.api.target.Target; import ghidra.framework.plugintool.ServiceProvider; -import ghidra.pcode.exec.PcodeExecutorStatePiece; -import ghidra.pcode.exec.trace.data.DefaultPcodeTraceMemoryAccess; -import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; import ghidra.trace.model.TraceTimeViewport; import ghidra.trace.model.guest.TracePlatform; -import ghidra.util.Msg; -import ghidra.util.task.TaskMonitor; /** * The default data-and-debugger-access shim for session memory */ -public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAccess - implements PcodeDebuggerMemoryAccess, InternalPcodeDebuggerDataAccess { +public class DefaultPcodeDebuggerMemoryAccess extends TranslatedPcodeDebuggerMemoryAccess { protected final ServiceProvider provider; - protected final Target target; /** * Construct a shim * - * @param provider the service provider (usually the tool) + * @param provider the service provider (usually the tool) to get the static mapping service * @param target the target * @param platform the associated platform, having the same trace as the recorder * @param snap the associated snap @@ -56,94 +42,12 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc */ protected DefaultPcodeDebuggerMemoryAccess(ServiceProvider provider, Target target, TracePlatform platform, long snap, TraceTimeViewport viewport) { - super(platform, snap, viewport); + super(target, platform, snap, viewport); this.provider = Objects.requireNonNull(provider); - this.target = target; } @Override - public boolean isLive() { - return InternalPcodeDebuggerDataAccess.super.isLive(); - } - - @Override - public ServiceProvider getServiceProvider() { - return provider; - } - - @Override - public Target getTarget() { - return target; - } - - @Override - public CompletableFuture readFromTargetMemory(AddressSetView guestView) { - if (!isLive()) { - return CompletableFuture.completedFuture(false); - } - AddressSetView hostView = platform.mapGuestToHost(guestView); - return target.readMemoryAsync(hostView, TaskMonitor.DUMMY).thenApply(__ -> true); - } - - @Override - public CompletableFuture writeTargetMemory(Address address, byte[] data) { - if (!isLive()) { - return CompletableFuture.completedFuture(false); - } - return target.writeMemoryAsync(address, data).thenApply(__ -> true); - } - - @Override - public AddressSetView readFromStaticImages(PcodeExecutorStatePiece piece, - AddressSetView guestView) { - // NOTE: If we expand to block, DON'T OVERWRITE KNOWN! - DebuggerStaticMappingService mappingService = - provider.getService(DebuggerStaticMappingService.class); - if (mappingService == null) { - return guestView; - } - - AddressSet remains = new AddressSet(guestView); - try { - boolean result = new AbstractMappedMemoryBytesVisitor(mappingService, new byte[4096]) { - @Override - protected int read(Memory memory, Address addr, byte[] dest, int size) - throws MemoryAccessException { - int read = super.read(memory, addr, dest, size); - if (read < size) { - Msg.warn(this, - String.format(" Partial read of %s. Wanted %d bytes. Got %d.", - addr, size, read)); - } - return read; - } - - @Override - protected boolean visitRange(Program program, AddressRange progRng, - MappedAddressRange mappedRng) throws MemoryAccessException { - Msg.debug(this, - "Filling in unknown trace memory in emulator using mapped image: " + - program + ": " + progRng); - return super.visitRange(program, progRng, mappedRng); - } - - @Override - protected void visitData(Address hostAddr, byte[] data, int size) { - Address guestAddr = platform.mapHostToGuest(hostAddr); - piece.setVarInternal(guestAddr.getAddressSpace(), guestAddr.getOffset(), size, - data); - remains.delete(guestAddr, guestAddr.add(size)); - } - }.visit(platform.getTrace(), snap, platform.mapGuestToHost(guestView)); - return result ? remains : guestView; - } - catch (MemoryAccessException e) { - throw new AssertionError(e); - } - } - - @Override - public PcodeTracePropertyAccess getPropertyAccess(String name, Class type) { - return new DefaultPcodeDebuggerPropertyAccess<>(this, name, type); + public DebuggerAddressTranslator getAddressTranslator() { + return provider.getService(DebuggerStaticMappingService.class); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerPropertyAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerPropertyAccess.java index 23fc7f0c5d..b07b072f4c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerPropertyAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerPropertyAccess.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,7 @@ */ package ghidra.app.plugin.core.debug.service.emulation.data; -import ghidra.app.services.DebuggerStaticMappingService; +import ghidra.debug.api.modules.DebuggerAddressTranslator; import ghidra.pcode.exec.trace.data.DefaultPcodeTracePropertyAccess; import ghidra.program.model.address.Address; import ghidra.program.model.util.PropertyMap; @@ -52,12 +52,11 @@ public class DefaultPcodeDebuggerPropertyAccess @Override protected T whenNull(Address hostAddress) { - DebuggerStaticMappingService mappingService = - data.getServiceProvider().getService(DebuggerStaticMappingService.class); - if (mappingService == null) { + DebuggerAddressTranslator translator = data.getAddressTranslator(); + if (translator == null) { return super.whenNull(hostAddress); } - ProgramLocation progLoc = mappingService.getOpenMappedLocation(new DefaultTraceLocation( + ProgramLocation progLoc = translator.getOpenMappedLocation(new DefaultTraceLocation( data.getPlatform().getTrace(), null, Lifespan.at(data.getSnap()), hostAddress)); if (progLoc == null) { return super.whenNull(hostAddress); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerRegistersAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerRegistersAccess.java index f001093d03..41c62e892b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerRegistersAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerRegistersAccess.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,8 +18,8 @@ package ghidra.app.plugin.core.debug.service.emulation.data; import java.util.concurrent.CompletableFuture; import ghidra.debug.api.emulation.PcodeDebuggerRegistersAccess; +import ghidra.debug.api.modules.DebuggerAddressTranslator; import ghidra.debug.api.target.Target; -import ghidra.framework.plugintool.ServiceProvider; import ghidra.pcode.exec.trace.data.DefaultPcodeTraceRegistersAccess; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; @@ -33,13 +33,11 @@ import ghidra.trace.model.thread.TraceThread; public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegistersAccess implements PcodeDebuggerRegistersAccess, InternalPcodeDebuggerDataAccess { - protected final ServiceProvider provider; protected final Target target; /** * Construct a shim * - * @param provider the service provider (usually the tool) * @param target the target * @param platform the associated platform, having the same trace as the recorder * @param snap the associated snap @@ -47,11 +45,9 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist * @param frame the associated frame, or 0 if not applicable * @param viewport the viewport, set to the same snapshot */ - protected DefaultPcodeDebuggerRegistersAccess(ServiceProvider provider, Target target, - TracePlatform platform, long snap, TraceThread thread, int frame, - TraceTimeViewport viewport) { + protected DefaultPcodeDebuggerRegistersAccess(Target target, TracePlatform platform, long snap, + TraceThread thread, int frame, TraceTimeViewport viewport) { super(platform, snap, thread, frame, viewport); - this.provider = provider; this.target = target; } @@ -61,8 +57,8 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist } @Override - public ServiceProvider getServiceProvider() { - return provider; + public DebuggerAddressTranslator getAddressTranslator() { + return null; } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/InternalPcodeDebuggerDataAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/InternalPcodeDebuggerDataAccess.java index 1bfdd51bcd..e150c0299b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/InternalPcodeDebuggerDataAccess.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/InternalPcodeDebuggerDataAccess.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,18 +15,34 @@ */ package ghidra.app.plugin.core.debug.service.emulation.data; +import ghidra.debug.api.modules.DebuggerAddressTranslator; import ghidra.debug.api.target.Target; -import ghidra.framework.plugintool.ServiceProvider; import ghidra.lifecycle.Internal; import ghidra.pcode.exec.trace.data.InternalPcodeTraceDataAccess; import ghidra.trace.model.TraceTimeViewport; @Internal public interface InternalPcodeDebuggerDataAccess extends InternalPcodeTraceDataAccess { - ServiceProvider getServiceProvider(); + /** + * {@return the address translator or null. If null is returned, then bytes cannot be loaded + * from mapped images.} + */ + DebuggerAddressTranslator getAddressTranslator(); + /** + * {@return the target or null} + */ Target getTarget(); + /** + * Check if the associated trace represents a live target + *

    + * To be live, there must be a non-null target, that target must still be valid, i.e., connected + * to a live debugger, and the current snapshot must have a viewport incorporating the target's + * current snapshot. + * + * @return true if alive or false if dead/offline + */ default boolean isLive() { Target target = getTarget(); if (target == null || !target.isValid()) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerAccess.java new file mode 100644 index 0000000000..c49f406b72 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerAccess.java @@ -0,0 +1,92 @@ +/* ### + * 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.service.emulation.data; + +import ghidra.debug.api.modules.DebuggerAddressTranslator; +import ghidra.debug.api.target.Target; +import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess; +import ghidra.pcode.exec.trace.data.PcodeTraceAccess; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.thread.TraceThread; + +/** + * The default target-and-trace access shim for a session with a provided + * {@linkplain DebuggerAddressTranslator address translator}. + */ +public abstract class TranslatedPcodeDebuggerAccess extends AbstractPcodeDebuggerAccess< + TranslatedPcodeDebuggerMemoryAccess, DefaultPcodeDebuggerRegistersAccess> { + + /** + * Construct a shim + * + * @param target the target + * @param platform the associated platform, having the same trace as the recorder + * @param snap the associated snap + */ + public TranslatedPcodeDebuggerAccess(Target target, + TracePlatform platform, long snap) { + super(target, platform, snap); + } + + /** + * Construct a shim + * + * @param target the target + * @param platform the associated platform, having the same trace as the recorder + * @param snap the associated snap + * @param threadsSnap the snap to use when finding associated threads between trace and emulator + */ + public TranslatedPcodeDebuggerAccess(Target target, TracePlatform platform, long snap, + long threadsSnap) { + super(target, platform, snap, threadsSnap); + } + + /** + * {@return the address translator or null. If null is returned, then bytes cannot be loaded + * from mapped images.} + * + * @see InternalPcodeDebuggerDataAccess#getAddressTranslator() + */ + public abstract DebuggerAddressTranslator getAddressTranslator(); + + /** + * {@inheritDoc} + * + * @implNote This does not return a Debugger access shim, but a Trace one, since we + * never expect a delayed write to affect the target. + */ + @Override + public PcodeTraceAccess deriveForWrite(long snap) { + return new DefaultPcodeTraceAccess(platform, snap, threadsSnap); + } + + @Override + protected TranslatedPcodeDebuggerMemoryAccess newDataForSharedState() { + return new TranslatedPcodeDebuggerMemoryAccess(target, platform, snap, viewport) { + @Override + public DebuggerAddressTranslator getAddressTranslator() { + return TranslatedPcodeDebuggerAccess.this.getAddressTranslator(); + } + }; + } + + @Override + protected DefaultPcodeDebuggerRegistersAccess newDataForLocalState(TraceThread thread, + int frame) { + return new DefaultPcodeDebuggerRegistersAccess(target, platform, snap, thread, frame, + viewport); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerMemoryAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerMemoryAccess.java new file mode 100644 index 0000000000..b6041a62ec --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerMemoryAccess.java @@ -0,0 +1,139 @@ +/* ### + * 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.service.emulation.data; + +import java.util.concurrent.CompletableFuture; + +import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor; +import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess; +import ghidra.debug.api.modules.DebuggerAddressTranslator; +import ghidra.debug.api.modules.MappedAddressRange; +import ghidra.debug.api.target.Target; +import ghidra.pcode.exec.PcodeExecutorStatePiece; +import ghidra.pcode.exec.trace.data.DefaultPcodeTraceMemoryAccess; +import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.trace.model.TraceTimeViewport; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; + +/** + * A data-and-debugger-access shim for memory with a provided {@linkplain DebuggerAddressTranslator + * address translator}. + */ +public abstract class TranslatedPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAccess + implements PcodeDebuggerMemoryAccess, InternalPcodeDebuggerDataAccess { + + protected final Target target; + + /** + * Construct a shim + * + * @param target the target + * @param platform the associated platform, having the same trace as the recorder + * @param snap the associated snap + * @param viewport the viewport, set to the same snapshot + */ + protected TranslatedPcodeDebuggerMemoryAccess(Target target, TracePlatform platform, long snap, + TraceTimeViewport viewport) { + super(platform, snap, viewport); + this.target = target; + } + + @Override + public boolean isLive() { + return InternalPcodeDebuggerDataAccess.super.isLive(); + } + + @Override + public Target getTarget() { + return target; + } + + @Override + public CompletableFuture readFromTargetMemory(AddressSetView guestView) { + if (!isLive()) { + return CompletableFuture.completedFuture(false); + } + AddressSetView hostView = platform.mapGuestToHost(guestView); + return target.readMemoryAsync(hostView, TaskMonitor.DUMMY).thenApply(__ -> true); + } + + @Override + public CompletableFuture writeTargetMemory(Address address, byte[] data) { + if (!isLive()) { + return CompletableFuture.completedFuture(false); + } + return target.writeMemoryAsync(address, data).thenApply(__ -> true); + } + + @Override + public AddressSetView readFromStaticImages(PcodeExecutorStatePiece piece, + AddressSetView guestView) { + // NOTE: If we expand to block, DON'T OVERWRITE KNOWN! + DebuggerAddressTranslator translator = getAddressTranslator(); + if (translator == null) { + return guestView; + } + + AddressSet remains = new AddressSet(guestView); + try { + boolean result = new AbstractMappedMemoryBytesVisitor(translator, new byte[4096]) { + @Override + protected int read(Memory memory, Address addr, byte[] dest, int size) + throws MemoryAccessException { + int read = super.read(memory, addr, dest, size); + if (read < size) { + Msg.warn(this, + String.format(" Partial read of %s. Wanted %d bytes. Got %d.", + addr, size, read)); + } + return read; + } + + @Override + protected boolean visitRange(Program program, AddressRange progRng, + MappedAddressRange mappedRng) throws MemoryAccessException { + Msg.debug(this, + "Filling in unknown trace memory in emulator using mapped image: " + + program + ": " + progRng); + return super.visitRange(program, progRng, mappedRng); + } + + @Override + protected void visitData(Address hostAddr, byte[] data, int size) { + Address guestAddr = platform.mapHostToGuest(hostAddr); + piece.setVarInternal(guestAddr.getAddressSpace(), guestAddr.getOffset(), size, + data); + remains.delete(guestAddr, guestAddr.add(size)); + } + }.visit(platform.getTrace(), snap, platform.mapGuestToHost(guestView)); + return result ? remains : guestView; + } + catch (MemoryAccessException e) { + throw new AssertionError(e); + } + } + + @Override + public PcodeTracePropertyAccess getPropertyAccess(String name, Class type) { + return new DefaultPcodeDebuggerPropertyAccess<>(this, name, type); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java index af28f96c6a..460538f158 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java @@ -33,7 +33,7 @@ import ghidra.util.datastruct.ListenerSet; public class DebuggerStaticMappingContext implements DebuggerAddressTranslator { - record ChangeCollector(DebuggerStaticMappingContext ctx, Set traces, + public record ChangeCollector(DebuggerStaticMappingContext ctx, Set traces, Set programs) implements AutoCloseable { static Set subtract(Set a, Set b) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java index cc61556d29..a77f83771b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java @@ -406,8 +406,15 @@ public class SymPcodeExecutor extends PcodeExecutor { // This should always end a basic block, so just do nothing } + /** + * {@inheritDoc} + *

    + * This could be a {@link PcodeOp#RETURN return} and the slaspec does not always set PC + * explicitly, so do it here, but don't perform any actual control transfer. + */ @Override protected void doExecuteIndirectBranch(PcodeOp op, PcodeFrame frame) { - // This should always end a basic block, so just do nothing + Sym offset = state.getVar(getIndirectBranchTarget(op), reason); + branchToOffset(op, offset, frame); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutorState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutorState.java index a5cca4703e..c0171546bb 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutorState.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutorState.java @@ -240,9 +240,10 @@ public class SymPcodeExecutorState implements PcodeExecutorState { * @return the address (stack offset or register) of the return address */ public Address computeAddressOfReturn() { - return switch (getVar(language.getProgramCounter(), Reason.INSPECT)) { + Register pc = language.getProgramCounter(); + return switch (getVar(pc, Reason.INSPECT)) { case StackDerefSym stackVar -> cSpec.getStackSpace().getAddress(stackVar.offset()); - case RegisterSym regVar -> regVar.register().getAddress(); + case RegisterSym regVar when regVar.register() != pc -> regVar.register().getAddress(); default -> null; }; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java index f31b5345bc..0d43e3235d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java @@ -19,7 +19,7 @@ import java.util.Collection; import java.util.Map.Entry; import java.util.Objects; -import ghidra.app.services.DebuggerStaticMappingService; +import ghidra.debug.api.modules.DebuggerAddressTranslator; import ghidra.debug.api.modules.MappedAddressRange; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; @@ -39,20 +39,20 @@ import ghidra.util.MathUtilities; * bytes from the mapped programs, along with the trace address where they apply. */ public abstract class AbstractMappedMemoryBytesVisitor { - private final DebuggerStaticMappingService mappingService; + private final DebuggerAddressTranslator translator; private final byte[] buffer; /** * Construct a visitor object * - * @param mappingService the mapping service + * @param translator the address translator * @param buffer a buffer for the data. This is passed directly into * {@link #visitData(Address, byte[], int)}. If a mapped range exceeds the buffer * size, the range is broken down into smaller pieces. */ - public AbstractMappedMemoryBytesVisitor(DebuggerStaticMappingService mappingService, + public AbstractMappedMemoryBytesVisitor(DebuggerAddressTranslator translator, byte[] buffer) { - this.mappingService = Objects.requireNonNull(mappingService); + this.translator = Objects.requireNonNull(translator); this.buffer = buffer; } @@ -100,7 +100,7 @@ public abstract class AbstractMappedMemoryBytesVisitor { public boolean visit(Trace trace, long snap, AddressSetView hostView) throws MemoryAccessException { boolean result = false; - for (Entry> ent : mappingService + for (Entry> ent : translator .getOpenMappedViews(trace, hostView, snap) .entrySet()) { result |= visitProgram(ent.getKey(), ent.getValue()); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ManagedDomainObject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ManagedDomainObject.java index 38cc076786..d1423faea1 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ManagedDomainObject.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ManagedDomainObject.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,12 +23,13 @@ import ghidra.framework.model.DomainObject; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; +import utility.function.ExceptionalFunction; -public class ManagedDomainObject implements AutoCloseable { +public class ManagedDomainObject implements AutoCloseable { public static final Cleaner CLEANER = Cleaner.create(); - private static class ObjectState implements Runnable { - private DomainObject obj; + private static class ObjectState implements Runnable { + private T obj; @Override public synchronized void run() { @@ -37,7 +38,7 @@ public class ManagedDomainObject implements AutoCloseable { } } - public synchronized DomainObject get() { + public synchronized T get() { if (!obj.getConsumerList().contains(this)) { throw new IllegalStateException("Domain object is closed"); } @@ -45,20 +46,26 @@ public class ManagedDomainObject implements AutoCloseable { } } - private final ObjectState state = new ObjectState(); + private final ObjectState state = new ObjectState<>(); - public ManagedDomainObject(DomainFile file, boolean okToUpgrade, boolean okToRecover, - TaskMonitor monitor) throws VersionException, CancelledException, IOException { - state.obj = file.getDomainObject(state, okToUpgrade, okToRecover, monitor); + public ManagedDomainObject(DomainFile file, Class type, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { + state.obj = type.cast(file.getDomainObject(state, false, false, monitor)); + CLEANER.register(this, state); + } + + public ManagedDomainObject(ExceptionalFunction supplier) + throws E { + state.obj = supplier.apply(state); CLEANER.register(this, state); } @Override - public void close() throws Exception { + public void close() { state.run(); } - public DomainObject get() { + public T get() { return state.get(); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java index fc81e09d8c..1519b1607d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java @@ -25,6 +25,7 @@ import org.antlr.runtime.tree.CommonTreeNodeStream; import ghidra.app.nav.NavigationUtils; import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationIntegration; +import ghidra.app.plugin.core.debug.service.emulation.Mode; import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess; import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.app.plugin.processors.sleigh.SleighLanguage; @@ -36,6 +37,7 @@ import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.SleighProgramCompiler.ErrorCollectingPcodeParser; import ghidra.pcode.exec.SleighUtils.LitIdMode; import ghidra.pcode.exec.trace.*; +import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess; import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess; import ghidra.pcode.utils.Utils; @@ -320,6 +322,39 @@ public enum DebuggerPcodeUtils { return compileExpression(provider, coordinates, current, source, LitIdMode.NORMAL); } + public record WriterAndState(Writer writer, PcodeExecutorState state) {} + + /** + * Get a p-code executor state for the given coordinates + * + *

    + * If a thread is included, the executor state will have access to both the memory and registers + * in the context of that thread. Otherwise, only memory access is permitted. + * + * @param provider the service provider (usually the tool) + * @param coordinates the coordinates + * @param mode determines whether or not writes affect the target + * @return the state + */ + public static WriterAndState executorStateForCoordinates(ServiceProvider provider, + DebuggerCoordinates coordinates, Mode mode) { + Trace trace = coordinates.getTrace(); + if (trace == null) { + throw new IllegalArgumentException("Coordinates have no trace"); + } + TracePlatform platform = coordinates.getPlatform(); + if (!(platform.getLanguage() instanceof SleighLanguage language)) { + throw new IllegalArgumentException( + "Given trace or platform does not use a Sleigh language"); + } + DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(provider, + coordinates.getTarget(), platform, coordinates.getViewSnap()); + Writer writer = DebuggerEmulationIntegration.bytesWriteMode(access, + coordinates.getThread(), coordinates.getFrame(), mode); + return new WriterAndState(writer, + new BytesPcodeExecutorState(language, writer.wrapFor(null))); + } + /** * Get a p-code executor state for the given coordinates * @@ -333,20 +368,7 @@ public enum DebuggerPcodeUtils { */ public static PcodeExecutorState executorStateForCoordinates(ServiceProvider provider, DebuggerCoordinates coordinates) { - Trace trace = coordinates.getTrace(); - if (trace == null) { - throw new IllegalArgumentException("Coordinates have no trace"); - } - TracePlatform platform = coordinates.getPlatform(); - if (!(platform.getLanguage() instanceof SleighLanguage language)) { - throw new IllegalArgumentException( - "Given trace or platform does not use a Sleigh language"); - } - DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(provider, - coordinates.getTarget(), platform, coordinates.getViewSnap()); - PcodeStateCallbacks cb = DebuggerEmulationIntegration.bytesImmediateWriteTarget(access, - coordinates.getThread(), coordinates.getFrame()); - return new BytesPcodeExecutorState(language, cb); + return executorStateForCoordinates(provider, coordinates, Mode.RW).state; } /** diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePluginScreenShots.java new file mode 100644 index 0000000000..bf95664191 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePluginScreenShots.java @@ -0,0 +1,145 @@ +/* ### + * 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.service.emulation; + +import java.io.IOException; +import java.util.List; + +import org.junit.*; + +import db.Transaction; +import ghidra.app.plugin.core.debug.gui.emulation.DebuggerEmulateFunctionDialog; +import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; +import ghidra.app.services.ProgramManager; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.model.DomainObject; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; +import ghidra.program.model.symbol.SourceType; +import ghidra.test.ToyProgramBuilder; +import ghidra.util.InvalidNameException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.ConsoleTaskMonitor; +import ghidra.util.task.TaskMonitor; +import help.screenshot.GhidraScreenShotGenerator; + +public class DebuggerEmulationServicePluginScreenShots extends GhidraScreenShotGenerator { + private static final TaskMonitor MONITOR = new ConsoleTaskMonitor(); + + private static Address addr(Program program, long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } + + private static AddressSetView set(Program program, long min, long max) { + return new AddressSet(addr(program, min), addr(program, max)); + } + + ProgramManager programManager; + DebuggerEmulationServicePlugin emuService; + Function function; + Program progam; + DataTypeManager dtm; + + DataType dtInt; + DataType dtCharPtrPtr; + DataType dtStructPtr; + + @Before + public void setUpMine() throws Throwable { + programManager = addPlugin(tool, ProgramManagerPlugin.class); + emuService = addPlugin(tool, DebuggerEmulationServicePlugin.class); + } + + @After + public void tearDownMine() { + if (program != null) { + program.release(this); + } + } + + // TODO: Propose this replace waitForProgram + public static void waitForDomainObject(DomainObject object) { + object.flushEvents(); + waitForSwing(); + } + + protected void intoProject(DomainObject obj) { + waitForDomainObject(obj); + DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder(); + waitForCondition(() -> { + try { + rootFolder.createFile(obj.getName(), obj, MONITOR); + return true; + } + catch (InvalidNameException | CancelledException e) { + throw new AssertionError(e); + } + catch (IOException e) { + // Usually "object is busy". Try again. + return false; + } + }); + } + + @SuppressWarnings("unchecked") + T resolve(T dt) { + try (Transaction tx = program.openTransaction("Resolved %s".formatted(dt))) { + DataTypeConflictHandler handler = DataTypeConflictHandler.DEFAULT_HANDLER; + return (T) dtm.resolve(dt, handler); + } + } + + @Test + public void testCaptureDebuggerEmulateFunctionDialog() throws Throwable { + program = createDefaultProgram("game", ToyProgramBuilder._X64, this); + intoProject(program); + dtm = program.getDataTypeManager(); + try (Transaction tx = program.openTransaction("Create Function")) { + program.getMemory() + .createInitializedBlock(".text", addr(program, 0x00400000), 0x10000, (byte) 0, + MONITOR, false); + function = program.getFunctionManager() + .createFunction("parse_opts", addr(program, 0x00401000), + set(program, 0x00401000, 0x00401100), SourceType.USER_DEFINED); + + dtInt = resolve(IntegerDataType.dataType); + dtCharPtrPtr = resolve(new PointerDataType(new PointerDataType(CharDataType.dataType))); + + Structure st = new StructureDataType("Options", 0, dtm); + st.add(dtInt, "width", ""); + st.add(dtInt, "height", ""); + dtStructPtr = resolve(new PointerDataType(st)); + + Variable returnVar = new ReturnParameterImpl(dtStructPtr, program); + Variable param1 = new ParameterImpl("argc", dtInt, program); + Variable param2 = new ParameterImpl("argv", dtCharPtrPtr, program); + + function.updateFunction("default", returnVar, List.of(param1, param2), + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, false, SourceType.ANALYSIS); + } + programManager.openProgram(program); + goTo(tool, progam, function.getEntryPoint()); + performAction(emuService.actionEmulateFunction, false); + + DebuggerEmulateFunctionDialog dialog = + waitForDialogComponent(DebuggerEmulateFunctionDialog.class); + + runSwing(() -> dialog.getComponent().requestFocus()); + captureDialog(dialog); + } +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java index 7026fa9e5d..2c747e0d50 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java @@ -93,8 +93,11 @@ public enum TraceEmulationIntegration { TraceThread thread, int frame) { Writer writer = new TraceWriter(access) { @Override - protected PcodeTraceRegistersAccess getRegAccess(PcodeThread ignored) { - return access.getDataForLocalState(thread, frame); + protected PcodeTraceDataAccess getDataAccess(PcodeTraceAccess access, + AddressSpace space, PcodeThread ignored) { + return space.isRegisterSpace() + ? access.getDataForLocalState(thread, frame) + : access.getDataForSharedState(); } }; writer.putHandler(new ImmediateBytesPieceHandler()); @@ -310,12 +313,13 @@ public enum TraceEmulationIntegration { * also have state assigned to abstract addresses. In such cases, it is up to the handler to * track what has been written. * + * @param writer the writer * @param into the destination trace access * @param thread the thread associated with the piece's state * @param piece the source state piece * @param written the portion that is known to have been written */ - void writeDown(PcodeTraceDataAccess into, PcodeThread thread, + void writeDown(TraceWriter writer, PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written); } @@ -346,7 +350,7 @@ public enum TraceEmulationIntegration { } @Override - public void writeDown(PcodeTraceDataAccess into, PcodeThread thread, + public void writeDown(TraceWriter writer, PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written) { } } @@ -398,20 +402,21 @@ public enum TraceEmulationIntegration { } @Override - public void writeDown(PcodeTraceDataAccess into, PcodeThread thread, + public void writeDown(TraceWriter writer, PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written) { for (AddressRange range : written) { AddressSpace space = range.getAddressSpace(); if (space.isUniqueSpace()) { continue; } + PcodeTraceDataAccess acc = writer.getDataAccess(into, space, thread); long lower = range.getMinAddress().getOffset(); long fullLen = range.getLength(); while (fullLen > 0) { int len = MathUtilities.unsignedMin(CHUNK_SIZE, fullLen); // NOTE: Would prefer less copying and less heap garbage.... byte[] bytes = piece.getVarInternal(space, lower, len, Reason.INSPECT); - into.putBytes(space.getAddress(lower), ByteBuffer.wrap(bytes)); + acc.putBytes(space.getAddress(lower), ByteBuffer.wrap(bytes)); lower += bytes.length; fullLen -= bytes.length; @@ -564,14 +569,15 @@ public enum TraceEmulationIntegration { * portion. */ @Override - public void writeDown(PcodeTraceDataAccess into, PcodeThread thread, + public void writeDown(TraceWriter writer, PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written) { - PcodeTracePropertyAccess

    property = - into.getPropertyAccess(getPropertyName(), getPropertyType()); AddressSet remains = new AddressSet(written); while (!remains.isEmpty()) { Address cur = remains.getMinAddress(); AddressSpace space = cur.getAddressSpace(); + PcodeTraceDataAccess acc = writer.getDataAccess(into, space, thread); + PcodeTracePropertyAccess

    property = + acc.getPropertyAccess(getPropertyName(), getPropertyType()); Entry entry = piece.getNextEntryInternal(space, cur.getOffset()); if (entry == null) { remains.delete(space.getMinAddress(), space.getMaxAddress()); @@ -662,18 +668,19 @@ public enum TraceEmulationIntegration { protected final PcodeTraceMemoryAccess memAccess; protected final Map, PcodeTraceRegistersAccess> regAccess = new HashMap<>(); + record PieceInfo(PcodeThread thread, AddressSet written) {} + /** * An address set to track what has actually been written. It's not enough to just use the * {@link SemisparseByteArray}'s initialized set, as that may be caching bytes from the * trace which are still {@link TraceMemoryState#UNKNOWN}. */ - protected final AddressSet memWritten = new AddressSet(); - protected final Map, AddressSet> regsWritten = new HashMap<>(); + //protected final AddressSet memWritten = new AddressSet(); + //protected final Map, AddressSet> regsWritten = new HashMap<>(); + protected final Map, PieceInfo> pieces = new HashMap<>(); protected final Map, PieceHandler> handlers = new HashMap<>(); - private PcodeMachine emulator; - /** * Construct a writer which sources state from the given access shim * @@ -689,11 +696,6 @@ public enum TraceEmulationIntegration { handlers.put(PieceType.forHandler(handler), handler); } - @Override - public void emulatorCreated(PcodeMachine emulator) { - this.emulator = emulator; - } - @Override public void threadCreated(PcodeThread thread) { access.getDataForLocalState(thread, 0).initializeThreadContext(thread); @@ -715,28 +717,17 @@ public enum TraceEmulationIntegration { * @param piece the piece * @param written the logged portions written */ - protected void writePieceDown(PcodeTraceDataAccess into, PcodeThread thread, + protected void writePieceDown(PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written) { PieceHandler handler = handlerFor(piece); - handler.writeDown(into, thread, piece, written); + handler.writeDown(this, into, thread, piece, written); } @Override public void writeDown(PcodeTraceAccess into) { - PcodeTraceMemoryAccess memInto = into.getDataForSharedState(); - for (PcodeExecutorStatePiece piece : emulator.getSharedState() - .streamPieces() - .toList()) { - writePieceDown(memInto, null, piece, memWritten); - } - for (PcodeThread thread : emulator.getAllThreads()) { - PcodeTraceRegistersAccess regInto = into.getDataForLocalState(thread, 0); - AddressSetView written = regsWritten.getOrDefault(thread, new AddressSet()); - for (PcodeExecutorStatePiece piece : thread.getState() - .streamPieces() - .toList()) { - writePieceDown(regInto, thread, piece, written); - } + for (Entry, PieceInfo> ent : pieces.entrySet()) { + PieceInfo info = ent.getValue(); + writePieceDown(into, info.thread, ent.getKey(), info.written); } } @@ -745,22 +736,20 @@ public enum TraceEmulationIntegration { writeDown(access.deriveForWrite(snap)); } - protected PcodeTraceRegistersAccess getRegAccess(PcodeThread thread) { - // Always use frame 0 - return regAccess.computeIfAbsent(thread, t -> access.getDataForLocalState(t, 0)); + protected PcodeTraceDataAccess getDataAccess(PcodeTraceAccess access, AddressSpace space, + PcodeThread thread) { + return space.isRegisterSpace() + ? access.getDataForLocalState(thread, 0) + : access.getDataForSharedState(); } @Override public void dataWritten(PcodeThread thread, - PcodeExecutorStatePiece piece, - Address address, int length, U value) { - PcodeTraceDataAccess acc = address.isRegisterAddress() - ? getRegAccess(thread) - : memAccess; - AddressSet written = address.isRegisterAddress() - ? regsWritten.computeIfAbsent(thread, t -> new AddressSet()) - : memWritten; - if (handlerFor(piece).dataWritten(acc, written, thread, piece, address, length, + PcodeExecutorStatePiece piece, Address address, int length, U value) { + PcodeTraceDataAccess acc = getDataAccess(access, address.getAddressSpace(), thread); + PieceInfo info = + pieces.computeIfAbsent(piece, p -> new PieceInfo(thread, new AddressSet())); + if (handlerFor(piece).dataWritten(acc, info.written, thread, piece, address, length, value)) { return; } @@ -769,12 +758,12 @@ public enum TraceEmulationIntegration { } Address end = address.addWrap(length - 1); if (address.compareTo(end) <= 0) { - written.add(address, end); + info.written.add(address, end); } else { AddressSpace space = address.getAddressSpace(); - written.add(address, space.getMaxAddress()); - written.add(space.getMinAddress(), end); + info.written.add(address, space.getMaxAddress()); + info.written.add(space.getMinAddress(), end); } } @@ -782,19 +771,18 @@ public enum TraceEmulationIntegration { public void dataWritten(PcodeThread thread, PcodeExecutorStatePiece piece, AddressSpace space, B offset, int length, U value) { - PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess; - AddressSet written = space.isRegisterSpace() - ? regsWritten.computeIfAbsent(thread, t -> new AddressSet()) - : memWritten; - handlerFor(piece).abstractWritten(acc, written, thread, piece, space, offset, length, - value); + PcodeTraceDataAccess acc = getDataAccess(access, space, thread); + PieceInfo info = + pieces.computeIfAbsent(piece, p -> new PieceInfo(thread, new AddressSet())); + handlerFor(piece).abstractWritten(acc, info.written, thread, piece, space, offset, + length, value); } @Override public int readUninitialized(PcodeThread thread, PcodeExecutorStatePiece piece, AddressSpace space, B offset, int length, Reason reason) { - PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess; + PcodeTraceDataAccess acc = getDataAccess(access, space, thread); return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length, reason); } @@ -809,7 +797,7 @@ public enum TraceEmulationIntegration { if (space.isUniqueSpace()) { return set; } - PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess; + PcodeTraceDataAccess acc = getDataAccess(access, space, thread); return handlerFor(piece).readUninitialized(acc, thread, piece, set); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java index 91604854f5..2b2050d375 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java @@ -16,6 +16,8 @@ package ghidra.pcode.exec.trace.data; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; import ghidra.program.model.address.*; import ghidra.program.model.lang.Language; @@ -36,6 +38,8 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace protected final TraceMemoryManager mm; + protected final Map> properties = new HashMap<>(); + /** * Construct a shim * @@ -204,7 +208,11 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace } @Override + @SuppressWarnings("unchecked") public PcodeTracePropertyAccess getPropertyAccess(String name, Class type) { - return new DefaultPcodeTracePropertyAccess<>(this, name, type); + synchronized (properties) { + return (PcodeTracePropertyAccess) properties.computeIfAbsent(name, + n -> new DefaultPcodeTracePropertyAccess<>(this, n, type)); + } } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java index 92d3194c55..4475ca17f2 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java @@ -26,6 +26,7 @@ import java.util.List; import org.junit.Test; import db.Transaction; +import generic.Unique; import ghidra.app.plugin.assembler.*; import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock; import ghidra.pcode.emu.PcodeEmulator; @@ -68,7 +69,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest emu.getSharedState().setVar(tb.addr(0x00400000), 0, false, tb.arr()); TraceWriter tw = (TraceWriter) writer; - assertEquals(tb.set(), tw.memWritten); + assertEquals(tb.set(), Unique.assertOne(tw.pieces.entrySet()).getValue().written()); } } diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java index 59814327f4..410b5a6a90 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java @@ -26,8 +26,8 @@ import ghidra.pcode.exec.PcodeArithmetic.Purpose; import ghidra.pcode.exec.PcodeExecutorStatePiece; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.trace.TraceEmulationIntegration.AbstractPropertyBasedPieceHandler; -import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess; -import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess; +import ghidra.pcode.exec.trace.TraceEmulationIntegration.TraceWriter; +import ghidra.pcode.exec.trace.data.*; import ghidra.program.model.address.*; import ghidra.symz3.model.SymValueZ3; @@ -105,9 +105,9 @@ public class SymZ3PieceHandler } @Override - public void writeDown(PcodeTraceDataAccess into, PcodeThread thread, + public void writeDown(TraceWriter writer, PcodeTraceAccess into, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView written) { - super.writeDown(into, thread, piece, written); + super.writeDown(writer, into, thread, piece, written); Set symWritten = abstractWritten.get(thread); if (symWritten == null) { return; @@ -128,7 +128,9 @@ public class SymZ3PieceHandler * abstractly? */ String val = buf.isEmpty() ? null : buf.toString(); - into.getPropertyAccess(NAME, String.class).put(Address.NO_ADDRESS, val); + into.getDataForSharedState() + .getPropertyAccess(NAME, String.class) + .put(Address.NO_ADDRESS, val); } @Override diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialogTestHelper.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialogTestHelper.java new file mode 100644 index 0000000000..d95ac48b90 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialogTestHelper.java @@ -0,0 +1,24 @@ +/* ### + * 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.datamgr.util; + +import ghidra.util.Swing; + +public class DataTypeChooserDialogTestHelper { + public static void clickOk(DataTypeChooserDialog dialog) { + Swing.runNow(dialog::okCallback); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java index bbbefffa90..b9f8e1f39d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java @@ -71,6 +71,7 @@ public class DialogToolbarButton extends EmptyBorderToggleButton { context.setSourceObject(e.getSource()); context.setContextProvider(contextProvider); + context.setEventClickModifiers(e.getModifiers()); dockingAction.actionPerformed(context); }); diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java index ea8ceae914..18a4bdb928 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java @@ -324,12 +324,8 @@ public abstract class AbstractPcodeMachine implements PcodeMachine { * addressed by formalizing and better exposing the notion of p-code stacks (of p-code * frames) */ - PcodeProgram pcode = compileSleigh("breakpoint:" + address, String.format(""" - if (!(%s)) goto ; - emu_swi(); - - emu_exec_decoded(); - """, sleighCondition)); + PcodeProgram pcode = compileSleigh("breakpoint:" + address, + SleighUtils.sleighForConditionalBreak(sleighCondition)); injects.put(address, pcode); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java index 7948ba8f4f..7e79354dbd 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java @@ -20,7 +20,7 @@ import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; // This treads on GP-4240 a bit. Still, enough to demonstrate the framework can support it. -@UseropLibrary("trig") +@UseropLibrary(id = "trig") public class TrigPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DiagnosticsPcodeUseropLibraryFactory.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DiagnosticsPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..4dbfae93eb --- /dev/null +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DiagnosticsPcodeUseropLibraryFactory.java @@ -0,0 +1,72 @@ +/* ### + * 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.pcode.exec; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; +import ghidra.pcode.utils.Utils; +import ghidra.program.model.pcode.Varnode; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +@UseropLibrary(id = "diagnostics", includeAlways = true) +public class DiagnosticsPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + + @Override + @SuppressWarnings("unchecked") + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return (PcodeUseropLibrary) DiagnosticsPcodeUseropLibrary.INSTANCE; + } + + public static class DiagnosticsPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + public static final DiagnosticsPcodeUseropLibrary INSTANCE = + new DiagnosticsPcodeUseropLibrary<>(); + + private DiagnosticsPcodeUseropLibrary() { + } + + @PcodeUserop + public void emu_probe(@OpExecutor PcodeExecutor exec, Varnode in) { + SleighLanguage language = exec.getLanguage(); + String name = in.toString(language); + T value = exec.getState().getVar(in, Reason.INSPECT); + byte[] concrete; + try { + concrete = exec.getArithmetic().toConcrete(value, Purpose.INSPECT); + } + catch (ConcretionError e) { + concrete = null; + } + String concreteString = concrete == null ? null + : "%s (%s)".formatted( + NumericUtilities.convertBytesToString(concrete, ":"), + Utils.bytesToBigInteger(concrete, in.getSize(), language.isBigEndian(), + false)); + if (value == concrete) { + Msg.debug(this, "%s = %s".formatted(name, concreteString)); + } + else if (concrete != null) { + Msg.debug(this, "%s = %s => %s (%s)".formatted(name, value, concreteString)); + } + else { + Msg.debug(this, "%s = %s".formatted(name, value)); + } + } + } +} diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeExecutor.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeExecutor.java index 1136dacdb8..e9496896d0 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeExecutor.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeExecutor.java @@ -67,6 +67,16 @@ public class PcodeExecutor { this.pcSize = pc != null ? pc.getNumBytes() : language.getDefaultSpace().getPointerSize(); } + /** + * Construct an executor with the given bindings + * + * @param state the state, providing arithmetic and storage + * @param reason a reason for reading the state with this executor + */ + public PcodeExecutor(PcodeExecutorState state, Reason reason) { + this((SleighLanguage) state.getLanguage(), state.getArithmetic(), state, reason); + } + /** * Get the executor's Sleigh language (processor model) * diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java index e1f09d3b79..8e793a9a48 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java @@ -17,7 +17,6 @@ package ghidra.pcode.exec; import java.lang.annotation.*; import java.util.*; -import java.util.stream.Collectors; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.program.model.lang.GhidraLanguagePropertyKeys; @@ -33,6 +32,7 @@ import ghidra.util.classfinder.ExtensionPoint; * The factory must have a public default constructor. */ public interface PcodeUseropLibraryFactory extends ExtensionPoint { + public static final int DEFAULT_ORDER = 100; /** The property key for useropLib ids in pspec files */ public static final String KEY_USEROP_LIBS = GhidraLanguagePropertyKeys.USEROP_LIBS; @@ -53,7 +53,23 @@ public interface PcodeUseropLibraryFactory extends ExtensionPoint { * * @return the id */ - String value(); + String id(); + + /** + * Specifies whether this library is always included, regardless of the id + * + * @return true to include always + */ + boolean includeAlways() default false; + + /** + * Determines in what order overrides are applied + *

    + * Higher values are applied later, and thus take precedence. + * + * @return the override order + */ + int order() default DEFAULT_ORDER; } /** @@ -109,8 +125,10 @@ public interface PcodeUseropLibraryFactory extends ExtensionPoint { * *

    * This composes all of the libraries named in the language's pspec file in the - * {@value #KEY_USEROP_LIBS} property. That property is a comma-separated list of the ids to - * compose. + * {@value #KEY_USEROP_LIBS} property. That property is a comma-separated list of the + * {@link UseropLibrary#id() ids} to compose. The composition also includes libraries marked as + * {@link UseropLibrary#includeAlways}. Once all included libraries are identified, they are + * sorted by {@link UseropLibrary#order()} and composed, overriding duplicate userops. * *

    * See the caveats in @@ -121,48 +139,71 @@ public interface PcodeUseropLibraryFactory extends ExtensionPoint { * @param language the language * @param arithmetic the arithmetic * @return the userop library - * @implNote currently, duplicate userops (by name) are not permitted. This may change in future - * versions. Thus, we compose libraries in the order listed, in case of that change, - * as it would matter. */ static PcodeUseropLibrary createUseropLibraryForLanguage(SleighLanguage language, PcodeArithmetic arithmetic) { List libIds = List.of(language.getProperty(KEY_USEROP_LIBS, "").split(",")); - Map matches = - ClassSearcher.getInstances((PcodeUseropLibraryFactory.class)) + List matches = + ClassSearcher.getInstances(PcodeUseropLibraryFactory.class) .stream() - .filter(f -> libIds.contains(f.getId())) - .collect(Collectors.toMap(f -> f.getId(), f -> f)); + .filter(f -> f.isIncludeAlways() || libIds.contains(f.getId())) + .sorted(Comparator.comparing(f -> f.getOrder())) + .toList(); PcodeUseropLibrary result = PcodeUseropLibrary.nil(); - for (String id : libIds) { - PcodeUseropLibraryFactory factory = matches.get(id); - if (factory == null) { - continue; - } - result = result.compose(factory.create(language, arithmetic)); + for (PcodeUseropLibraryFactory factory : matches) { + result = result.compose(factory.create(language, arithmetic), true); } return result; } - /** - * Get the id of this factory - * - *

    - * This gets the id from the {@link UseropLibrary} annotation. You should not override this - * function without a good reason. - * - * @return the id - */ - default String getId() { + default UseropLibrary getAnnotation() { UseropLibrary annot = this.getClass().getAnnotation(UseropLibrary.class); if (annot == null) { Msg.warn(this, "%s %s is missing @%s annotation".formatted( PcodeUseropLibraryFactory.class.getSimpleName(), this.getClass(), UseropLibrary.class.getSimpleName())); - return null; } - return annot.value(); + return annot; + } + + /** + * Get the id of this factory + *

    + * This gets {@link UseropLibrary#id()} from the annotation. You should not override this + * function without a good reason. + * + * @return the id + */ + default String getId() { + UseropLibrary annot = getAnnotation(); + return annot == null ? null : annot.id(); + } + + /** + * Check if this factory's library should always be included. + *

    + * This gets {@link UseropLibrary#includeAlways()} from the annotation. You should not override + * this function without a good reason. + * + * @return true if always included + */ + default boolean isIncludeAlways() { + UseropLibrary annot = getAnnotation(); + return annot == null ? false : annot.includeAlways(); + } + + /** + * Get the override order of this library. + *

    + * This gets {@link UseropLibrary#order()} from the annotation. You should not override this + * function without a good reason. + * + * @return the order + */ + default int getOrder() { + UseropLibrary annot = getAnnotation(); + return annot == null ? DEFAULT_ORDER : annot.order(); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDB.java index 818f5382f3..78f72906cd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDB.java @@ -176,4 +176,8 @@ public class BookmarkDB extends DbObject implements Bookmark { super.checkDeleted(); } + @Override + public boolean isDeleted() { + return isDeleted(mgr.lock); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java index be8be30fc6..5552253b9d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java @@ -21,12 +21,12 @@ import java.util.function.Consumer; import ghidra.docking.settings.Settings; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.address.*; +import ghidra.program.model.lang.Language; import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; import ghidra.util.DataConverter; import ghidra.util.StringUtilities; -import ghidra.util.exception.DuplicateNameException; /** * Basic implementation for a pointer dataType @@ -521,10 +521,23 @@ public class PointerDataType extends BuiltIn implements Pointer { "Address space not defined: " + spaceName + ":" + formatOffset(addrOffset)); return null; } + if (targetSpace.isNonLoadedMemorySpace()) { + errorHandler.accept("Address space is non-loaded: " + spaceName + ":" + + formatOffset(addrOffset)); + return null; + } } if (targetSpace == null) { targetSpace = buf.getAddress().getAddressSpace(); } + if (targetSpace.isRegisterSpace()) { + Language language = buf.getLanguage(); + if (language == null) { + errorHandler.accept("Language not specified"); + return null; + } + targetSpace = language.getDefaultDataSpace(); + } if (targetSpace instanceof SegmentedAddressSpace) { if (size != 2 && size != 4) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Bookmark.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Bookmark.java index 09037c870b..b097c9d44b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Bookmark.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Bookmark.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * 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. @@ -60,4 +59,9 @@ public interface Bookmark extends Comparable { * @param comment single line comment */ void set(String category, String comment); + + /** + * {@return true if this bookmark has been deleted and is no longer valid.} + */ + boolean isDeleted(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemBuffer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemBuffer.java index fd71af2756..48e80cb1cd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemBuffer.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemBuffer.java @@ -17,8 +17,11 @@ package ghidra.program.model.mem; import java.io.InputStream; import java.math.BigInteger; +import java.util.Optional; import ghidra.program.model.address.Address; +import ghidra.program.model.lang.Language; +import ghidra.program.model.listing.Program; /** * MemBuffer provides an array like interface into memory at a @@ -119,6 +122,17 @@ public interface MemBuffer { */ public Memory getMemory(); + /** + * {@return the Language defining the addresses in this buffer. This may be null, and the + * default implementation returns null if the memory or its program is null.} + */ + public default Language getLanguage() { + return Optional.ofNullable(getMemory()) + .map(Memory::getProgram) + .map(Program::getLanguage) + .orElse(null); + } + /** * Returns true if the underlying bytes are in big-endian order, false if they are little endian. * @return true if the underlying bytes are in big-endian order, false if they are little endian. diff --git a/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java index a89fef3963..47d68b8ae6 100644 --- a/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java @@ -19,7 +19,7 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; -@UseropLibrary("m68k") +@UseropLibrary(id = "m68k") public class M68kPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java index 67876a556a..582ea9a189 100644 --- a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java @@ -26,7 +26,7 @@ import ghidra.program.model.pcode.Varnode; * For the TBL and TBX instructions, see * https://developer.arm.com/documentation/ddi0602/2024-12/SIMD-FP-Instructions/TBL--Table-vector-lookup- */ -@UseropLibrary("aarch64") +@UseropLibrary(id = "aarch64") public class Aarch64PcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java index cb29cbe441..e71d591753 100644 --- a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java @@ -27,7 +27,7 @@ import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.pcode.Varnode; import ghidra.util.Msg; -@UseropLibrary("arm") +@UseropLibrary(id = "arm") public class ArmPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/Hexagon/src/main/java/ghidra/program/emulation/HexagonPcodeUseropLibraryFactory.java b/Ghidra/Processors/Hexagon/src/main/java/ghidra/program/emulation/HexagonPcodeUseropLibraryFactory.java index eea13a06d9..cfa6d1d82f 100644 --- a/Ghidra/Processors/Hexagon/src/main/java/ghidra/program/emulation/HexagonPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/Hexagon/src/main/java/ghidra/program/emulation/HexagonPcodeUseropLibraryFactory.java @@ -23,7 +23,7 @@ import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; import ghidra.program.model.pcode.Varnode; -@UseropLibrary("hexagon") +@UseropLibrary(id = "hexagon") public class HexagonPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java index b4736866dd..acbc45081f 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java @@ -23,7 +23,7 @@ import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; import ghidra.program.model.lang.*; -@UseropLibrary("mips") +@UseropLibrary(id = "mips") public class MipsPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java index 23a038860d..b8f153f4c5 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java @@ -19,7 +19,7 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; -@UseropLibrary("ppc") +@UseropLibrary(id = "ppc") public class PpcPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java index 11e6a63953..77a3c0a336 100644 --- a/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java @@ -20,7 +20,7 @@ import ghidra.pcode.error.LowlevelError; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; -@UseropLibrary("xtensa") +@UseropLibrary(id = "xtensa") public class XtensaPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java index bc416798d3..4409245160 100644 --- a/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java @@ -19,7 +19,7 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; -@UseropLibrary("tricore") +@UseropLibrary(id = "tricore") public class TricorePcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java b/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java index 4bee10029d..a24b86cf0c 100644 --- a/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java @@ -19,7 +19,7 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; -@UseropLibrary("x86") +@UseropLibrary(id = "x86") public class X86PcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/TraceRmiPythonClientTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/TraceRmiPythonClientTest.java index 23a45009ab..461ed715eb 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/TraceRmiPythonClientTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/TraceRmiPythonClientTest.java @@ -314,19 +314,19 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { return stdout; } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { @@ -373,12 +373,8 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { print(trace) exit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject obj = openDomainObject("/New Traces/test")) { - switch (obj.get()) { - case Trace trace -> { - } - default -> fail("Wrong type"); - } + try (ManagedDomainObject obj = openTrace("/New Traces/test")) { + // it worked and has the correct type } } @@ -543,8 +539,8 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { exit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject obj = openDomainObject("/New Traces/test")) { - Trace trace = (Trace) obj.get(); + try (ManagedDomainObject obj = openTrace("/New Traces/test")) { + Trace trace = obj.get(); TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(0, false); assertEquals("Test", snapshot.getDescription()); } @@ -562,8 +558,8 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { exit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject obj = openDomainObject("/New Traces/test")) { - Trace trace = (Trace) obj.get(); + try (ManagedDomainObject obj = openTrace("/New Traces/test")) { + Trace trace = obj.get(); TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(10, false); assertEquals("Test", snapshot.getDescription()); } @@ -589,8 +585,8 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { long snap = Long.parseLong(matchOne(out, Pattern.compile("---SNAP:(-?\\d*)---")).group(1)); assertThat(snap, Matchers.lessThan(0L)); - try (ManagedDomainObject obj = openDomainObject("/New Traces/test")) { - Trace trace = (Trace) obj.get(); + try (ManagedDomainObject obj = openTrace("/New Traces/test")) { + Trace trace = obj.get(); TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, false); assertEquals("Test", snapshot.getDescription()); } @@ -613,8 +609,8 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest { long snap = Long.parseLong(matchOne(out, Pattern.compile("---SNAP:(-?\\d*)---")).group(1)); assertThat(snap, Matchers.lessThan(0L)); - try (ManagedDomainObject obj = openDomainObject("/New Traces/test")) { - Trace trace = (Trace) obj.get(); + try (ManagedDomainObject obj = openTrace("/New Traces/test")) { + Trace trace = obj.get(); TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, false); assertEquals("Test", snapshot.getDescription()); } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/AbstractDbgEngTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/AbstractDbgEngTraceRmiTest.java index 12ab937987..b3a0b47a4e 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/AbstractDbgEngTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/AbstractDbgEngTraceRmiTest.java @@ -16,7 +16,7 @@ package agent.dbgeng.rmi; import static org.junit.Assert.*; -import static org.junit.Assume.*; +import static org.junit.Assume.assumeTrue; import java.io.*; import java.net.*; @@ -44,6 +44,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRangeImpl; import ghidra.pty.testutil.DummyProc; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.Trace; import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet; import ghidra.trace.model.target.TraceObject; @@ -458,19 +459,19 @@ public abstract class AbstractDbgEngTraceRmiTest extends AbstractGhidraHeadedDeb return new RegDump(); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngCommandsTest.java index 213ca5b3c0..d8235cc6c7 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngCommandsTest.java @@ -112,8 +112,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_create('notepad.exe', wait=True) quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("x86:LE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("windows", @@ -129,7 +129,7 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_start() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -149,8 +149,9 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { .formatted(PREAMBLE, addr)); DomainFile dfMyToy = env.getProject().getProjectData().getFile("/New Traces/pydbg/myToy"); assertNotNull(dfMyToy); - try (ManagedDomainObject mdo = new ManagedDomainObject(dfMyToy, false, false, monitor)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = + new ManagedDomainObject<>(dfMyToy, Trace.class, monitor)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("Toy:BE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -276,8 +277,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_stop() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } @@ -291,8 +292,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_save() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size()); } } @@ -308,8 +309,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); assertEquals(0, snapshot.getKey()); assertEquals("Scripted snapshot", snapshot.getDescription()); @@ -333,8 +334,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); @@ -362,8 +363,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String eval = extractOutSection(out, "---Start---"); @@ -395,8 +396,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); @@ -426,8 +427,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr, count)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -474,8 +475,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { quit() """.formatted(PREAMBLE, addr, count)); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -505,8 +506,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -530,8 +531,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -557,8 +558,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -583,8 +584,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr, extra, pydbgExpr, gtype)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -737,8 +738,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -768,8 +769,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { print('---') quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -815,8 +816,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS @@ -855,8 +856,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS""", @@ -879,7 +880,7 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { // NB: we're losing a race here, regularly //assertSame(mdo.get(), traceManager.getCurrentTrace()); assertEquals("Test.Objects[1]", @@ -903,8 +904,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Not concerned about specifics, so long as disassembly occurs long total = 0; for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) { @@ -928,8 +929,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection processes = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[]")) @@ -950,8 +951,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection available = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Available[]")) @@ -976,8 +977,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); List procBreakLocVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Processes[].Debug.Breakpoints[]")) @@ -1012,8 +1013,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); List procBreakVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Processes[].Debug.Breakpoints[]")) @@ -1052,8 +1053,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Assumes LLDB on Linux amd64 TraceObject env = Objects.requireNonNull( @@ -1077,8 +1078,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getMemoryManager().getAllRegions(); @@ -1098,8 +1099,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule modBash = @@ -1120,8 +1121,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection threads = tb.trace.getThreadManager().getAllThreads(); assertThat(threads.size(), greaterThan(2)); @@ -1140,8 +1141,8 @@ public class DbgEngCommandsTest extends AbstractDbgEngTraceRmiTest { ghidra_trace_kill() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics List stack = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngHooksTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngHooksTest.java index 36692903ed..5ec396ff1a 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngHooksTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngHooksTest.java @@ -15,9 +15,11 @@ */ package agent.dbgeng.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.util.*; @@ -40,7 +42,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest { private static final long RUN_TIMEOUT_MS = 5000; private static final long RETRY_MS = 500; - record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) + record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); @@ -61,19 +63,19 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest { protected PythonAndTrace startAndSyncPython(String exec) throws Exception { PythonAndConnection conn = startAndConnectPython(); try { - ManagedDomainObject mdo; + ManagedDomainObject mdo; conn.execute("from ghidradbg.commands import *"); conn.execute( "util.set_convenience_variable('ghidra-language', 'x86:LE:64:default')"); if (exec != null) { start(conn, exec); - mdo = waitDomainObject("/New Traces/pydbg/" + exec); + mdo = waitTrace("/New Traces/pydbg/" + exec); } else { conn.execute("ghidra_trace_start()"); - mdo = waitDomainObject("/New Traces/pydbg/noname"); + mdo = waitTrace("/New Traces/pydbg/noname"); } - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + tb = new ToyDBTraceBuilder(mdo.get()); return new PythonAndTrace(conn, mdo); } catch (Exception e) { @@ -215,6 +217,8 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest { * For the moment, we favor option (2), as we'd prefer never to display inaccurate data, * especially as non-stale. The lost observations are a small price to pay, since they're not * particularly important for the interactive use case. + * + * @throws Exception because */ @Test public void testOnMemoryChanged() throws Exception { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngMethodsTest.java index 5a6110108d..c57c6eb76c 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngMethodsTest.java @@ -15,6 +15,7 @@ */ package agent.dbgeng.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.*; @@ -58,8 +59,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { start(conn, null); RemoteMethod evaluate = conn.getMethod("evaluate"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("11", evaluate.invoke(Map.ofEntries( Map.entry("session", tb.obj("Sessions[0]")), @@ -84,7 +85,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { start(conn, "notepad.exe"); conn.execute("ghidra_trace_kill()"); } - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { // Just confirm it's present } } @@ -97,8 +98,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txCreate(conn, "Sessions[0].Available"); RemoteMethod refreshAvailable = conn.getMethod("refresh_available"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject available = Objects.requireNonNull(tb.objAny0("Sessions[].Available")); refreshAvailable.invoke(Map.of("node", available)); @@ -120,8 +121,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("pc = util.get_pc()"); conn.execute("util.dbg.bp(expr=pc)"); @@ -157,8 +158,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshProcWatchpoints = conn.getMethod("refresh_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("pc = util.get_pc()"); conn.execute("util.dbg.ba(expr=pc, access=DbgEng.DEBUG_BREAK_EXECUTE)"); @@ -202,8 +203,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txCreate(conn, "Sessions[0].Processes"); RemoteMethod refreshProcesses = conn.getMethod("refresh_processes"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject processes = Objects.requireNonNull(tb.objAny0("Sessions[].Processes")); refreshProcesses.invoke(Map.of("node", processes)); @@ -225,8 +226,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject env = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[].Environment")); @@ -248,8 +249,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject threads = fakeEmpty(proc, "Threads"); @@ -269,8 +270,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshStack = conn.getMethod("refresh_stack"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "frames"); TraceObject stack = Objects.requireNonNull( @@ -298,8 +299,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { conn.execute("ghidra_trace_txcommit()"); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("util.dbg.cmd('r rax=0xdeadbeef')"); @@ -323,8 +324,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject memory = fakeEmpty(proc, "Memory"); @@ -355,8 +356,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshModules = conn.getMethod("refresh_modules"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject modules = fakeEmpty(proc, "Modules"); @@ -378,8 +379,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod activateThread = conn.getMethod("activate_thread"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); @@ -408,8 +409,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod removeProcess = conn.getMethod("remove_process"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/netstat.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/netstat.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); removeProcess.invoke(Map.of("process", proc2)); @@ -428,8 +429,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "available"); RemoteMethod attachObj = conn.getMethod("attach_obj"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject target = Objects.requireNonNull(tb.obj( "Sessions[0].Available[%d]".formatted(dproc.pid))); attachObj.invoke(Map.ofEntries( @@ -450,8 +451,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "available"); RemoteMethod attachPid = conn.getMethod("attach_pid"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); Objects.requireNonNull(tb.obj( "Sessions[0].Available[%d]".formatted(dproc.pid))); attachPid.invoke(Map.ofEntries( @@ -472,8 +473,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod detach = conn.getMethod("detach"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/netstat.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/netstat.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); detach.invoke(Map.of("process", proc)); @@ -491,8 +492,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod launch = conn.getMethod("launch_loader"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); launch.invoke(Map.ofEntries( Map.entry("session", tb.obj("Sessions[0]")), @@ -512,8 +513,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod launch = conn.getMethod("launch"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); launch.invoke(Map.ofEntries( Map.entry("session", tb.obj("Sessions[0]")), @@ -536,8 +537,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod kill = conn.getMethod("kill"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -559,8 +560,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { RemoteMethod go = conn.getMethod("go"); RemoteMethod interrupt = conn.getMethod("interrupt"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -584,8 +585,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); txPut(conn, "threads"); @@ -619,8 +620,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod stepOver = conn.getMethod("step_over"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); txPut(conn, "threads"); @@ -649,8 +650,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod stepTo = conn.getMethod("step_to"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); TraceObject thread = @@ -682,8 +683,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod stepOut = conn.getMethod("step_out"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); txPut(conn, "threads"); @@ -712,8 +713,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -733,8 +734,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); breakExpression.invoke(Map.of("expression", "entry")); @@ -752,8 +753,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_hw_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -773,8 +774,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_hw_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); breakExpression.invoke(Map.of("expression", "entry")); @@ -792,8 +793,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_read_range"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -816,8 +817,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_read_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of("expression", "ntdll!LdrInitShimEngineDynamic")); long address = getAddressAtOffset(conn, 0); @@ -837,8 +838,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_write_range"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -861,8 +862,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_write_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of("expression", "ntdll!LdrInitShimEngineDynamic")); long address = getAddressAtOffset(conn, 0); @@ -882,8 +883,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_access_range"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped("Missed initial stop"); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -906,8 +907,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_access_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of("expression", "ntdll!LdrInitShimEngineDynamic")); long address = getAddressAtOffset(conn, 0); @@ -928,8 +929,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { RemoteMethod breakAddress = conn.getMethod("break_address"); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long address = getAddressAtOffset(conn, 0); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -955,8 +956,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { RemoteMethod breakAddress = conn.getMethod("break_address"); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long address = getAddressAtOffset(conn, 0); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -1015,8 +1016,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { try (PythonAndConnection conn = startAndConnectPython()) { openTtdTrace(conn); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/cmd01.run")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/cmd01.run")) { + tb = new ToyDBTraceBuilder(mdo.get()); } } } @@ -1033,8 +1034,8 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest { txPut(conn, "events"); RemoteMethod activate = conn.getMethod("activate_frame"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/cmd01.run")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/pydbg/cmd01.run")) { + tb = new ToyDBTraceBuilder(mdo.get()); traceManager.openTrace(tb.trace); traceManager.activateTrace(tb.trace); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngStackUnwindTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngStackUnwindTest.java index f0daffb95f..4e20baec33 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngStackUnwindTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngStackUnwindTest.java @@ -15,7 +15,8 @@ */ package agent.dbgeng.rmi; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.IOException; @@ -87,7 +88,7 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { DecompilerProvider decompilerProvider; DecompilerPanel decompilerPanel; - record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) + record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); @@ -119,10 +120,10 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { } @SuppressWarnings("resource") - protected PythonAndTrace startAndSyncPython(ManagedDomainObject mdo) throws Exception { + protected PythonAndTrace startAndSyncPython(ManagedDomainObject mdo) throws Exception { PythonAndConnection conn = startAndConnectPython(); try { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + tb = new ToyDBTraceBuilder(mdo.get()); return new PythonAndTrace(conn, mdo); } catch (Exception e) { @@ -145,8 +146,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -173,8 +174,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -203,8 +204,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -233,8 +234,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -260,8 +261,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -287,8 +288,8 @@ public class DbgEngStackUnwindTest extends AbstractDbgEngTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_dbgeng"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/AbstractDrgnTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/AbstractDrgnTraceRmiTest.java index fcac0d7779..7afeca207e 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/AbstractDrgnTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/AbstractDrgnTraceRmiTest.java @@ -46,6 +46,7 @@ import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginsConfiguration; import ghidra.framework.plugintool.util.*; import ghidra.pty.testutil.DummyProc; +import ghidra.trace.model.Trace; import ghidra.util.Msg; import ghidra.util.SystemUtilities; import junit.framework.AssertionFailedError; @@ -320,19 +321,19 @@ public abstract class AbstractDrgnTraceRmiTest extends AbstractGhidraHeadedDebug return xout.split(head)[1].split("---")[0].replace("(python)", "").trim(); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnCommandsTest.java index d4e30c0db0..25b6b60f4d 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnCommandsTest.java @@ -15,6 +15,7 @@ */ package agent.drgn.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.assumeFalse; @@ -110,8 +111,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_create() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("x86:LE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("gcc", @@ -127,7 +128,7 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_start() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/drgn/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -147,8 +148,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { .formatted(PREAMBLE, addr)); DomainFile df = env.getProject().getProjectData().getFile("/New Traces/myToy"); assertNotNull(df); - try (ManagedDomainObject mdo = new ManagedDomainObject(df, false, false, monitor)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = new ManagedDomainObject<>(df, Trace.class, monitor)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("Toy:BE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -266,8 +267,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); assertEquals(0, snapshot.getKey()); assertEquals("Scripted snapshot", snapshot.getDescription()); @@ -289,8 +290,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr, count)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -330,8 +331,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { quit() """.formatted(PREAMBLE, addr, count)); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -361,8 +362,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/drgn/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -386,8 +387,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/drgn/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -412,8 +413,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -437,8 +438,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr, extra, drgnExpr, gtype)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -590,8 +591,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -621,8 +622,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { print('---') quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/drgn/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -667,8 +668,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { print('---') quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS @@ -706,8 +707,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { print('---') quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS""", @@ -729,7 +730,7 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_activate('Test.Objects[1]') quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { + try (ManagedDomainObject mdo = openTrace(MDO)) { assertSame(mdo.get(), traceManager.getCurrentTrace()); assertEquals("Test.Objects[1]", traceManager.getCurrentObject().getCanonicalPath().toString()); @@ -751,8 +752,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Not concerned about specifics, so long as disassembly occurs long total = 0; for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) { @@ -776,8 +777,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/drgn/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection processes = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[]")) @@ -798,8 +799,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Assumes LLDB on Linux amd64 TraceObject envobj = Objects.requireNonNull(tb.objAny("Processes[].Environment", Lifespan.at(0))); @@ -825,8 +826,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { quit() """.formatted(PREAMBLE, addr)); assumeFalse(stdout.contains("IGNOREME")); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getMemoryManager().getAllRegions(); @@ -849,8 +850,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { quit() """.formatted(PREAMBLE, addr)); assumeFalse(stdout.contains("IGNOREME")); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule modBash = @@ -870,8 +871,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection threads = tb.trace.getThreadManager().getAllThreads(); assertEquals(1, threads.size()); @@ -889,8 +890,8 @@ public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest { ghidra_trace_txcommit() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics List stack = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnMethodsTest.java index 4486990ab5..3a1902a99c 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/drgn/rmi/DrgnMethodsTest.java @@ -15,6 +15,7 @@ */ package agent.drgn.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.*; import static org.junit.Assume.assumeFalse; @@ -59,7 +60,7 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { try (PythonAndConnection conn = startAndConnectDrgn()) { start(conn, null); } - try (ManagedDomainObject mdo = openDomainObject(MDO)) { + try (ManagedDomainObject mdo = openTrace(MDO)) { // Just confirm it's present } } @@ -72,8 +73,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { RemoteMethod attachCore = conn.getMethod("attach_core"); RemoteMethod refreshProcesses = conn.getMethod("refresh_processes"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject processes = Objects.requireNonNull(tb.objAny0("Processes")); refreshProcesses.invoke(Map.of("node", processes)); @@ -106,8 +107,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject envobj = Objects.requireNonNull(tb.objAny0(path)); refreshEnvironment.invoke(Map.of("node", envobj)); @@ -128,8 +129,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { txCreate(conn, path); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject threads = Objects.requireNonNull(tb.objAny0(path)); refreshThreads.invoke(Map.of("node", threads)); @@ -148,8 +149,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshStack = conn.getMethod("refresh_stack"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "frames"); TraceObject stack = Objects.requireNonNull(tb.objAny0(path)); @@ -177,8 +178,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { conn.execute("ghidra_trace_txcommit()"); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject registers = Objects.requireNonNull(tb.objAny(path, Lifespan.at(0))); refreshRegisters.invoke(Map.of("node", registers)); @@ -204,8 +205,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { assumeFalse(out.equals("False")); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject memory = Objects.requireNonNull(tb.objAny0(path)); refreshMappings.invoke(Map.of("node", memory)); @@ -229,8 +230,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { assumeFalse(out.equals("False")); RemoteMethod refreshModules = conn.getMethod("refresh_modules"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject modules = Objects.requireNonNull(tb.objAny0(path)); refreshModules.invoke(Map.of("node", modules)); @@ -251,8 +252,8 @@ public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest { txPut(conn, "processes"); RemoteMethod activateThread = conn.getMethod("activate_thread"); - try (ManagedDomainObject mdo = openDomainObject(MDO)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(MDO)) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/AbstractGdbTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/AbstractGdbTraceRmiTest.java index 84fb8db08f..1c618cc500 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/AbstractGdbTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/AbstractGdbTraceRmiTest.java @@ -46,6 +46,7 @@ import ghidra.framework.plugintool.util.*; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRangeImpl; import ghidra.pty.testutil.DummyProc; +import ghidra.trace.model.Trace; import ghidra.trace.model.TraceExecutionState; import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet; @@ -446,19 +447,19 @@ public abstract class AbstractGdbTraceRmiTest extends AbstractGhidraHeadedDebugg waitForPass(() -> assertFalse(df.isOpen())); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbCommandsTest.java index 0115cc5c71..a80e80bb65 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbCommandsTest.java @@ -116,8 +116,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace start quit """.formatted(PREAMBLE, addr, target)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(PLAT.lang(), tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); String id = tb.trace.getBaseCompilerSpec().getCompilerSpecID().getIdAsString(); @@ -133,7 +133,7 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace start quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -152,8 +152,9 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { """.formatted(PREAMBLE, addr, target)); DomainFile dfMyToy = env.getProject().getProjectData().getFile("/New Traces/myToy"); assertNotNull(dfMyToy); - try (ManagedDomainObject mdo = new ManagedDomainObject(dfMyToy, false, false, monitor)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = + new ManagedDomainObject<>(dfMyToy, Trace.class, monitor)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("Toy:BE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -285,8 +286,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { quit """.formatted(PREAMBLE, addr, target)); waitDomainObjectClosed("/New Traces/no-save"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/no-save")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/no-save")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } @@ -302,8 +303,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { quit """.formatted(PREAMBLE, addr, target)); waitDomainObjectClosed("/New Traces/save"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/save")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/save")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size()); } } @@ -321,8 +322,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr, target)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); assertEquals(0, snapshot.getKey()); assertEquals("Scripted snapshot", snapshot.getDescription()); @@ -348,8 +349,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); @@ -381,8 +382,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); AddressSpace ram2 = tb.trace.getBaseAddressFactory().getAddressSpace("ram2"); assertNotNull(ram2); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); @@ -414,8 +415,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String eval = extractOutSection(out, "---Start---"); @@ -448,8 +449,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); @@ -482,8 +483,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd(), count)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); AddressSpace t1f0 = tb.trace.getBaseAddressFactory() .getAddressSpace("Inferiors[1].Threads[1].Stack[0].Registers"); @@ -527,8 +528,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd(), count)); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); AddressSpace t1f0 = tb.trace.getBaseAddressFactory() .getAddressSpace("Inferiors[1].Threads[1].Stack[0].Registers"); @@ -560,8 +561,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -585,8 +586,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -612,8 +613,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -641,8 +642,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, expPrint, PLAT.startCmd(), extra, gdbExpr, gtype)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -802,8 +803,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -833,8 +834,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { echo \\n--- quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -879,8 +880,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, expPrint, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS @@ -923,8 +924,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS""", @@ -950,7 +951,7 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { assertSame(mdo.get(), traceManager.getCurrentTrace()); assertEquals("Test.Objects[1]", traceManager.getCurrentObject().getCanonicalPath().toString()); @@ -976,8 +977,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Not concerned about specifics, so long as disassembly occurs long total = 0; for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) { @@ -1000,8 +1001,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection inferiors = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Inferiors[]")) @@ -1025,8 +1026,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-commit quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection available = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Available[]")) @@ -1056,8 +1057,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); List infBreakLocVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Inferiors[1].Breakpoints[]")) @@ -1098,8 +1099,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Assumes GDB on Linux amd64 TraceObject env = Objects.requireNonNull(tb.obj("Inferiors[1].Environment")); assertEquals("gdb", env.getValue(0, "_debugger").getValue()); @@ -1124,8 +1125,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getMemoryManager().getAllRegions(); @@ -1148,8 +1149,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule modExpPrint = @@ -1173,8 +1174,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Unique.assertOne(tb.trace.getThreadManager().getAllThreads()); } @@ -1197,8 +1198,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, target, PLAT.startCmd())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expRead"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expRead"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics List stack = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbHooksTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbHooksTest.java index 08606d8b9d..c67f374cc8 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbHooksTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbHooksTest.java @@ -45,7 +45,8 @@ public class GdbHooksTest extends AbstractGdbTraceRmiTest { private static final long RUN_TIMEOUT_MS = 20000; private static final long RETRY_MS = 500; - record GdbAndTrace(GdbAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { + record GdbAndTrace(GdbAndConnection conn, ManagedDomainObject mdo) + implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); } @@ -84,8 +85,8 @@ public class GdbHooksTest extends AbstractGdbTraceRmiTest { set ghidra-language x86:LE:64:default ghidra trace start ghidra trace sync-enable"""); - ManagedDomainObject mdo = waitDomainObject("/New Traces/gdb/noname"); - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + ManagedDomainObject mdo = waitTrace("/New Traces/gdb/noname"); + tb = new ToyDBTraceBuilder(mdo.get()); return new GdbAndTrace(conn, mdo); } catch (Exception e) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbMethodsTest.java index 09925c7f4f..b38317eedc 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbMethodsTest.java @@ -79,7 +79,7 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start kill""".formatted(target, PLAT.startCmd()))); } - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { // Just confirm it's present } } @@ -93,8 +93,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start ghidra trace tx-open "Fake" 'ghidra trace create-obj Available'"""); RemoteMethod refreshAvailable = conn.getMethod("refresh_available"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject available = Objects.requireNonNull(tb.obj("Available")); refreshAvailable.invoke(Map.of("node", available)); @@ -120,8 +120,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { starti""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute(""" @@ -171,8 +171,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { starti""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod refreshInfBreakpoints = conn.getMethod("refresh_inf_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject locations = Objects.requireNonNull(tb.obj("Inferiors[1].Breakpoints")); @@ -218,8 +218,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start ghidra trace tx-open "Fake" 'ghidra trace create-obj Inferiors'"""); RemoteMethod refreshInferiors = conn.getMethod("refresh_inferiors"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inferiors = Objects.requireNonNull(tb.obj("Inferiors")); refreshInferiors.invoke(Map.of("node", inferiors)); @@ -246,8 +246,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open "Fake" 'ghidra trace create-obj %s'""".formatted(target, path)); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject env = Objects.requireNonNull(tb.obj(path)); refreshEnvironment.invoke(Map.of("node", env)); @@ -273,8 +273,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open "Fake" 'ghidra trace create-obj %s'""".formatted(target, path)); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject threads = Objects.requireNonNull(tb.obj(path)); refreshThreads.invoke(Map.of("node", threads)); @@ -299,8 +299,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED, path)); RemoteMethod refreshStack = conn.getMethod("refresh_stack"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expRead"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expRead"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject stack = Objects.requireNonNull(tb.obj(path)); @@ -333,8 +333,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { start""" .formatted(target, INSTRUMENT_STOPPED, path)); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute(""" @@ -380,8 +380,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open "Fake" 'ghidra trace create-obj %s'""".formatted(target, PLAT.startCmd(), path)); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject memory = Objects.requireNonNull(tb.obj(path)); refreshMappings.invoke(Map.of("node", memory)); @@ -412,8 +412,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open "Fake" 'ghidra trace create-obj %s'""".formatted(target, path)); RemoteMethod refreshModules = conn.getMethod("refresh_modules"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject modules = Objects.requireNonNull(tb.obj(path)); refreshModules.invoke(Map.of("node", modules)); @@ -436,8 +436,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start ghidra trace tx-open Init 'ghidra trace put-inferiors'"""); RemoteMethod activateInferior = conn.getMethod("activate_inferior"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); List list = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Inferiors[]")) @@ -474,8 +474,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open Start2 'ghidra trace put-threads'""".formatted(target, PLAT.startCmd(), target, PLAT.startCmd())); RemoteMethod activateThread = conn.getMethod("activate_thread"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); PathPattern pattern = PathFilter.parse("Inferiors[].Threads[]").getSingletonPattern(); @@ -508,8 +508,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod activateFrame = conn.getMethod("activate_frame"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expRead"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expRead"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-frames'"); @@ -539,8 +539,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start ghidra trace tx-open Init 'ghidra trace put-inferiors'"""); RemoteMethod deleteInferior = conn.getMethod("delete_inferior"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inf2 = Objects.requireNonNull(tb.obj("Inferiors[2]")); deleteInferior.invoke(Map.of("inferior", inf2)); @@ -564,8 +564,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open Init 'ghidra trace put-available' ghidra trace tx-open Init 'ghidra trace put-inferiors'"""); RemoteMethod attachObj = conn.getMethod("attach_obj"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject target = Objects.requireNonNull(tb.obj("Available[%d]".formatted(proc.pid))); attachObj.invoke(Map.of("target", target)); @@ -589,8 +589,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace start ghidra trace tx-open Init 'ghidra trace put-inferiors'"""); RemoteMethod attachPid = conn.getMethod("attach_pid"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); attachPid.invoke(Map.of("inferior", inf, "pid", proc.pid)); @@ -616,8 +616,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { attach %d""" .formatted(INSTRUMENT_STOPPED, proc.pid)); RemoteMethod detach = conn.getMethod("detach"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -639,8 +639,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open Init 'ghidra trace put-inferiors'""" .formatted(INSTRUMENT_STOPPED)); RemoteMethod launchMain = conn.getMethod("launch_main"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); launchMain.invoke(Map.ofEntries( @@ -663,8 +663,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open Init 'ghidra trace put-inferiors'""" .formatted(INSTRUMENT_STOPPED)); RemoteMethod launchLoader = conn.getMethod("launch_loader"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); launchLoader.invoke(Map.ofEntries( @@ -693,8 +693,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { ghidra trace tx-open Init 'ghidra trace put-inferiors'""" .formatted(INSTRUMENT_STOPPED, INSTRUMENT_RUNNING)); RemoteMethod launchRun = conn.getMethod("launch_run"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/gdb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/gdb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); launchRun.invoke(Map.ofEntries( @@ -729,8 +729,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod kill = conn.getMethod("kill"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -765,8 +765,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s start""" .formatted(target, INSTRUMENT_STOPPED, INSTRUMENT_RUNNING)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expSpin"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expSpin"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Before do-put-before"); @@ -804,8 +804,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-threads'"); @@ -836,8 +836,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod step_over = conn.getMethod("step_over"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-threads'"); @@ -883,8 +883,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod stepOut = conn.getMethod("step_out"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-threads'"); @@ -912,8 +912,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod stepAdvance = conn.getMethod("step_advance"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-threads'"); @@ -941,8 +941,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod stepReturn = conn.getMethod("step_return"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-threads'"); @@ -969,8 +969,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakSwExecuteAddress = conn.getMethod("break_sw_execute_address"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -994,8 +994,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakSwExecuteExpression = conn.getMethod("break_sw_execute_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); // Use *main instead of main, because some gdb will instead do @@ -1018,8 +1018,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakHwExecuteAddress = conn.getMethod("break_hw_execute_address"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1044,8 +1044,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakHwExecuteExpression = conn.getMethod("break_hw_execute_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); breakHwExecuteExpression.invoke(Map.of("expression", "*main")); @@ -1068,8 +1068,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakReadRange = conn.getMethod("break_read_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1096,8 +1096,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakReadExpression = conn.getMethod("break_read_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); breakReadExpression.invoke(Map.of("expression", "main")); @@ -1120,8 +1120,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakWriteRange = conn.getMethod("break_write_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1148,8 +1148,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakWriteExpression = conn.getMethod("break_write_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); breakWriteExpression.invoke(Map.of("expression", "main")); @@ -1172,8 +1172,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakAccessRange = conn.getMethod("break_access_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1200,8 +1200,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakAccessExpression = conn.getMethod("break_access_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); breakAccessExpression.invoke(Map.of("expression", "main")); @@ -1224,8 +1224,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { %s""" .formatted(target, INSTRUMENT_STOPPED, PLAT.startCmd())); RemoteMethod breakEvent = conn.getMethod("break_ext_event"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); TraceObject inf = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1252,8 +1252,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-breakpoints'"); TraceObject bpt = Objects.requireNonNull(tb.obj("Breakpoints[1]")); @@ -1279,8 +1279,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod toggleBreakpointLocation = conn.getMethod("toggle_breakpoint_location"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-breakpoints'"); // NB. Requires canonical path. Inf[1].Brk[1] is a link @@ -1322,8 +1322,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace put-breakpoints'"); TraceObject bpt = Objects.requireNonNull(tb.obj("Breakpoints[1]")); @@ -1348,8 +1348,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod readMem = conn.getMethod("read_mem"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); long snap = 0; long pcOff = Long.decode(conn.executeCapture("print/x $pc").split("=")[1].strip()); @@ -1381,8 +1381,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod writeMem = conn.getMethod("write_mem"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); long pcOff = Long.decode(conn.executeCapture("print/x $pc").split("=")[1].strip()); TraceObject inf1 = Objects.requireNonNull(tb.obj("Inferiors[1]")); @@ -1411,8 +1411,8 @@ public class GdbMethodsTest extends AbstractGdbTraceRmiTest { run""" .formatted(target, INSTRUMENT_STOPPED)); RemoteMethod writeReg = conn.getMethod("write_reg"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(); conn.execute("ghidra trace tx-open Init 'ghidra trace putreg'"); TraceObject frame = diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/AbstractJavaTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/AbstractJavaTraceRmiTest.java index af16da241e..2a79acc9bf 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/AbstractJavaTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/AbstractJavaTraceRmiTest.java @@ -45,6 +45,7 @@ import ghidra.framework.plugintool.PluginsConfiguration; import ghidra.framework.plugintool.util.*; import ghidra.program.model.address.Address; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.Trace; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObjectValue; import ghidra.trace.model.target.path.PathFilter; @@ -401,22 +402,22 @@ public abstract class AbstractJavaTraceRmiTest extends AbstractGhidraHeadedDebug return new MemDump(address, buf.toByteArray()); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile dfx = waitForPass(() -> { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); return df; }, TIMEOUT_SECONDS * 1000, 500); - return new ManagedDomainObject(dfx, false, false, monitor); + return new ManagedDomainObject<>(dfx, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaCommandsTest.java index 13a401783c..c8d0a45d6e 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaCommandsTest.java @@ -15,6 +15,7 @@ */ package agent.java.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.*; @@ -123,8 +124,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceStart("HelloWorld.class"); /exit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("JVM:BE:32:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -140,7 +141,7 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceStart(null); /exit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/jdi/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/jdi/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -241,8 +242,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } finally { @@ -264,8 +265,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertNotEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } } @@ -280,8 +281,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(2, tb.trace.getTimeManager().getAllSnapshots().size()); TraceSnapshot snapshot = getLastSnapshot(); assertEquals(1, snapshot.getKey()); @@ -306,8 +307,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); ByteBuffer buf = ByteBuffer.allocate(dump.data().length); @@ -334,8 +335,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); String eval = extractOutSection(out, "---Start---"); List lines = List.of(eval.split("\n")); @@ -366,8 +367,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); Arrays.fill(dump.data(), 0, 8, (byte) 0); @@ -390,8 +391,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), @@ -423,8 +424,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), @@ -452,8 +453,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, TESTSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1].Bob")); assertNotNull(object); @@ -475,8 +476,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, TESTSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1].Bob")); assertNotNull(object); @@ -501,8 +502,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, TESTSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -526,8 +527,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, TESTSETUP, extra, value, schema)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); @@ -626,8 +627,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("VMs")); @@ -654,8 +655,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { /exit """ .formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("VMs")); @@ -687,8 +688,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, TESTSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -721,8 +722,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/jdi/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/jdi/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -762,8 +763,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, TESTSETUP)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/Test")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/Test")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals( "Parent Key Span Value Type \n" + "Test.Objects[1] [0..+inf) vbool true BOOL \n" + @@ -803,7 +804,7 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { assertEquals("VMs[OpenJDK 64-Bit Server VM].Threads[Finalizer]", traceManager.getCurrentObject().getCanonicalPath().toString()); } @@ -822,8 +823,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); // Not concerned about specifics, so long as disassembly occurs long total = 0; @@ -848,8 +849,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); Collection vms = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), PathFilter.parse("VMs[]")) @@ -870,8 +871,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); Collection processes = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), PathFilter.parse("VMs[].Processes")) @@ -892,8 +893,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule mod = Unique.assertOne( all.stream().filter(m -> m.getName(SNAP).contains("Thread.class"))); @@ -913,8 +914,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); Collection threads = tb.trace.getThreadManager().getAllThreads(); assertEquals(4, threads.size()); Set names = new HashSet<>(); @@ -941,8 +942,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceDisconnect(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List stack = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), @@ -964,8 +965,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); Collection all = tb.trace.getMemoryManager().getAllRegions(); assertThat(all.size(), greaterThan(90)); @@ -984,8 +985,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List events = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), @@ -1010,8 +1011,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { cmds.ghidraTraceSave(); /exit """.formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List breaks = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), @@ -1044,8 +1045,8 @@ public class JavaCommandsTest extends AbstractJavaTraceRmiTest { /exit """ .formatted(PREAMBLE, addr, HWSETUP_W_STATE)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = getLastSnapshot().getKey(); List breaks = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(snap), diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaHooksTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaHooksTest.java index 109d754de1..16dab68eef 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaHooksTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaHooksTest.java @@ -42,8 +42,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { start(conn, "HelloWorld.class"); RemoteMethod refreshMethod = conn.getMethod("refresh_method"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("VirtualMachine vm = manager.getCurrentVM();"); conn.execute("ThreadReference thread = manager.getCurrentThread();"); conn.execute("EventRequestManager mgr = vm.eventRequestManager();"); @@ -85,8 +85,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakOnEnter = conn.getMethod("break_enter_thread"); RemoteMethod refreshMethod = conn.getMethod("refresh_method"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -135,8 +135,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakOnExit = conn.getMethod("break_exit_thread"); RemoteMethod refreshMethod = conn.getMethod("refresh_method"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -184,8 +184,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakLoad = conn.getMethod("break_load_container"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -216,8 +216,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakStart = conn.getMethod("break_started_container"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -245,8 +245,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakDeath = conn.getMethod("break_death_container"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -280,8 +280,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshMethod = conn.getMethod("refresh_method"); RemoteMethod refreshLocations = conn.getMethod("refresh_locations"); RemoteMethod breakOnExecute = conn.getMethod("break_location"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -328,8 +328,8 @@ public class JavaHooksTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod refreshFields = conn.getMethod("refresh_canonical_fields"); RemoteMethod breakOnAccess = conn.getMethod("break_access"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java index 03d45b55ee..070cb2e6f0 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java @@ -68,7 +68,7 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { public void testExecute() throws Exception { try (JshellAndConnection conn = startAndConnectJshell()) { start(conn, "HelloWorld.class"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { // Just confirm it's present } } @@ -81,8 +81,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshEvents = conn.getMethod("refresh_events"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -120,8 +120,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -169,8 +169,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshVM = conn.getMethod("refresh_vm"); RemoteMethod refreshProcess = conn.getMethod("refresh_process"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "VMs"); TraceObject vm = waitForObject("VMs[]"); @@ -198,8 +198,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { start(conn, "HelloWorld.class"); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "VMs"); TraceObject threads = waitForObject("VMs[].Threads"); @@ -221,8 +221,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod refreshStack = conn.getMethod("refresh_stack"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject events = waitForObject("VMs[].Events"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -272,8 +272,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -309,8 +309,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshMemory = conn.getMethod("refresh_memory"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -335,8 +335,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshClasses = conn.getMethod("refresh_reference_types"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -361,8 +361,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshModules = conn.getMethod("refresh_modules"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -387,8 +387,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { start(conn, "HelloWorld.class"); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "VMs"); TraceObject threads = waitForObject("VMs[].Threads"); @@ -422,8 +422,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { start(conn, "HelloWorld.class"); RemoteMethod kill = conn.getMethod("kill"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "VMs"); TraceObject vm = waitForObject("VMs[]"); @@ -450,8 +450,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod setClsFilt = conn.getMethod("set_class_filter"); RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject events = waitForObject("VMs[].Events"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -498,8 +498,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod setClsFilt = conn.getMethod("set_class_filter"); RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod stepOver = conn.getMethod("step_over"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject events = waitForObject("VMs[].Events"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -541,8 +541,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod stepOut = conn.getMethod("step_out"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject events = waitForObject("VMs[].Events"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -586,8 +586,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod breakOnEnter = conn.getMethod("break_enter_thread"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -622,8 +622,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshMethod = conn.getMethod("refresh_method"); RemoteMethod refreshLocations = conn.getMethod("refresh_locations"); RemoteMethod breakOnExecute = conn.getMethod("break_location"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -668,8 +668,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod resume = conn.getMethod("resume_thread"); RemoteMethod refreshFields = conn.getMethod("refresh_canonical_fields"); RemoteMethod breakOnAccess = conn.getMethod("break_access"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -708,8 +708,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); @@ -750,8 +750,8 @@ public class JavaMethodsTest extends AbstractJavaTraceRmiTest { RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/HelloWorld.class")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "Threads"); TraceObject thread = waitForObject("VMs[].Threads[main]"); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/AbstractLldbTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/AbstractLldbTraceRmiTest.java index e99dfc4245..ec3e0adf9f 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/AbstractLldbTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/AbstractLldbTraceRmiTest.java @@ -15,7 +15,8 @@ */ package agent.lldb.rmi; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.*; import java.io.*; @@ -51,6 +52,7 @@ import ghidra.pty.*; import ghidra.pty.testutil.DummyProc; import ghidra.pty.windows.AnsiBufferedInputStream; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.Trace; import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet; import ghidra.trace.model.target.TraceObject; @@ -501,19 +503,19 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug waitForPass(() -> assertFalse(df.isOpen())); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbCommandsTest.java index 0767f7084d..ff3dbe1d37 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbCommandsTest.java @@ -113,8 +113,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace start quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(PLAT.lang(), tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals(PLAT.cSpec(), @@ -130,7 +130,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace start quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -150,8 +150,9 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { .formatted(PREAMBLE, addr, getSpecimenPrint())); DomainFile dfMyToy = env.getProject().getProjectData().getFile("/New Traces/myToy"); assertNotNull(dfMyToy); - try (ManagedDomainObject mdo = new ManagedDomainObject(dfMyToy, false, false, monitor)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = + new ManagedDomainObject<>(dfMyToy, Trace.class, monitor)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("Toy:BE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -274,8 +275,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); waitDomainObjectClosed("/New Traces/no-save"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/no-save")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/no-save")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } @@ -291,8 +292,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); waitDomainObjectClosed("/New Traces/save"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/save")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/save")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size()); } } @@ -310,8 +311,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace save quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); assertEquals(0, snapshot.getKey()); assertEquals("Scripted snapshot", snapshot.getDescription()); @@ -337,8 +338,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); MemDump dump = parseHexDump(extractOutSection(out, "---Dump---")); @@ -371,8 +372,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String eval = extractOutSectionWithPrompt(out, "---Start---"); @@ -409,8 +410,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String xout = extractOutSectionWithPrompt(out, "---Dump---"); @@ -442,8 +443,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -503,8 +504,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { quit """.formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg())); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -552,8 +553,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace save quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -578,8 +579,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace save quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -608,8 +609,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -637,8 +638,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint(), extra, lldbExpr, gtype)); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -828,8 +829,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -860,8 +861,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { script print("---") quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -899,8 +900,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:0000dead ADDRESS @@ -937,8 +938,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); assertTrue(extractOutSectionWithPrompt(out, "---GetValues---").contains(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:0000dead ADDRESS\ @@ -962,7 +963,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { assertSame(mdo.get(), traceManager.getCurrentTrace()); assertEquals("Test.Objects[1]", traceManager.getCurrentObject().getCanonicalPath().toString()); @@ -987,8 +988,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Not concerned about specifics, so long as disassembly occurs long total = 0; for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) { @@ -1011,8 +1012,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace save quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection processes = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[]")) @@ -1034,8 +1035,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { ghidra trace save quit """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection available = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Available[]")) @@ -1062,8 +1063,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); List procBreakLocVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[].Breakpoints[][1]")) @@ -1100,8 +1101,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); List procWatchLocVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[].Watchpoints[]")) @@ -1143,8 +1144,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Assumes LLDB on Linux amd64 TraceObject env = Objects.requireNonNull(tb.objAny("Processes[].Environment", Lifespan.at(0))); @@ -1170,8 +1171,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getMemoryManager().getAllRegions(); @@ -1194,8 +1195,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule modExpPrint = @@ -1219,8 +1220,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { kill quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Unique.assertOne(tb.trace.getThreadManager().getAllThreads()); } @@ -1246,8 +1247,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest { quit """.formatted(PREAMBLE, addr, getSpecimenPrint())); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics List stack = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbHooksTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbHooksTest.java index dbe4a95844..b2aed3d959 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbHooksTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbHooksTest.java @@ -43,7 +43,8 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest { private static final long RUN_TIMEOUT_MS = 5000; private static final long RETRY_MS = 500; - record LldbAndTrace(LldbAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { + record LldbAndTrace(LldbAndConnection conn, ManagedDomainObject mdo) + implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); } @@ -82,8 +83,8 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest { LldbAndConnection conn = startAndConnectLldb(); try { conn.execute("ghidra trace start"); - ManagedDomainObject mdo = waitDomainObject("/New Traces/lldb/noname"); - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + ManagedDomainObject mdo = waitTrace("/New Traces/lldb/noname"); + tb = new ToyDBTraceBuilder(mdo.get()); return new LldbAndTrace(conn, mdo); } catch (Exception e) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbMethodsTest.java index af42d81888..1c850e2687 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbMethodsTest.java @@ -70,7 +70,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { conn.execute("kill"); conn.success(); } - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { // Just confirm it's present } } @@ -82,8 +82,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txCreate(conn, "Available"); RemoteMethod refreshAvailable = conn.getMethod("refresh_available"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject available = Objects.requireNonNull(tb.objAny0("Available")); refreshAvailable.invoke(Map.of("node", available)); @@ -107,8 +107,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "breakpoints"); RemoteMethod refreshProcBreakpoints = conn.getMethod("refresh_proc_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject locations = @@ -144,8 +144,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshProcWatchpoints = conn.getMethod("refresh_proc_watchpoints"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject locations = @@ -192,8 +192,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txCreate(conn, "Processes[1]"); RemoteMethod refreshProcesses = conn.getMethod("refresh_processes"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject processes = Objects.requireNonNull(tb.objAny0("Processes")); refreshProcesses.invoke(Map.of("node", processes)); @@ -217,8 +217,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject env = Objects.requireNonNull(tb.objAny0(path)); refreshEnvironment.invoke(Map.of("node", env)); @@ -240,8 +240,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txCreate(conn, path); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject threads = Objects.requireNonNull(tb.objAny0(path)); refreshThreads.invoke(Map.of("node", threads)); @@ -263,8 +263,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { breakAt(conn, "wrapputs"); RemoteMethod refreshStack = conn.getMethod("refresh_stack"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -294,8 +294,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { conn.execute("ghidra trace tx-commit"); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("expr $%s = 0xdeadbeef".formatted(PLAT.intReg())); @@ -322,8 +322,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txCreate(conn, path); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject memory = Objects.requireNonNull(tb.objAny0(path)); refreshMappings.invoke(Map.of("node", memory)); @@ -345,8 +345,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txCreate(conn, path); RemoteMethod refreshModules = conn.getMethod("refresh_modules"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject modules = Objects.requireNonNull(tb.objAny0(path)); refreshModules.invoke(Map.of("node", modules)); @@ -374,9 +374,9 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { breakAt(conn, "work"); RemoteMethod activateThread = conn.getMethod("activate_thread"); - try (ManagedDomainObject mdo = openDomainObject( + try (ManagedDomainObject mdo = openTrace( IS_WINDOWS ? projectName("expCreateThreadExit") : projectName("expCloneExit"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -414,8 +414,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { breakAt(conn, "wrapputs"); RemoteMethod activateFrame = conn.getMethod("activate_frame"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -446,8 +446,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod removeProcess = conn.getMethod("remove_process"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Processes[]")); removeProcess.invoke(Map.of("process", proc2)); @@ -471,8 +471,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod attachObj = conn.getMethod("attach"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject target = Objects.requireNonNull(tb.obj("Available[%d]".formatted(dproc.pid))); attachObj.invoke(Map.of("target", target)); @@ -496,8 +496,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod attachPid = conn.getMethod("attach_pid"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]", Lifespan.at(0))); attachPid.invoke(Map.of("process", proc, "pid", dproc.pid)); @@ -518,8 +518,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { //conn.execute("process attach -p %d".formatted(dproc.pid)); RemoteMethod detach = conn.getMethod("detach"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); detach.invoke(Map.of("process", proc)); @@ -539,8 +539,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod launch = conn.getMethod("launch_loader"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); launch.invoke(Map.ofEntries( @@ -562,8 +562,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod launch = conn.getMethod("launch"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/lldb/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); launch.invoke(Map.ofEntries( @@ -593,8 +593,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod kill = conn.getMethod("kill"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); @@ -648,8 +648,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod step_into = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -678,8 +678,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod step_over = conn.getMethod("step_over"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); txPut(conn, "threads"); conn.execute("script lldb.debugger.SetAsync(False)"); @@ -708,8 +708,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod step_advance = conn.getMethod("step_advance"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); txPut(conn, "threads"); conn.execute("script lldb.debugger.SetAsync(False)"); @@ -743,9 +743,9 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { RemoteMethod activate = conn.getMethod("activate_thread"); RemoteMethod step_out = conn.getMethod("step_out"); - try (ManagedDomainObject mdo = - openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = + openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -776,8 +776,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { RemoteMethod activate = conn.getMethod("activate_thread"); RemoteMethod ret = conn.getMethod("step_return"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); waitTxDone(); @@ -804,8 +804,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_address"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]); @@ -826,8 +826,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); breakExpression.invoke(Map.of("expression", "main")); @@ -847,8 +847,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_hw_address"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); @@ -869,8 +869,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_hw_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); breakExpression.invoke(Map.of("expression", "`(void(*)())main`")); @@ -891,8 +891,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_read_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); @@ -918,8 +918,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_read_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of( "expression", "`(void(*)())main`", @@ -941,8 +941,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_write_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); @@ -970,8 +970,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_write_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of( "expression", "`(void(*)())main`", @@ -995,8 +995,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakRange = conn.getMethod("break_access_range"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); @@ -1022,8 +1022,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_access_expression"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of( "expression", "`(void(*)())main`", @@ -1046,8 +1046,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExc = conn.getMethod("break_exception"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExc.invoke(Map.of("lang", "C++")); @@ -1066,8 +1066,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); conn.execute("breakpoint set -n main"); @@ -1090,8 +1090,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod toggleBreakpointLocation = conn.getMethod("toggle_breakpoint_location"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); conn.execute("breakpoint set -n main"); @@ -1115,8 +1115,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { txPut(conn, "processes"); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); waitStopped(conn); conn.execute("breakpoint set -n main"); @@ -1140,8 +1140,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest { RemoteMethod breakExpression = conn.getMethod("break_read_expression"); RemoteMethod deleteWatchpoint = conn.getMethod("delete_watchpoint"); - try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace(projectName("expPrint"))) { + tb = new ToyDBTraceBuilder(mdo.get()); breakExpression.invoke(Map.of( "expression", "`(void(*)())main`", diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbStackUnwindTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbStackUnwindTest.java index 43258e27c4..56b95db254 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbStackUnwindTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbStackUnwindTest.java @@ -15,7 +15,8 @@ */ package agent.lldb.rmi; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.IOException; @@ -87,7 +88,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { DecompilerProvider decompilerProvider; DecompilerPanel decompilerPanel; - record LldbAndTrace(LldbAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { + record LldbAndTrace(LldbAndConnection conn, ManagedDomainObject mdo) + implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); } @@ -122,10 +124,10 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { } @SuppressWarnings("resource") - protected LldbAndTrace startAndSyncLldb(ManagedDomainObject mdo) throws Exception { + protected LldbAndTrace startAndSyncLldb(ManagedDomainObject mdo) throws Exception { LldbAndConnection conn = startAndConnectLldb(); try { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + tb = new ToyDBTraceBuilder(mdo.get()); return new LldbAndTrace(conn, mdo); } catch (Exception e) { @@ -148,8 +150,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -178,8 +180,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -210,8 +212,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -242,8 +244,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -271,8 +273,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); @@ -300,8 +302,8 @@ public class LldbStackUnwindTest extends AbstractLldbTraceRmiTest { tool.acceptDomainFiles(dfs); DomainFile traceDF = getDomainFile("expSpin_lldb"); - ManagedDomainObject mdo = new ManagedDomainObject(traceDF, false, false, monitor); - resyncMappings((Trace) mdo.get()); + ManagedDomainObject mdo = new ManagedDomainObject<>(traceDF, Trace.class, monitor); + resyncMappings(mdo.get()); tool.acceptDomainFiles(new DomainFile[] { traceDF }); programs = openPrograms(dfs); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java index abd1cda957..0caa1e52e9 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java @@ -16,7 +16,7 @@ package agent.x64dbg.rmi; import static org.junit.Assert.*; -import static org.junit.Assume.*; +import static org.junit.Assume.assumeTrue; import java.io.*; import java.net.*; @@ -44,6 +44,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRangeImpl; import ghidra.pty.testutil.DummyProc; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.Trace; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObjectValue; import ghidra.util.*; @@ -468,19 +469,19 @@ public abstract class AbstractX64dbgTraceRmiTest extends AbstractGhidraHeadedDeb return new RegDump(); } - protected ManagedDomainObject openDomainObject(String path) throws Exception { + protected ManagedDomainObject openTrace(String path) throws Exception { DomainFile df = env.getProject().getProjectData().getFile(path); assertNotNull(df); - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } - protected ManagedDomainObject waitDomainObject(String path) throws Exception { + protected ManagedDomainObject waitTrace(String path) throws Exception { DomainFile df; long start = System.currentTimeMillis(); while (true) { df = env.getProject().getProjectData().getFile(path); if (df != null) { - return new ManagedDomainObject(df, false, false, monitor); + return new ManagedDomainObject<>(df, Trace.class, monitor); } Thread.sleep(1000); if (System.currentTimeMillis() - start > 30000) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgCommandsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgCommandsTest.java index 11101eb710..9131731858 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgCommandsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgCommandsTest.java @@ -15,6 +15,7 @@ */ package agent.x64dbg.rmi; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; @@ -111,8 +112,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("x86:LE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("windows", @@ -129,7 +130,7 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { assertThat(mdo.get(), instanceOf(Trace.class)); } } @@ -150,8 +151,9 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { .formatted(PREAMBLE, addr, NOTEPAD)); DomainFile dfMyToy = env.getProject().getProjectData().getFile("/New Traces/x64dbg/myToy"); assertNotNull(dfMyToy); - try (ManagedDomainObject mdo = new ManagedDomainObject(dfMyToy, false, false, monitor)) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = + new ManagedDomainObject<>(dfMyToy, Trace.class, monitor)) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("Toy:BE:64:default", tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("default", @@ -280,8 +282,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size()); } @@ -296,8 +298,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size()); } } @@ -314,8 +316,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); assertEquals(0, snapshot.getKey()); assertEquals("Scripted snapshot", snapshot.getDescription()); @@ -343,8 +345,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); traceManager.openTrace(tb.trace); traceManager.activateTrace(tb.trace); @@ -379,8 +381,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String eval = extractOutSection(out, "---Start---"); @@ -414,8 +416,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); String pc = extractOutSection(out, "---PC---"); @@ -448,8 +450,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD, count)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -486,8 +488,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { quit() """.formatted(PREAMBLE, addr, NOTEPAD, count)); // The spaces will be left over, but the values should be zeroed - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); List regVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), @@ -518,8 +520,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -544,8 +546,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -572,8 +574,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -599,8 +601,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD, extra, x64dbgExpr, gtype)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -754,8 +756,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -786,8 +788,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject object = tb.trace.getObjectManager() .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); assertNotNull(object); @@ -834,8 +836,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS @@ -875,8 +877,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals(""" Parent Key Span Value Type Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS""", @@ -900,7 +902,7 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { assertSame(mdo.get(), traceManager.getCurrentTrace()); assertEquals("Test.Objects[1]", traceManager.getCurrentObject().getCanonicalPath().toString()); @@ -924,8 +926,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Not concerned about specifics, so long as disassembly occurs long total = 0; for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) { @@ -950,8 +952,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection processes = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[]")) @@ -973,8 +975,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection available = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Available[]")) @@ -1004,8 +1006,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { quit() """ .formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); List procSBreakLocVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Processes[].Debug.Software Breakpoints[]")) @@ -1049,8 +1051,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { quit() """ .formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); List procBreakVals = tb.trace.getObjectManager() .getValuePaths(Lifespan.at(0), PathFilter.parse("Sessions[].Processes[].Debug.Hardware Breakpoints[]")) @@ -1087,8 +1089,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Assumes LLDB on Linux amd64 TraceObject env = Objects.requireNonNull( @@ -1113,8 +1115,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getMemoryManager().getAllRegions(); @@ -1135,8 +1137,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection all = tb.trace.getModuleManager().getAllModules(); TraceModule modBash = @@ -1158,8 +1160,8 @@ public class X64dbgCommandsTest extends AbstractX64dbgTraceRmiTest { util.terminate_session() quit() """.formatted(PREAMBLE, addr, NOTEPAD)); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); // Would be nice to control / validate the specifics Collection threads = tb.trace.getThreadManager().getAllThreads(); assertThat(threads.size(), greaterThan(2)); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgHooksTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgHooksTest.java index 043700ee18..a4fd1e372d 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgHooksTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgHooksTest.java @@ -15,8 +15,11 @@ */ package agent.x64dbg.rmi; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.util.*; @@ -37,7 +40,7 @@ public class X64dbgHooksTest extends AbstractX64dbgTraceRmiTest { private static final long RUN_TIMEOUT_MS = 5000; private static final long RETRY_MS = 500; - record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) + record PythonAndTrace(PythonAndConnection conn, ManagedDomainObject mdo) implements AutoCloseable { public void execute(String cmd) { conn.execute(cmd); @@ -70,21 +73,20 @@ public class X64dbgHooksTest extends AbstractX64dbgTraceRmiTest { protected PythonAndTrace startAndSyncPython(String exec) throws Exception { PythonAndConnection conn = startAndConnectPython(); try { - ManagedDomainObject mdo; + ManagedDomainObject mdo; conn.execute("from ghidraxdbg.commands import *"); conn.execute( "util.set_convenience_variable('ghidra-language', 'x86:LE:64:default')"); if (exec != null) { start(conn, exec); - mdo = waitDomainObject( - "/New Traces/x64dbg/" + exec.substring(exec.lastIndexOf("\\") + 1)); + mdo = waitTrace("/New Traces/x64dbg/" + exec.substring(exec.lastIndexOf("\\") + 1)); } else { conn.execute("ghidra_trace_start()"); - mdo = waitDomainObject("/New Traces/x64dbg/noname"); + mdo = waitTrace("/New Traces/x64dbg/noname"); } clearBreakpoints(conn); - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + tb = new ToyDBTraceBuilder(mdo.get()); return new PythonAndTrace(conn, mdo); } catch (Exception e) { diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java index 1126812ded..284a7295c0 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java @@ -15,8 +15,11 @@ */ package agent.x64dbg.rmi; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import java.util.*; @@ -48,8 +51,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { start(conn, null); RemoteMethod evaluate = conn.getMethod("evaluate"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); assertEquals("11", evaluate.invoke(Map.ofEntries( Map.entry("session", tb.obj("Sessions[0]")), @@ -77,7 +80,7 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { conn.execute("ghidra_trace_kill()"); conn.execute("util.terminate_session()"); } - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { // Just confirm it's present } } @@ -91,8 +94,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { RemoteMethod refreshAvailable = conn.getMethod("refresh_available"); conn.execute("util.terminate_session()"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject available = Objects.requireNonNull(tb.objAny0("Sessions[].Available")); refreshAvailable.invoke(Map.of("node", available)); @@ -114,8 +117,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("pc = util.get_pc()"); clearBreakpoints(conn); @@ -163,8 +166,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshProcWatchpoints = conn.getMethod("refresh_breakpoints"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("pc = util.get_pc()"); clearBreakpoints(conn); @@ -214,8 +217,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txCreate(conn, "Sessions[0].Processes"); RemoteMethod refreshProcesses = conn.getMethod("refresh_processes"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject processes = Objects.requireNonNull(tb.objAny0("Sessions[].Processes")); refreshProcesses.invoke(Map.of("node", processes)); @@ -238,8 +241,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "all"); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject env = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[].Environment")); @@ -262,8 +265,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject threads = fakeEmpty(proc, "Threads"); @@ -287,8 +290,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { conn.execute("ghidra_trace_txcommit()"); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); conn.execute("util.dbg.cmd('rax=0xdeadbeef')"); @@ -313,8 +316,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject memory = fakeEmpty(proc, "Memory"); @@ -346,8 +349,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod refreshModules = conn.getMethod("refresh_modules"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = tb.objAny0("Sessions[].Processes[]"); TraceObject modules = fakeEmpty(proc, "Modules"); @@ -370,8 +373,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod activateThread = conn.getMethod("activate_thread"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); @@ -401,8 +404,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod removeProcess = conn.getMethod("remove_process"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/NETSTAT.EXE")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/NETSTAT.EXE")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); removeProcess.invoke(Map.of("process", proc2)); @@ -423,8 +426,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "available"); RemoteMethod attachObj = conn.getMethod("attach_obj"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject target = Objects.requireNonNull(tb.obj( "Sessions[0].Available[%d]".formatted(dproc.pid))); attachObj.invoke(Map.ofEntries( @@ -447,8 +450,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "available"); RemoteMethod attachPid = conn.getMethod("attach_pid"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); Objects.requireNonNull(tb.obj( "Sessions[0].Available[%d]".formatted(dproc.pid))); try { @@ -476,8 +479,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod detach = conn.getMethod("detach"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/NETSTAT.EXE")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/NETSTAT.EXE")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); detach.invoke(Map.of("process", proc)); @@ -497,8 +500,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod launch = conn.getMethod("launch"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/noname")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/noname")) { + tb = new ToyDBTraceBuilder(mdo.get()); launch.invoke(Map.ofEntries( Map.entry("Session", tb.obj("Sessions[0]")), @@ -523,8 +526,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod kill = conn.getMethod("kill"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); kill.invoke(Map.of("process", proc)); @@ -547,8 +550,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { RemoteMethod go = conn.getMethod("go"); RemoteMethod interrupt = conn.getMethod("interrupt"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -573,8 +576,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod stepInto = conn.getMethod("step_into"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); TraceObject thread = @@ -609,8 +612,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod stepOver = conn.getMethod("step_over"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); TraceObject thread = @@ -641,8 +644,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { RemoteMethod stepInto = conn.getMethod("step_into"); RemoteMethod stepOut = conn.getMethod("step_out"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); txPut(conn, "threads"); TraceObject thread = @@ -673,8 +676,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -699,8 +702,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); breakExpression.invoke(Map.of("expression", "CreateFileW")); @@ -723,8 +726,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddress = conn.getMethod("break_hw_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -750,8 +753,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_hw_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); breakExpression.invoke(Map.of("expression", "CreateFileW")); @@ -774,8 +777,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddr = conn.getMethod("break_read_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); @@ -803,8 +806,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_read_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); breakExpression.invoke(Map.of("expression", "CreateFileW")); @@ -830,8 +833,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddr = conn.getMethod("break_write_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); @@ -859,8 +862,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_write_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); @@ -887,8 +890,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakAddr = conn.getMethod("break_access_address"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); TraceObject proc = Objects.requireNonNull(tb.objAny0("Sessions[].Processes[]")); @@ -915,8 +918,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { txPut(conn, "processes"); RemoteMethod breakExpression = conn.getMethod("break_access_expression"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); breakExpression.invoke(Map.of("expression", "CreateFileW")); @@ -943,8 +946,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { RemoteMethod breakAddress = conn.getMethod("break_address"); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); @@ -977,8 +980,8 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { RemoteMethod breakAddress = conn.getMethod("break_address"); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); - try (ManagedDomainObject mdo = openDomainObject("/New Traces/x64dbg/notepad.exe")) { - tb = new ToyDBTraceBuilder((Trace) mdo.get()); + try (ManagedDomainObject mdo = openTrace("/New Traces/x64dbg/notepad.exe")) { + tb = new ToyDBTraceBuilder(mdo.get()); clearBreakpoints(conn); diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialogTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialogTest.java new file mode 100644 index 0000000000..269b4d203d --- /dev/null +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialogTest.java @@ -0,0 +1,553 @@ +/* ### + * 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.emulation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeFalse; + +import java.lang.annotation.*; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; + +import javax.swing.JDialog; +import javax.swing.JTextField; +import javax.swing.tree.TreePath; + +import org.hamcrest.Matchers; +import org.junit.*; + +import db.Transaction; +import docking.ActionContext; +import docking.action.DockingActionIf; +import docking.widgets.table.EnumeratedColumnTableModel; +import generic.Unique; +import generic.test.ConcurrentTestExceptionHandler; +import ghidra.app.plugin.assembler.Assembler; +import ghidra.app.plugin.assembler.Assemblers; +import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; +import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialogTestHelper; +import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerIntegrationTest; +import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointMarkerPlugin; +import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsPlugin; +import ghidra.app.plugin.core.debug.gui.emulation.FunctionEmulationHarness.ReturnAddressInfo; +import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; +import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersPlugin; +import ghidra.app.plugin.core.debug.gui.time.DebuggerTimePlugin; +import ghidra.app.plugin.core.debug.service.breakpoint.ProgramBreakpoint; +import ghidra.app.plugin.core.function.FunctionPlugin; +import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.debug.api.breakpoint.LogicalBreakpoint; +import ghidra.pcode.exec.BytesPcodeArithmetic; +import ghidra.pcode.exec.PcodeArithmetic; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.Register; +import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; +import ghidra.program.model.symbol.SourceType; +import ghidra.trace.database.ToyDBTraceBuilder; +import ghidra.util.task.TaskMonitorComponent; +import utility.function.ExceptionalCallback; + +public class DebuggerEmulateFunctionDialogTest extends AbstractGhidraHeadedDebuggerIntegrationTest { + final static List DEFAULT_ASM = List.of( + "load r10, [r12]", + "imm r9, #8", + "add r12, r9", + "load r12, [r12]", + "add r12, r10", + "add r12, r11", + "ret"); + + PcodeArithmetic ba; + Function function; + DataType dtLongLong; + Pointer dtStructPtr; + DebuggerEmulateFunctionDialog dialog; + Address addressOfLoad; // Address of *last* load instruction + SleighLanguage language; + DataTypeManager dtm; + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @interface AltAsm { + String[] value(); + } + + @SuppressWarnings("unchecked") + T resolve(T dt) { + try (Transaction tx = program.openTransaction("Resolved %s".formatted(dt))) { + DataTypeConflictHandler handler = DataTypeConflictHandler.DEFAULT_HANDLER; + return (T) dtm.resolve(dt, handler); + } + } + + String[] getAsm() throws Exception { + Method method = null; + try { + method = getClass().getMethod(name.getMethodName()); + } + catch (NoSuchMethodException e) { + return DEFAULT_ASM.toArray(String[]::new); + } + AltAsm annotation = method.getAnnotation(AltAsm.class); + if (annotation == null) { + return DEFAULT_ASM.toArray(String[]::new); + } + return annotation.value(); + } + + @Before + public void setupEmulateFunctionTest() throws Exception { + createProgram(); + language = (SleighLanguage) program.getLanguage(); + dtm = program.getDataTypeManager(); + intoProject(program); + ba = BytesPcodeArithmetic.forLanguage(program.getLanguage()); + try (Transaction tx = program.openTransaction("Create function")) { + Address entry = addr(program, 0x00400000); + + program.getMemory() + .createInitializedBlock(".text", entry, 0x1000, (byte) 0, monitor, false); + + Assembler asm = Assemblers.getAssembler(program); + AddressSet body = new AddressSet(); + for (Instruction i : asm.assemble(entry, getAsm())) { + body.add(new AddressRangeImpl(i.getMinAddress(), i.getMaxAddress())); + if ("load".equals(i.getMnemonicString())) { + addressOfLoad = i.getAddress(); + } + } + + function = program.getFunctionManager() + .createFunction("main", entry, body, SourceType.IMPORTED); + + dtLongLong = resolve(LongLongDataType.dataType); + + Structure st = new StructureDataType("myStruct", 0, dtm); + st.add(dtLongLong, "first", ""); + st.add(dtLongLong, "second", ""); + dtStructPtr = resolve(new PointerDataType(st)); + + Variable returnVar = new ReturnParameterImpl(dtLongLong, program); + Variable param1 = new ParameterImpl("param1", dtStructPtr, program); + Variable param2 = new ParameterImpl("param2", dtLongLong, program); + + function.updateFunction("default", returnVar, List.of(param1, param2), + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, false, SourceType.ANALYSIS); + } + + programManager.openProgram(program); + + dialog = runSwing(() -> new DebuggerEmulateFunctionDialog(tool, function)); + + runSwing(() -> tool.showDialog(dialog)); + } + + @After + public void tearDownEmulateFunctionTest() { + runSwing(() -> dialog.close()); + } + + //@Test + public void testManual() throws Exception { + assumeFalse(BATCH_MODE); + ConcurrentTestExceptionHandler.disable(); + + addPlugin(tool, DataTypeManagerPlugin.class); + addPlugin(tool, CodeBrowserPlugin.class); + addPlugin(tool, FunctionPlugin.class); + addPlugin(tool, DebuggerListingPlugin.class); + addPlugin(tool, DebuggerTimePlugin.class); + addPlugin(tool, DebuggerRegistersPlugin.class); + addPlugin(tool, DebuggerBreakpointsPlugin.class); + addPlugin(tool, DebuggerBreakpointMarkerPlugin.class); + addPlugin(tool, GhidraScriptMgrPlugin.class); + waitForSwing(); + + while (runSwing(() -> dialog.isShowing())) { + Thread.sleep(1000); + } + } + + record NameType(String name, DataType type) implements Comparable { + public static final Comparator NATURAL_ORDER = + Comparator.comparing(NameType::name); + + static NameType extract(VarRow row) { + return new NameType(row.getName(), row.getType()); + } + + static List extract(EnumeratedColumnTableModel model) { + return model.getModelData().stream().map(NameType::extract).sorted().toList(); + } + + @Override + public int compareTo(NameType o) { + return NATURAL_ORDER.compare(this, o); + } + } + + InputRow findInput(String name) { + return runSwing(() -> dialog.findRow(dialog.inputsTableModel, name)).orElseThrow(); + } + + OutputRow findOutput(String name) { + return runSwing(() -> dialog.findRow(dialog.outputsTableModel, name)).orElseThrow(); + } + + void performInputAction(String name, boolean wait) { + ActionContext ctx = runSwing(() -> dialog.inputActions.getActionContext(null)); + performAction(dialog.inputActions.byName(name), ctx, wait); + } + + void performOutputAction(String name, boolean wait) { + ActionContext ctx = runSwing(() -> dialog.outputActions.getActionContext(null)); + performAction(dialog.outputActions.byName(name), ctx, wait); + } + + @Test + public void testRetInRegister() throws Exception { + try (FunctionEmulationHarness harness = + FunctionEmulationHarness.start(tool, function, monitor)) { + ReturnAddressInfo retInfo = harness.locateReturnAddress(); + Register lr = harness.language.getRegister("lr"); + assertEquals(lr.getAddress(), retInfo.location()); + assertEquals(lr.getAddress(), retInfo.computePhysicalLocation(harness.initEval, + harness.program.getCompilerSpec())); + } + } + + @Test + @AltAsm({ + "pop lr", + "ret" }) + public void testRetOnStack() throws Exception { + try (FunctionEmulationHarness harness = + FunctionEmulationHarness.start(tool, function, monitor)) { + ReturnAddressInfo retInfo = harness.locateReturnAddress(); + AddressSpace stack = harness.program.getAddressFactory().getStackSpace(); + AddressSpace data = harness.language.getDefaultDataSpace(); + assertEquals(stack.getAddress(8), retInfo.location()); + assertEquals(data.getAddress(0x00005008), retInfo + .computePhysicalLocation(harness.initEval, harness.program.getCompilerSpec())); + } + } + + @Test + public void testDefaultVars() throws Exception { + assertEquals( + List.of( + new NameType("param1", dtStructPtr), + new NameType("param2", dtLongLong)), + NameType.extract(dialog.inputsTableModel)); + assertEquals( + List.of( + new NameType("", dtLongLong)), + NameType.extract(dialog.outputsTableModel)); + } + + @Test + public void testRefreshParameters() throws Exception { + InputRow param1 = findInput("param1"); + assertEquals(dtStructPtr, param1.getType()); + + try (Transaction tx = program.openTransaction("Modify param1")) { + Parameter p1 = function.getParameter(0); + assertEquals("param1", p1.getName()); + p1.setDataType(dtLongLong, SourceType.USER_DEFINED); + } + waitForDomainObject(program); + performInputAction("Refresh Parameter Inputs", true); + + param1 = findInput("param1"); + assertEquals(dtLongLong, param1.getType()); + } + + @Test + public void testRefreshReturn() throws Exception { + OutputRow ret = findOutput(""); + assertEquals(dtLongLong, ret.getType()); + + try (Transaction tx = program.openTransaction("Modify return")) { + function.getReturn().setDataType(dtStructPtr, SourceType.USER_DEFINED); + } + waitForDomainObject(program); + performOutputAction("Refresh Return Outputs", true); + + ret = findOutput(""); + assertEquals(dtStructPtr, ret.getType()); + } + + void expecting(Class cls, ExceptionalCallback cb) throws T { + try { + cb.call(); + fail("Did not get expected exception %s".formatted(cls.getSimpleName())); + } + catch (Throwable e) { + if (cls.isInstance(e)) { + return; + } + throw e; + } + } + + static final String RETURN = ""; + + @Test + public void testRefreshVoidReturn() throws Exception { + findOutput(RETURN); + + try (Transaction tx = program.openTransaction("Modify return")) { + function.getReturn().setDataType(VoidDataType.dataType, SourceType.USER_DEFINED); + } + waitForDomainObject(program); + performOutputAction("Refresh Return Outputs", true); + + expecting(NoSuchElementException.class, () -> { + findOutput(RETURN); + }); + } + + TreePath path(String... path) { + return new TreePath(path); + } + + @Test + public void testAddVararg() throws Exception { + addPlugin(tool, DataTypeManagerPlugin.class); + + DockingActionIf actionAddVararg = dialog.inputActions.byName("Add Vararg Input"); + assertFalse(actionAddVararg.isEnabled()); + + try (Transaction tx = program.openTransaction("Enable varargs")) { + function.setVarArgs(true); + } + waitForDomainObject(program); + + assertTrue(actionAddVararg.isEnabled()); + performInputAction("Add Vararg Input", false); + DataTypeChooserDialog typeDialog = waitForDialogComponent(DataTypeChooserDialog.class); + waitForTasks(); + + typeDialog.setSelectedPath(path("Data Types", "BuiltInTypes", "float")); + waitForTasks(); + DataTypeChooserDialogTestHelper.clickOk(typeDialog); + + InputRow input = findInput("Vararg 3"); + assertEquals(resolve(FloatDataType.dataType), input.getType()); + } + + @Test + public void testExpandStructPtr() throws Exception { + InputRow param1 = findInput("param1"); + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Allocate and Add Pointer Inputs", true); + + InputRow param1First = findInput("(param1)->first"); + assertEquals(dtLongLong, param1First.getType()); + InputRow param1Second = findInput("(param1)->second"); + assertEquals(dtLongLong, param1Second.getType()); + } + + @Test + public void testExpandSimplePtr() throws Exception { + InputRow param1 = findInput("param1"); + DataType dtLongLongPtr = resolve(new PointerDataType(dtLongLong)); + param1.setType(dtLongLongPtr); + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Allocate and Add Pointer Inputs", true); + + InputRow param1Deref = findInput("*(param1)"); + assertEquals(dtLongLong, param1Deref.getType()); + } + + void placeInject(Address address, String inject) { + try (Transaction tx = program.openTransaction("Place inject")) { + Bookmark bm = program.getBookmarkManager() + .setBookmark(address, LogicalBreakpoint.ENABLED_BOOKMARK_TYPE, "x;1", ""); + ProgramBreakpoint brk = ProgramBreakpoint.fromBookmark(program, bm); + brk.setEmuSleigh(inject); + } + waitForDomainObject(program); + } + + @Test + public void testFullUse() throws Exception { + InputRow param1 = findInput("param1"); + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Allocate and Add Pointer Inputs", true); + + InputRow param1First = findInput("(param1)->first"); + param1First.setValue(ba.fromConst(6, param1First.length)); + InputRow param1Second = findInput("(param1)->second"); + param1Second.setRepr("7h"); + InputRow param2 = findInput("param2"); + param2.setRepr("8h"); + + placeInject(addressOfLoad, """ + emu_probe(r12); + emu_exec_decoded(); + """); + + runSwing(() -> dialog.okCallback()); + waitForTasks(); + + OutputRow ret = findOutput(""); + assertEquals("15h", ret.getRepr()); + OutputRow probe = findOutput("Probe 1"); + assertEquals(0x7beef008L, ba.toLong(probe.value, Purpose.INSPECT)); + } + + @Test + public void testAddCustom() throws Exception { + performInputAction("Add Custom Input", false); + JDialog exprDialog = waitForJDialog("Add Custom Input"); + JTextField exprField = findComponent(exprDialog, JTextField.class); + exprField.setText("r10"); + runSwing(() -> findButtonByText(exprDialog, "OK").doClick()); + + assertNotNull(findInput("Custom 1")); + } + + @Test + public void testConflictingInputs() throws Exception { + InputRow custom1 = + new InputRow(language, "Custom 1", VarStorage.fromExpression(language, "r12"), null); + dialog.inputsTableModel.add(custom1); + InputRow param1 = findInput("param1"); + assertEquals(custom1.storage, param1.storage); + + custom1.setValueStr("0x1"); + param1.setValueStr("0x7"); + + runSwing(() -> dialog.okCallback()); + waitForTasks(); + String status = runSwing(() -> dialog.getStatusText()); + assertThat(status, + Matchers.either(Matchers.equalTo("Input r12 conflicts: 0x1 != 0x7")) + .or(Matchers.equalTo("Input r12 conflicts: 0x7 != 0x1"))); + } + + @Test + public void testRemovedDependencyNotApplied() throws Exception { + InputRow param1 = findInput("param1"); + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Allocate and Add Pointer Inputs", true); + + InputRow param1First = findInput("(param1)->first"); + param1First.setValue(ba.fromConst(6, param1First.length)); + InputRow param1Second = findInput("(param1)->second"); + param1Second.setRepr("7h"); + InputRow param2 = findInput("param2"); + param2.setRepr("8h"); + + // Not zeros. Should be alloced address + assertFalse(Arrays.equals(new byte[param1.length], param1.getValue())); + + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Remove Input", true); + + runSwing(() -> dialog.okCallback()); + waitForTasks(); + + tb = new ToyDBTraceBuilder(Unique.assertOne(traceManager.getOpenTraces())); + // Should wind up at zero, because param1 is not be applied + ByteBuffer buf = ByteBuffer.allocate(dtStructPtr.getDataType().getLength()) + .order(language.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + tb.trace.getMemoryManager().getBytes(0, tb.addr(0), buf); + buf.flip(); + assertEquals(6, buf.getLong()); + assertEquals(7, buf.getLong()); + } + + @Test + public void testDependencyRefreshedStillAffectsDependents() throws Exception { + InputRow param1 = findInput("param1"); + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Allocate and Add Pointer Inputs", true); + + InputRow param1First = findInput("(param1)->first"); + param1First.setValue(ba.fromConst(6, param1First.length)); + InputRow param1Second = findInput("(param1)->second"); + param1Second.setRepr("7h"); + InputRow param2 = findInput("param2"); + param2.setRepr("8h"); + + runSwing(() -> dialog.inputsFilterPanel.setSelectedItem(param1)); + performInputAction("Remove Input", true); + performInputAction("Refresh Parameter Inputs", true); + InputRow param1a = findInput("param1"); + assertNotSame(param1, param1a); + + Address newAddr = program.getAddressFactory().getAddress("00600000"); + param1a.setValue(ba.fromConst(newAddr)); + + runSwing(() -> dialog.okCallback()); + waitForTasks(); + + tb = new ToyDBTraceBuilder(Unique.assertOne(traceManager.getOpenTraces())); + // Should wind up at zero, because param1 is not be applied + ByteBuffer buf = ByteBuffer.allocate(dtStructPtr.getDataType().getLength()) + .order(language.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + tb.trace.getMemoryManager().getBytes(0, newAddr, buf); + buf.flip(); + assertEquals(6, buf.getLong()); + assertEquals(7, buf.getLong()); + } + + @Test + public void testCancelLoop() throws Exception { + placeInject(addressOfLoad, """ + goto 0x%s; + """.formatted(function.getEntryPoint())); + + runSwing(() -> dialog.okCallback()); + + TaskMonitorComponent monitor = + waitForValue(() -> findComponent(dialog, TaskMonitorComponent.class)); + runSwing(() -> monitor.cancel()); + waitForTasks(); + } + + /** + * i.e., an expression is placed inside the probe vs. just a varnode + * + * @throws Exception because + */ + @Test + public void testProbeUnique() throws Exception { + // Note: r12 should be 8 at this address + placeInject(addressOfLoad, """ + emu_probe(r12 + 0x1234); + emu_exec_decoded(); + """); + + runSwing(() -> dialog.okCallback()); + waitForTasks(); + + OutputRow probe = findOutput("Probe 1"); + assertEquals("$Unique", probe.getStorage().toString()); + assertEquals(0x123c, ba.toLong(probe.value, Purpose.INSPECT)); + } +}