GP-3887: Update Debugger course for Trace RMI.

This commit is contained in:
Dan 2024-04-22 10:11:25 -04:00
parent 190f1eaa1e
commit a93a695e6a
79 changed files with 2235 additions and 1663 deletions

View file

@ -26,7 +26,6 @@ import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
import ghidra.framework.plugintool.PluginTool;
@ -104,9 +103,9 @@ public interface AutoMapSpec extends ExtensionPoint {
if (mappingService == null || programManager == null) {
return;
}
BackgroundCommand cmd = new BackgroundCommand(getTaskTitle(), true, true, false) {
BackgroundCommand<Trace> cmd = new BackgroundCommand<>(getTaskTitle(), true, true, false) {
@Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
public boolean applyTo(Trace trace, TaskMonitor monitor) {
try {
performMapping(mappingService, trace, programManager, monitor);
return true;

View file

@ -799,7 +799,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
protected void executeCapture(AddressRange range, Target target, TaskMonitor monitor)
throws Exception {
target.readMemory(new AddressSet(range), monitor);
target.readMemoryAsync(new AddressSet(range), monitor).get();
}
protected void executePlan(TaskMonitor monitor) throws Exception {

View file

@ -64,6 +64,11 @@ public interface DisplaysObjectValues {
.collect(Collectors.joining(":"));
}
default String getStringsDisplay(String[] strings) {
return Stream.of(strings)
.collect(Collectors.joining(":"));
}
default String getPrimitiveValueDisplay(Object value) {
assert !(value instanceof TraceObject);
assert !(value instanceof TraceObjectValue);
@ -89,6 +94,9 @@ public interface DisplaysObjectValues {
if (value instanceof long[] longs) {
return getLongsDisplay(longs);
}
if (value instanceof String[] strings) {
return getStringsDisplay(strings);
}
return value.toString();
}

View file

@ -42,13 +42,13 @@ public class DebuggerModuleMapProposalDialog
implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> {
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getModule().getBase()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()),
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null
? e.getToProgram().getName()
: e.getToProgram().getDomainFile().getName())),
STATIC_BASE("Static Base", Address.class, e -> e.getToProgram().getImageBase()),
SIZE("Size", Long.class, e -> e.getModuleRange().getLength()),
STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()),
SIZE("Size", Long.class, e -> e.getFromRange().getLength()),
MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, ModuleMapEntry::setMemorize);
private final String header;

View file

@ -32,6 +32,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {
ProgramOpenedPluginEvent.class,
ProgramActivatedPluginEvent.class,
ProgramLocationPluginEvent.class,
ProgramClosedPluginEvent.class,
@ -65,7 +66,10 @@ public class DebuggerModulesPlugin extends AbstractDebuggerPlugin {
@Override
public void processEvent(PluginEvent event) {
super.processEvent(event);
if (event instanceof ProgramActivatedPluginEvent ev) {
if (event instanceof ProgramOpenedPluginEvent ev) {
provider.programOpened(ev.getProgram());
}
else if (event instanceof ProgramActivatedPluginEvent ev) {
provider.setProgram(ev.getActiveProgram());
}
else if (event instanceof ProgramLocationPluginEvent ev) {

View file

@ -1078,6 +1078,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
actionMapSectionTo.getPopupMenuData().setMenuItemName(name);
}
public void programOpened(Program program) {
// TODO: Debounce this?
cueAutoMap = true;
doCuedAutoMap();
}
public void programClosed(Program program) {
if (currentProgram == program) {
currentProgram = null;

View file

@ -614,7 +614,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
pcodeTable.setTableHeader(null);
pcodeTable.setBackground(COLOR_BACKGROUND);
pcodeTable.setSelectionBackground(COLOR_BACKGROUND_CURSOR);
pcodeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
pcodeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
pcodeTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;

View file

@ -106,6 +106,9 @@ public class UniqueRow {
}
public RefType getRefType() {
if (provider.pcodeTable.getSelectedRowCount() != 1) {
return RefType.NONE;
}
int index = provider.pcodeTable.getSelectedRow();
if (index == -1) {
return RefType.NONE;

View file

@ -42,8 +42,8 @@ import ghidra.trace.database.DBTrace;
import ghidra.trace.model.*;
import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.TraceConflictedMappingException;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.target.*;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.thread.*;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.*;
@ -70,7 +70,8 @@ public class ProgramEmulationUtils {
<attribute name='Modules' schema='ModuleContainer' />
<attribute name='Threads' schema='ThreadContainer' />
</schema>
<schema name='BreakpointContainer' elementResync='NEVER' attributeResync='NEVER'>
<schema name='BreakpointContainer' canonical='yes' elementResync='NEVER'
attributeResync='NEVER'>
<interface name='BreakpointSpecContainer' />
<interface name='BreakpointLocationContainer' />
<element schema='Breakpoint' />
@ -494,6 +495,20 @@ public class ProgramEmulationUtils {
throw new EmulatorOutOfMemoryException();
}
protected static void createObjects(Trace trace) {
TraceObjectManager om = trace.getObjectManager();
om.createRootObject(EMU_SESSION_SCHEMA);
om.createObject(TraceObjectKeyPath.parse("Breakpoints"))
.insert(Lifespan.ALL, ConflictResolution.DENY);
om.createObject(TraceObjectKeyPath.parse("Memory"))
.insert(Lifespan.ALL, ConflictResolution.DENY);
om.createObject(TraceObjectKeyPath.parse("Modules"))
.insert(Lifespan.ALL, ConflictResolution.DENY);
om.createObject(TraceObjectKeyPath.parse("Threads"))
.insert(Lifespan.ALL, ConflictResolution.DENY);
}
/**
* Create a new trace with a single thread, ready for emulation of the given program
*
@ -510,7 +525,8 @@ public class ProgramEmulationUtils {
try {
trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
try (Transaction tx = trace.openTransaction("Emulate")) {
trace.getObjectManager().createRootObject(EMU_SESSION_SCHEMA);
createObjects(trace);
TraceSnapshot initial =
trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
long snap = initial.getKey();

View file

@ -34,7 +34,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.ComparatorMath;
import ghidra.util.Msg;
@ -163,11 +162,17 @@ public enum DebuggerStaticMappingUtils {
private Address min = null;
private Address max = null;
public void consider(Address min, Address max) {
this.min = this.min == null ? min : ComparatorMath.cmin(this.min, min);
this.max = this.max == null ? max : ComparatorMath.cmax(this.max, max);
}
public void consider(AddressRange range) {
min = min == null ? range.getMinAddress()
: ComparatorMath.cmin(min, range.getMinAddress());
max = max == null ? range.getMaxAddress()
: ComparatorMath.cmax(max, range.getMaxAddress());
consider(range.getMinAddress(), range.getMaxAddress());
}
public void consider(Address address) {
consider(address, address);
}
public Address getMin() {
@ -181,6 +186,10 @@ public enum DebuggerStaticMappingUtils {
public long getLength() {
return max.subtract(min) + 1;
}
public AddressRange getRange() {
return new AddressRangeImpl(getMin(), getMax());
}
}
public static boolean isReal(MemoryBlock block) {

View file

@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.service.modules;
import java.util.*;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.modules.ModuleMapProposal;
import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
@ -27,6 +28,7 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.modules.TraceModule;
import ghidra.util.MathUtilities;
public class DefaultModuleMapProposal
extends AbstractMapProposal<TraceModule, Program, ModuleMapEntry>
@ -81,20 +83,24 @@ public class DefaultModuleMapProposal
* @param program the program image whose size to compute
* @return the size
*/
public static long computeImageSize(Program program) {
Address imageBase = program.getImageBase();
long imageSize = 0;
public static AddressRange computeImageRange(Program program) {
Extrema extrema = new Extrema();
// TODO: How to handle Harvard architectures?
for (MemoryBlock block : program.getMemory().getBlocks()) {
if (!includeBlock(program, block)) {
continue;
}
imageSize = Math.max(imageSize, block.getEnd().subtract(imageBase) + 1);
// includeBlock checks address space is same as image base
extrema.consider(block.getAddressRange());
}
return imageSize;
if (program.getImageBase().getOffset() != 0) {
extrema.consider(program.getImageBase());
}
return extrema.getRange();
}
protected AddressRange moduleRange;
protected AddressRange imageRange;
protected boolean memorize = false;
/**
@ -113,6 +119,7 @@ public class DefaultModuleMapProposal
AddressRange moduleRange) {
super(module.getTrace(), module, program, program);
this.moduleRange = moduleRange;
this.imageRange = quantize(computeImageRange(program));
}
@Override
@ -125,9 +132,18 @@ public class DefaultModuleMapProposal
return getModule().getLifespan();
}
private long getLength() {
return MathUtilities.unsignedMin(moduleRange.getLength(), imageRange.getLength());
}
@Override
public AddressRange getFromRange() {
return moduleRange;
try {
return new AddressRangeImpl(moduleRange.getMinAddress(), getLength());
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
}
}
@Override
@ -138,21 +154,13 @@ public class DefaultModuleMapProposal
@Override
public void setProgram(Program program) {
setToObject(program, program);
try {
this.moduleRange = quantize(
new AddressRangeImpl(getModule().getBase(), computeImageSize(program)));
}
catch (AddressOverflowException e) {
// This is terribly unlikely
throw new IllegalArgumentException(
"Specified program is too large for module's memory space");
}
this.imageRange = quantize(computeImageRange(program));
}
@Override
public AddressRange getToRange() {
try {
return new AddressRangeImpl(getToProgram().getImageBase(), moduleRange.getLength());
return new AddressRangeImpl(imageRange.getMinAddress(), getLength());
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
@ -174,10 +182,8 @@ public class DefaultModuleMapProposal
// indexed by region's offset from module base
protected final NavigableMap<Long, ModuleRegionMatcher> matchers = new TreeMap<>();
protected Address imageBase;
protected Address moduleBase;
protected long imageSize;
protected AddressRange moduleRange; // TODO: This is now in the trace schema. Use it.
protected AddressRange imageRange;
protected AddressRange moduleRange;
protected DefaultModuleMapProposal(TraceModule module, Program program) {
super(module.getTrace(), program);
@ -196,8 +202,8 @@ public class DefaultModuleMapProposal
}
private void processProgram() {
imageBase = program.getImageBase();
imageSize = DefaultModuleMapEntry.computeImageSize(program);
imageRange = quantize(DefaultModuleMapEntry.computeImageRange(program));
Address imageBase = imageRange.getMinAddress(); // not precisely, but good enough
// TODO: How to handle Harvard architectures?
for (MemoryBlock block : program.getMemory().getBlocks()) {
if (!DefaultModuleMapEntry.includeBlock(program, block)) {
@ -211,13 +217,8 @@ public class DefaultModuleMapProposal
* Must be called after processProgram, so that image size is known
*/
private void processModule() {
moduleBase = module.getBase();
try {
moduleRange = quantize(new AddressRangeImpl(moduleBase, imageSize));
}
catch (AddressOverflowException e) {
return; // Just score it as having no matches?
}
moduleRange = quantize(module.getRange());
Address moduleBase = moduleRange.getMinAddress();
Lifespan lifespan = module.getLifespan();
for (TraceMemoryRegion region : module.getTrace()
.getMemoryManager()