GP-5379: Require snap parameter in object getters/setters. Lots of fallout.

This commit is contained in:
Dan 2025-03-03 17:29:24 +00:00
parent 22a4de14ea
commit 789a9fa4b7
167 changed files with 2527 additions and 2788 deletions

View file

@ -241,6 +241,7 @@ public interface DebuggerStaticMappingService {
* @param entries the entries to add * @param entries the entries to add
* @param monitor a monitor to cancel the operation * @param monitor a monitor to cancel the operation
* @param truncateExisting true to delete or truncate the lifespan of overlapping entries * @param truncateExisting true to delete or truncate the lifespan of overlapping entries
* @throws CancelledException if the user cancels
* @see #addMapping(TraceLocation, ProgramLocation, long, boolean) * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
* @throws TraceConflictedMappingException if a conflicting mapping overlaps the source and * @throws TraceConflictedMappingException if a conflicting mapping overlaps the source and
* {@code truncateExisting} is false. * {@code truncateExisting} is false.
@ -259,6 +260,7 @@ public interface DebuggerStaticMappingService {
* @param entries the entries to add * @param entries the entries to add
* @param monitor a monitor to cancel the operation * @param monitor a monitor to cancel the operation
* @param truncateExisting true to delete or truncate the lifespan of overlapping entries * @param truncateExisting true to delete or truncate the lifespan of overlapping entries
* @throws CancelledException if the user cancels
* @see #addMapping(TraceLocation, ProgramLocation, long, boolean) * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
*/ */
void addSectionMappings(Collection<SectionMapEntry> entries, TaskMonitor monitor, void addSectionMappings(Collection<SectionMapEntry> entries, TaskMonitor monitor,
@ -275,6 +277,7 @@ public interface DebuggerStaticMappingService {
* @param entries the entries to add * @param entries the entries to add
* @param monitor a monitor to cancel the operation * @param monitor a monitor to cancel the operation
* @param truncateExisting true to delete or truncate the lifespan of overlapping entries * @param truncateExisting true to delete or truncate the lifespan of overlapping entries
* @throws CancelledException if the user cancels
* @see #addMapping(TraceLocation, ProgramLocation, long, boolean) * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
*/ */
void addRegionMappings(Collection<RegionMapEntry> entries, TaskMonitor monitor, void addRegionMappings(Collection<RegionMapEntry> entries, TaskMonitor monitor,
@ -421,17 +424,18 @@ public interface DebuggerStaticMappingService {
* The service maintains an index of likely module names to domain files in the active project. * The service maintains an index of likely module names to domain files in the active project.
* This will search that index for the module's full file path. Failing that, it will search * This will search that index for the module's full file path. Failing that, it will search
* just for the module's file name. Among the programs found, it first prefers those whose * just for the module's file name. Among the programs found, it first prefers those whose
* module name list (see {@link ProgramModuleIndexer#setModulePaths(Program, List)}) include the * module name list includes the sought module. Then, it prefers those whose executable path
* sought module. Then, it prefers those whose executable path (see * (see {@link Program#setExecutablePath(String)}) matches the sought module. Finally, it
* {@link Program#setExecutablePath(String)}) matches the sought module. Finally, it prefers * prefers matches on the program name and the domain file name. Ties in name matching are
* matches on the program name and the domain file name. Ties in name matching are broken by * broken by looking for domain files in the same folders as those programs already mapped into
* looking for domain files in the same folders as those programs already mapped into the trace * the trace in the given address space.
* in the given address space.
* *
* @param space the fallback address space if the module is missing its base
* @param module the trace module * @param module the trace module
* @param snap the snapshot to consider
* @return the, possibly empty, set of probable matches * @return the, possibly empty, set of probable matches
*/ */
DomainFile findBestModuleProgram(AddressSpace space, TraceModule module); DomainFile findBestModuleProgram(AddressSpace space, TraceModule module, long snap);
/** /**
* Propose a module map for the given module to the given program * Propose a module map for the given module to the given program
@ -440,14 +444,15 @@ public interface DebuggerStaticMappingService {
* Note, no sanity check is performed on the given parameters. This will simply propose the * Note, no sanity check is performed on the given parameters. This will simply propose the
* given module-program pair. It is strongly advised to use * given module-program pair. It is strongly advised to use
* {@link ModuleMapProposal#computeScore()} to assess the proposal. Alternatively, use * {@link ModuleMapProposal#computeScore()} to assess the proposal. Alternatively, use
* {@link #proposeModuleMap(TraceModule, Collection)} to have the service select the best-scored * {@link #proposeModuleMap(TraceModule, long, Collection)} to have the service select the
* mapping from a collection of proposed programs. * best-scored mapping from a collection of proposed programs.
* *
* @param module the module to consider * @param module the module to consider
* @param snap the source snapshot key
* @param program the destination program to consider * @param program the destination program to consider
* @return the proposal * @return the proposal
*/ */
ModuleMapProposal proposeModuleMap(TraceModule module, Program program); ModuleMapProposal proposeModuleMap(TraceModule module, long snap, Program program);
/** /**
* Compute the best-scored module map for the given module and programs * Compute the best-scored module map for the given module and programs
@ -460,10 +465,12 @@ public interface DebuggerStaticMappingService {
* @see ModuleMapProposal#computeScore() * @see ModuleMapProposal#computeScore()
* *
* @param module the module to consider * @param module the module to consider
* @param snap the source snapshot key
* @param programs a set of proposed destination programs * @param programs a set of proposed destination programs
* @return the best-scored proposal, or {@code null} if no program is proposed * @return the best-scored proposal, or {@code null} if no program is proposed
*/ */
ModuleMapProposal proposeModuleMap(TraceModule module, Collection<? extends Program> programs); ModuleMapProposal proposeModuleMap(TraceModule module, long snap,
Collection<? extends Program> programs);
/** /**
* Compute the "best" map of trace module to program for each given module given a collection of * Compute the "best" map of trace module to program for each given module given a collection of
@ -476,11 +483,13 @@ public interface DebuggerStaticMappingService {
* {@code null} values. * {@code null} values.
* *
* @param modules the modules to map * @param modules the modules to map
* @param snap the source snapshot key
* @param programs the set of proposed destination programs * @param programs the set of proposed destination programs
* @return the proposal * @return the proposal
*/ */
Map<TraceModule, ModuleMapProposal> proposeModuleMaps( Map<TraceModule, ModuleMapProposal> proposeModuleMaps(
Collection<? extends TraceModule> modules, Collection<? extends Program> programs); Collection<? extends TraceModule> modules, long snap,
Collection<? extends Program> programs);
/** /**
* Propose a singleton section map from the given section to the given program memory block * Propose a singleton section map from the given section to the given program memory block
@ -489,15 +498,17 @@ public interface DebuggerStaticMappingService {
* Note, no sanity check is performed on the given parameters. This will simply give a singleton * Note, no sanity check is performed on the given parameters. This will simply give a singleton
* map of the given entry. It is strongly advised to use * map of the given entry. It is strongly advised to use
* {@link SectionMapProposal#computeScore()} to assess the proposal. Alternatively, use * {@link SectionMapProposal#computeScore()} to assess the proposal. Alternatively, use
* {@link #proposeSectionMap(TraceModule, Collection)} to have the service select the * {@link #proposeSectionMap(TraceModule, long, Collection)} to have the service select the
* best-scored mapping from a collection of proposed programs. * best-scored mapping from a collection of proposed programs.
* *
* @param section the section to map * @param section the section to map
* @param snap the source snapshot key
* @param program the destination program * @param program the destination program
* @param block the memory block in the destination program * @param block the memory block in the destination program
* @return the proposed map * @return the proposed map
*/ */
SectionMapProposal proposeSectionMap(TraceSection section, Program program, MemoryBlock block); SectionMapProposal proposeSectionMap(TraceSection section, long snap, Program program,
MemoryBlock block);
/** /**
* Propose a section map for the given module to the given program * Propose a section map for the given module to the given program
@ -506,14 +517,15 @@ public interface DebuggerStaticMappingService {
* Note, no sanity check is performed on the given parameters. This will do its best to map * Note, no sanity check is performed on the given parameters. This will do its best to map
* sections from the given module to memory blocks in the given program. It is strongly advised * sections from the given module to memory blocks in the given program. It is strongly advised
* to use {@link SectionMapProposal#computeScore()} to assess the proposal. Alternatively, use * to use {@link SectionMapProposal#computeScore()} to assess the proposal. Alternatively, use
* {@link #proposeSectionMap(TraceModule, Collection)} to have the service select the * {@link #proposeSectionMap(TraceModule, long, Collection)} to have the service select the
* best-scored mapping from a collection of proposed programs. * best-scored mapping from a collection of proposed programs.
* *
* @param module the module whose sections to map * @param module the module whose sections to map
* @param snap the source snapshot key
* @param program the destination program whose blocks to consider * @param program the destination program whose blocks to consider
* @return the proposed map * @return the proposed map
*/ */
SectionMapProposal proposeSectionMap(TraceModule module, Program program); SectionMapProposal proposeSectionMap(TraceModule module, long snap, Program program);
/** /**
* Proposed the best-scored section map for the given module and programs * Proposed the best-scored section map for the given module and programs
@ -526,10 +538,11 @@ public interface DebuggerStaticMappingService {
* @see SectionMapProposal#computeScore() * @see SectionMapProposal#computeScore()
* *
* @param module the module whose sections to map * @param module the module whose sections to map
* @param snap the source snapshot key
* @param programs a set of proposed destination programs * @param programs a set of proposed destination programs
* @return the best-scored map, or {@code null} if no program is proposed * @return the best-scored map, or {@code null} if no program is proposed
*/ */
SectionMapProposal proposeSectionMap(TraceModule module, SectionMapProposal proposeSectionMap(TraceModule module, long snap,
Collection<? extends Program> programs); Collection<? extends Program> programs);
/** /**
@ -543,11 +556,13 @@ public interface DebuggerStaticMappingService {
* {@code null} values. * {@code null} values.
* *
* @param modules the modules to map * @param modules the modules to map
* @param snap the source snapshot key
* @param programs a set of proposed destination programs * @param programs a set of proposed destination programs
* @return the composite proposal * @return the composite proposal
*/ */
Map<TraceModule, SectionMapProposal> proposeSectionMaps( Map<TraceModule, SectionMapProposal> proposeSectionMaps(
Collection<? extends TraceModule> modules, Collection<? extends Program> programs); Collection<? extends TraceModule> modules, long snap,
Collection<? extends Program> programs);
/** /**
* Propose a singleton region map from the given region to the given program memory block * Propose a singleton region map from the given region to the given program memory block
@ -556,15 +571,16 @@ public interface DebuggerStaticMappingService {
* Note, no sanity check is performed on the given parameters. This will simply give a singleton * Note, no sanity check is performed on the given parameters. This will simply give a singleton
* map of the given entry. It is strongly advised to use * map of the given entry. It is strongly advised to use
* {@link RegionMapProposal#computeScore()} to assess the proposal. Alternatively, use * {@link RegionMapProposal#computeScore()} to assess the proposal. Alternatively, use
* {@link #proposeRegionMap(Collection, Collection)} to have the service select the best-scored * {@link #proposeRegionMaps(Collection, long, Collection)} to have the service select the
* mapping from a collection of proposed programs. * best-scored mapping from a collection of proposed programs.
* *
* @param region the region to map * @param region the region to map
* @param snap the source snapshot key
* @param program the destination program * @param program the destination program
* @param block the memory block in the destination program * @param block the memory block in the destination program
* @return the proposed map * @return the proposed map
*/ */
RegionMapProposal proposeRegionMap(TraceMemoryRegion region, Program program, RegionMapProposal proposeRegionMap(TraceMemoryRegion region, long snap, Program program,
MemoryBlock block); MemoryBlock block);
/** /**
@ -575,14 +591,16 @@ public interface DebuggerStaticMappingService {
* regions to memory blocks in the given program. For the best results, regions should all * regions to memory blocks in the given program. For the best results, regions should all
* comprise the same module, and the minimum address among the regions should be the module's * comprise the same module, and the minimum address among the regions should be the module's
* base address. It is strongly advised to use {@link RegionMapProposal#computeScore()} to * base address. It is strongly advised to use {@link RegionMapProposal#computeScore()} to
* assess the proposal. Alternatively, use {@link #proposeRegionMap(Collection, Collection)} to * assess the proposal. Alternatively, use
* have the service select the best-scored mapping from a collection of proposed programs. * {@link #proposeRegionMaps(Collection, long, Collection)} to have the service select the
* best-scored mapping from a collection of proposed programs.
* *
* @param region the region to map * @param regions the regions to map
* @param snap the source snapshot key
* @param program the destination program whose blocks to consider * @param program the destination program whose blocks to consider
* @return the proposed map * @return the proposed map
*/ */
RegionMapProposal proposeRegionMap(Collection<? extends TraceMemoryRegion> regions, RegionMapProposal proposeRegionMap(Collection<? extends TraceMemoryRegion> regions, long snap,
Program program); Program program);
/** /**
@ -597,11 +615,12 @@ public interface DebuggerStaticMappingService {
* regions into likely modules. For the best results, the minimum address of each module should * regions into likely modules. For the best results, the minimum address of each module should
* be among the regions. * be among the regions.
* *
* @param modules the modules to map * @param regions the regions to map
* @param snap the source snapshot key
* @param programs a set of proposed destination programs * @param programs a set of proposed destination programs
* @return the composite proposal * @return the composite proposal
*/ */
Map<Collection<TraceMemoryRegion>, RegionMapProposal> proposeRegionMaps( Map<Collection<TraceMemoryRegion>, RegionMapProposal> proposeRegionMaps(
Collection<? extends TraceMemoryRegion> regions, Collection<? extends TraceMemoryRegion> regions, long snap,
Collection<? extends Program> programs); Collection<? extends Program> programs);
} }

View file

@ -93,7 +93,7 @@ public interface AutoMapSpec extends ExtensionPoint {
boolean objectHasType(TraceObjectValue value); boolean objectHasType(TraceObjectValue value);
String getInfoForObjects(Trace trace); String getInfoForObjects(Trace trace, long snap);
default boolean hasTask() { default boolean hasTask() {
return true; return true;
@ -103,7 +103,7 @@ public interface AutoMapSpec extends ExtensionPoint {
return getMenuName(); return getMenuName();
} }
default void runTask(PluginTool tool, Trace trace) { default void runTask(PluginTool tool, Trace trace, long snap) {
DebuggerStaticMappingService mappingService = DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class); tool.getService(DebuggerStaticMappingService.class);
ProgramManager programManager = tool.getService(ProgramManager.class); ProgramManager programManager = tool.getService(ProgramManager.class);
@ -114,7 +114,7 @@ public interface AutoMapSpec extends ExtensionPoint {
@Override @Override
public boolean applyTo(Trace trace, TaskMonitor monitor) { public boolean applyTo(Trace trace, TaskMonitor monitor) {
try { try {
performMapping(mappingService, trace, programManager, monitor); performMapping(mappingService, trace, snap, programManager, monitor);
return true; return true;
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -132,12 +132,13 @@ public interface AutoMapSpec extends ExtensionPoint {
* *
* @param mappingService the mapping service * @param mappingService the mapping service
* @param trace the trace * @param trace the trace
* @param snap the snap
* @param programs the programs to consider * @param programs the programs to consider
* @param monitor a task monitor * @param monitor a task monitor
* @return true if any mappings were added * @return true if any mappings were added
* @throws CancelledException if the task monitor cancelled the task * @throws CancelledException if the task monitor cancelled the task
*/ */
boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, long snap,
List<Program> programs, TaskMonitor monitor) throws CancelledException; List<Program> programs, TaskMonitor monitor) throws CancelledException;
/** /**
@ -145,13 +146,15 @@ public interface AutoMapSpec extends ExtensionPoint {
* *
* @param mappingService the mapping service * @param mappingService the mapping service
* @param trace the trace * @param trace the trace
* @param snap the snap
* @param programManager the program manager * @param programManager the program manager
* @param monitor a task monitor * @param monitor a task monitor
* @return true if any mappings were added * @return true if any mappings were added
* @throws CancelledException if the task monitor cancelled the task * @throws CancelledException if the task monitor cancelled the task
*/ */
default boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, default boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
ProgramManager programManager, TaskMonitor monitor) throws CancelledException { long snap, ProgramManager programManager, TaskMonitor monitor)
return performMapping(mappingService, trace, programs(programManager), monitor); throws CancelledException {
return performMapping(mappingService, trace, snap, programs(programManager), monitor);
} }
} }

View file

@ -25,14 +25,20 @@ import ghidra.trace.model.target.TraceObjectValue;
public class DebuggerObjectActionContext extends DefaultActionContext { public class DebuggerObjectActionContext extends DefaultActionContext {
private final List<TraceObjectValue> objectValues; private final List<TraceObjectValue> objectValues;
private final long snap;
public DebuggerObjectActionContext(Collection<TraceObjectValue> objectValues, public DebuggerObjectActionContext(Collection<TraceObjectValue> objectValues,
ComponentProvider provider, Component sourceComponent) { ComponentProvider provider, Component sourceComponent, long snap) {
super(provider, sourceComponent); super(provider, sourceComponent);
this.objectValues = List.copyOf(objectValues); this.objectValues = List.copyOf(objectValues);
this.snap = snap;
} }
public List<TraceObjectValue> getObjectValues() { public List<TraceObjectValue> getObjectValues() {
return objectValues; return objectValues;
} }
public long getSnap() {
return snap;
}
} }

View file

@ -33,6 +33,13 @@ public interface ModuleMapProposal extends MapProposal<TraceModule, Program, Mod
*/ */
TraceModule getModule(); TraceModule getModule();
/**
* Get the module name for this entry (may depend on the snap)
*
* @return the name
*/
String getModuleName();
/** /**
* Get the address range of the module in the trace, as computed from the matched program's * Get the address range of the module in the trace, as computed from the matched program's
* image size * image size

View file

@ -16,6 +16,7 @@
package ghidra.debug.api.modules; package ghidra.debug.api.modules;
import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry; import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
@ -34,6 +35,20 @@ public interface RegionMapProposal
*/ */
TraceMemoryRegion getRegion(); TraceMemoryRegion getRegion();
/**
* Get the region's name (may depend on snap)
*
* @return the name
*/
String getRegionName();
/**
* Get the region's minimum address (may depend on snap)
*
* @return the address
*/
Address getRegionMinAddress();
/** /**
* Get the matched memory block * Get the matched memory block
* *

View file

@ -16,6 +16,7 @@
package ghidra.debug.api.modules; package ghidra.debug.api.modules;
import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry; import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
@ -36,6 +37,20 @@ public interface SectionMapProposal
*/ */
TraceSection getSection(); TraceSection getSection();
/**
* Get the section name (may depend on the snap)
*
* @return the name
*/
String getSectionName();
/**
* Get the start address of the section (may depend on the snap)
*
* @return the start address
*/
Address getSectionStart();
/** /**
* Get the module containing the section * Get the module containing the section
* *
@ -43,6 +58,13 @@ public interface SectionMapProposal
*/ */
TraceModule getModule(); TraceModule getModule();
/**
* Get the name of the module containing the section (may depend on snap)
*
* @return the name
*/
String getModuleName();
/** /**
* Get the matched memory block * Get the matched memory block
* *
@ -71,5 +93,6 @@ public interface SectionMapProposal
* *
* @return the program * @return the program
*/ */
@Override
Program getProgram(); Program getProgram();
} }

View file

@ -320,8 +320,9 @@ public class DebuggerCoordinates {
return objThread.getCanonicalPath(); return objThread.getCanonicalPath();
} }
TraceStack stack; TraceStack stack;
long snap = time.getSnap();
try { try {
stack = thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false); stack = thread.getTrace().getStackManager().getStack(thread, snap, false);
} }
catch (IllegalStateException e) { catch (IllegalStateException e) {
// Schema does not specify a stack // Schema does not specify a stack
@ -330,7 +331,7 @@ public class DebuggerCoordinates {
if (stack == null) { if (stack == null) {
return objThread.getCanonicalPath(); return objThread.getCanonicalPath();
} }
TraceStackFrame frame = stack.getFrame(frameLevel, false); TraceStackFrame frame = stack.getFrame(snap, frameLevel, false);
if (frame == null) { if (frame == null) {
return objThread.getCanonicalPath(); return objThread.getCanonicalPath();
} }
@ -406,9 +407,8 @@ public class DebuggerCoordinates {
return NOWHERE; return NOWHERE;
} }
long snap = newTime.getSnap(); long snap = newTime.getSnap();
Lifespan threadLifespan = thread == null ? null : thread.getLifespan(); boolean isThreadValid = thread == null ? false : thread.isValid(snap);
TraceThread newThread = threadLifespan != null && threadLifespan.contains(snap) ? thread TraceThread newThread = isThreadValid ? thread : resolveThread(trace, target, newTime);
: resolveThread(trace, target, newTime);
// This will cause the frame to reset to 0 on every snap change. That's fair.... // This will cause the frame to reset to 0 on every snap change. That's fair....
Integer newFrame = resolveFrame(newThread, newTime); Integer newFrame = resolveFrame(newThread, newTime);
KeyPath threadOrFramePath = resolvePath(newThread, newFrame, newTime); KeyPath threadOrFramePath = resolvePath(newThread, newFrame, newTime);

View file

@ -1444,8 +1444,10 @@ public interface FlatDebuggerAPI {
} }
default ActionContext createContext(TraceObject object) { default ActionContext createContext(TraceObject object) {
Trace trace = object.getTrace();
DebuggerCoordinates coords = getTraceManager().getCurrentFor(trace);
TraceObjectValue value = object.getCanonicalParents(Lifespan.ALL).findAny().orElseThrow(); TraceObjectValue value = object.getCanonicalParents(Lifespan.ALL).findAny().orElseThrow();
return new DebuggerObjectActionContext(List.of(value), null, null); return new DebuggerObjectActionContext(List.of(value), null, null, coords.getSnap());
} }
default ActionContext createContext(TraceThread thread) { default ActionContext createContext(TraceThread thread) {
@ -1720,8 +1722,7 @@ public interface FlatDebuggerAPI {
* This does not consider the current snap. It only considers a live target thread in the * This does not consider the current snap. It only considers a live target thread in the
* present. In other words, if the user rewinds trace history to a point where the thread was * present. In other words, if the user rewinds trace history to a point where the thread was
* alive, this method still considers that thread terminated. To compute state with respect to * alive, this method still considers that thread terminated. To compute state with respect to
* trace history, use {@link TraceThread#getLifespan()} and check if it contains the current * trace history, use {@link TraceThread#isValid(long)}.
* snap.
* *
* @param thread * @param thread
* @return the thread's execution state * @return the thread's execution state

View file

@ -190,7 +190,8 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
return false; return false;
} }
if (spec.performMapping(mappingService, trace, List.of(program), monitor)) { long snap = connection.getLastSnapshot(trace);
if (spec.performMapping(mappingService, trace, snap, List.of(program), monitor)) {
return true; return true;
} }
@ -206,7 +207,7 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
return true; // Probably shouldn't happen, but if it does, say "success" return true; // Probably shouldn't happen, but if it does, say "success"
} }
ProgramLocation probe = new ProgramLocation(program, probeAddress); ProgramLocation probe = new ProgramLocation(program, probeAddress);
long snap = connection.getLastSnapshot(trace);
return mappingService.getOpenMappedLocation(trace, probe, snap) != null; return mappingService.getOpenMappedLocation(trace, probe, snap) != null;
} }

View file

@ -938,7 +938,8 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
// Want addresses satisfying {@code known | (readOnly & everKnown)} // Want addresses satisfying {@code known | (readOnly & everKnown)}
TraceMemoryManager memoryManager = open.trace.getMemoryManager(); TraceMemoryManager memoryManager = open.trace.getMemoryManager();
AddressSetView readOnly = memoryManager.getRegionsAddressSetWith(snap, r -> !r.isWrite()); AddressSetView readOnly =
memoryManager.getRegionsAddressSetWith(snap, r -> !r.isWrite(snap));
AddressSetView everKnown = memoryManager.getAddressesWithState(Lifespan.since(snap), AddressSetView everKnown = memoryManager.getAddressesWithState(Lifespan.since(snap),
s -> s == TraceMemoryState.KNOWN); s -> s == TraceMemoryState.KNOWN);
AddressSetView roEverKnown = new IntersectionAddressSetView(readOnly, everKnown); AddressSetView roEverKnown = new IntersectionAddressSetView(readOnly, everKnown);

View file

@ -1334,7 +1334,7 @@ public class TraceRmiTarget extends AbstractTarget {
RemoteParameter paramFrame = writeReg.params.get("frame"); RemoteParameter paramFrame = writeReg.params.get("frame");
if (paramFrame != null) { if (paramFrame != null) {
TraceStack stack = trace.getStackManager().getLatestStack(thread, getSnap()); TraceStack stack = trace.getStackManager().getLatestStack(thread, getSnap());
TraceStackFrame frame = stack.getFrame(frameLevel, false); TraceStackFrame frame = stack.getFrame(getSnap(), frameLevel, false);
if (!(frame instanceof TraceObjectStackFrame tof)) { if (!(frame instanceof TraceObjectStackFrame tof)) {
Msg.error(this, "Non-object trace with TraceRmi!"); Msg.error(this, "Non-object trace with TraceRmi!");
return AsyncUtils.nil(); return AsyncUtils.nil();
@ -1568,10 +1568,11 @@ public class TraceRmiTarget extends AbstractTarget {
@Override @Override
public boolean isBreakpointValid(TraceBreakpoint breakpoint) { public boolean isBreakpointValid(TraceBreakpoint breakpoint) {
if (breakpoint.getName().endsWith("emu-" + breakpoint.getMinAddress())) { long snap = getSnap();
if (breakpoint.getName(snap).endsWith("emu-" + breakpoint.getMinAddress(snap))) {
return false; return false;
} }
if (!breakpoint.isAlive(getSnap())) { if (!breakpoint.isValid(snap)) {
return false; return false;
} }
return true; return true;

View file

@ -288,9 +288,8 @@ public class PopulateDemoTrace extends GhidraScript {
int pc2 = 0; int pc2 = 0;
/** /**
* For clarity, I will add each tick to the trace in its own transaction. The * For clarity, I will add each tick to the trace in its own transaction. The Transaction
* Transaction class eases the syntax and reduces errors in starting and ending * class eases the syntax and reduces errors in starting and ending transactions.
* transactions.
*/ */
try (Transaction tx = trace.openTransaction("Populate First Snapshot")) { try (Transaction tx = trace.openTransaction("Populate First Snapshot")) {
/** /**
@ -699,7 +698,7 @@ public class PopulateDemoTrace extends GhidraScript {
long snap = long snap =
trace.getTimeManager().createSnapshot("Stepped Thread 2: CALL exit").getKey(); trace.getTimeManager().createSnapshot("Stepped Thread 2: CALL exit").getKey();
thread2.setDestructionSnap(snap); thread2.remove(snap);
} }
/** /**
@ -709,7 +708,7 @@ public class PopulateDemoTrace extends GhidraScript {
long snap = long snap =
trace.getTimeManager().createSnapshot("Stepped Thread 1: CALL exit").getKey(); trace.getTimeManager().createSnapshot("Stepped Thread 1: CALL exit").getKey();
thread1.setDestructionSnap(snap); thread1.remove(snap);
} }
/** /**

View file

@ -117,13 +117,13 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
// It has never been known up to this snap // It has never been known up to this snap
return null; return null;
} }
TraceMemoryRegion region = long ks = mrent.getKey().getY1();
memoryManager.getRegionContaining(mrent.getKey().getY1(), start); TraceMemoryRegion region = memoryManager.getRegionContaining(ks, start);
if (region == null || region.isWrite()) { if (region == null || region.isWrite(ks)) {
// It could have changed this snap, so unknown // It could have changed this snap, so unknown
return null; return null;
} }
return mrent.getKey().getY1(); return ks;
} }
/** /**
@ -133,8 +133,8 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
* The view contains the addresses in {@code known | (readOnly & everKnown)}, where {@code * The view contains the addresses in {@code known | (readOnly & everKnown)}, where {@code
* known} is the set of addresses in the {@link TraceMemoryState#KNOWN} state, {@code readOnly} * known} is the set of addresses in the {@link TraceMemoryState#KNOWN} state, {@code readOnly}
* is the set of addresses in a {@link TraceMemoryRegion} having * is the set of addresses in a {@link TraceMemoryRegion} having
* {@link TraceMemoryRegion#isWrite()} false, and {@code everKnown} is the set of addresses in * {@link TraceMemoryRegion#isWrite(long)} false, and {@code everKnown} is the set of addresses
* the {@link TraceMemoryState#KNOWN} state in any previous snapshot. * in the {@link TraceMemoryState#KNOWN} state in any previous snapshot.
* *
* <p> * <p>
* In plainer English, we want addresses that have freshly read bytes right now, or addresses in * In plainer English, we want addresses that have freshly read bytes right now, or addresses in
@ -158,7 +158,7 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
} }
TraceMemoryManager memoryManager = trace.getMemoryManager(); TraceMemoryManager memoryManager = trace.getMemoryManager();
AddressSetView readOnly = AddressSetView readOnly =
memoryManager.getRegionsAddressSetWith(ks, r -> !r.isWrite()); memoryManager.getRegionsAddressSetWith(ks, r -> !r.isWrite(ks));
AddressSetView everKnown = memoryManager.getAddressesWithState(Lifespan.since(ks), AddressSetView everKnown = memoryManager.getAddressesWithState(Lifespan.since(ks),
s -> s == TraceMemoryState.KNOWN); s -> s == TraceMemoryState.KNOWN);
AddressSetView roEverKnown = new IntersectionAddressSetView(readOnly, everKnown); AddressSetView roEverKnown = new IntersectionAddressSetView(readOnly, everKnown);

View file

@ -81,18 +81,19 @@ public class DebuggerBlockChooserDialog extends ReusableDialogComponentProvider
return score; return score;
} }
public double score(TraceSection section, DebuggerStaticMappingService service) { public double score(TraceSection section, long snap, DebuggerStaticMappingService service) {
if (section == null) { if (section == null) {
return score = 0; return score = 0;
} }
return score = service.proposeSectionMap(section, program, block).computeScore(); return score = service.proposeSectionMap(section, snap, program, block).computeScore();
} }
public double score(TraceMemoryRegion region, DebuggerStaticMappingService service) { public double score(TraceMemoryRegion region, long snap,
DebuggerStaticMappingService service) {
if (region == null) { if (region == null) {
return score = 0; return score = 0;
} }
return score = service.proposeRegionMap(region, program, block).computeScore(); return score = service.proposeRegionMap(region, snap, program, block).computeScore();
} }
public ProgramLocation getProgramLocation() { public ProgramLocation getProgramLocation() {
@ -201,15 +202,15 @@ public class DebuggerBlockChooserDialog extends ReusableDialogComponentProvider
} }
public Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool, TraceSection section, public Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool, TraceSection section,
Collection<Program> programs) { long snap, Collection<Program> programs) {
DebuggerStaticMappingService service = tool.getService(DebuggerStaticMappingService.class); DebuggerStaticMappingService service = tool.getService(DebuggerStaticMappingService.class);
return chooseBlock(tool, programs, rec -> rec.score(section, service)); return chooseBlock(tool, programs, rec -> rec.score(section, snap, service));
} }
public Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool, TraceMemoryRegion region, public Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool, TraceMemoryRegion region,
Collection<Program> programs) { long snap, Collection<Program> programs) {
DebuggerStaticMappingService service = tool.getService(DebuggerStaticMappingService.class); DebuggerStaticMappingService service = tool.getService(DebuggerStaticMappingService.class);
return chooseBlock(tool, programs, rec -> rec.score(region, service)); return chooseBlock(tool, programs, rec -> rec.score(region, snap, service));
} }
protected Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool, protected Map.Entry<Program, MemoryBlock> chooseBlock(PluginTool tool,

View file

@ -142,13 +142,14 @@ public class DebuggerLocationLabel extends JLabel {
if (sections.isEmpty()) { if (sections.isEmpty()) {
return null; return null;
} }
long snap = current.getSnap();
/** /**
* TODO: DB's R-Tree could probably do this natively. Not sure it's an optimization, though, * TODO: DB's R-Tree could probably do this natively. Not sure it's an optimization, though,
* since few, if any, overlapping sections are expected. * since few, if any, overlapping sections are expected.
*/ */
sections.sort(ComparatorUtils.chainedComparator(List.of( sections.sort(ComparatorUtils.chainedComparator(List.of(
Comparator.comparing(s -> s.getRange().getMinAddress()), Comparator.comparing(s -> s.getRange(snap).getMinAddress()),
Comparator.comparing(s -> -s.getRange().getLength())))); Comparator.comparing(s -> -s.getRange(snap).getLength()))));
return sections.get(sections.size() - 1); return sections.get(sections.size() - 1);
} }
@ -162,10 +163,11 @@ public class DebuggerLocationLabel extends JLabel {
if (modules.isEmpty()) { if (modules.isEmpty()) {
return null; return null;
} }
long snap = current.getSnap();
// TODO: DB's R-Tree could probably do this natively // TODO: DB's R-Tree could probably do this natively
modules.sort(ComparatorUtils.chainedComparator(List.of( modules.sort(ComparatorUtils.chainedComparator(List.of(
Comparator.comparing(m -> m.getRange().getMinAddress()), Comparator.comparing(m -> m.getRange(snap).getMinAddress()),
Comparator.comparing(m -> -m.getRange().getLength())))); Comparator.comparing(m -> -m.getRange(snap).getLength()))));
return modules.get(modules.size() - 1); return modules.get(modules.size() - 1);
} }
@ -185,18 +187,19 @@ public class DebuggerLocationLabel extends JLabel {
if (address == null) { if (address == null) {
return "(nowhere)"; return "(nowhere)";
} }
long snap = current.getSnap();
try { try {
TraceSection section = getNearestSectionContaining(); TraceSection section = getNearestSectionContaining();
if (section != null) { if (section != null) {
return section.getModule().getName() + ":" + section.getName(); return section.getModule().getName(snap) + ":" + section.getName(snap);
} }
TraceModule module = getNearestModuleContaining(); TraceModule module = getNearestModuleContaining();
if (module != null) { if (module != null) {
return module.getName(); return module.getName(snap);
} }
TraceMemoryRegion region = getRegionContaining(); TraceMemoryRegion region = getRegionContaining();
if (region != null) { if (region != null) {
return region.getName(); return region.getName(snap);
} }
return "(unknown)"; return "(unknown)";
} }

View file

@ -81,14 +81,14 @@ public class ByModuleAutoMapSpec implements AutoMapSpec {
} }
@Override @Override
public String getInfoForObjects(Trace trace) { public String getInfoForObjects(Trace trace, long snap) {
String modPart = trace.getModuleManager() String modPart = trace.getModuleManager()
.getAllModules() .getLoadedModules(snap)
.stream() .stream()
.map(m -> m.getName() + ":" + m.getBase()) .map(m -> m.getName(snap) + ":" + m.getBase(snap))
.sorted() .sorted()
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
String regPart = ByRegionAutoMapSpec.getInfoForRegions(trace); String regPart = ByRegionAutoMapSpec.getInfoForRegions(trace, snap);
return modPart + ";" + regPart; return modPart + ";" + regPart;
} }
@ -99,9 +99,9 @@ public class ByModuleAutoMapSpec implements AutoMapSpec {
@Override @Override
public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException { long snap, List<Program> programs, TaskMonitor monitor) throws CancelledException {
Map<?, ModuleMapProposal> maps = mappingService Map<?, ModuleMapProposal> maps = mappingService
.proposeModuleMaps(trace.getModuleManager().getAllModules(), programs); .proposeModuleMaps(trace.getModuleManager().getLoadedModules(snap), snap, programs);
Collection<ModuleMapEntry> entries = MapProposal.flatten(maps.values()); Collection<ModuleMapEntry> entries = MapProposal.flatten(maps.values());
entries = MapProposal.removeOverlapping(entries); entries = MapProposal.removeOverlapping(entries);
mappingService.addModuleMappings(entries, monitor, false); mappingService.addModuleMappings(entries, monitor, false);

View file

@ -64,18 +64,18 @@ public class ByRegionAutoMapSpec implements AutoMapSpec {
return value.getParent().queryInterface(TraceObjectMemoryRegion.class) != null; return value.getParent().queryInterface(TraceObjectMemoryRegion.class) != null;
} }
static String getInfoForRegions(Trace trace) { static String getInfoForRegions(Trace trace, long snap) {
return trace.getMemoryManager() return trace.getMemoryManager()
.getAllRegions() .getRegionsAtSnap(snap)
.stream() .stream()
.map(r -> r.getName() + ":" + r.getMinAddress()) .map(r -> r.getName(snap) + ":" + r.getMinAddress(snap))
.sorted() .sorted()
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
} }
@Override @Override
public String getInfoForObjects(Trace trace) { public String getInfoForObjects(Trace trace, long snap) {
return getInfoForRegions(trace); return getInfoForRegions(trace, snap);
} }
@Override @Override
@ -85,9 +85,9 @@ public class ByRegionAutoMapSpec implements AutoMapSpec {
@Override @Override
public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException { long snap, List<Program> programs, TaskMonitor monitor) throws CancelledException {
Map<?, RegionMapProposal> maps = mappingService Map<?, RegionMapProposal> maps = mappingService
.proposeRegionMaps(trace.getMemoryManager().getAllRegions(), programs); .proposeRegionMaps(trace.getMemoryManager().getRegionsAtSnap(snap), snap, programs);
Collection<RegionMapEntry> entries = MapProposal.flatten(maps.values()); Collection<RegionMapEntry> entries = MapProposal.flatten(maps.values());
entries = MapProposal.removeOverlapping(entries); entries = MapProposal.removeOverlapping(entries);
mappingService.addRegionMappings(entries, monitor, false); mappingService.addRegionMappings(entries, monitor, false);

View file

@ -65,11 +65,12 @@ public class BySectionAutoMapSpec implements AutoMapSpec {
} }
@Override @Override
public String getInfoForObjects(Trace trace) { public String getInfoForObjects(Trace trace, long snap) {
return trace.getModuleManager() return trace.getModuleManager()
.getAllSections() .getAllSections()
.stream() .stream()
.map(s -> s.getName() + ":" + s.getStart()) .filter(s -> s.isValid(snap))
.map(s -> s.getName(snap) + ":" + s.getStart(snap))
.sorted() .sorted()
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
} }
@ -81,9 +82,9 @@ public class BySectionAutoMapSpec implements AutoMapSpec {
@Override @Override
public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException { long snap, List<Program> programs, TaskMonitor monitor) throws CancelledException {
Map<?, SectionMapProposal> maps = mappingService Map<?, SectionMapProposal> maps = mappingService.proposeSectionMaps(
.proposeSectionMaps(trace.getModuleManager().getAllModules(), programs); trace.getModuleManager().getLoadedModules(snap), snap, programs);
Collection<SectionMapEntry> entries = MapProposal.flatten(maps.values()); Collection<SectionMapEntry> entries = MapProposal.flatten(maps.values());
entries = MapProposal.removeOverlapping(entries); entries = MapProposal.removeOverlapping(entries);
mappingService.addSectionMappings(entries, monitor, false); mappingService.addSectionMappings(entries, monitor, false);

View file

@ -61,7 +61,7 @@ public class NoneAutoMapSpec implements AutoMapSpec {
} }
@Override @Override
public String getInfoForObjects(Trace trace) { public String getInfoForObjects(Trace trace, long snap) {
return ""; return "";
} }
@ -71,7 +71,7 @@ public class NoneAutoMapSpec implements AutoMapSpec {
} }
@Override @Override
public void runTask(PluginTool tool, Trace trace) { public void runTask(PluginTool tool, Trace trace, long snap) {
// Don't bother launching a task that does nothing // Don't bother launching a task that does nothing
} }
@ -82,7 +82,7 @@ public class NoneAutoMapSpec implements AutoMapSpec {
@Override @Override
public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException { long snap, List<Program> programs, TaskMonitor monitor) throws CancelledException {
return false; return false;
} }
} }

View file

@ -62,7 +62,7 @@ public class OneToOneAutoMapSpec implements AutoMapSpec {
} }
@Override @Override
public String getInfoForObjects(Trace trace) { public String getInfoForObjects(Trace trace, long snap) {
return ""; return "";
} }
@ -77,12 +77,11 @@ public class OneToOneAutoMapSpec implements AutoMapSpec {
@Override @Override
public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace, public boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException { long snap, List<Program> programs, TaskMonitor monitor) throws CancelledException {
boolean result = false; boolean result = false;
for (Program program : programs) { for (Program program : programs) {
try { try {
mappingService.addIdentityMapping(trace, program, mappingService.addIdentityMapping(trace, program, Lifespan.nowOn(snap), false);
Lifespan.nowOn(trace.getProgramView().getSnap()), false);
result = true; result = true;
} }
catch (TraceConflictedMappingException e) { catch (TraceConflictedMappingException e) {

View file

@ -85,7 +85,7 @@ public enum PCByStackLocationTrackingSpec implements LocationTrackingSpec, Locat
return null; return null;
} }
int level = coordinates.getFrame(); int level = coordinates.getFrame();
TraceStackFrame frame = stack.getFrame(level, false); TraceStackFrame frame = stack.getFrame(snap, level, false);
if (frame == null) { if (frame == null) {
return null; return null;
} }

View file

@ -22,7 +22,8 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.memory.TraceMemoryState; import ghidra.trace.model.memory.TraceMemoryState;
@ -64,8 +65,7 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca
if (reg == null) { if (reg == null) {
return null; return null;
} }
Lifespan lifespan = thread.getLifespan(); if (!thread.isValid(snap)) {
if (lifespan == null || !lifespan.contains(snap)) {
return null; return null;
} }
TraceMemorySpace regs = reg.getAddressSpace().isRegisterSpace() TraceMemorySpace regs = reg.getAddressSpace().isRegisterSpace()

View file

@ -60,7 +60,8 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
} }
Target target = coordinates.getTarget(); Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager(); TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
AddressSetView alreadyKnown = mm.getAddressesWithState(coordinates.getSnap(), visible, long snap = coordinates.getSnap();
AddressSetView alreadyKnown = mm.getAddressesWithState(snap, visible,
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR); s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown); AddressSet toRead = visible.subtract(alreadyKnown);
@ -70,19 +71,18 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
AddressSet everKnown = new AddressSet(); AddressSet everKnown = new AddressSet();
for (AddressRange range : visible) { for (AddressRange range : visible) {
for (Entry<TraceAddressSnapRange, TraceMemoryState> ent : mm for (Entry<TraceAddressSnapRange, TraceMemoryState> ent : mm.getMostRecentStates(snap,
.getMostRecentStates(coordinates.getSnap(), range)) { range)) {
everKnown.add(ent.getKey().getRange()); everKnown.add(ent.getKey().getRange());
} }
} }
AddressSet readOnly = new AddressSet(); AddressSet readOnly = new AddressSet();
for (AddressRange range : visible) { for (AddressRange range : visible) {
for (TraceMemoryRegion region : mm for (TraceMemoryRegion region : mm.getRegionsIntersecting(Lifespan.at(snap), range)) {
.getRegionsIntersecting(Lifespan.at(coordinates.getSnap()), range)) { if (region.isWrite(snap)) {
if (region.isWrite()) {
continue; continue;
} }
readOnly.add(region.getRange()); readOnly.add(region.getRange(snap));
} }
} }
toRead.delete(everKnown.intersect(readOnly)); toRead.delete(everKnown.intersect(readOnly));

View file

@ -25,7 +25,6 @@ import ghidra.pcode.exec.SleighUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.thread.TraceThread;
public class BreakpointLocationRow { public class BreakpointLocationRow {
private final DebuggerBreakpointsProvider provider; private final DebuggerBreakpointsProvider provider;
@ -36,13 +35,16 @@ public class BreakpointLocationRow {
this.loc = loc; this.loc = loc;
} }
private long getSnap() {
return provider.traceManager.getCurrentFor(loc.getTrace()).getSnap();
}
public String getName() { public String getName() {
return loc.getName(); return loc.getName(getSnap());
} }
public boolean isEnabled() { public boolean isEnabled() {
long snap = provider.traceManager.getCurrentFor(loc.getTrace()).getSnap(); return loc.isEnabled(getSnap());
return loc.isEnabled(snap);
} }
public State getState() { public State getState() {
@ -75,12 +77,12 @@ public class BreakpointLocationRow {
public void setName(String name) { public void setName(String name) {
try (Transaction tid = loc.getTrace().openTransaction("Set breakpoint name")) { try (Transaction tid = loc.getTrace().openTransaction("Set breakpoint name")) {
loc.setName(name); loc.setName(getSnap(), name);
} }
} }
public Address getAddress() { public Address getAddress() {
return loc.getMinAddress(); return loc.getMinAddress(getSnap());
} }
public ProgramLocation getProgramLocation() { public ProgramLocation getProgramLocation() {
@ -92,25 +94,26 @@ public class BreakpointLocationRow {
} }
public String getThreads() { public String getThreads() {
return loc.getThreads() long snap = getSnap();
return loc.getThreads(snap)
.stream() .stream()
.map(TraceThread::getName) .map(t -> t.getName(snap))
.collect(Collectors.toSet()) .collect(Collectors.toSet())
.toString(); .toString();
} }
public String getComment() { public String getComment() {
return loc.getComment(); return loc.getComment(getSnap());
} }
public void setComment(String comment) { public void setComment(String comment) {
try (Transaction tid = loc.getTrace().openTransaction("Set breakpoint comment")) { try (Transaction tid = loc.getTrace().openTransaction("Set breakpoint comment")) {
loc.setComment(comment); loc.setComment(getSnap(), comment);
} }
} }
public boolean hasSleigh() { public boolean hasSleigh() {
return !SleighUtils.UNCONDITIONAL_BREAK.equals(loc.getEmuSleigh()); return !SleighUtils.UNCONDITIONAL_BREAK.equals(loc.getEmuSleigh(getSnap()));
} }
public TraceBreakpoint getTraceBreakpoint() { public TraceBreakpoint getTraceBreakpoint() {

View file

@ -699,7 +699,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
private boolean isVisible(TraceBreakpoint location) { private boolean isVisible(TraceBreakpoint location) {
long snap = traceManager.getCurrentFor(trace).getSnap(); long snap = traceManager.getCurrentFor(trace).getSnap();
return location.isAlive(snap); return location.isValid(snap);
} }
private void locationAdded(TraceBreakpoint location) { private void locationAdded(TraceBreakpoint location) {
@ -1336,7 +1336,8 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
} }
else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) { else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) {
for (TraceBreakpoint tb : locCtx.getLocations()) { for (TraceBreakpoint tb : locCtx.getLocations()) {
if (!EXECUTE_KINDS.containsAll(tb.getKinds())) { long snap = traceManager.getCurrentFor(tb.getTrace()).getSnap();
if (!EXECUTE_KINDS.containsAll(tb.getKinds(snap))) {
return false; return false;
} }
} }
@ -1368,7 +1369,8 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
} }
else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) { else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) {
for (TraceBreakpoint tb : locCtx.getLocations()) { for (TraceBreakpoint tb : locCtx.getLocations()) {
String s = tb.getEmuSleigh(); long snap = traceManager.getCurrentFor(tb.getTrace()).getSnap();
String s = tb.getEmuSleigh(snap);
if (sleigh != null && !sleigh.equals(s)) { if (sleigh != null && !sleigh.equals(s)) {
return null; return null;
} }
@ -1393,7 +1395,8 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
} }
else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) { else if (ctx instanceof DebuggerBreakpointLocationsActionContext locCtx) {
for (TraceBreakpoint tb : locCtx.getLocations()) { for (TraceBreakpoint tb : locCtx.getLocations()) {
tb.setEmuSleigh(sleigh); long snap = traceManager.getCurrentFor(tb.getTrace()).getSnap();
tb.setEmuSleigh(snap, sleigh);
} }
} }
else { else {

View file

@ -620,23 +620,26 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
protected String computeRegionString(AddressRange rng) { protected String computeRegionString(AddressRange rng) {
TraceMemoryManager mm = source.getTrace().getMemoryManager(); TraceMemoryManager mm = source.getTrace().getMemoryManager();
long snap = source.getSnap();
Collection<? extends TraceMemoryRegion> regions = Collection<? extends TraceMemoryRegion> regions =
mm.getRegionsIntersecting(Lifespan.at(source.getSnap()), rng); mm.getRegionsIntersecting(Lifespan.at(snap), rng);
return regions.isEmpty() ? "UNKNOWN" : regions.iterator().next().getName(); return regions.isEmpty() ? "UNKNOWN" : regions.iterator().next().getName(snap);
} }
protected String computeModulesString(AddressRange rng) { protected String computeModulesString(AddressRange rng) {
TraceModuleManager mm = source.getTrace().getModuleManager(); TraceModuleManager mm = source.getTrace().getModuleManager();
long snap = source.getSnap();
Collection<? extends TraceModule> modules = Collection<? extends TraceModule> modules =
mm.getModulesIntersecting(Lifespan.at(source.getSnap()), rng); mm.getModulesIntersecting(Lifespan.at(snap), rng);
return modules.stream().map(m -> m.getName()).collect(Collectors.joining(",")); return modules.stream().map(m -> m.getName(snap)).collect(Collectors.joining(","));
} }
protected String computeSectionsString(AddressRange rng) { protected String computeSectionsString(AddressRange rng) {
TraceModuleManager mm = source.getTrace().getModuleManager(); TraceModuleManager mm = source.getTrace().getModuleManager();
long snap = source.getSnap();
Collection<? extends TraceSection> sections = Collection<? extends TraceSection> sections =
mm.getSectionsIntersecting(Lifespan.at(source.getSnap()), rng); mm.getSectionsIntersecting(Lifespan.at(snap), rng);
return sections.stream().map(s -> s.getName()).collect(Collectors.joining(",")); return sections.stream().map(s -> s.getName(snap)).collect(Collectors.joining(","));
} }
protected void createEntry(Collection<RangeEntry> result, AddressRange srcRange, protected void createEntry(Collection<RangeEntry> result, AddressRange srcRange,
@ -692,10 +695,11 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
protected List<AddressRange> breakRangeByRegions(AddressRange srcRange) { protected List<AddressRange> breakRangeByRegions(AddressRange srcRange) {
AddressSet remains = new AddressSet(srcRange); AddressSet remains = new AddressSet(srcRange);
List<AddressRange> result = new ArrayList<>(); List<AddressRange> result = new ArrayList<>();
long snap = source.getSnap();
for (TraceMemoryRegion region : source.getTrace() for (TraceMemoryRegion region : source.getTrace()
.getMemoryManager() .getMemoryManager()
.getRegionsIntersecting(Lifespan.at(source.getSnap()), srcRange)) { .getRegionsIntersecting(Lifespan.at(snap), srcRange)) {
AddressRange range = region.getRange().intersect(srcRange); AddressRange range = region.getRange(snap).intersect(srcRange);
result.add(range); result.add(range);
remains.delete(range); remains.delete(range);
} }

View file

@ -219,21 +219,22 @@ public class DebuggerCopyPlan {
@Override @Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into, public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception { Address intoAddress, TaskMonitor monitor) throws Exception {
long snap = from.getSnap();
for (TraceBreakpoint bpt : from.getTrace() for (TraceBreakpoint bpt : from.getTrace()
.getBreakpointManager() .getBreakpointManager()
.getBreakpointsIntersecting(Lifespan.at(from.getSnap()), fromRange)) { .getBreakpointsIntersecting(Lifespan.at(from.getSnap()), fromRange)) {
monitor.checkCancelled(); monitor.checkCancelled();
long off = bpt.getMinAddress().subtract(fromRange.getMinAddress()); long off = bpt.getMinAddress(snap).subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off); Address dest = intoAddress.add(off);
ProgramBreakpoint pb = ProgramBreakpoint pb =
new ProgramBreakpoint(into, dest, bpt.getLength(), bpt.getKinds()); new ProgramBreakpoint(into, dest, bpt.getLength(snap), bpt.getKinds(snap));
if (bpt.isEnabled(from.getSnap())) { if (bpt.isEnabled(from.getSnap())) {
pb.enable(); pb.enable();
} }
else { else {
pb.disable(); pb.disable();
} }
pb.setEmuSleigh(bpt.getEmuSleigh()); pb.setEmuSleigh(bpt.getEmuSleigh(snap));
} }
} }
}, },

View file

@ -1081,13 +1081,13 @@ public class DebuggerListingProvider extends CodeViewerProvider {
Set<DomainFile> toOpen = new HashSet<>(); Set<DomainFile> toOpen = new HashSet<>();
TraceModuleManager modMan = trace.getModuleManager(); TraceModuleManager modMan = trace.getModuleManager();
Collection<TraceModule> modules = Stream.concat( Collection<TraceModule> modules = Stream.concat(
modMan.getModulesAt(snap, address).stream().filter(m -> m.getSections().isEmpty()), modMan.getModulesAt(snap, address).stream().filter(m -> m.getSections(snap).isEmpty()),
modMan.getSectionsAt(snap, address).stream().map(s -> s.getModule())) modMan.getSectionsAt(snap, address).stream().map(s -> s.getModule()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// Attempt to open probable matches. All others, list to import // Attempt to open probable matches. All others, list to import
for (TraceModule mod : modules) { for (TraceModule mod : modules) {
DomainFile match = mappingService.findBestModuleProgram(space, mod); DomainFile match = mappingService.findBestModuleProgram(space, mod, snap);
if (match == null) { if (match == null) {
missing.add(mod); missing.add(mod);
} }
@ -1108,7 +1108,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
for (TraceModule mod : missing) { for (TraceModule mod : missing) {
consoleService.log(DebuggerResources.ICON_LOG_ERROR, consoleService.log(DebuggerResources.ICON_LOG_ERROR,
"<html>The module <b><tt>" + HTMLUtilities.escapeHTML(mod.getName()) + "<html>The module <b><tt>" + HTMLUtilities.escapeHTML(mod.getName(snap)) +
"</tt></b> was not found in the project</html>", "</tt></b> was not found in the project</html>",
new DebuggerMissingModuleActionContext(mod)); new DebuggerMissingModuleActionContext(mod));
} }
@ -1139,12 +1139,13 @@ public class DebuggerListingProvider extends CodeViewerProvider {
if (!affectedTraces.contains(module.getTrace())) { if (!affectedTraces.contains(module.getTrace())) {
continue; continue;
} }
if (isMapped(module.getRange())) { long snap = traceManager.getCurrentFor(module.getTrace()).getSnap();
if (isMapped(module.getRange(snap))) {
consoleService.removeFromLog(mmCtx); consoleService.removeFromLog(mmCtx);
continue; continue;
} }
for (TraceSection section : module.getSections()) { for (TraceSection section : module.getSections(snap)) {
if (isMapped(section.getRange())) { if (isMapped(section.getRange(snap))) {
consoleService.removeFromLog(mmCtx); consoleService.removeFromLog(mmCtx);
continue nextCtx; continue nextCtx;
} }

View file

@ -71,13 +71,14 @@ public class MemoryStateListingBackgroundColorModel implements ListingBackground
} }
protected Color getUnknownColor(Address address) { protected Color getUnknownColor(Address address) {
long snap = view.getSnap();
Entry<TraceAddressSnapRange, TraceMemoryState> ent = Entry<TraceAddressSnapRange, TraceMemoryState> ent =
memory.getViewMostRecentStateEntry(view.getSnap(), address); memory.getViewMostRecentStateEntry(snap, address);
if (ent == null || ent.getValue() != TraceMemoryState.KNOWN) { if (ent == null || ent.getValue() != TraceMemoryState.KNOWN) {
return COLOR_BACKGROUND_UNKNOWN; return COLOR_BACKGROUND_UNKNOWN;
} }
TraceMemoryRegion region = memory.getRegionContaining(ent.getKey().getY1(), address); TraceMemoryRegion region = memory.getRegionContaining(ent.getKey().getY1(), address);
if (region != null && !region.isWrite()) { if (region != null && !region.isWrite(snap)) {
return COLOR_BACKGROUND_UNKNOWN_BLENDED; return COLOR_BACKGROUND_UNKNOWN_BLENDED;
} }
return COLOR_BACKGROUND_UNKNOWN; return COLOR_BACKGROUND_UNKNOWN;

View file

@ -40,7 +40,8 @@ import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.*; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.memory.TraceMemoryManager; import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.util.TraceEvents; import ghidra.trace.util.TraceEvents;
@ -53,7 +54,6 @@ public class DebuggerLegacyRegionsPanel extends JPanel {
protected enum RegionTableColumns protected enum RegionTableColumns
implements EnumeratedTableColumn<RegionTableColumns, RegionRow> { implements EnumeratedTableColumn<RegionTableColumns, RegionRow> {
NAME("Name", String.class, RegionRow::getName, RegionRow::setName), NAME("Name", String.class, RegionRow::getName, RegionRow::setName),
LIFESPAN("Lifespan", Lifespan.class, RegionRow::getLifespan),
START("Start", Address.class, RegionRow::getMinAddress), START("Start", Address.class, RegionRow::getMinAddress),
END("End", Address.class, RegionRow::getMaxAddress), END("End", Address.class, RegionRow::getMaxAddress),
LENGTH("Length", Long.class, RegionRow::getLength), LENGTH("Length", Long.class, RegionRow::getLength),
@ -159,7 +159,7 @@ public class DebuggerLegacyRegionsPanel extends JPanel {
} }
AddressSet sel = new AddressSet(); AddressSet sel = new AddressSet();
for (TraceMemoryRegion s : regions) { for (TraceMemoryRegion s : regions) {
sel.add(s.getRange()); sel.add(s.getRange(0));
} }
ProgramSelection ps = new ProgramSelection(sel); ProgramSelection ps = new ProgramSelection(sel);
listingService.setCurrentSelection(ps); listingService.setCurrentSelection(ps);

View file

@ -42,8 +42,8 @@ public class DebuggerRegionMapProposalDialog
protected enum RegionMapTableColumns protected enum RegionMapTableColumns
implements EnumeratedTableColumn<RegionMapTableColumns, RegionMapEntry> { implements EnumeratedTableColumn<RegionMapTableColumns, RegionMapEntry> {
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
REGION_NAME("Region", String.class, e -> e.getRegion().getName()), REGION_NAME("Region", String.class, e -> e.getRegionName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getRegion().getMinAddress()), DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getRegionMinAddress()),
CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()), CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()),
PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()), PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()),
BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()), BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()),

View file

@ -193,7 +193,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
AddressSet sel = new AddressSet(); AddressSet sel = new AddressSet();
for (TraceMemoryRegion s : regions) { for (TraceMemoryRegion s : regions) {
sel.add(s.getRange()); sel.add(s.getRange(current.getSnap()));
} }
ProgramSelection ps = new ProgramSelection(sel); ProgramSelection ps = new ProgramSelection(sel);
listingService.setCurrentSelection(ps); listingService.setCurrentSelection(ps);
@ -403,7 +403,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
return; return;
} }
Map<?, RegionMapProposal> map = staticMappingService.proposeRegionMaps(regions, Map<?, RegionMapProposal> map = staticMappingService.proposeRegionMaps(regions,
List.of(programManager.getAllOpenPrograms())); current.getSnap(), List.of(programManager.getAllOpenPrograms()));
Collection<RegionMapEntry> proposal = MapProposal.flatten(map.values()); Collection<RegionMapEntry> proposal = MapProposal.flatten(map.values());
promptRegionProposal(proposal); promptRegionProposal(proposal);
} }
@ -416,7 +416,8 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
if (program == null) { if (program == null) {
return; return;
} }
RegionMapProposal map = staticMappingService.proposeRegionMap(regions, program); RegionMapProposal map =
staticMappingService.proposeRegionMap(regions, current.getSnap(), program);
Collection<RegionMapEntry> proposal = map.computeMap().values(); Collection<RegionMapEntry> proposal = map.computeMap().values();
promptRegionProposal(proposal); promptRegionProposal(proposal);
} }
@ -431,7 +432,8 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
return; return;
} }
RegionMapProposal map = RegionMapProposal map =
staticMappingService.proposeRegionMap(region, location.getProgram(), block); staticMappingService.proposeRegionMap(region, current.getSnap(), location.getProgram(),
block);
promptRegionProposal(map.computeMap().values()); promptRegionProposal(map.computeMap().values());
} }
@ -548,7 +550,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
Msg.warn(this, "No program manager!"); Msg.warn(this, "No program manager!");
return null; return null;
} }
return blockChooserDialog.chooseBlock(getTool(), region, return blockChooserDialog.chooseBlock(getTool(), region, current.getSnap(),
List.of(programManager.getAllOpenPrograms())); List.of(programManager.getAllOpenPrograms()));
} }

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.debug.gui.memory;
import db.Transaction; import db.Transaction;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
public class RegionRow { public class RegionRow {
@ -34,84 +33,71 @@ public class RegionRow {
public void setName(String name) { public void setName(String name) {
try (Transaction tx = region.getTrace().openTransaction("Rename region")) { try (Transaction tx = region.getTrace().openTransaction("Rename region")) {
region.setName(name); region.setName(0, name);
} }
} }
public String getName() { public String getName() {
return region.getName(); return region.getName(0);
}
public Lifespan getLifespan() {
return region.getLifespan();
}
public long getCreatedSnap() {
return region.getCreationSnap();
}
public String getDestroyedSnap() {
long snap = region.getDestructionSnap();
return snap == Long.MAX_VALUE ? "" : Long.toString(snap);
} }
public AddressRange getRange() { public AddressRange getRange() {
return region.getRange(); return region.getRange(0);
} }
public Address getMaxAddress() { public Address getMaxAddress() {
return region.getMaxAddress(); return region.getMaxAddress(0);
} }
public Address getMinAddress() { public Address getMinAddress() {
return region.getMinAddress(); return region.getMinAddress(0);
} }
public long getLength() { public long getLength() {
return region.getLength(); return region.getLength(0);
} }
public void setRead(boolean read) { public void setRead(boolean read) {
try (Transaction tx = try (Transaction tx =
region.getTrace().openTransaction("Toggle region read flag")) { region.getTrace().openTransaction("Toggle region read flag")) {
region.setRead(read); region.setRead(0, read);
} }
} }
public boolean isRead() { public boolean isRead() {
return region.isRead(); return region.isRead(0);
} }
public void setWrite(boolean write) { public void setWrite(boolean write) {
try (Transaction tx = try (Transaction tx =
region.getTrace().openTransaction("Toggle region write flag")) { region.getTrace().openTransaction("Toggle region write flag")) {
region.setWrite(write); region.setWrite(0, write);
} }
} }
public boolean isWrite() { public boolean isWrite() {
return region.isWrite(); return region.isWrite(0);
} }
public void setExecute(boolean execute) { public void setExecute(boolean execute) {
try (Transaction tx = try (Transaction tx =
region.getTrace().openTransaction("Toggle region execute flag")) { region.getTrace().openTransaction("Toggle region execute flag")) {
region.setExecute(execute); region.setExecute(0, execute);
} }
} }
public boolean isExecute() { public boolean isExecute() {
return region.isExecute(); return region.isExecute(0);
} }
public void setVolatile(boolean vol) { public void setVolatile(boolean vol) {
try (Transaction tx = try (Transaction tx =
region.getTrace().openTransaction("Toggle region volatile flag")) { region.getTrace().openTransaction("Toggle region volatile flag")) {
region.setVolatile(vol); region.setVolatile(0, vol);
} }
} }
public boolean isVolatile() { public boolean isVolatile() {
return region.isVolatile(); return region.isVolatile(0);
} }
} }

View file

@ -21,14 +21,13 @@ import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer; import ghidra.async.AsyncTimer;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.database.module.TraceObjectSection;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.*;
import ghidra.trace.model.breakpoint.TraceBreakpointManager; import ghidra.trace.model.memory.*;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThreadManager; import ghidra.trace.model.thread.*;
import ghidra.trace.util.TraceEvents; import ghidra.trace.util.TraceEvents;
import ghidra.util.Swing; import ghidra.util.Swing;
@ -91,60 +90,79 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
} }
private void threadChanged(TraceThread thread) { private void threadChanged(TraceThread thread) {
if (!trackThreads || !trackTrace) { if (!trackThreads || !trackTrace || !(thread instanceof TraceObjectThread objThread)) {
return; return;
} }
AddressFactory factory = thread.getTrace().getBaseAddressFactory(); AddressFactory factory = thread.getTrace().getBaseAddressFactory();
AddressSpace defaultSpace = factory.getDefaultAddressSpace(); AddressSpace defaultSpace = factory.getDefaultAddressSpace();
Long threadId = thread.getKey(); Long threadId = thread.getKey();
AddressRange rng = rng(defaultSpace, threadId, threadId); AddressRange rng = rng(defaultSpace, threadId, threadId);
MemoryBox box = new MemoryBox("Thread " + thread.getName(), MemviewBoxType.THREAD, rng, TraceObject obj = objThread.getObject();
thread.getLifespan()); obj.getCanonicalParents(Lifespan.ALL).forEach(p -> {
MemoryBox box = new MemoryBox("Thread " + thread.getName(p.getMinSnap()),
MemviewBoxType.THREAD, rng, p.getLifespan());
updateList.add(box); updateList.add(box);
});
updateLabelDebouncer.contact(null); updateLabelDebouncer.contact(null);
} }
private void regionChanged(TraceMemoryRegion region) { private void regionChanged(TraceMemoryRegion region) {
if (!trackRegions || !trackTrace) { if (!trackRegions || !trackTrace ||
!(region instanceof TraceObjectMemoryRegion objRegion)) {
return; return;
} }
MemoryBox box = new MemoryBox("Region " + region.getName(), MemviewBoxType.VIRTUAL_ALLOC,
region.getRange(), region.getLifespan()); TraceObject obj = objRegion.getObject();
obj.getOrderedValues(Lifespan.ALL, TraceObjectMemoryRegion.KEY_RANGE, true).forEach(v -> {
MemoryBox box = new MemoryBox("Region " + region.getName(v.getMinSnap()),
MemviewBoxType.VIRTUAL_ALLOC, v.castValue(), v.getLifespan());
updateList.add(box); updateList.add(box);
});
updateLabelDebouncer.contact(null); updateLabelDebouncer.contact(null);
} }
private void moduleChanged(TraceModule module) { private void moduleChanged(TraceModule module) {
if (!trackModules || !trackTrace) { if (!trackModules || !trackTrace || !(module instanceof TraceObjectModule objModule)) {
return; return;
} }
AddressRange range = module.getRange();
if (range == null) { TraceObject obj = objModule.getObject();
return; obj.getOrderedValues(Lifespan.ALL, TraceObjectModule.KEY_RANGE, true).forEach(v -> {
} MemoryBox box = new MemoryBox("Module " + module.getName(v.getMinSnap()),
MemoryBox box = new MemoryBox("Module " + module.getName(), MemviewBoxType.MODULE, range, MemviewBoxType.MODULE, v.castValue(), v.getLifespan());
module.getLifespan());
updateList.add(box); updateList.add(box);
});
updateLabelDebouncer.contact(null); updateLabelDebouncer.contact(null);
} }
private void sectionChanged(TraceSection section) { private void sectionChanged(TraceSection section) {
if (!trackSections || !trackTrace) { if (!trackSections || !trackTrace || !(section instanceof TraceObjectSection objSection)) {
return; return;
} }
MemoryBox box = new MemoryBox("Section " + section.getName(), MemviewBoxType.IMAGE,
section.getRange(), section.getModule().getLifespan()); TraceObject obj = objSection.getObject();
obj.getOrderedValues(Lifespan.ALL, TraceObjectSection.KEY_RANGE, true).forEach(v -> {
MemoryBox box = new MemoryBox("Module " + section.getName(v.getMinSnap()),
MemviewBoxType.IMAGE, v.castValue(), v.getLifespan());
updateList.add(box); updateList.add(box);
});
updateLabelDebouncer.contact(null); updateLabelDebouncer.contact(null);
} }
private void breakpointChanged(TraceBreakpoint bpt) { private void breakpointChanged(TraceBreakpoint bpt) {
if (!trackBreakpoints || !trackTrace) { if (!trackBreakpoints || !trackTrace ||
!(bpt instanceof TraceObjectBreakpointLocation objBpt)) {
return; return;
} }
MemoryBox box = new MemoryBox("Breakpoint " + bpt.getName(), MemviewBoxType.BREAKPOINT,
bpt.getRange(), bpt.getLifespan()); TraceObject obj = objBpt.getObject();
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
.forEach(v -> {
MemoryBox box = new MemoryBox("Module " + bpt.getName(v.getMinSnap()),
MemviewBoxType.BREAKPOINT, v.castValue(), v.getLifespan());
updateList.add(box); updateList.add(box);
});
updateLabelDebouncer.contact(null); updateLabelDebouncer.contact(null);
} }
@ -238,7 +256,7 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
TraceModuleManager moduleManager = trace.getModuleManager(); TraceModuleManager moduleManager = trace.getModuleManager();
for (TraceModule module : moduleManager.getAllModules()) { for (TraceModule module : moduleManager.getAllModules()) {
moduleChanged(module); moduleChanged(module);
Collection<? extends TraceSection> sections = module.getSections(); Collection<? extends TraceSection> sections = module.getAllSections();
for (TraceSection section : sections) { for (TraceSection section : sections) {
sectionChanged(section); sectionChanged(section);
} }

View file

@ -34,7 +34,7 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.target.*; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.iface.TraceObjectInterface; import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
@ -115,7 +115,8 @@ public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterf
List<ValueRow> sel = getSelectedItems(); List<ValueRow> sel = getSelectedItems();
if (!sel.isEmpty()) { if (!sel.isEmpty()) {
myActionContext = new DebuggerObjectActionContext( myActionContext = new DebuggerObjectActionContext(
sel.stream().map(r -> r.getValue()).collect(Collectors.toList()), provider, table); sel.stream().map(r -> r.getValue()).collect(Collectors.toList()), provider, table,
current.getSnap());
} }
} }

View file

@ -541,7 +541,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
.map(n -> n.getValue()) .map(n -> n.getValue())
.filter(o -> o != null) // Root for no trace would return null .filter(o -> o != null) // Root for no trace would return null
.collect(Collectors.toList()), .collect(Collectors.toList()),
DebuggerModelProvider.this, objectsTreePanel); DebuggerModelProvider.this, objectsTreePanel, current.getSnap());
} }
} }
@ -610,7 +610,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return new DebuggerObjectActionContext(sel.stream() return new DebuggerObjectActionContext(sel.stream()
.map(r -> r.getValue()) .map(r -> r.getValue())
.collect(Collectors.toList()), .collect(Collectors.toList()),
DebuggerModelProvider.this, elementsTablePanel); DebuggerModelProvider.this, elementsTablePanel, current.getSnap());
} }
} }
@ -664,7 +664,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return new DebuggerObjectActionContext(sel.stream() return new DebuggerObjectActionContext(sel.stream()
.map(r -> Objects.requireNonNull(r.getPath().getLastEntry())) .map(r -> Objects.requireNonNull(r.getPath().getLastEntry()))
.collect(Collectors.toList()), .collect(Collectors.toList()),
DebuggerModelProvider.this, attributesTablePanel); DebuggerModelProvider.this, attributesTablePanel, current.getSnap());
} }
} }
@ -768,7 +768,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return; return;
} }
Map<String, ActionEntry> actions = target.collectActions(ActionName.REFRESH, Map<String, ActionEntry> actions = target.collectActions(ActionName.REFRESH,
new DebuggerObjectActionContext(List.of(value), this, objectsTreePanel)); new DebuggerObjectActionContext(List.of(value), this, objectsTreePanel,
current.getSnap()));
for (ActionEntry ent : actions.values()) { for (ActionEntry ent : actions.values()) {
if (ent.requiresPrompt()) { if (ent.requiresPrompt()) {
continue; continue;

View file

@ -53,7 +53,8 @@ public class DebuggerLegacyModulesPanel extends JPanel {
DebuggerModuleActionContext context) { DebuggerModuleActionContext context) {
return context.getSelectedModules() return context.getSelectedModules()
.stream() .stream()
.flatMap(m -> m.getSections().stream()) // snap does not matter for legacy module
.flatMap(m -> m.getSections(0).stream())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -61,7 +62,8 @@ public class DebuggerLegacyModulesPanel extends JPanel {
DebuggerModuleActionContext context) { DebuggerModuleActionContext context) {
AddressSet sel = new AddressSet(); AddressSet sel = new AddressSet();
for (TraceModule module : getSelectedModulesFromContext(context)) { for (TraceModule module : getSelectedModulesFromContext(context)) {
sel.add(module.getRange()); // snap does not matter for legacy module
sel.add(module.getRange(0));
} }
return sel; return sel;
} }
@ -73,7 +75,6 @@ public class DebuggerLegacyModulesPanel extends JPanel {
SHORT_NAME("Name", String.class, ModuleRow::getShortName), SHORT_NAME("Name", String.class, ModuleRow::getShortName),
NAME("Module Name", String.class, ModuleRow::getName, ModuleRow::setName), NAME("Module Name", String.class, ModuleRow::getName, ModuleRow::setName),
MAPPING("Mapping", String.class, ModuleRow::getMapping), MAPPING("Mapping", String.class, ModuleRow::getMapping),
LIFESPAN("Lifespan", Lifespan.class, ModuleRow::getLifespan),
LENGTH("Length", Long.class, ModuleRow::getLength); LENGTH("Length", Long.class, ModuleRow::getLength);
private final String header; private final String header;
@ -251,7 +252,7 @@ public class DebuggerLegacyModulesPanel extends JPanel {
moduleTableModel.addAllItems(moduleManager.getAllModules()); moduleTableModel.addAllItems(moduleManager.getAllModules());
} }
public void setTrace(Trace trace) { private void setTrace(Trace trace) {
if (currentTrace == trace) { if (currentTrace == trace) {
return; return;
} }

View file

@ -48,7 +48,7 @@ public class DebuggerLegacySectionsPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext( protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerSectionActionContext context) { DebuggerSectionActionContext context) {
return context.getSelectedSections(false) return context.getSelectedSections(false, 0)
.stream() .stream()
.map(r -> r.getModule()) .map(r -> r.getModule())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -56,14 +56,14 @@ public class DebuggerLegacySectionsPanel extends JPanel {
protected static Set<TraceSection> getSelectedSectionsFromContext( protected static Set<TraceSection> getSelectedSectionsFromContext(
DebuggerSectionActionContext context, boolean allowExpansion) { DebuggerSectionActionContext context, boolean allowExpansion) {
return context.getSelectedSections(allowExpansion); return context.getSelectedSections(allowExpansion, 0);
} }
protected static AddressSetView getSelectedAddressesFromContext( protected static AddressSetView getSelectedAddressesFromContext(
DebuggerSectionActionContext context) { DebuggerSectionActionContext context) {
AddressSet sel = new AddressSet(); AddressSet sel = new AddressSet();
for (TraceSection section : getSelectedSectionsFromContext(context, false)) { for (TraceSection section : getSelectedSectionsFromContext(context, false)) {
sel.add(section.getRange()); sel.add(section.getRange(0));
} }
return sel; return sel;
} }

View file

@ -41,7 +41,7 @@ public class DebuggerModuleMapProposalDialog
protected enum ModuleMapTableColumns protected enum ModuleMapTableColumns
implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> { implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> {
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()), MODULE_NAME("Module", String.class, e -> e.getModuleName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()), DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()),
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()), CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null

View file

@ -190,7 +190,7 @@ public class DebuggerModulesPanel extends AbstractObjectsTableBasedPanel<TraceOb
TraceObject child = value.getChild(); TraceObject child = value.getChild();
TraceObjectModule module = child.queryInterface(TraceObjectModule.class); TraceObjectModule module = child.queryInterface(TraceObjectModule.class);
if (module != null) { if (module != null) {
result.addAll(module.getSections()); result.addAll(module.getSections(ctx.getSnap()));
continue; continue;
} }
TraceObjectSection section = child.queryInterface(TraceObjectSection.class); TraceObjectSection section = child.queryInterface(TraceObjectSection.class);
@ -208,12 +208,12 @@ public class DebuggerModulesPanel extends AbstractObjectsTableBasedPanel<TraceOb
TraceObject child = value.getChild(); TraceObject child = value.getChild();
TraceObjectModule module = child.queryInterface(TraceObjectModule.class); TraceObjectModule module = child.queryInterface(TraceObjectModule.class);
if (module != null) { if (module != null) {
result.add(module.getRange()); result.add(module.getRange(ctx.getSnap()));
continue; continue;
} }
TraceObjectSection section = child.queryInterface(TraceObjectSection.class); TraceObjectSection section = child.queryInterface(TraceObjectSection.class);
if (section != null) { if (section != null) {
result.add(section.getRange()); result.add(section.getRange(ctx.getSnap()));
continue; continue;
} }
} }

View file

@ -402,13 +402,20 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
return; return;
} }
couldHaveChanged = false; couldHaveChanged = false;
String infosThisTime = spec.getInfoForObjects(trace); DebuggerTraceManagerService traceManager =
tool.getService(DebuggerTraceManagerService.class);
if (traceManager == null) {
return;
}
DebuggerCoordinates current = traceManager.getCurrentFor(trace);
long snap = current.getSnap();
String infosThisTime = spec.getInfoForObjects(trace, snap);
if (Objects.equals(infosThisTime, infosLastTime)) { if (Objects.equals(infosThisTime, infosLastTime)) {
return; return;
} }
infosLastTime = infosThisTime; infosLastTime = infosThisTime;
spec.runTask(tool, trace); spec.runTask(tool, trace, snap);
} }
public void forceMap() { public void forceMap() {
@ -593,7 +600,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
boolean filterSectionsByModules = false; boolean filterSectionsByModules = false;
private final Map<Trace, AutoMapState> autoMapStateByTrace = new WeakHashMap<>(); private final Map<Trace, AutoMapState> autoMapStateByTrace = new WeakHashMap<>();
private AutoMapState forMappingListener;
DockingAction actionImportMissingModule; DockingAction actionImportMissingModule;
DockingAction actionMapMissingModule; DockingAction actionMapMissingModule;
@ -634,7 +640,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
private void importModuleFromFileSystem(TraceModule module) { private void importModuleFromFileSystem(TraceModule module) {
GhidraFileChooser chooser = new GhidraFileChooser(getComponent()); GhidraFileChooser chooser = new GhidraFileChooser(getComponent());
chooser.setSelectedFile(new File(module.getName())); chooser.setSelectedFile(new File(module.getName(current.getSnap())));
File file = chooser.getSelectedFile(); File file = chooser.getSelectedFile();
chooser.dispose(); chooser.dispose();
if (file == null) { // Perhaps cancelled if (file == null) { // Perhaps cancelled
@ -837,7 +843,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
return !ctx.getSelectedModules().isEmpty(); return !ctx.getSelectedModules().isEmpty();
} }
if (context instanceof DebuggerSectionActionContext ctx) { if (context instanceof DebuggerSectionActionContext ctx) {
return !ctx.getSelectedSections(false).isEmpty(); return !ctx.getSelectedSections(false, current.getSnap()).isEmpty();
} }
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return !ctx.getObjectValues().isEmpty(); return !ctx.getObjectValues().isEmpty();
@ -978,10 +984,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
if (staticMappingService == null) { if (staticMappingService == null) {
return; return;
} }
Program program = context.getProgram(); Program program = context.getProgram();
Trace trace = context.getTrace(); Trace trace = context.getTrace();
long snap = traceManager.getCurrentFor(trace).getSnap();
Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps( Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps(
trace.getModuleManager().getAllModules(), List.of(program)); trace.getModuleManager().getAllModules(), snap, List.of(program));
Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values()); Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values());
promptModuleProposal(proposal, FMT_NO_MODULES_PROPOSAL_RETRY.formatted( promptModuleProposal(proposal, FMT_NO_MODULES_PROPOSAL_RETRY.formatted(
trace.getDomainFile().getName(), program.getDomainFile().getName())); trace.getDomainFile().getName(), program.getDomainFile().getName()));
@ -1022,10 +1031,10 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
Program program = context.getProgram(); Program program = context.getProgram();
ModuleMapProposal proposal = ModuleMapProposal proposal =
staticMappingService.proposeModuleMap(module, program); staticMappingService.proposeModuleMap(module, snap, program);
Map<TraceModule, ModuleMapEntry> map = proposal.computeMap(); Map<TraceModule, ModuleMapEntry> map = proposal.computeMap();
promptModuleProposal(map.values(), FMT_NO_MODULES_PROPOSAL_CURRENT.formatted( promptModuleProposal(map.values(), FMT_NO_MODULES_PROPOSAL_CURRENT.formatted(
module.getName(), program.getDomainFile().getName())); module.getName(snap), program.getDomainFile().getName()));
} }
private void activatedMapMissingProgramIdentically( private void activatedMapMissingProgramIdentically(
@ -1089,16 +1098,16 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
return; return;
} }
long snap = current.getSnap();
ProgramSelection progSel = listingService.getCurrentSelection(); ProgramSelection progSel = listingService.getCurrentSelection();
TraceModuleManager moduleManager = current.getTrace().getModuleManager(); TraceModuleManager moduleManager = current.getTrace().getModuleManager();
if (progSel != null && !progSel.isEmpty()) { if (progSel != null && !progSel.isEmpty()) {
long snap = traceManager.getCurrentSnap();
Set<TraceModule> modSel = new HashSet<>(); Set<TraceModule> modSel = new HashSet<>();
Set<TraceSection> sectionSel = new HashSet<>(); Set<TraceSection> sectionSel = new HashSet<>();
for (AddressRange range : progSel) { for (AddressRange range : progSel) {
for (TraceModule module : moduleManager for (TraceModule module : moduleManager
.getModulesIntersecting(Lifespan.at(snap), range)) { .getModulesIntersecting(Lifespan.at(snap), range)) {
if (module.getSections().isEmpty()) { if (module.getSections(snap).isEmpty()) {
modSel.add(module); modSel.add(module);
} }
} }
@ -1127,7 +1136,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
TraceModule bestModule = null; TraceModule bestModule = null;
for (TraceModule module : moduleManager for (TraceModule module : moduleManager
.getLoadedModules(traceManager.getCurrentSnap())) { .getLoadedModules(traceManager.getCurrentSnap())) {
Address base = module.getBase(); Address base = module.getBase(snap);
if (base == null || base.getAddressSpace() != address.getAddressSpace()) { if (base == null || base.getAddressSpace() != address.getAddressSpace()) {
continue; continue;
} }
@ -1138,12 +1147,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
if (base.compareTo(address) > 0) { if (base.compareTo(address) > 0) {
continue; continue;
} }
if (base.compareTo(bestModule.getBase()) <= 0) { if (base.compareTo(bestModule.getBase(snap)) <= 0) {
continue; continue;
} }
bestModule = module; bestModule = module;
} }
if (bestModule.getSections().isEmpty()) { if (bestModule.getSections(snap).isEmpty()) {
setSelectedModules(Set.of(bestModule)); setSelectedModules(Set.of(bestModule));
return; return;
} }
@ -1168,7 +1177,8 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
if (staticMappingService == null) { if (staticMappingService == null) {
return; return;
} }
Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps(modules, Map<TraceModule, ModuleMapProposal> map =
staticMappingService.proposeModuleMaps(modules, current.getSnap(),
List.of(programManager.getAllOpenPrograms())); List.of(programManager.getAllOpenPrograms()));
Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values()); Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values());
promptModuleProposal(proposal, NO_MODULES_PROPOSAL_SEL); promptModuleProposal(proposal, NO_MODULES_PROPOSAL_SEL);
@ -1182,7 +1192,8 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
if (program == null) { if (program == null) {
return; return;
} }
ModuleMapProposal proposal = staticMappingService.proposeModuleMap(module, program); ModuleMapProposal proposal =
staticMappingService.proposeModuleMap(module, current.getSnap(), program);
Map<TraceModule, ModuleMapEntry> map = proposal.computeMap(); Map<TraceModule, ModuleMapEntry> map = proposal.computeMap();
promptModuleProposal(map.values(), NO_MODULES_PROPOSAL_SEL); promptModuleProposal(map.values(), NO_MODULES_PROPOSAL_SEL);
} }
@ -1209,7 +1220,8 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
} }
Set<TraceModule> modules = Set<TraceModule> modules =
sections.stream().map(TraceSection::getModule).collect(Collectors.toSet()); sections.stream().map(TraceSection::getModule).collect(Collectors.toSet());
Map<?, SectionMapProposal> map = staticMappingService.proposeSectionMaps(modules, Map<?, SectionMapProposal> map =
staticMappingService.proposeSectionMaps(modules, current.getSnap(),
List.of(programManager.getAllOpenPrograms())); List.of(programManager.getAllOpenPrograms()));
Collection<SectionMapEntry> proposal = MapProposal.flatten(map.values()); Collection<SectionMapEntry> proposal = MapProposal.flatten(map.values());
Collection<SectionMapEntry> filtered = proposal.stream() Collection<SectionMapEntry> filtered = proposal.stream()
@ -1232,7 +1244,8 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
return; return;
} }
TraceModule module = modules.iterator().next(); TraceModule module = modules.iterator().next();
SectionMapProposal map = staticMappingService.proposeSectionMap(module, program); SectionMapProposal map =
staticMappingService.proposeSectionMap(module, current.getSnap(), program);
Collection<SectionMapEntry> proposal = map.computeMap().values(); Collection<SectionMapEntry> proposal = map.computeMap().values();
Collection<SectionMapEntry> filtered = proposal.stream() Collection<SectionMapEntry> filtered = proposal.stream()
.filter(e -> sections.contains(e.getSection())) .filter(e -> sections.contains(e.getSection()))
@ -1249,8 +1262,8 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
if (block == null) { if (block == null) {
return; return;
} }
SectionMapProposal map = SectionMapProposal map = staticMappingService.proposeSectionMap(section, current.getSnap(),
staticMappingService.proposeSectionMap(section, location.getProgram(), block); location.getProgram(), block);
promptSectionProposal(map.computeMap().values()); promptSectionProposal(map.computeMap().values());
} }
@ -1444,7 +1457,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
Msg.warn(this, "No program manager!"); Msg.warn(this, "No program manager!");
return null; return null;
} }
return blockChooserDialog.chooseBlock(getTool(), section, return blockChooserDialog.chooseBlock(getTool(), section, current.getSnap(),
List.of(programManager.getAllOpenPrograms())); List.of(programManager.getAllOpenPrograms()));
} }

View file

@ -45,10 +45,10 @@ public class DebuggerSectionActionContext extends DefaultActionContext {
this.forcedSingle = forcedSingle; this.forcedSingle = forcedSingle;
} }
public Set<TraceSection> getSelectedSections(boolean allowExpansion) { public Set<TraceSection> getSelectedSections(boolean allowExpansion, long snap) {
if (forcedSingle && allowExpansion) { if (forcedSingle && allowExpansion) {
return selectedSections.stream() return selectedSections.stream()
.flatMap(s -> s.getModule().getSections().stream()) .flatMap(s -> s.getModule().getSections(snap).stream())
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
} }
return selectedSections; return selectedSections;

View file

@ -42,9 +42,9 @@ public class DebuggerSectionMapProposalDialog
protected enum SectionMapTableColumns protected enum SectionMapTableColumns
implements EnumeratedTableColumn<SectionMapTableColumns, SectionMapEntry> { implements EnumeratedTableColumn<SectionMapTableColumns, SectionMapEntry> {
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()), REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()), MODULE_NAME("Module", String.class, e -> e.getModuleName()),
SECTION_NAME("Section", String.class, e -> e.getSection().getName()), SECTION_NAME("Section", String.class, e -> e.getSectionName()),
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getSection().getStart()), DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getSectionStart()),
CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()), CHOOSE("Choose", String.class, e -> "Choose Block", (e, s) -> nop()),
PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()), PROGRAM_NAME("Program", String.class, e -> e.getToProgram().getName()),
BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()), BLOCK_NAME("Block", String.class, e -> e.getBlock().getName()),

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.debug.gui.modules;
import db.Transaction; import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
public class ModuleRow { public class ModuleRow {
@ -36,16 +35,16 @@ public class ModuleRow {
public void setName(String name) { public void setName(String name) {
try (Transaction tx = module.getTrace().openTransaction("Renamed module")) { try (Transaction tx = module.getTrace().openTransaction("Renamed module")) {
module.setName(name); module.setName(0, name);
} }
} }
public String getShortName() { public String getShortName() {
return DebuggerStaticMappingUtils.computeModuleShortName(module.getName()); return DebuggerStaticMappingUtils.computeModuleShortName(module.getName(0));
} }
public String getName() { public String getName() {
return module.getName(); return module.getName(0);
} }
public String getMapping() { public String getMapping() {
@ -54,31 +53,18 @@ public class ModuleRow {
// 2. Range/Life changes to this module // 2. Range/Life changes to this module
// 3. Snapshot navigation // 3. Snapshot navigation
return DebuggerStaticMappingUtils.computeMappedFiles(module.getTrace(), return DebuggerStaticMappingUtils.computeMappedFiles(module.getTrace(),
provider.current.getSnap(), module.getRange()); provider.current.getSnap(), module.getRange(0));
} }
public Address getBase() { public Address getBase() {
return module.getBase(); return module.getBase(0);
} }
public Address getMaxAddress() { public Address getMaxAddress() {
return module.getMaxAddress(); return module.getMaxAddress(0);
}
public long getLoadedSnap() {
return module.getLoadedSnap();
}
public Long getUnloadedSnap() {
long snap = module.getUnloadedSnap();
return snap == Long.MAX_VALUE ? null : snap;
}
public Lifespan getLifespan() {
return module.getLifespan();
} }
public long getLength() { public long getLength() {
return module.getLength(); return module.getLength(0);
} }
} }

View file

@ -18,8 +18,6 @@ package ghidra.app.plugin.core.debug.gui.modules;
import db.Transaction; import db.Transaction;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.DefaultTraceLocation;
import ghidra.trace.model.TraceLocation;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.modules.TraceSection; import ghidra.trace.model.modules.TraceSection;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -42,7 +40,7 @@ public class SectionRow {
public void setName(String name) { public void setName(String name) {
try (Transaction tx = section.getTrace().openTransaction("Rename section")) { try (Transaction tx = section.getTrace().openTransaction("Rename section")) {
section.setName(name); section.setName(0, name);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
Msg.showError(this, null, "Rename Section", Msg.showError(this, null, "Rename Section",
@ -51,31 +49,26 @@ public class SectionRow {
} }
public String getName() { public String getName() {
return section.getName(); return section.getName(0);
} }
public String getModuleName() { public String getModuleName() {
return section.getModule().getName(); return section.getModule().getName(0);
} }
public AddressRange getRange() { public AddressRange getRange() {
return section.getRange(); return section.getRange(0);
} }
public Address getStart() { public Address getStart() {
return section.getStart(); return section.getStart(0);
} }
public Address getEnd() { public Address getEnd() {
return section.getEnd(); return section.getEnd(0);
} }
public long getLength() { public long getLength() {
return section.getRange().getLength(); return section.getRange(0).getLength();
}
public TraceLocation getTraceLocation() {
return new DefaultTraceLocation(section.getModule().getTrace(), null,
section.getModule().getLifespan(), section.getStart());
} }
} }

View file

@ -783,7 +783,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
protected String computeSubTitle() { protected String computeSubTitle() {
TraceThread curThread = current.getThread(); TraceThread curThread = current.getThread();
return curThread == null ? "" : curThread.getName(); return curThread == null ? "" : curThread.getName(current.getSnap());
} }
protected void updateSubTitle() { protected void updateSubTitle() {

View file

@ -129,9 +129,6 @@ public class DebuggerLegacyStackPanel extends JPanel {
} }
private void stackAdded(TraceStack stack) { private void stackAdded(TraceStack stack) {
if (stack.getSnap() != current.getViewSnap()) {
return;
}
TraceThread curThread = current.getThread(); TraceThread curThread = current.getThread();
if (curThread != stack.getThread()) { if (curThread != stack.getThread()) {
return; return;

View file

@ -145,7 +145,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
protected String computeSubTitle() { protected String computeSubTitle() {
TraceThread curThread = current.getThread(); TraceThread curThread = current.getThread();
return curThread == null ? "" : curThread.getName(); return curThread == null ? "" : curThread.getName(current.getSnap());
} }
protected void updateSubTitle() { protected void updateSubTitle() {

View file

@ -269,9 +269,10 @@ public enum VariableValueUtils {
RegisterValue spRV = regs.getValue(platform, viewSnap, sp); RegisterValue spRV = regs.getValue(platform, viewSnap, sp);
Address spVal = cSpec.getStackBaseSpace().getAddress(spRV.getUnsignedValue().longValue()); Address spVal = cSpec.getStackBaseSpace().getAddress(spRV.getUnsignedValue().longValue());
Address max; Address max;
TraceMemoryRegion stackRegion = mem.getRegionContaining(coordinates.getSnap(), spVal); long snap = coordinates.getSnap();
TraceMemoryRegion stackRegion = mem.getRegionContaining(snap, spVal);
if (stackRegion != null) { if (stackRegion != null) {
max = stackRegion.getMaxAddress(); max = stackRegion.getMaxAddress(snap);
} }
else { else {
long toMax = spVal.getAddressSpace().getMaxAddress().subtract(spVal); long toMax = spVal.getAddressSpace().getMaxAddress().subtract(spVal);
@ -408,7 +409,7 @@ public enum VariableValueUtils {
if (stack == null) { if (stack == null) {
return null; return null;
} }
TraceStackFrame frame = stack.getFrame(0, false); TraceStackFrame frame = stack.getFrame(snap, 0, false);
if (frame == null) { if (frame == null) {
return null; return null;
} }

View file

@ -38,10 +38,10 @@ import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.AutoService; import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.trace.model.*; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.thread.TraceThreadManager; import ghidra.trace.model.thread.TraceThreadManager;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.util.TraceEvents; import ghidra.trace.util.TraceEvents;
import ghidra.util.database.ObjectKey; import ghidra.util.database.ObjectKey;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@ -62,12 +62,9 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
true, true), true, true),
MODULE("Module", String.class, ThreadRow::getModule, true, false), MODULE("Module", String.class, ThreadRow::getModule, true, false),
SP("SP", Address.class, ThreadRow::getStackPointer, true, false), SP("SP", Address.class, ThreadRow::getStackPointer, true, false),
CREATED("Created", Long.class, ThreadRow::getCreationSnap, true, false),
DESTROYED("Destroyed", String.class, ThreadRow::getDestructionSnap, true, false),
STATE("State", ThreadState.class, ThreadRow::getState, true, true), STATE("State", ThreadState.class, ThreadRow::getState, true, true),
COMMENT("Comment", String.class, ThreadRow::getComment, ThreadRow::setComment, true, false), COMMENT("Comment", String.class, ThreadRow::getComment, ThreadRow::setComment, true, false);
PLOT("Plot", Lifespan.class, ThreadRow::getLifespan, false, true);
private final String header; private final String header;
private final Function<ThreadRow, ?> getter; private final Function<ThreadRow, ?> getter;
@ -145,9 +142,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
listenFor(TraceEvents.THREAD_CHANGED, this::threadChanged); listenFor(TraceEvents.THREAD_CHANGED, this::threadChanged);
listenFor(TraceEvents.THREAD_LIFESPAN_CHANGED, this::threadChanged); listenFor(TraceEvents.THREAD_LIFESPAN_CHANGED, this::threadChanged);
listenFor(TraceEvents.THREAD_DELETED, this::threadDeleted); listenFor(TraceEvents.THREAD_DELETED, this::threadDeleted);
listenFor(TraceEvents.SNAPSHOT_ADDED, this::snapAdded);
listenFor(TraceEvents.SNAPSHOT_DELETED, this::snapDeleted);
} }
private void objectRestored(DomainObjectChangeRecord rec) { private void objectRestored(DomainObjectChangeRecord rec) {
@ -165,14 +159,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
private void threadDeleted(TraceThread thread) { private void threadDeleted(TraceThread thread) {
threadTableModel.deleteItem(thread); threadTableModel.deleteItem(thread);
} }
private void snapAdded(TraceSnapshot snapshot) {
updateTimelineMax();
}
private void snapDeleted() {
updateTimelineMax();
}
} }
private final DebuggerThreadsProvider provider; private final DebuggerThreadsProvider provider;
@ -187,10 +173,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
private final ForThreadsListener forThreadsListener = new ForThreadsListener(); private final ForThreadsListener forThreadsListener = new ForThreadsListener();
/* package access for testing */
final SpanTableCellRenderer<Long> spanRenderer = new SpanTableCellRenderer<>();
final RangeCursorTableHeaderRenderer<Long> headerRenderer;
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() { final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
@Override @Override
public String getFilterString(Object t, Settings settings) { public String getFilterString(Object t, Settings settings) {
@ -225,8 +207,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this); this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
threadTableModel = new ThreadTableModel(provider); threadTableModel = new ThreadTableModel(provider);
headerRenderer = new RangeCursorTableHeaderRenderer<>(0L,
threadTableModel.getColumn(ThreadTableColumns.PLOT.ordinal()));
threadTable = new GhidraTable(threadTableModel); threadTable = new GhidraTable(threadTableModel);
threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(new JScrollPane(threadTable)); add(new JScrollPane(threadTable));
@ -269,32 +249,10 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
TableColumnModel columnModel = threadTable.getColumnModel(); TableColumnModel columnModel = threadTable.getColumnModel();
TableColumn colName = columnModel.getColumn(ThreadTableColumns.NAME.ordinal()); TableColumn colName = columnModel.getColumn(ThreadTableColumns.NAME.ordinal());
colName.setCellRenderer(boldCurrentRenderer); colName.setCellRenderer(boldCurrentRenderer);
TableColumn colCreated = columnModel.getColumn(ThreadTableColumns.CREATED.ordinal());
colCreated.setCellRenderer(boldCurrentRenderer);
TableColumn colDestroyed = columnModel.getColumn(ThreadTableColumns.DESTROYED.ordinal());
colDestroyed.setCellRenderer(boldCurrentRenderer);
TableColumn colState = columnModel.getColumn(ThreadTableColumns.STATE.ordinal()); TableColumn colState = columnModel.getColumn(ThreadTableColumns.STATE.ordinal());
colState.setCellRenderer(boldCurrentRenderer); colState.setCellRenderer(boldCurrentRenderer);
TableColumn colComment = columnModel.getColumn(ThreadTableColumns.COMMENT.ordinal()); TableColumn colComment = columnModel.getColumn(ThreadTableColumns.COMMENT.ordinal());
colComment.setCellRenderer(boldCurrentRenderer); colComment.setCellRenderer(boldCurrentRenderer);
TableColumn colPlot = columnModel.getColumn(ThreadTableColumns.PLOT.ordinal());
colPlot.setCellRenderer(spanRenderer);
colPlot.setHeaderRenderer(headerRenderer);
headerRenderer.addSeekListener(seekListener = pos -> {
long snap = Math.round(pos);
if (snap < 0) {
snap = 0;
}
long max =
current.getTrace() == null ? 0 : current.getTrace().getTimeManager().getMaxSnap();
if (snap > max) {
snap = max;
}
traceManager.activateSnap(snap);
myActionContext = new DebuggerSnapActionContext(current.getTrace(), snap);
provider.legacyThreadsPanelContextChanged();
});
} }
private void removeOldListeners() { private void removeOldListeners() {
@ -326,7 +284,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
current = coordinates; current = coordinates;
doSetTrace(coordinates.getTrace()); doSetTrace(coordinates.getTrace());
doSetThread(coordinates.getThread()); doSetThread(coordinates.getThread());
doSetSnap(coordinates.getSnap());
} }
private void doSetThread(TraceThread thread) { private void doSetThread(TraceThread thread) {
@ -339,11 +296,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
threadTableModel.fireTableDataChanged(); threadTableModel.fireTableDataChanged();
} }
private void doSetSnap(long snap) {
headerRenderer.setCursorPosition(snap);
threadTable.getTableHeader().repaint();
}
protected void loadThreads() { protected void loadThreads() {
threadTableModel.clear(); threadTableModel.clear();
Trace curTrace = current.getTrace(); Trace curTrace = current.getTrace();
@ -352,16 +304,6 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
} }
TraceThreadManager manager = curTrace.getThreadManager(); TraceThreadManager manager = curTrace.getThreadManager();
threadTableModel.addAllItems(manager.getAllThreads()); threadTableModel.addAllItems(manager.getAllThreads());
updateTimelineMax();
}
protected void updateTimelineMax() {
Trace trace = current.getTrace();
long max = orZero(trace == null ? null : trace.getTimeManager().getMaxSnap());
Lifespan fullRange = Lifespan.span(0, max + 1);
spanRenderer.setFullRange(fullRange);
headerRenderer.setFullRange(fullRange);
threadTable.getTableHeader().repaint();
} }
private void threadRowSelected(ListSelectionEvent e) { private void threadRowSelected(ListSelectionEvent e) {

View file

@ -23,7 +23,8 @@ import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.trace.model.*; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -46,12 +47,12 @@ public class ThreadRow {
public void setName(String name) { public void setName(String name) {
try (Transaction tx = thread.getTrace().openTransaction("Rename thread")) { try (Transaction tx = thread.getTrace().openTransaction("Rename thread")) {
thread.setName(name); thread.setName(0, name);
} }
} }
public String getName() { public String getName() {
return thread.getName(); return thread.getName(0);
} }
private Address computeProgramCounter(DebuggerCoordinates coords) { private Address computeProgramCounter(DebuggerCoordinates coords) {
@ -82,33 +83,19 @@ public class ThreadRow {
return SPLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(), coords); return SPLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(), coords);
} }
public long getCreationSnap() {
return thread.getCreationSnap();
}
// TODO: Use a renderer to make this transformation instead, otherwise sorting is off.
public String getDestructionSnap() {
long snap = thread.getDestructionSnap();
return snap == Long.MAX_VALUE ? "" : Long.toString(snap);
}
public Lifespan getLifespan() {
return thread.getLifespan();
}
public void setComment(String comment) { public void setComment(String comment) {
try (Transaction tx = thread.getTrace().openTransaction("Set thread comment")) { try (Transaction tx = thread.getTrace().openTransaction("Set thread comment")) {
thread.setComment(comment); thread.setComment(0, comment);
} }
} }
public String getComment() { public String getComment() {
return thread.getComment(); return thread.getComment(0);
} }
public ThreadState getState() { public ThreadState getState() {
// TODO: Once transition to TraceRmi is complete, this is all in TraceObjectManager // TODO: Once transition to TraceRmi is complete, this is all in TraceObjectManager
if (!thread.isAlive()) { if (!thread.isValid(Long.MAX_VALUE)) {
return ThreadState.TERMINATED; return ThreadState.TERMINATED;
} }
if (provider.targetService == null) { if (provider.targetService == null) {

View file

@ -48,7 +48,7 @@ public class SnapshotRow {
public String getEventThreadName() { public String getEventThreadName() {
TraceThread thread = snapshot.getEventThread(); TraceThread thread = snapshot.getEventThread();
return thread == null ? "" : thread.getName(); return thread == null ? "" : thread.getName(snapshot.getKey());
} }
public String getSchedule() { public String getSchedule() {

View file

@ -66,7 +66,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
Collection<? extends TraceModule> modules = Collection<? extends TraceModule> modules =
trace.getModuleManager().getModulesAt(snap, first.getMinAddress()); trace.getModuleManager().getModulesAt(snap, first.getMinAddress());
Msg.debug(this, "Disassembling in modules: " + Msg.debug(this, "Disassembling in modules: " +
modules.stream().map(TraceModule::getName).collect(Collectors.joining(","))); modules.stream().map(m -> m.getName(snap)).collect(Collectors.joining(",")));
Set<Mode> modes = modules.stream() Set<Mode> modes = modules.stream()
.map(m -> modeForModule(target, trace, snap, m)) .map(m -> modeForModule(target, trace, snap, m))
.filter(m -> m != Mode.UNK) .filter(m -> m != Mode.UNK)
@ -101,7 +101,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
TraceModule module) { TraceModule module) {
if (target != null && target.getSnap() == snap) { if (target != null && target.getSnap() == snap) {
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
set.add(module.getBase(), module.getBase()); // Recorder should read page set.add(module.getBase(snap), module.getBase(snap)); // Recorder should read page
try { try {
target.readMemory(set, TaskMonitor.DUMMY); target.readMemory(set, TaskMonitor.DUMMY);
trace.flushEvents(); trace.flushEvents();
@ -110,7 +110,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase()); MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase(snap));
try (ByteProvider bp = new MemBufferByteProvider(bufferAt)) { try (ByteProvider bp = new MemBufferByteProvider(bufferAt)) {
PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false); PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false);
NTHeader ntHeader = pe.getNTHeader(); NTHeader ntHeader = pe.getNTHeader();

View file

@ -36,7 +36,8 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
/** /**
* Add an item to enable a target breakpoint * Add an item to enable a target breakpoint
* *
* @param loc the target breakpoint * @param target the target
* @param bpt the target breakpoint
* @return the added item * @return the added item
*/ */
public EnableTargetBreakpointActionItem planEnableTarget(Target target, TraceBreakpoint bpt) { public EnableTargetBreakpointActionItem planEnableTarget(Target target, TraceBreakpoint bpt) {
@ -49,10 +50,11 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
* Add an item to enable an emulated breakpoint * Add an item to enable an emulated breakpoint
* *
* @param bpt the trace breakpoint * @param bpt the trace breakpoint
* @param snap the snap
* @return the added item * @return the added item
*/ */
public EnableEmuBreakpointActionItem planEnableEmu(TraceBreakpoint bpt) { public EnableEmuBreakpointActionItem planEnableEmu(TraceBreakpoint bpt, long snap) {
EnableEmuBreakpointActionItem action = new EnableEmuBreakpointActionItem(bpt); EnableEmuBreakpointActionItem action = new EnableEmuBreakpointActionItem(bpt, snap);
add(action); add(action);
return action; return action;
} }
@ -60,7 +62,8 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
/** /**
* Add an item to disable a target breakpoint * Add an item to disable a target breakpoint
* *
* @param loc the target breakpoint * @param target the target
* @param bpt the target breakpoint
* @return the added item * @return the added item
*/ */
public DisableTargetBreakpointActionItem planDisableTarget(Target target, TraceBreakpoint bpt) { public DisableTargetBreakpointActionItem planDisableTarget(Target target, TraceBreakpoint bpt) {
@ -74,10 +77,11 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
* Add an item to disable an emulated breakpoint * Add an item to disable an emulated breakpoint
* *
* @param bpt the trace breakpoint * @param bpt the trace breakpoint
* @param snap the snap
* @return the added item * @return the added item
*/ */
public DisableEmuBreakpointActionItem planDisableEmu(TraceBreakpoint bpt) { public DisableEmuBreakpointActionItem planDisableEmu(TraceBreakpoint bpt, long snap) {
DisableEmuBreakpointActionItem action = new DisableEmuBreakpointActionItem(bpt); DisableEmuBreakpointActionItem action = new DisableEmuBreakpointActionItem(bpt, snap);
add(action); add(action);
return action; return action;
} }
@ -85,7 +89,8 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
/** /**
* Add an item to delete a target breakpoint * Add an item to delete a target breakpoint
* *
* @param loc the target breakpoint * @param target the target
* @param bpt the target breakpoint
* @return the added item * @return the added item
*/ */
public DeleteTargetBreakpointActionItem planDeleteTarget(Target target, TraceBreakpoint bpt) { public DeleteTargetBreakpointActionItem planDeleteTarget(Target target, TraceBreakpoint bpt) {
@ -98,10 +103,11 @@ public class BreakpointActionSet extends LinkedHashSet<BreakpointActionItem> {
* Add an item to delete an emulated breakpoint * Add an item to delete an emulated breakpoint
* *
* @param bpt the trace breakpoint * @param bpt the trace breakpoint
* @param snap the snap
* @return the added item * @return the added item
*/ */
public DeleteEmuBreakpointActionItem planDeleteEmu(TraceBreakpoint bpt) { public DeleteEmuBreakpointActionItem planDeleteEmu(TraceBreakpoint bpt, long snap) {
DeleteEmuBreakpointActionItem action = new DeleteEmuBreakpointActionItem(bpt); DeleteEmuBreakpointActionItem action = new DeleteEmuBreakpointActionItem(bpt, snap);
add(action); add(action);
return action; return action;
} }

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug.service.breakpoint; package ghidra.app.plugin.core.debug.service.breakpoint;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.function.*; import java.util.function.*;
@ -219,7 +220,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
private void breakpointAdded(TraceBreakpoint tb) { private void breakpointAdded(TraceBreakpoint tb) {
if (!tb.isAlive(info.snap)) { if (!tb.isValid(info.snap)) {
return; return;
} }
try { try {
@ -232,7 +233,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
private void breakpointChanged(TraceBreakpoint tb) { private void breakpointChanged(TraceBreakpoint tb) {
if (!tb.isAlive(info.snap)) { if (!tb.isValid(info.snap)) {
return; return;
} }
try { try {
@ -338,7 +339,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
protected abstract class AbstractInfo { protected abstract class AbstractInfo {
final NavigableMap<Address, Set<LogicalBreakpointInternal>> breakpointsByAddress = final NavigableMap<Address, Set<LogicalBreakpointInternal>> logicalByAddress =
new TreeMap<>(); new TreeMap<>();
public AbstractInfo() { public AbstractInfo() {
@ -350,47 +351,30 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
long length, Collection<TraceBreakpointKind> kinds); long length, Collection<TraceBreakpointKind> kinds);
protected LogicalBreakpointInternal getOrCreateLogicalBreakpointFor(AddCollector a, protected LogicalBreakpointInternal getOrCreateLogicalBreakpointFor(AddCollector a,
Address address, TraceBreakpoint tb) throws TrackedTooSoonException { Address address, TraceBreakpoint tb, long snap) throws TrackedTooSoonException {
Set<LogicalBreakpointInternal> set = Set<LogicalBreakpointInternal> set =
breakpointsByAddress.computeIfAbsent(address, __ -> new HashSet<>()); logicalByAddress.computeIfAbsent(address, __ -> new HashSet<>());
for (LogicalBreakpointInternal lb : set) { for (LogicalBreakpointInternal lb : set) {
if (lb.canMerge(tb)) { if (lb.canMerge(tb, snap)) {
return lb; return lb;
} }
} }
LogicalBreakpointInternal lb = LogicalBreakpointInternal lb =
createLogicalBreakpoint(address, tb.getLength(), tb.getKinds()); createLogicalBreakpoint(address, tb.getLength(snap), tb.getKinds(snap));
set.add(lb); set.add(lb);
a.added(lb); a.added(lb);
return lb; return lb;
} }
protected LogicalBreakpointInternal removeFromLogicalBreakpoint(RemoveCollector r,
Address address, TraceBreakpoint tb) {
Set<LogicalBreakpointInternal> set = breakpointsByAddress.get(address);
if (set == null) {
return null;
}
for (LogicalBreakpointInternal lb : Set.copyOf(set)) {
if (lb.untrackBreakpoint(tb)) {
if (lb.isEmpty()) {
// If being disposed, this info is no longer in the map
removeLogicalBreakpoint(address, lb);
removeLogicalBreakpointGlobally(lb);
r.removed(lb);
}
else {
r.updated(lb);
}
return lb;
}
}
return null;
}
protected boolean removeLogicalBreakpoint(Address address, LogicalBreakpoint lb) { protected boolean removeLogicalBreakpoint(Address address, LogicalBreakpoint lb) {
Set<LogicalBreakpointInternal> set = breakpointsByAddress.get(address); for (TraceBreakpoint tb : lb.getTraceBreakpoints()) {
InfoPerTrace info = traceInfos.get(tb.getTrace());
if (info != null) {
info.logicalByBreakpoint.remove(tb);
}
}
Set<LogicalBreakpointInternal> set = logicalByAddress.get(address);
if (set == null) { if (set == null) {
return false; return false;
} }
@ -398,7 +382,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
return false; return false;
} }
if (set.isEmpty()) { if (set.isEmpty()) {
breakpointsByAddress.remove(address); logicalByAddress.remove(address);
} }
return true; return true;
} }
@ -429,7 +413,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
protected void forgetMismappedBreakpoints(RemoveCollector r, Set<Trace> additionalTraces, protected void forgetMismappedBreakpoints(RemoveCollector r, Set<Trace> additionalTraces,
Set<Program> additionalPrograms) { Set<Program> additionalPrograms) {
for (Set<LogicalBreakpointInternal> set : List.copyOf(breakpointsByAddress.values())) { for (Set<LogicalBreakpointInternal> set : List.copyOf(logicalByAddress.values())) {
for (LogicalBreakpointInternal lb : Set.copyOf(set)) { for (LogicalBreakpointInternal lb : Set.copyOf(set)) {
if (isMismapped(lb)) { if (isMismapped(lb)) {
removeLogicalBreakpointGlobally(lb); removeLogicalBreakpointGlobally(lb);
@ -442,6 +426,8 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
protected class InfoPerTrace extends AbstractInfo { protected class InfoPerTrace extends AbstractInfo {
final Map<TraceBreakpoint, LogicalBreakpointInternal> logicalByBreakpoint = new HashMap<>();
final Trace trace; final Trace trace;
final TraceBreakpointsListener breakpointListener; final TraceBreakpointsListener breakpointListener;
@ -508,9 +494,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
* has changed or become invalid, or because it has no live breakpoint in target mode. * has changed or become invalid, or because it has no live breakpoint in target mode.
*/ */
ControlMode mode = getMode(trace); ControlMode mode = getMode(trace);
for (Set<LogicalBreakpointInternal> set : List.copyOf(breakpointsByAddress.values())) {
for (LogicalBreakpointInternal lb : Set.copyOf(set)) { for (Entry<TraceBreakpoint, LogicalBreakpointInternal> ent : Set
for (TraceBreakpoint tb : Set.copyOf(lb.getTraceBreakpoints(trace))) { .copyOf(logicalByBreakpoint.entrySet())) {
TraceBreakpoint tb = ent.getKey();
LogicalBreakpoint lb = ent.getValue();
if (!mode.useEmulatedBreakpoints() && if (!mode.useEmulatedBreakpoints() &&
(target == null || !target.isBreakpointValid(tb))) { (target == null || !target.isBreakpointValid(tb))) {
forgetTraceBreakpoint(r, tb); forgetTraceBreakpoint(r, tb);
@ -520,7 +508,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
forgetTraceBreakpoint(r, tb); forgetTraceBreakpoint(r, tb);
continue; continue;
} }
if (!tb.isAlive(snap)) { if (!tb.isValid(snap)) {
forgetTraceBreakpoint(r, tb); forgetTraceBreakpoint(r, tb);
continue; continue;
} }
@ -533,8 +521,6 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
} }
} }
}
}
protected void trackTraceBreakpoints(AddCollector a) { protected void trackTraceBreakpoints(AddCollector a) {
ControlMode mode = getMode(trace); ControlMode mode = getMode(trace);
@ -571,7 +557,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
*/ */
return null; return null;
} }
Address minAddress = tb.getMinAddress(); Address minAddress = tb.getMinAddress(snap);
if (minAddress == null) { if (minAddress == null) {
return null; return null;
} }
@ -585,7 +571,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
(target == null || !target.isBreakpointValid(tb))) { (target == null || !target.isBreakpointValid(tb))) {
return; return;
} }
Address traceAddr = tb.getMinAddress(); Address traceAddr = tb.getMinAddress(snap);
if (traceAddr == null) { if (traceAddr == null) {
return; // Will update via breakpointChanged when address is set return; // Will update via breakpointChanged when address is set
} }
@ -593,24 +579,44 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
LogicalBreakpointInternal lb; LogicalBreakpointInternal lb;
if (progLoc != null) { if (progLoc != null) {
InfoPerProgram progInfo = programInfos.get(progLoc.getProgram()); InfoPerProgram progInfo = programInfos.get(progLoc.getProgram());
lb = progInfo.getOrCreateLogicalBreakpointFor(a, progLoc.getByteAddress(), tb); lb = progInfo.getOrCreateLogicalBreakpointFor(a, progLoc.getByteAddress(), tb,
snap);
} }
else { else {
lb = getOrCreateLogicalBreakpointFor(a, traceAddr, tb); lb = getOrCreateLogicalBreakpointFor(a, traceAddr, tb, snap);
} }
assert breakpointsByAddress.get(traceAddr).contains(lb); assert logicalByAddress.get(traceAddr).contains(lb);
logicalByBreakpoint.put(tb, lb);
if (lb.trackBreakpoint(tb) || forceUpdate) { if (lb.trackBreakpoint(tb) || forceUpdate) {
a.updated(lb); a.updated(lb);
} }
} }
protected LogicalBreakpointInternal removeFromLogicalBreakpoint(RemoveCollector r,
TraceBreakpoint tb) {
LogicalBreakpointInternal lb = logicalByBreakpoint.remove(tb);
if (lb == null || !lb.untrackBreakpoint(tb)) {
return null;
}
if (lb.isEmpty()) {
// If being disposed, this info is no longer in the map
removeLogicalBreakpoint(lb.getTraceAddress(trace), lb);
removeLogicalBreakpointGlobally(lb);
r.removed(lb);
}
else {
r.updated(lb);
}
return lb;
}
protected void forgetTraceBreakpoint(RemoveCollector r, TraceBreakpoint tb) { protected void forgetTraceBreakpoint(RemoveCollector r, TraceBreakpoint tb) {
LogicalBreakpointInternal lb = removeFromLogicalBreakpoint(r, tb.getMinAddress(), tb); LogicalBreakpointInternal lb = removeFromLogicalBreakpoint(r, tb);
if (lb == null) { if (lb == null) {
return; // Warnings already logged return; // Warnings already logged
} }
assert lb.isEmpty() == (breakpointsByAddress.get(tb.getMinAddress()) == null || assert lb.isEmpty() == (logicalByAddress.get(lb.getTraceAddress(trace)) == null ||
!breakpointsByAddress.get(tb.getMinAddress()).contains(lb)); !logicalByAddress.get(tb.getMinAddress(snap)).contains(lb));
} }
public TraceLocation toDynamicLocation(ProgramLocation loc) { public TraceLocation toDynamicLocation(ProgramLocation loc) {
@ -641,7 +647,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
lb.setTraceAddress(ti.trace, loc.getAddress()); lb.setTraceAddress(ti.trace, loc.getAddress());
lb.setTarget(ti.trace, ti.target); lb.setTarget(ti.trace, ti.target);
ti.breakpointsByAddress.computeIfAbsent(loc.getAddress(), __ -> new HashSet<>()) ti.logicalByAddress.computeIfAbsent(loc.getAddress(), __ -> new HashSet<>())
.add(lb); .add(lb);
} }
} }
@ -659,7 +665,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
Bookmark pb) { Bookmark pb) {
Address address = pb.getAddress(); Address address = pb.getAddress();
Set<LogicalBreakpointInternal> set = Set<LogicalBreakpointInternal> set =
breakpointsByAddress.computeIfAbsent(address, __ -> new HashSet<>()); logicalByAddress.computeIfAbsent(address, __ -> new HashSet<>());
for (LogicalBreakpointInternal lb : set) { for (LogicalBreakpointInternal lb : set) {
if (lb.canMerge(program, pb)) { if (lb.canMerge(program, pb)) {
return lb; return lb;
@ -676,7 +682,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
protected LogicalBreakpointInternal removeFromLogicalBreakpoint(RemoveCollector r, protected LogicalBreakpointInternal removeFromLogicalBreakpoint(RemoveCollector r,
Bookmark pb, boolean forChange) { Bookmark pb, boolean forChange) {
Address address = pb.getAddress(); Address address = pb.getAddress();
Set<LogicalBreakpointInternal> set = breakpointsByAddress.get(address); Set<LogicalBreakpointInternal> set = logicalByAddress.get(address);
if (set == null) { if (set == null) {
Msg.error(this, "Breakpoint " + pb + " was not tracked before removal!"); Msg.error(this, "Breakpoint " + pb + " was not tracked before removal!");
return null; return null;
@ -731,7 +737,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
* NOTE: A change in the program (other than bookmark address), should not affect the * NOTE: A change in the program (other than bookmark address), should not affect the
* mapped trace addresses. That would require a change in the trace. * mapped trace addresses. That would require a change in the trace.
*/ */
for (Set<LogicalBreakpointInternal> set : List.copyOf(breakpointsByAddress.values())) { for (Set<LogicalBreakpointInternal> set : List.copyOf(logicalByAddress.values())) {
for (LogicalBreakpointInternal lb : Set.copyOf(set)) { for (LogicalBreakpointInternal lb : Set.copyOf(set)) {
Bookmark pb = lb.getProgramBookmark(); Bookmark pb = lb.getProgramBookmark();
if (pb == null) { if (pb == null) {
@ -765,7 +771,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
protected void trackProgramBreakpoint(AddCollector a, Bookmark pb) { protected void trackProgramBreakpoint(AddCollector a, Bookmark pb) {
LogicalBreakpointInternal lb = getOrCreateLogicalBreakpointFor(a, pb); LogicalBreakpointInternal lb = getOrCreateLogicalBreakpointFor(a, pb);
assert breakpointsByAddress.get(pb.getAddress()).contains(lb); assert logicalByAddress.get(pb.getAddress()).contains(lb);
if (lb.trackBreakpoint(pb)) { if (lb.trackBreakpoint(pb)) {
a.updated(lb); a.updated(lb);
} }
@ -781,7 +787,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
private boolean isConsistentAfterRemoval(Bookmark pb, LogicalBreakpointInternal lb, private boolean isConsistentAfterRemoval(Bookmark pb, LogicalBreakpointInternal lb,
boolean forChange) { boolean forChange) {
Set<LogicalBreakpointInternal> present = breakpointsByAddress.get(pb.getAddress()); Set<LogicalBreakpointInternal> present = logicalByAddress.get(pb.getAddress());
boolean shouldBeAbsent = lb.isEmpty() && !forChange; boolean shouldBeAbsent = lb.isEmpty() && !forChange;
boolean isAbsent = present == null || !present.contains(lb); boolean isAbsent = present == null || !present.contains(lb);
return shouldBeAbsent == isAbsent; return shouldBeAbsent == isAbsent;
@ -967,7 +973,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
tInfo.dispose(c.r); tInfo.dispose(c.r);
for (InfoPerProgram pInfo : programInfos.values()) { for (InfoPerProgram pInfo : programInfos.values()) {
for (Set<LogicalBreakpointInternal> set : pInfo.breakpointsByAddress.values()) { for (Set<LogicalBreakpointInternal> set : pInfo.logicalByAddress.values()) {
for (LogicalBreakpointInternal lb : set) { for (LogicalBreakpointInternal lb : set) {
lb.removeTrace(trace); lb.removeTrace(trace);
c.a.updated(lb); c.a.updated(lb);
@ -1021,7 +1027,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
synchronized (lock) { synchronized (lock) {
Set<LogicalBreakpoint> records = new HashSet<>(); Set<LogicalBreakpoint> records = new HashSet<>();
for (AbstractInfo info : allInfos) { for (AbstractInfo info : allInfos) {
for (Set<LogicalBreakpointInternal> recsAtAddress : info.breakpointsByAddress for (Set<LogicalBreakpointInternal> recsAtAddress : info.logicalByAddress
.values()) { .values()) {
records.addAll(recsAtAddress); records.addAll(recsAtAddress);
} }
@ -1046,7 +1052,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
if (info == null) { if (info == null) {
return Collections.emptyNavigableMap(); return Collections.emptyNavigableMap();
} }
return copyOf(info.breakpointsByAddress); return copyOf(info.logicalByAddress);
} }
} }
@ -1057,12 +1063,12 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
if (info == null) { if (info == null) {
return Collections.emptyNavigableMap(); return Collections.emptyNavigableMap();
} }
return copyOf(info.breakpointsByAddress); return copyOf(info.logicalByAddress);
} }
} }
protected Set<LogicalBreakpoint> doGetBreakpointsAt(AbstractInfo info, Address address) { protected Set<LogicalBreakpoint> doGetBreakpointsAt(AbstractInfo info, Address address) {
Set<LogicalBreakpointInternal> set = info.breakpointsByAddress.get(address); Set<LogicalBreakpointInternal> set = info.logicalByAddress.get(address);
if (set == null) { if (set == null) {
return Set.of(); return Set.of();
} }
@ -1097,7 +1103,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
public LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt) { public LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt) {
Trace trace = bpt.getTrace(); Trace trace = bpt.getTrace();
synchronized (lock) { synchronized (lock) {
Address address = bpt.getMinAddress(); InfoPerTrace info = traceInfos.get(trace);
if (info == null) {
return null;
}
Address address = bpt.getMinAddress(info.snap);
if (address == null) { if (address == null) {
return null; return null;
} }
@ -1250,9 +1260,16 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
void accept(BreakpointActionSet actions, Target target, TraceBreakpoint tb); void accept(BreakpointActionSet actions, Target target, TraceBreakpoint tb);
} }
interface EmuBreakpointConsumer {
void accept(BreakpointActionSet actions, TraceBreakpoint tb, long snap);
}
interface ProgramBreakpointConsumer {
void accept(LogicalBreakpoint lb);
}
private void planActOnLoc(BreakpointActionSet actions, TraceBreakpoint tb, private void planActOnLoc(BreakpointActionSet actions, TraceBreakpoint tb,
TargetBreakpointConsumer targetBptConsumer, TargetBreakpointConsumer targetBptConsumer, EmuBreakpointConsumer emuLocConsumer) {
BiConsumer<BreakpointActionSet, TraceBreakpoint> emuLocConsumer) {
ControlMode mode = getMode(tb.getTrace()); ControlMode mode = getMode(tb.getTrace());
if (mode.useEmulatedBreakpoints()) { if (mode.useEmulatedBreakpoints()) {
planActOnLocEmu(actions, tb, emuLocConsumer); planActOnLocEmu(actions, tb, emuLocConsumer);
@ -1272,14 +1289,19 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
} }
private void planActOnLocEmu(BreakpointActionSet actions, TraceBreakpoint tb, private void planActOnLocEmu(BreakpointActionSet actions, TraceBreakpoint tb,
BiConsumer<BreakpointActionSet, TraceBreakpoint> emuLocConsumer) { EmuBreakpointConsumer emuLocConsumer) {
emuLocConsumer.accept(actions, tb); InfoPerTrace info = traceInfos.get(tb.getTrace());
if (info == null) {
Msg.error(this, "No longer tracking " + tb);
return;
}
emuLocConsumer.accept(actions, tb, info.snap);
} }
protected CompletableFuture<Void> actOnLocs(Collection<TraceBreakpoint> col, protected CompletableFuture<Void> actOnLocs(Collection<TraceBreakpoint> col,
TargetBreakpointConsumer targetBptConsumer, TargetBreakpointConsumer targetBptConsumer,
BiConsumer<BreakpointActionSet, TraceBreakpoint> emuLocConsumer, EmuBreakpointConsumer emuLocConsumer,
Consumer<LogicalBreakpoint> progConsumer) { ProgramBreakpointConsumer progConsumer) {
BreakpointActionSet actions = new BreakpointActionSet(); BreakpointActionSet actions = new BreakpointActionSet();
for (TraceBreakpoint tb : col) { for (TraceBreakpoint tb : col) {
LogicalBreakpoint lb = getBreakpoint(tb); LogicalBreakpoint lb = getBreakpoint(tb);

View file

@ -21,17 +21,18 @@ import db.Transaction;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
public record DeleteEmuBreakpointActionItem(TraceBreakpoint bpt) implements BreakpointActionItem { public record DeleteEmuBreakpointActionItem(TraceBreakpoint bpt, long snap)
implements BreakpointActionItem {
@Override @Override
public CompletableFuture<Void> execute() { public CompletableFuture<Void> execute() {
try (Transaction tx = try (Transaction tx =
bpt.getTrace().openTransaction("Delete Emulated Breakpoint")) { bpt.getTrace().openTransaction("Delete Emulated Breakpoint")) {
String emuName = PlaceEmuBreakpointActionItem.createName(bpt.getMinAddress()); String emuName = PlaceEmuBreakpointActionItem.createName(bpt.getMinAddress(snap));
if (bpt.getPath().contains(emuName)) { if (bpt.getPath().contains(emuName)) {
bpt.delete(); bpt.delete();
} }
else { else {
bpt.setEmuEnabled(false); bpt.setEmuEnabled(snap, false);
} }
} }
return AsyncUtils.nil(); return AsyncUtils.nil();

View file

@ -21,13 +21,13 @@ import db.Transaction;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
public record DisableEmuBreakpointActionItem(TraceBreakpoint bpt) public record DisableEmuBreakpointActionItem(TraceBreakpoint bpt, long snap)
implements BreakpointActionItem { implements BreakpointActionItem {
@Override @Override
public CompletableFuture<Void> execute() { public CompletableFuture<Void> execute() {
try (Transaction tx = try (Transaction tx =
bpt.getTrace().openTransaction("Disable Emulated Breakpoint")) { bpt.getTrace().openTransaction("Disable Emulated Breakpoint")) {
bpt.setEmuEnabled(false); bpt.setEmuEnabled(snap, false);
} }
return AsyncUtils.nil(); return AsyncUtils.nil();
} }

View file

@ -21,12 +21,13 @@ import db.Transaction;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
public record EnableEmuBreakpointActionItem(TraceBreakpoint bpt) implements BreakpointActionItem { public record EnableEmuBreakpointActionItem(TraceBreakpoint bpt, long snap)
implements BreakpointActionItem {
@Override @Override
public CompletableFuture<Void> execute() { public CompletableFuture<Void> execute() {
try (Transaction tx = try (Transaction tx =
bpt.getTrace().openTransaction("Enable Emulated Breakpoint")) { bpt.getTrace().openTransaction("Enable Emulated Breakpoint")) {
bpt.setEmuEnabled(true); bpt.setEmuEnabled(snap, true);
} }
return AsyncUtils.nil(); return AsyncUtils.nil();
} }

View file

@ -56,10 +56,11 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint {
* breakpoint history provider handles displaying records from the past, including dead traces. * breakpoint history provider handles displaying records from the past, including dead traces.
* *
* @param breakpoint the trace breakpoint to check * @param breakpoint the trace breakpoint to check
* @param snap the snap
* @return true if it can be aggregated. * @return true if it can be aggregated.
* @throws TrackedTooSoonException if the containing trace is still being added to the manager * @throws TrackedTooSoonException if the containing trace is still being added to the manager
*/ */
boolean canMerge(TraceBreakpoint breakpoint) throws TrackedTooSoonException; boolean canMerge(TraceBreakpoint breakpoint, long snap) throws TrackedTooSoonException;
boolean trackBreakpoint(Bookmark bookmark); boolean trackBreakpoint(Bookmark bookmark);
@ -74,7 +75,6 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint {
* *
* @param actions the destination action set (plan) * @param actions the destination action set (plan)
* @param trace a trace, if actions should be limited to the given trace * @param trace a trace, if actions should be limited to the given trace
* @return a future which completes when the actions are populated
*/ */
void planEnable(BreakpointActionSet actions, Trace trace); void planEnable(BreakpointActionSet actions, Trace trace);
@ -83,7 +83,6 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint {
* *
* @param actions the destination action set (plan) * @param actions the destination action set (plan)
* @param trace a trace, if actions should be limited to the given trace * @param trace a trace, if actions should be limited to the given trace
* @return a future which completes when the actions are populated
*/ */
void planDisable(BreakpointActionSet actions, Trace trace); void planDisable(BreakpointActionSet actions, Trace trace);
@ -92,7 +91,6 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint {
* *
* @param actions the destination action set (plan) * @param actions the destination action set (plan)
* @param trace a trace, if actions should be limited to the given trace * @param trace a trace, if actions should be limited to the given trace
* @return a future which completes when the actions are populated
*/ */
void planDelete(BreakpointActionSet actions, Trace trace); void planDelete(BreakpointActionSet actions, Trace trace);
} }

View file

@ -278,8 +278,8 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal {
} }
@Override @Override
public boolean canMerge(TraceBreakpoint breakpoint) { public boolean canMerge(TraceBreakpoint breakpoint, long snap) {
if (!Objects.equals(kinds, breakpoint.getKinds())) { if (!Objects.equals(kinds, breakpoint.getKinds(snap))) {
return false; return false;
} }
return breaks.canMerge(breakpoint); return breaks.canMerge(breakpoint);

View file

@ -478,7 +478,7 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal {
} }
@Override @Override
public boolean canMerge(TraceBreakpoint breakpoint) throws TrackedTooSoonException { public boolean canMerge(TraceBreakpoint breakpoint, long snap) throws TrackedTooSoonException {
TraceBreakpointSet breaks; TraceBreakpointSet breaks;
synchronized (traceBreaks) { synchronized (traceBreaks) {
breaks = traceBreaks.get(breakpoint.getTrace()); breaks = traceBreaks.get(breakpoint.getTrace());
@ -494,10 +494,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal {
*/ */
throw new TrackedTooSoonException(); throw new TrackedTooSoonException();
} }
if (length != breakpoint.getLength()) { if (length != breakpoint.getLength(snap)) {
return false; return false;
} }
if (!Objects.equals(kinds, breakpoint.getKinds())) { if (!Objects.equals(kinds, breakpoint.getKinds(snap))) {
return false; return false;
} }
return breaks.canMerge(breakpoint); return breaks.canMerge(breakpoint);

View file

@ -112,8 +112,8 @@ public record PlaceEmuBreakpointActionItem(Trace trace, long snap, Address addre
TraceBreakpoint bpt = trace.getBreakpointManager() TraceBreakpoint bpt = trace.getBreakpointManager()
.addBreakpoint(computePath(), Lifespan.at(snap), .addBreakpoint(computePath(), Lifespan.at(snap),
BreakpointActionItem.range(address, length), Set.of(), kinds, false, null); BreakpointActionItem.range(address, length), Set.of(), kinds, false, null);
bpt.setName(createName(address)); bpt.setName(snap, createName(address));
bpt.setEmuSleigh(emuSleigh); bpt.setEmuSleigh(snap, emuSleigh);
return AsyncUtils.nil(); return AsyncUtils.nil();
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {

View file

@ -192,9 +192,10 @@ class TraceBreakpointSet {
*/ */
public String computeSleigh() { public String computeSleigh() {
String sleigh = null; String sleigh = null;
long snap = getSnap();
synchronized (breakpoints) { synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
String s = bpt.obj.getEmuSleigh(); String s = bpt.obj.getEmuSleigh(snap);
if (sleigh != null && !sleigh.equals(s)) { if (sleigh != null && !sleigh.equals(s)) {
return null; return null;
} }
@ -211,10 +212,11 @@ class TraceBreakpointSet {
*/ */
public void setEmuSleigh(String emuSleigh) { public void setEmuSleigh(String emuSleigh) {
this.emuSleigh = emuSleigh; this.emuSleigh = emuSleigh;
long snap = getSnap();
try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) { try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) {
synchronized (breakpoints) { synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
bpt.obj.setEmuSleigh(emuSleigh); bpt.obj.setEmuSleigh(snap, emuSleigh);
} }
} }
} }
@ -253,9 +255,10 @@ class TraceBreakpointSet {
* @return true if the set actually changed as a result * @return true if the set actually changed as a result
*/ */
public boolean add(TraceBreakpoint bpt) { public boolean add(TraceBreakpoint bpt) {
if (SleighUtils.UNCONDITIONAL_BREAK.equals(bpt.getEmuSleigh()) && emuSleigh != null) { long snap = getSnap();
if (SleighUtils.UNCONDITIONAL_BREAK.equals(bpt.getEmuSleigh(snap)) && emuSleigh != null) {
try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) { try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) {
bpt.setEmuSleigh(emuSleigh); bpt.setEmuSleigh(snap, emuSleigh);
} }
} }
synchronized (breakpoints) { synchronized (breakpoints) {
@ -276,7 +279,7 @@ class TraceBreakpointSet {
if (trace != bpt.getTrace()) { if (trace != bpt.getTrace()) {
return false; return false;
} }
if (!address.equals(bpt.getMinAddress())) { if (!address.equals(bpt.getMinAddress(getSnap()))) {
return false; return false;
} }
return true; return true;
@ -365,7 +368,7 @@ class TraceBreakpointSet {
private void planEnableEmu(BreakpointActionSet actions) { private void planEnableEmu(BreakpointActionSet actions) {
synchronized (breakpoints) { synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planEnableEmu(bpt.obj); actions.planEnableEmu(bpt.obj, getSnap());
} }
} }
} }
@ -399,7 +402,7 @@ class TraceBreakpointSet {
private void planDisableEmu(BreakpointActionSet actions) { private void planDisableEmu(BreakpointActionSet actions) {
synchronized (breakpoints) { synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDisableEmu(bpt.obj); actions.planDisableEmu(bpt.obj, getSnap());
} }
} }
} }
@ -433,7 +436,7 @@ class TraceBreakpointSet {
private void planDeleteEmu(BreakpointActionSet actions) { private void planDeleteEmu(BreakpointActionSet actions) {
synchronized (breakpoints) { synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDeleteEmu(bpt.obj); actions.planDeleteEmu(bpt.obj, getSnap());
} }
} }
} }

View file

@ -664,30 +664,30 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
if (!bpt.isEmuEnabled(snap)) { if (!bpt.isEmuEnabled(snap)) {
continue; continue;
} }
Set<TraceBreakpointKind> kinds = bpt.getKinds(); Set<TraceBreakpointKind> kinds = bpt.getKinds(snap);
boolean isExecute = boolean isExecute =
kinds.contains(TraceBreakpointKind.HW_EXECUTE) || kinds.contains(TraceBreakpointKind.HW_EXECUTE) ||
kinds.contains(TraceBreakpointKind.SW_EXECUTE); kinds.contains(TraceBreakpointKind.SW_EXECUTE);
boolean isRead = kinds.contains(TraceBreakpointKind.READ); boolean isRead = kinds.contains(TraceBreakpointKind.READ);
boolean isWrite = kinds.contains(TraceBreakpointKind.WRITE); boolean isWrite = kinds.contains(TraceBreakpointKind.WRITE);
if (isExecute) { if (isExecute) {
Address minAddress = bpt.getMinAddress(snap);
try { try {
emu.inject(bpt.getMinAddress(), bpt.getEmuSleigh()); emu.inject(minAddress, bpt.getEmuSleigh(snap));
} }
catch (Exception e) { // This is a bit broad... catch (Exception e) { // This is a bit broad...
Msg.error(this, Msg.error(this, "Error compiling breakpoint Sleigh at " + minAddress, e);
"Error compiling breakpoint Sleigh at " + bpt.getMinAddress(), e); emu.inject(minAddress, "emu_injection_err();");
emu.inject(bpt.getMinAddress(), "emu_injection_err();");
} }
} }
if (isRead && isWrite) { if (isRead && isWrite) {
emu.addAccessBreakpoint(bpt.getRange(), AccessKind.RW); emu.addAccessBreakpoint(bpt.getRange(snap), AccessKind.RW);
} }
else if (isRead) { else if (isRead) {
emu.addAccessBreakpoint(bpt.getRange(), AccessKind.R); emu.addAccessBreakpoint(bpt.getRange(snap), AccessKind.R);
} }
else if (isWrite) { else if (isWrite) {
emu.addAccessBreakpoint(bpt.getRange(), AccessKind.W); emu.addAccessBreakpoint(bpt.getRange(snap), AccessKind.W);
} }
} }
} }

View file

@ -447,14 +447,14 @@ public class ProgramEmulationUtils {
} }
PathPattern patRegion = computePatternRegion(trace); PathPattern patRegion = computePatternRegion(trace);
String threadName = KeyPath.parseIfIndex(thread.getName()); String threadName = KeyPath.parseIfIndex(thread.getName(snap));
String path = patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName) String path = patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName)
.getSingletonPath() .getSingletonPath()
.toString(); .toString();
TraceMemoryManager mm = trace.getMemoryManager(); TraceMemoryManager mm = trace.getMemoryManager();
try { try {
return mm.createRegion(path, snap, alloc, return mm.createRegion(path, snap, alloc,
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(); TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(snap);
} }
catch (TraceOverlappedRegionException e) { catch (TraceOverlappedRegionException e) {
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict", Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
@ -515,7 +515,7 @@ public class ProgramEmulationUtils {
TraceMemoryManager mm = trace.getMemoryManager(); TraceMemoryManager mm = trace.getMemoryManager();
try { try {
return mm.createRegion(path, snap, alloc, return mm.createRegion(path, snap, alloc,
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(); TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(snap);
} }
catch (TraceOverlappedRegionException e) { catch (TraceOverlappedRegionException e) {
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict", Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
@ -581,13 +581,13 @@ public class ProgramEmulationUtils {
for (AddressRange candidate : left) { for (AddressRange candidate : left) {
if (Long.compareUnsigned(candidate.getLength(), size) >= 0) { if (Long.compareUnsigned(candidate.getLength(), size) >= 0) {
AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size); AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size);
String threadName = KeyPath.parseIfIndex(thread.getName()); String threadName = KeyPath.parseIfIndex(thread.getName(snap));
String path = patRegion String path = patRegion
.applyKeys(alloc.getMinAddress() + "-stack " + threadName) .applyKeys(alloc.getMinAddress() + "-stack " + threadName)
.getSingletonPath() .getSingletonPath()
.toString(); .toString();
return mm.createRegion(path, snap, alloc, return mm.createRegion(path, snap, alloc,
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(); TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(snap);
} }
} }
} }
@ -682,6 +682,13 @@ public class ProgramEmulationUtils {
/** /**
* Same as {@link #doLaunchEmulationThread(Trace, long, Program, Address, Address)}, but within * Same as {@link #doLaunchEmulationThread(Trace, long, Program, Address, Address)}, but within
* a transaction * a transaction
*
* @param trace the trace to contain the new thread
* @param snap the creation snap for the new thread
* @param program the program whose context to use for initial register values
* @param tracePc the program counter in the trace's memory map
* @param programPc the program counter in the program's memory map
* @return the new thread
*/ */
public static TraceThread launchEmulationThread(Trace trace, long snap, Program program, public static TraceThread launchEmulationThread(Trace trace, long snap, Program program,
Address tracePc, Address programPc) { Address tracePc, Address programPc) {

View file

@ -25,12 +25,15 @@ import ghidra.trace.model.*;
public abstract class AbstractMapEntry<T, P> implements MapEntry<T, P> { public abstract class AbstractMapEntry<T, P> implements MapEntry<T, P> {
protected final Trace fromTrace; protected final Trace fromTrace;
protected final T fromObject; protected final T fromObject;
protected final long snap;
protected Program toProgram; protected Program toProgram;
protected P toObject; protected P toObject;
public AbstractMapEntry(Trace fromTrace, T fromObject, Program toProgram, P toObject) { public AbstractMapEntry(Trace fromTrace, T fromObject, long snap, Program toProgram,
P toObject) {
this.fromTrace = fromTrace; this.fromTrace = fromTrace;
this.fromObject = fromObject; this.fromObject = fromObject;
this.snap = snap;
this.toProgram = toProgram; this.toProgram = toProgram;
this.toObject = toObject; this.toObject = toObject;
} }
@ -60,6 +63,11 @@ public abstract class AbstractMapEntry<T, P> implements MapEntry<T, P> {
return fromObject; return fromObject;
} }
@Override
public Lifespan getFromLifespan() {
return Lifespan.nowOn(snap);
}
@Override @Override
public TraceLocation getFromTraceLocation() { public TraceLocation getFromTraceLocation() {
return new DefaultTraceLocation(fromTrace, null, getFromLifespan(), return new DefaultTraceLocation(fromTrace, null, getFromLifespan(),

View file

@ -30,14 +30,18 @@ public abstract class AbstractMapProposal<T, P, E extends MapEntry<T, P>>
protected abstract static class Matcher<T, P> { protected abstract static class Matcher<T, P> {
protected final T fromObject; protected final T fromObject;
protected final long snap;
protected final P toObject; protected final P toObject;
protected final AddressRange fromRange; protected final AddressRange fromRange;
protected final AddressRange toRange; protected final AddressRange toRange;
protected final double score; protected final double score;
protected Matcher(T fromObject, P toObject) { protected Matcher(T fromObject, long snap, P toObject) {
this.fromObject = fromObject; this.fromObject = fromObject;
this.snap = snap;
this.toObject = toObject; this.toObject = toObject;
this.fromRange = fromObject == null ? null : getFromRange(); this.fromRange = fromObject == null ? null : getFromRange();
this.toRange = toObject == null ? null : getToRange(); this.toRange = toObject == null ? null : getToRange();
this.score = fromObject == null || toObject == null ? 0 : computeScore(); this.score = fromObject == null || toObject == null ? 0 : computeScore();
@ -80,9 +84,15 @@ public abstract class AbstractMapProposal<T, P, E extends MapEntry<T, P>>
} }
protected static abstract class MatcherMap<K, T, P, M extends Matcher<T, P>> { protected static abstract class MatcherMap<K, T, P, M extends Matcher<T, P>> {
protected final long snap;
protected Map<K, Set<T>> fromsByJoin = new LinkedHashMap<>(); protected Map<K, Set<T>> fromsByJoin = new LinkedHashMap<>();
protected Map<T, M> map = new LinkedHashMap<>(); protected Map<T, M> map = new LinkedHashMap<>();
public MatcherMap(long snap) {
this.snap = snap;
}
protected abstract M newMatcher(T fromObject, P toObject); protected abstract M newMatcher(T fromObject, P toObject);
protected abstract K getFromJoinKey(T fromObject); protected abstract K getFromJoinKey(T fromObject);

View file

@ -72,15 +72,22 @@ public enum DebuggerStaticMappingProposals {
} }
protected interface ProposalGenerator<F, T, MP extends MapProposal<?, ?, ?>> { protected interface ProposalGenerator<F, T, MP extends MapProposal<?, ?, ?>> {
MP proposeMap(F from, T to); MP proposeMap(F from, long snap, T to);
MP proposeBestMap(F from, Collection<? extends T> tos); MP proposeBestMap(F from, long snap, Collection<? extends T> tos);
Map<F, MP> proposeBestMaps(Collection<? extends F> froms, Collection<? extends T> tos); Map<F, MP> proposeBestMaps(Collection<? extends F> froms, long snap,
Collection<? extends T> tos);
} }
protected abstract static class AbstractProposalGenerator // protected abstract static class AbstractProposalGenerator //
<F, T, J, MP extends MapProposal<?, ?, ?>> { <F, T, J, MP extends MapProposal<?, ?, ?>> {
protected final long snap;
public AbstractProposalGenerator(long snap) {
this.snap = snap;
}
protected abstract MP proposeMap(F from, T to); protected abstract MP proposeMap(F from, T to);
protected abstract J computeFromJoinKey(F from); protected abstract J computeFromJoinKey(F from);
@ -133,29 +140,31 @@ public enum DebuggerStaticMappingProposals {
} }
@Override @Override
public ModuleMapProposal proposeMap(TraceModule from, Program to) { public ModuleMapProposal proposeMap(TraceModule from, long snap, Program to) {
return new DefaultModuleMapProposal(from, to); return new DefaultModuleMapProposal(from, snap, to);
} }
@Override @Override
public ModuleMapProposal proposeBestMap(TraceModule from, public ModuleMapProposal proposeBestMap(TraceModule from, long snap,
Collection<? extends Program> tos) { Collection<? extends Program> tos) {
Collection<IndexEntry> entries = indexer.filter(indexer.getBestEntries(from), tos); Collection<IndexEntry> entries =
DomainFile df = indexer.getBestMatch(from, null, entries); indexer.filter(indexer.getBestEntries(from, snap), tos);
DomainFile df = indexer.getBestMatch(from, snap, null, entries);
if (df == null) { if (df == null) {
return null; return null;
} }
try (PeekOpenedDomainObject peek = new PeekOpenedDomainObject(df)) { try (PeekOpenedDomainObject peek = new PeekOpenedDomainObject(df)) {
return proposeMap(from, (Program) peek.object); return proposeMap(from, snap, (Program) peek.object);
} }
} }
@Override @Override
public Map<TraceModule, ModuleMapProposal> proposeBestMaps( public Map<TraceModule, ModuleMapProposal> proposeBestMaps(
Collection<? extends TraceModule> froms, Collection<? extends Program> tos) { Collection<? extends TraceModule> froms, long snap,
Collection<? extends Program> tos) {
Map<TraceModule, ModuleMapProposal> result = new LinkedHashMap<>(); Map<TraceModule, ModuleMapProposal> result = new LinkedHashMap<>();
for (TraceModule f : froms) { for (TraceModule f : froms) {
ModuleMapProposal map = proposeBestMap(f, tos); ModuleMapProposal map = proposeBestMap(f, snap, tos);
if (map != null) { if (map != null) {
result.put(f, map); result.put(f, map);
} }
@ -164,16 +173,20 @@ public enum DebuggerStaticMappingProposals {
} }
} }
protected static class SectionMapProposalGenerator public static class SectionMapProposalGenerator
extends AbstractProposalGenerator<TraceModule, Program, String, SectionMapProposal> { extends AbstractProposalGenerator<TraceModule, Program, String, SectionMapProposal> {
public SectionMapProposalGenerator(long snap) {
super(snap);
}
@Override @Override
protected SectionMapProposal proposeMap(TraceModule from, Program to) { protected SectionMapProposal proposeMap(TraceModule from, Program to) {
return new DefaultSectionMapProposal(from, to); return new DefaultSectionMapProposal(from, snap, to);
} }
@Override @Override
protected String computeFromJoinKey(TraceModule from) { protected String computeFromJoinKey(TraceModule from) {
return getLastLower(from.getName()); return getLastLower(from.getName(snap));
} }
@Override @Override
@ -182,14 +195,18 @@ public enum DebuggerStaticMappingProposals {
} }
} }
protected static class RegionMapProposalGenerator extends public static class RegionMapProposalGenerator extends
AbstractProposalGenerator<Collection<TraceMemoryRegion>, Program, Set<String>, // AbstractProposalGenerator<Collection<TraceMemoryRegion>, Program, Set<String>, //
RegionMapProposal> { RegionMapProposal> {
public RegionMapProposalGenerator(long snap) {
super(snap);
}
@Override @Override
protected RegionMapProposal proposeMap(Collection<TraceMemoryRegion> from, protected RegionMapProposal proposeMap(Collection<TraceMemoryRegion> from,
Program to) { Program to) {
return new DefaultRegionMapProposal(from, to); return new DefaultRegionMapProposal(from, snap, to);
} }
@Override @Override
@ -206,14 +223,11 @@ public enum DebuggerStaticMappingProposals {
} }
} }
// TODO: Should these also take advantage of the program-module index?
protected static final SectionMapProposalGenerator SECTIONS = new SectionMapProposalGenerator();
protected static final RegionMapProposalGenerator REGIONS = new RegionMapProposalGenerator();
public static RegionMapProposal proposeRegionMap( public static RegionMapProposal proposeRegionMap(
Collection<? extends TraceMemoryRegion> regions, Collection<? extends TraceMemoryRegion> regions, long snap,
Collection<? extends Program> programs) { Collection<? extends Program> programs) {
return REGIONS.proposeBestMap(Collections.unmodifiableCollection(regions), programs); return new RegionMapProposalGenerator(snap)
.proposeBestMap(Collections.unmodifiableCollection(regions), programs);
} }
public static <V, J> Set<Set<V>> groupByComponents(Collection<? extends V> vertices, public static <V, J> Set<Set<V>> groupByComponents(Collection<? extends V> vertices,
@ -248,8 +262,9 @@ public enum DebuggerStaticMappingProposals {
} }
catch (IllegalArgumentException e) { // Parse error catch (IllegalArgumentException e) { // Parse error
Msg.error(DebuggerStaticMappingProposals.class, Msg.error(DebuggerStaticMappingProposals.class,
"Encountered unparsable path: " + region.getName()); "Encountered unparsable path: " + region.getPath());
key = region.getName(); // Not a great fallback, but it'll have to do // Path should always parse in object mode. In legacy mode, snap doesn't matter
key = region.getName(0); // Not a great fallback, but it'll have to do
} }
return Stream.of(key.split("\\s+")) return Stream.of(key.split("\\s+"))
.filter(n -> n.replaceAll("[0-9A-Fa-f]+", "").length() >= 5) .filter(n -> n.replaceAll("[0-9A-Fa-f]+", "").length() >= 5)

View file

@ -29,7 +29,7 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingProposals.ModuleMapProposalGenerator; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingProposals.*;
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils; import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils; import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.services.*; import ghidra.app.services.*;
@ -362,7 +362,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
try (Transaction tx = ent.getKey().openTransaction("Memorize module mapping")) { try (Transaction tx = ent.getKey().openTransaction("Memorize module mapping")) {
for (ModuleMapEntry entry : ent.getValue()) { for (ModuleMapEntry entry : ent.getValue()) {
ProgramModuleIndexer.addModulePaths(entry.getToProgram(), ProgramModuleIndexer.addModulePaths(entry.getToProgram(),
List.of(entry.getModule().getName())); List.of(entry.getModuleName()));
} }
} }
} }
@ -561,71 +561,75 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
} }
@Override @Override
public DomainFile findBestModuleProgram(AddressSpace space, TraceModule module) { public DomainFile findBestModuleProgram(AddressSpace space, TraceModule module, long snap) {
return programModuleIndexer.getBestMatch(space, module, programManager.getCurrentProgram()); return programModuleIndexer.getBestMatch(space, module, snap,
programManager.getCurrentProgram());
} }
@Override @Override
public ModuleMapProposal proposeModuleMap(TraceModule module, Program program) { public ModuleMapProposal proposeModuleMap(TraceModule module, long snap, Program program) {
return moduleMapProposalGenerator.proposeMap(module, program); return moduleMapProposalGenerator.proposeMap(module, snap, program);
} }
@Override @Override
public ModuleMapProposal proposeModuleMap(TraceModule module, public ModuleMapProposal proposeModuleMap(TraceModule module, long snap,
Collection<? extends Program> programs) { Collection<? extends Program> programs) {
return moduleMapProposalGenerator.proposeBestMap(module, orderCurrentFirst(programs)); return moduleMapProposalGenerator.proposeBestMap(module, snap, orderCurrentFirst(programs));
} }
@Override @Override
public Map<TraceModule, ModuleMapProposal> proposeModuleMaps( public Map<TraceModule, ModuleMapProposal> proposeModuleMaps(
Collection<? extends TraceModule> modules, Collection<? extends Program> programs) { Collection<? extends TraceModule> modules, long snap,
return moduleMapProposalGenerator.proposeBestMaps(modules, orderCurrentFirst(programs));
}
@Override
public SectionMapProposal proposeSectionMap(TraceSection section, Program program,
MemoryBlock block) {
return new DefaultSectionMapProposal(section, program, block);
}
@Override
public SectionMapProposal proposeSectionMap(TraceModule module, Program program) {
return DebuggerStaticMappingProposals.SECTIONS.proposeMap(module, program);
}
@Override
public SectionMapProposal proposeSectionMap(TraceModule module,
Collection<? extends Program> programs) { Collection<? extends Program> programs) {
return DebuggerStaticMappingProposals.SECTIONS.proposeBestMap(module, return moduleMapProposalGenerator.proposeBestMaps(modules, snap,
orderCurrentFirst(programs));
}
@Override
public SectionMapProposal proposeSectionMap(TraceSection section, long snap, Program program,
MemoryBlock block) {
return new DefaultSectionMapProposal(section, snap, program, block);
}
@Override
public SectionMapProposal proposeSectionMap(TraceModule module, long snap, Program program) {
return new SectionMapProposalGenerator(snap).proposeMap(module, program);
}
@Override
public SectionMapProposal proposeSectionMap(TraceModule module, long snap,
Collection<? extends Program> programs) {
return new SectionMapProposalGenerator(snap).proposeBestMap(module,
orderCurrentFirst(programs)); orderCurrentFirst(programs));
} }
@Override @Override
public Map<TraceModule, SectionMapProposal> proposeSectionMaps( public Map<TraceModule, SectionMapProposal> proposeSectionMaps(
Collection<? extends TraceModule> modules, Collection<? extends Program> programs) { Collection<? extends TraceModule> modules, long snap,
return DebuggerStaticMappingProposals.SECTIONS.proposeBestMaps(modules, Collection<? extends Program> programs) {
return new SectionMapProposalGenerator(snap).proposeBestMaps(modules,
orderCurrentFirst(programs)); orderCurrentFirst(programs));
} }
@Override @Override
public RegionMapProposal proposeRegionMap(TraceMemoryRegion region, Program program, public RegionMapProposal proposeRegionMap(TraceMemoryRegion region, long snap, Program program,
MemoryBlock block) { MemoryBlock block) {
return new DefaultRegionMapProposal(region, program, block); return new DefaultRegionMapProposal(region, snap, program, block);
} }
@Override @Override
public RegionMapProposal proposeRegionMap(Collection<? extends TraceMemoryRegion> regions, public RegionMapProposal proposeRegionMap(Collection<? extends TraceMemoryRegion> regions,
Program program) { long snap, Program program) {
return DebuggerStaticMappingProposals.REGIONS return new RegionMapProposalGenerator(snap)
.proposeMap(Collections.unmodifiableCollection(regions), program); .proposeMap(Collections.unmodifiableCollection(regions), program);
} }
@Override @Override
public Map<Collection<TraceMemoryRegion>, RegionMapProposal> proposeRegionMaps( public Map<Collection<TraceMemoryRegion>, RegionMapProposal> proposeRegionMaps(
Collection<? extends TraceMemoryRegion> regions, Collection<? extends TraceMemoryRegion> regions, long snap,
Collection<? extends Program> programs) { Collection<? extends Program> programs) {
Set<Set<TraceMemoryRegion>> groups = Set<Set<TraceMemoryRegion>> groups =
DebuggerStaticMappingProposals.groupRegionsByLikelyModule(regions); DebuggerStaticMappingProposals.groupRegionsByLikelyModule(regions);
return DebuggerStaticMappingProposals.REGIONS.proposeBestMaps(groups, programs); return new RegionMapProposalGenerator(snap).proposeBestMaps(groups, programs);
} }
} }

View file

@ -353,10 +353,10 @@ public enum DebuggerStaticMappingUtils {
if (trace == null) { if (trace == null) {
return null; return null;
} }
for (TraceModule module : trace.getModuleManager() long snap = coordinates.getSnap();
.getModulesAt(coordinates.getSnap(), pc)) { for (TraceModule module : trace.getModuleManager().getModulesAt(snap, pc)) {
// Just take the first // Just take the first
return computeModuleShortName(module.getName()); return computeModuleShortName(module.getName(snap));
} }
return null; return null;
} }

View file

@ -26,7 +26,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.util.MathUtilities; import ghidra.util.MathUtilities;
@ -112,12 +111,13 @@ public class DefaultModuleMapProposal
* to obtain these. * to obtain these.
* *
* @param module the module * @param module the module
* @param snap the first snap
* @param program the matched program * @param program the matched program
* @param moduleRange a range from the module base the size of the program's image * @param moduleRange a range from the module base the size of the program's image
*/ */
protected DefaultModuleMapEntry(TraceModule module, Program program, protected DefaultModuleMapEntry(TraceModule module, long snap, Program program,
AddressRange moduleRange) { AddressRange moduleRange) {
super(module.getTrace(), module, program, program); super(module.getTrace(), module, snap, program, program);
this.moduleRange = moduleRange; this.moduleRange = moduleRange;
this.imageRange = quantize(computeImageRange(program)); this.imageRange = quantize(computeImageRange(program));
} }
@ -128,8 +128,8 @@ public class DefaultModuleMapProposal
} }
@Override @Override
public Lifespan getFromLifespan() { public String getModuleName() {
return getModule().getLifespan(); return getModule().getName(snap);
} }
private long getLength() { private long getLength() {
@ -179,15 +179,17 @@ public class DefaultModuleMapProposal
} }
protected final TraceModule module; protected final TraceModule module;
protected final long snap;
// indexed by region's offset from module base // indexed by region's offset from module base
protected final NavigableMap<Long, ModuleRegionMatcher> matchers = new TreeMap<>(); protected final NavigableMap<Long, ModuleRegionMatcher> matchers = new TreeMap<>();
protected AddressRange imageRange; protected AddressRange imageRange;
protected AddressRange moduleRange; protected AddressRange moduleRange;
protected DefaultModuleMapProposal(TraceModule module, Program program) { protected DefaultModuleMapProposal(TraceModule module, long snap, Program program) {
super(module.getTrace(), program); super(module.getTrace(), program);
this.module = module; this.module = module;
this.snap = snap;
processProgram(); processProgram();
processModule(); processModule();
} }
@ -198,7 +200,7 @@ public class DefaultModuleMapProposal
} }
private ModuleRegionMatcher getMatcher(long baseOffset) { private ModuleRegionMatcher getMatcher(long baseOffset) {
return matchers.computeIfAbsent(baseOffset, ModuleRegionMatcher::new); return matchers.computeIfAbsent(baseOffset, b -> new ModuleRegionMatcher(snap));
} }
private void processProgram() { private void processProgram() {
@ -217,15 +219,12 @@ public class DefaultModuleMapProposal
* Must be called after processProgram, so that image size is known * Must be called after processProgram, so that image size is known
*/ */
private void processModule() { private void processModule() {
moduleRange = quantize(module.getRange()); moduleRange = quantize(module.getRange(snap));
Address moduleBase = moduleRange.getMinAddress(); Address moduleBase = moduleRange.getMinAddress();
Lifespan lifespan = module.getLifespan();
for (TraceMemoryRegion region : module.getTrace() for (TraceMemoryRegion region : module.getTrace()
.getMemoryManager() .getMemoryManager()
.getRegionsIntersecting(lifespan, moduleRange)) { .getRegionsIntersecting(Lifespan.at(snap), moduleRange)) {
Address min = region instanceof TraceObjectMemoryRegion objReg Address min = region.getMinAddress(snap);
? objReg.getMinAddress(lifespan.lmin())
: region.getMinAddress();
getMatcher(min.subtract(moduleBase)).region = region; getMatcher(min.subtract(moduleBase)).region = region;
} }
} }
@ -246,7 +245,7 @@ public class DefaultModuleMapProposal
@Override @Override
public Map<TraceModule, ModuleMapEntry> computeMap() { public Map<TraceModule, ModuleMapEntry> computeMap() {
return Map.of(module, new DefaultModuleMapEntry(module, program, moduleRange)); return Map.of(module, new DefaultModuleMapEntry(module, snap, program, moduleRange));
} }
@Override @Override

View file

@ -23,7 +23,6 @@ import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
@ -35,9 +34,9 @@ public class DefaultRegionMapProposal
extends AbstractMapEntry<TraceMemoryRegion, MemoryBlock> extends AbstractMapEntry<TraceMemoryRegion, MemoryBlock>
implements RegionMapEntry { implements RegionMapEntry {
public DefaultRegionMapEntry(TraceMemoryRegion region, public DefaultRegionMapEntry(TraceMemoryRegion region, long snap,
Program program, MemoryBlock block) { Program program, MemoryBlock block) {
super(region.getTrace(), region, program, block); super(region.getTrace(), region, snap, program, block);
} }
@Override @Override
@ -46,13 +45,18 @@ public class DefaultRegionMapProposal
} }
@Override @Override
public AddressRange getFromRange() { public String getRegionName() {
return getRegion().getRange(); return getRegion().getName(snap);
} }
@Override @Override
public Lifespan getFromLifespan() { public Address getRegionMinAddress() {
return getRegion().getLifespan(); return getRegion().getMinAddress(snap);
}
@Override
public AddressRange getFromRange() {
return getRegion().getRange(snap);
} }
@Override @Override
@ -72,13 +76,13 @@ public class DefaultRegionMapProposal
} }
protected class RegionMatcher extends Matcher<TraceMemoryRegion, MemoryBlock> { protected class RegionMatcher extends Matcher<TraceMemoryRegion, MemoryBlock> {
public RegionMatcher(TraceMemoryRegion region, MemoryBlock block) { public RegionMatcher(TraceMemoryRegion region, long snap, MemoryBlock block) {
super(region, block); super(region, snap, block);
} }
@Override @Override
protected AddressRange getFromRange() { protected AddressRange getFromRange() {
return fromObject == null ? null : fromObject.getRange(); return fromObject == null ? null : fromObject.getRange(snap);
} }
@Override @Override
@ -99,7 +103,8 @@ public class DefaultRegionMapProposal
if (fOff == tOff) { if (fOff == tOff) {
return 10; return 10;
} }
} catch (IllegalArgumentException e) { }
catch (IllegalArgumentException e) {
// fell-through // fell-through
} }
return 0; return 0;
@ -108,9 +113,14 @@ public class DefaultRegionMapProposal
protected class RegionMatcherMap protected class RegionMatcherMap
extends MatcherMap<Void, TraceMemoryRegion, MemoryBlock, RegionMatcher> { extends MatcherMap<Void, TraceMemoryRegion, MemoryBlock, RegionMatcher> {
public RegionMatcherMap(long snap) {
super(snap);
}
@Override @Override
protected RegionMatcher newMatcher(TraceMemoryRegion region, MemoryBlock block) { protected RegionMatcher newMatcher(TraceMemoryRegion region, MemoryBlock block) {
return new RegionMatcher(region, block); return new RegionMatcher(region, snap, block);
} }
@Override @Override
@ -132,28 +142,36 @@ public class DefaultRegionMapProposal
} }
protected final List<TraceMemoryRegion> regions; protected final List<TraceMemoryRegion> regions;
protected final long snap;
protected final Address fromBase; protected final Address fromBase;
protected final Address toBase; protected final Address toBase;
protected final RegionMatcherMap matchers = new RegionMatcherMap(); protected final RegionMatcherMap matchers;
protected DefaultRegionMapProposal(Collection<? extends TraceMemoryRegion> regions, protected DefaultRegionMapProposal(Collection<? extends TraceMemoryRegion> regions, long snap,
Program program) { Program program) {
super(getTrace(regions), program); super(getTrace(regions), program);
this.snap = snap;
this.regions = Collections.unmodifiableList(regions.stream() this.regions = Collections.unmodifiableList(regions.stream()
.sorted(Comparator.comparing(r -> r.getMinAddress())) .sorted(Comparator.comparing(r -> r.getMinAddress(snap)))
.collect(Collectors.toList())); .collect(Collectors.toList()));
this.fromBase = computeFromBase(); this.fromBase = computeFromBase();
this.toBase = program.getImageBase(); this.toBase = program.getImageBase();
this.matchers = new RegionMatcherMap(snap);
processRegions(); processRegions();
processProgram(); processProgram();
} }
protected DefaultRegionMapProposal(TraceMemoryRegion region, Program program, protected DefaultRegionMapProposal(TraceMemoryRegion region, long snap, Program program,
MemoryBlock block) { MemoryBlock block) {
super(region.getTrace(), program); super(region.getTrace(), program);
this.regions = List.of(region); this.regions = List.of(region);
this.fromBase = region.getMinAddress(); this.snap = snap;
this.fromBase = region.getMinAddress(snap);
this.toBase = program.getImageBase(); this.toBase = program.getImageBase();
this.matchers = new RegionMatcherMap(snap);
processRegions(); processRegions();
matchers.processToObject(block); matchers.processToObject(block);
} }
@ -162,7 +180,7 @@ public class DefaultRegionMapProposal
if (regions.isEmpty()) { if (regions.isEmpty()) {
return null; return null;
} }
return regions.get(0).getMinAddress(); return regions.get(0).getMinAddress(snap);
} }
private void processRegions() { private void processRegions() {
@ -184,8 +202,8 @@ public class DefaultRegionMapProposal
@Override @Override
public Map<TraceMemoryRegion, RegionMapEntry> computeMap() { public Map<TraceMemoryRegion, RegionMapEntry> computeMap() {
return matchers return matchers.computeMap(
.computeMap(m -> new DefaultRegionMapEntry(m.fromObject, program, m.toObject)); m -> new DefaultRegionMapEntry(m.fromObject, snap, program, m.toObject));
} }
@Override @Override

View file

@ -20,11 +20,9 @@ import java.util.Map;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.modules.SectionMapProposal; import ghidra.debug.api.modules.SectionMapProposal;
import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry; import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.modules.TraceSection; import ghidra.trace.model.modules.TraceSection;
@ -50,8 +48,9 @@ public class DefaultSectionMapProposal
* @param program the program containing the matched block * @param program the program containing the matched block
* @param block the matched memory block * @param block the matched memory block
*/ */
protected DefaultSectionMapEntry(TraceSection section, Program program, MemoryBlock block) { protected DefaultSectionMapEntry(TraceSection section, long snap, Program program,
super(section.getTrace(), section, program, block); MemoryBlock block) {
super(section.getTrace(), section, snap, program, block);
} }
@Override @Override
@ -59,19 +58,29 @@ public class DefaultSectionMapProposal
return getFromObject().getModule(); return getFromObject().getModule();
} }
@Override
public String getModuleName() {
return getModule().getName(snap);
}
@Override @Override
public TraceSection getSection() { public TraceSection getSection() {
return getFromObject(); return getFromObject();
} }
@Override @Override
public Lifespan getFromLifespan() { public String getSectionName() {
return getModule().getLifespan(); return getSection().getName(snap);
}
@Override
public Address getSectionStart() {
return getSection().getStart(snap);
} }
@Override @Override
public AddressRange getFromRange() { public AddressRange getFromRange() {
return getSection().getRange(); return getSection().getRange(snap);
} }
@Override @Override
@ -91,13 +100,13 @@ public class DefaultSectionMapProposal
} }
protected static class SectionMatcher extends Matcher<TraceSection, MemoryBlock> { protected static class SectionMatcher extends Matcher<TraceSection, MemoryBlock> {
public SectionMatcher(TraceSection section, MemoryBlock block) { public SectionMatcher(TraceSection section, long snap, MemoryBlock block) {
super(section, block); super(section, snap, block);
} }
@Override @Override
protected AddressRange getFromRange() { protected AddressRange getFromRange() {
return fromObject == null ? null : fromObject.getRange(); return fromObject == null ? null : fromObject.getRange(snap);
} }
@Override @Override
@ -109,14 +118,19 @@ public class DefaultSectionMapProposal
protected static class SectionMatcherMap protected static class SectionMatcherMap
extends MatcherMap<String, TraceSection, MemoryBlock, SectionMatcher> { extends MatcherMap<String, TraceSection, MemoryBlock, SectionMatcher> {
public SectionMatcherMap(long snap) {
super(snap);
}
@Override @Override
protected SectionMatcher newMatcher(TraceSection section, MemoryBlock block) { protected SectionMatcher newMatcher(TraceSection section, MemoryBlock block) {
return new SectionMatcher(section, block); return new SectionMatcher(section, snap, block);
} }
@Override @Override
protected String getFromJoinKey(TraceSection section) { protected String getFromJoinKey(TraceSection section) {
return section.getName(); return section.getName(snap);
} }
@Override @Override
@ -126,18 +140,27 @@ public class DefaultSectionMapProposal
} }
protected final TraceModule module; protected final TraceModule module;
protected final SectionMatcherMap matchers = new SectionMatcherMap(); protected final long snap;
protected DefaultSectionMapProposal(TraceModule module, Program program) { protected final SectionMatcherMap matchers;
protected DefaultSectionMapProposal(TraceModule module, long snap, Program program) {
super(module.getTrace(), program); super(module.getTrace(), program);
this.module = module; this.module = module;
this.snap = snap;
this.matchers = new SectionMatcherMap(snap);
processModule(); processModule();
processProgram(); processProgram();
} }
protected DefaultSectionMapProposal(TraceSection section, Program program, MemoryBlock block) { protected DefaultSectionMapProposal(TraceSection section, long snap, Program program,
MemoryBlock block) {
super(section.getTrace(), program); super(section.getTrace(), program);
this.module = section.getModule(); this.module = section.getModule();
this.snap = snap;
this.matchers = new SectionMatcherMap(snap);
matchers.processFromObject(section); matchers.processFromObject(section);
matchers.processToObject(block); matchers.processToObject(block);
} }
@ -148,7 +171,7 @@ public class DefaultSectionMapProposal
} }
private void processModule() { private void processModule() {
for (TraceSection section : module.getSections()) { for (TraceSection section : module.getSections(snap)) {
matchers.processFromObject(section); matchers.processFromObject(section);
} }
} }
@ -166,8 +189,8 @@ public class DefaultSectionMapProposal
@Override @Override
public Map<TraceSection, SectionMapEntry> computeMap() { public Map<TraceSection, SectionMapEntry> computeMap() {
return matchers return matchers.computeMap(
.computeMap(m -> new DefaultSectionMapEntry(m.fromObject, program, m.toObject)); m -> new DefaultSectionMapEntry(m.fromObject, snap, program, m.toObject));
} }
@Override @Override

View file

@ -19,10 +19,12 @@ import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
class ModuleRegionMatcher { class ModuleRegionMatcher {
final long snap;
MemoryBlock block; MemoryBlock block;
TraceMemoryRegion region; TraceMemoryRegion region;
public ModuleRegionMatcher(long baseOffset) { public ModuleRegionMatcher(long snap) {
this.snap = snap;
} }
int score() { int score() {
@ -30,7 +32,7 @@ class ModuleRegionMatcher {
return 0; // Unmatched return 0; // Unmatched
} }
int score = 3; // For the matching offset int score = 3; // For the matching offset
if (block.getSize() == region.getLength()) { if (block.getSize() == region.getLength(snap)) {
score += 10; score += 10;
} }
return score; return score;

View file

@ -28,6 +28,7 @@ import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
// TODO: Consider making this a front-end plugin? // TODO: Consider making this a front-end plugin?
@ -351,8 +352,8 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
return projectData.getFileByID(entries.stream().max(comparator).get().dfID); return projectData.getFileByID(entries.stream().max(comparator).get().dfID);
} }
public DomainFile getBestMatch(AddressSpace space, TraceModule module, Program currentProgram, public DomainFile getBestMatch(AddressSpace space, TraceModule module, long snap,
Collection<IndexEntry> entries) { Program currentProgram, Collection<IndexEntry> entries) {
if (entries.isEmpty()) { if (entries.isEmpty()) {
return null; return null;
} }
@ -361,7 +362,7 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
.getStaticMappingManager() .getStaticMappingManager()
.findAllOverlapping( .findAllOverlapping(
new AddressRangeImpl(space.getMinAddress(), space.getMaxAddress()), new AddressRangeImpl(space.getMinAddress(), space.getMaxAddress()),
module.getLifespan()) Lifespan.at(snap))
.stream() .stream()
.map(m -> ProgramURLUtils.getDomainFileFromOpenProject(project, .map(m -> ProgramURLUtils.getDomainFileFromOpenProject(project,
m.getStaticProgramURL())) m.getStaticProgramURL()))
@ -379,17 +380,17 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
return selectBest(entries, libraries, folderUses, currentProgram); return selectBest(entries, libraries, folderUses, currentProgram);
} }
public DomainFile getBestMatch(TraceModule module, Program currentProgram, public DomainFile getBestMatch(TraceModule module, long snap, Program currentProgram,
Collection<IndexEntry> entries) { Collection<IndexEntry> entries) {
Address base = module.getBase(); Address base = module.getBase(snap);
AddressSpace space = base == null AddressSpace space = base == null
? module.getTrace().getBaseAddressFactory().getDefaultAddressSpace() ? module.getTrace().getBaseAddressFactory().getDefaultAddressSpace()
: base.getAddressSpace(); : base.getAddressSpace();
return getBestMatch(space, module, currentProgram, entries); return getBestMatch(space, module, snap, currentProgram, entries);
} }
public List<IndexEntry> getBestEntries(TraceModule module) { public List<IndexEntry> getBestEntries(TraceModule module, long snap) {
String modulePathName = module.getName().toLowerCase(); String modulePathName = module.getName(snap).toLowerCase();
List<IndexEntry> entries = new ArrayList<>(index.getByName(modulePathName)); List<IndexEntry> entries = new ArrayList<>(index.getByName(modulePathName));
if (!entries.isEmpty()) { if (!entries.isEmpty()) {
return entries; return entries;
@ -399,8 +400,9 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
return entries; return entries;
} }
public DomainFile getBestMatch(AddressSpace space, TraceModule module, Program currentProgram) { public DomainFile getBestMatch(AddressSpace space, TraceModule module, long snap,
return getBestMatch(space, module, currentProgram, getBestEntries(module)); Program currentProgram) {
return getBestMatch(space, module, snap, currentProgram, getBestEntries(module, snap));
} }
public Collection<IndexEntry> filter(Collection<IndexEntry> entries, public Collection<IndexEntry> filter(Collection<IndexEntry> entries,

View file

@ -181,7 +181,7 @@ public class StackUnwinder {
try { try {
TraceStack stack = trace.getStackManager().getLatestStack(thread, viewSnap); TraceStack stack = trace.getStackManager().getLatestStack(thread, viewSnap);
if (stack != null) { if (stack != null) {
TraceStackFrame frame = stack.getFrame(level, false); TraceStackFrame frame = stack.getFrame(viewSnap, level, false);
if (frame != null) { if (frame != null) {
pcVal = frame.getProgramCounter(viewSnap); pcVal = frame.getProgramCounter(viewSnap);
} }
@ -209,8 +209,7 @@ public class StackUnwinder {
return unwind(coordinates, level, pcVal, spVal, state, new SavedRegisterMap(), monitor); return unwind(coordinates, level, pcVal, spVal, state, new SavedRegisterMap(), monitor);
} }
record StaticAndUnwind(Address staticPc, UnwindInfo info) { record StaticAndUnwind(Address staticPc, UnwindInfo info) {}
}
/** /**
* Compute the unwind information for the given program counter and context * Compute the unwind information for the given program counter and context

View file

@ -39,8 +39,8 @@ import ghidra.util.Msg;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
/** /**
* Container for all the decompiler elements the users "selects" via the menu. * Container for all the decompiler elements the users "selects" via the menu. This data is used to
* This data is used to build queries. * build queries.
*/ */
public class AngrTaintState extends AbstractTaintState { public class AngrTaintState extends AbstractTaintState {
@ -158,7 +158,7 @@ public class AngrTaintState extends AbstractTaintState {
Collection<? extends TraceMemoryRegion> allRegions = Collection<? extends TraceMemoryRegion> allRegions =
memoryManager.getRegionsAtSnap(current.getSnap()); memoryManager.getRegionsAtSnap(current.getSnap());
for (TraceMemoryRegion region : allRegions) { for (TraceMemoryRegion region : allRegions) {
AddressRange range = region.getRange(); AddressRange range = region.getRange(current.getSnap());
Address min = range.getMinAddress(); Address min = range.getMinAddress();
int len = (int) range.getLength(); int len = (int) range.getLength();
byte[] bytes = new byte[len]; byte[] bytes = new byte[len];

View file

@ -98,9 +98,9 @@ public class DebuggerCopyActionsPluginScreenShots extends GhidraScreenShotGenera
DBTraceModuleManager mods = tb.trace.getModuleManager(); DBTraceModuleManager mods = tb.trace.getModuleManager();
TraceModule modEcho = mods.addLoadedModule("Modules[/bin/echo]", "/bin/echo", TraceModule modEcho = mods.addLoadedModule("Modules[/bin/echo]", "/bin/echo",
tb.range(0x55550000, 0x5556ffff), snap); tb.range(0x55550000, 0x5556ffff), snap);
modEcho.addSection("Modules[/bin/echo].Sections[.text]", ".text", modEcho.addSection(snap, "Modules[/bin/echo].Sections[.text]", ".text",
tb.range(0x55550000, 0x5555ffff)); tb.range(0x55550000, 0x5555ffff));
modEcho.addSection("Modules[/bin/echo].Sections[.data]", ".data", modEcho.addSection(snap, "Modules[/bin/echo].Sections[.data]", ".data",
tb.range(0x55560000, 0x5556ffff)); tb.range(0x55560000, 0x5556ffff));
} }

View file

@ -78,17 +78,17 @@ public class DebuggerModulesPluginScreenShots extends GhidraScreenShotGenerator
TraceModule bin = tb.trace.getModuleManager() TraceModule bin = tb.trace.getModuleManager()
.addLoadedModule("Modules[/bin/bash]", "/bin/bash", .addLoadedModule("Modules[/bin/bash]", "/bin/bash",
tb.range(0x00400000, 0x0060ffff), snap); tb.range(0x00400000, 0x0060ffff), snap);
bin.addSection("Modules[/bin/bash].Sections[.text]", ".text", bin.addSection(snap, "Modules[/bin/bash].Sections[.text]", ".text",
tb.range(0x00400000, 0x0040ffff)); tb.range(0x00400000, 0x0040ffff));
bin.addSection("Modules[/bin/bash].Sections[.data]", ".data", bin.addSection(snap, "Modules[/bin/bash].Sections[.data]", ".data",
tb.range(0x00600000, 0x0060ffff)); tb.range(0x00600000, 0x0060ffff));
TraceModule lib = tb.trace.getModuleManager() TraceModule lib = tb.trace.getModuleManager()
.addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6", .addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6",
tb.range(0x7fac0000, 0x7faeffff), snap); tb.range(0x7fac0000, 0x7faeffff), snap);
lib.addSection("Modules[/lib/libc.so.6].Sections[.text]", ".text", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.text]", ".text",
tb.range(0x7fac0000, 0x7facffff)); tb.range(0x7fac0000, 0x7facffff));
lib.addSection("Modules[/lib/libc.so.6].Sections[.data]", ".data", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.data]", ".data",
tb.range(0x7fae0000, 0x7faeffff)); tb.range(0x7fae0000, 0x7faeffff));
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
@ -111,19 +111,18 @@ public class DebuggerModulesPluginScreenShots extends GhidraScreenShotGenerator
TraceModule bin = tb.trace.getModuleManager() TraceModule bin = tb.trace.getModuleManager()
.addLoadedModule("Modules[/bin/bash]", "/bin/bash", .addLoadedModule("Modules[/bin/bash]", "/bin/bash",
tb.range(0x00400000, 0x0060ffff), snap); tb.range(0x00400000, 0x0060ffff), snap);
bin.addSection("Modules[/bin/bash].Sections[.text]", ".text", bin.addSection(snap, "Modules[/bin/bash].Sections[.text]", ".text",
tb.range(0x00400000, 0x0040ffff)); tb.range(0x00400000, 0x0040ffff));
bin.addSection("Modules[/bin/bash].Sections[.data]", ".data", bin.addSection(snap, "Modules[/bin/bash].Sections[.data]", ".data",
tb.range(0x00600000, 0x0060ffff)); tb.range(0x00600000, 0x0060ffff));
TraceModule lib = tb.trace.getModuleManager() TraceModule lib = tb.trace.getModuleManager()
.addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6", .addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6",
tb.range(0x7fac0000, 0x7faeffff), snap); tb.range(0x7fac0000, 0x7faeffff), snap);
lib.addSection("Modules[/lib/libc.so.6].Sections[.text]", ".text", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.text]", ".text",
tb.range(0x7fac0000, 0x7facffff)); tb.range(0x7fac0000, 0x7facffff));
lib.addSection("Modules[/lib/libc.so.6].Sections[.data]", ".data", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.data]", ".data",
tb.range(0x7fae0000, 0x7faeffff)); tb.range(0x7fae0000, 0x7faeffff));
} }
progBash = createDefaultProgram("bash", ProgramBuilder._X64, this); progBash = createDefaultProgram("bash", ProgramBuilder._X64, this);

View file

@ -25,7 +25,8 @@ import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.app.services.*; import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.app.services.ProgramManager;
import ghidra.debug.api.modules.MapProposal; import ghidra.debug.api.modules.MapProposal;
import ghidra.debug.api.modules.ModuleMapProposal; import ghidra.debug.api.modules.ModuleMapProposal;
import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry; import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
@ -80,24 +81,25 @@ public class DebuggerStaticMappingPluginScreenShots extends GhidraScreenShotGene
@Test @Test
public void testCaptureDebuggerStaticMappingPlugin() throws Throwable { public void testCaptureDebuggerStaticMappingPlugin() throws Throwable {
DomainFolder root = tool.getProject().getProjectData().getRootFolder(); DomainFolder root = tool.getProject().getProjectData().getRootFolder();
final long snap;
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA); tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA);
long snap = tb.trace.getTimeManager().createSnapshot("First").getKey(); snap = tb.trace.getTimeManager().createSnapshot("First").getKey();
TraceModule bin = tb.trace.getModuleManager() TraceModule bin = tb.trace.getModuleManager()
.addLoadedModule("Modules[/bin/echo]", "/bin/echo", .addLoadedModule("Modules[/bin/echo]", "/bin/echo",
tb.range(0x00400000, 0x0060ffff), snap); tb.range(0x00400000, 0x0060ffff), snap);
bin.addSection("Modules[/bin/echo].Sections[.text]", ".text", bin.addSection(snap, "Modules[/bin/echo].Sections[.text]", ".text",
tb.range(0x00400000, 0x0040ffff)); tb.range(0x00400000, 0x0040ffff));
bin.addSection("Modules[/bin/echo].Sections[.data]", ".data", bin.addSection(snap, "Modules[/bin/echo].Sections[.data]", ".data",
tb.range(0x00600000, 0x0060ffff)); tb.range(0x00600000, 0x0060ffff));
TraceModule lib = tb.trace.getModuleManager() TraceModule lib = tb.trace.getModuleManager()
.addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6", .addLoadedModule("Modules[/lib/libc.so.6]", "/lib/libc.so.6",
tb.range(0x7fac0000, 0x7faeffff), snap); tb.range(0x7fac0000, 0x7faeffff), snap);
lib.addSection("Modules[/lib/libc.so.6].Sections[.text]", ".text", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.text]", ".text",
tb.range(0x7fac0000, 0x7facffff)); tb.range(0x7fac0000, 0x7facffff));
lib.addSection("Modules[/lib/libc.so.6].Sections[.data]", ".data", lib.addSection(snap, "Modules[/lib/libc.so.6].Sections[.data]", ".data",
tb.range(0x7fae0000, 0x7faeffff)); tb.range(0x7fae0000, 0x7faeffff));
} }
@ -137,7 +139,7 @@ public class DebuggerStaticMappingPluginScreenShots extends GhidraScreenShotGene
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
Map<TraceModule, ModuleMapProposal> proposal = Map<TraceModule, ModuleMapProposal> proposal =
mappingService.proposeModuleMaps(tb.trace.getModuleManager().getAllModules(), mappingService.proposeModuleMaps(tb.trace.getModuleManager().getAllModules(),
List.of(programManager.getAllOpenPrograms())); snap, List.of(programManager.getAllOpenPrograms()));
Collection<ModuleMapEntry> entries = MapProposal.flatten(proposal.values()); Collection<ModuleMapEntry> entries = MapProposal.flatten(proposal.values());
mappingService.addModuleMappings(entries, TaskMonitor.DUMMY, false); mappingService.addModuleMappings(entries, TaskMonitor.DUMMY, false);
} }

View file

@ -64,8 +64,8 @@ import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.stack.TraceStack; import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame; import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.target.schema.SchemaContext; import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.Scheduler; import ghidra.trace.model.time.schedule.Scheduler;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
@ -144,14 +144,14 @@ public class DebuggerStackPluginScreenShots extends GhidraScreenShotGenerator
thread = tb.getOrAddThread("Processes[1].Threads[1]", snap); thread = tb.getOrAddThread("Processes[1].Threads[1]", snap);
TraceStack stack = tb.trace.getStackManager().getStack(thread, snap, true); TraceStack stack = tb.trace.getStackManager().getStack(thread, snap, true);
stack.setDepth(3, true); stack.setDepth(snap, 3, true);
TraceStackFrame frame; TraceStackFrame frame;
frame = stack.getFrame(0, false); frame = stack.getFrame(snap, 0, false);
frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00404321)); frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00404321));
frame = stack.getFrame(1, false); frame = stack.getFrame(snap, 1, false);
frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00401234)); frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00401234));
frame = stack.getFrame(2, false); frame = stack.getFrame(snap, 2, false);
frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00401001)); frame.setProgramCounter(Lifespan.ALL, tb.addr(0x00401001));
} }
root.createFile("trace", tb.trace, TaskMonitor.DUMMY); root.createFile("trace", tb.trace, TaskMonitor.DUMMY);

View file

@ -46,7 +46,6 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.target.TraceObject.ConflictResolution; import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import help.screenshot.GhidraScreenShotGenerator; import help.screenshot.GhidraScreenShotGenerator;
@ -94,10 +93,6 @@ public class DebuggerThreadsPluginScreenShots extends GhidraScreenShotGenerator
return set; return set;
} }
protected boolean nullOrDead(TraceThread thread) {
return thread == null || !thread.isAlive();
}
private void populateTrace() throws Exception { private void populateTrace() throws Exception {
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
DBTraceObjectManager om = tb.trace.getObjectManager(); DBTraceObjectManager om = tb.trace.getObjectManager();

View file

@ -65,8 +65,8 @@ import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.iface.TraceObjectEnvironment; import ghidra.trace.model.target.iface.TraceObjectEnvironment;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.schema.SchemaContext; import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
@ -228,7 +228,7 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
DBTraceStackManager manager = tb.trace.getStackManager(); DBTraceStackManager manager = tb.trace.getStackManager();
TraceStack stack = manager.getStack(thread, snap, true); TraceStack stack = manager.getStack(thread, snap, true);
TraceStackFrame frame = stack.getFrame(0, true); TraceStackFrame frame = stack.getFrame(snap, 0, true);
frame.setProgramCounter(Lifespan.nowOn(snap), tb.addr(offset)); frame.setProgramCounter(Lifespan.nowOn(snap), tb.addr(offset));
} }
} }

View file

@ -47,12 +47,13 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion; import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.modules.TraceStaticMapping; import ghidra.trace.model.modules.TraceStaticMapping;
import ghidra.trace.model.target.*; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObject.ConflictResolution; import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.TraceObjectManager;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.schema.SchemaContext; import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@Category(NightlyCategory.class) @Category(NightlyCategory.class)
@ -503,7 +504,7 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerTes
waitForSwing(); waitForSwing();
TraceMemoryRegion region = Unique.assertOne(tb.trace.getMemoryManager().getAllRegions()); TraceMemoryRegion region = Unique.assertOne(tb.trace.getMemoryManager().getAllRegions());
assertEquals(tb.range(0, 0xfff), region.getRange()); assertEquals(tb.range(0, 0xfff), region.getRange(0));
} }
@Test @Test

View file

@ -60,7 +60,8 @@ import ghidra.trace.model.modules.TraceStaticMapping;
import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObject.ConflictResolution; import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.TraceObjectManager; import ghidra.trace.model.target.TraceObjectManager;
import ghidra.trace.model.target.path.*; import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.path.PathPattern;
import ghidra.trace.model.target.schema.SchemaContext; import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext; import ghidra.trace.model.target.schema.XmlSchemaContext;
@ -364,7 +365,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
MemoryBlock block = addBlock(); MemoryBlock block = addBlock();
try (Transaction tx = program.openTransaction("Change name")) { try (Transaction tx = program.openTransaction("Change name")) {
program.setName(modExe.getName()); program.setName(modExe.getName(0));
} }
waitForDomainObject(program); waitForDomainObject(program);
waitForPass(() -> assertSectionTableSize(4)); waitForPass(() -> assertSectionTableSize(4));
@ -524,7 +525,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
try (Transaction tx = program.openTransaction("Change name")) { try (Transaction tx = program.openTransaction("Change name")) {
program.setImageBase(addr(program, 0x00400000), true); program.setImageBase(addr(program, 0x00400000), true);
program.setName(modExe.getName()); program.setName(modExe.getName(0));
addBlock(); // So the program has a size addBlock(); // So the program has a size
} }
@ -591,7 +592,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
MemoryBlock block = addBlock(); MemoryBlock block = addBlock();
try (Transaction tx = program.openTransaction("Change name")) { try (Transaction tx = program.openTransaction("Change name")) {
program.setName(modExe.getName()); program.setName(modExe.getName(0));
} }
waitForDomainObject(program); waitForDomainObject(program);
waitForTasks(); waitForTasks();
@ -680,7 +681,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
waitForTasks(); waitForTasks();
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
modExe.setName("/bin/echo"); // File has to exist modExe.setName(0, "/bin/echo"); // File has to exist
} }
waitForPass(() -> assertModuleTableSize(2)); waitForPass(() -> assertModuleTableSize(2));

View file

@ -493,7 +493,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread), .addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
// Force "partial instruction" // Force "partial instruction"
tb.setEmuSleigh(""" tb.setEmuSleigh(0, """
r1 = 0xbeef; r1 = 0xbeef;
emu_swi(); emu_swi();
emu_exec_decoded(); emu_exec_decoded();
@ -567,7 +567,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread), .addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
// Force "partial instruction" // Force "partial instruction"
tb.setEmuSleigh(""" tb.setEmuSleigh(0, """
r1 = 0xbeef; r1 = 0xbeef;
emu_swi(); emu_swi();
emu_exec_decoded(); emu_exec_decoded();
@ -633,7 +633,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread), .addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
// Force "partial instruction" // Force "partial instruction"
tb.setEmuSleigh(""" tb.setEmuSleigh(0, """
r1 = 0xbeef; r1 = 0xbeef;
pcodeop_one(r1); pcodeop_one(r1);
emu_exec_decoded(); emu_exec_decoded();
@ -707,7 +707,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TraceBreakpoint tb = trace.getBreakpointManager() TraceBreakpoint tb = trace.getBreakpointManager()
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrI2, Set.of(thread), .addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrI2, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
tb.setEmuSleigh(""" tb.setEmuSleigh(0, """
r1 = 0x5678; r1 = 0x5678;
emu_swi(); emu_swi();
emu_exec_decoded(); emu_exec_decoded();
@ -852,7 +852,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TraceBreakpoint tb = trace.getBreakpointManager() TraceBreakpoint tb = trace.getBreakpointManager()
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrI2, Set.of(thread), .addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrI2, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
tb.setEmuSleigh(""" tb.setEmuSleigh(0, """
r1 = 0x5678; r1 = 0x5678;
emu_exec_decoded(); emu_exec_decoded();
"""); """);
@ -970,7 +970,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TraceThread newTraceThread = ProgramEmulationUtils.doLaunchEmulationThread(tb.trace, TraceThread newTraceThread = ProgramEmulationUtils.doLaunchEmulationThread(tb.trace,
newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000)); newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000));
newTraceThread.setName("MyThread"); newTraceThread.setName(newSnap, "MyThread");
@SuppressWarnings("unused") @SuppressWarnings("unused")
PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath()); PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath());

View file

@ -43,8 +43,8 @@ import ghidra.trace.model.modules.*;
import ghidra.trace.model.target.TraceObject.ConflictResolution; import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.schema.SchemaContext; import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext;
import ghidra.util.Msg; import ghidra.util.Msg;
// Not technically a GUI test, but must be carried out in the context of a plugin tool // Not technically a GUI test, but must be carried out in the context of a plugin tool
@ -690,6 +690,6 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
TraceModule modBash = objModBash.queryInterface(TraceObjectModule.class); TraceModule modBash = objModBash.queryInterface(TraceObjectModule.class);
assertEquals(Map.of(), assertEquals(Map.of(),
mappingService.proposeModuleMaps(List.of(modBash), List.of(program))); mappingService.proposeModuleMaps(List.of(modBash), 0, List.of(program)));
} }
} }

View file

@ -980,7 +980,7 @@ public class StackUnwinderTest extends AbstractGhidraHeadedDebuggerTest {
bptUnwind = tb.trace.getBreakpointManager() bptUnwind = tb.trace.getBreakpointManager()
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), entry, Set.of(), .addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), entry, Set.of(),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "unwind stack"); Set.of(TraceBreakpointKind.SW_EXECUTE), true, "unwind stack");
bptUnwind.setEmuSleigh(""" bptUnwind.setEmuSleigh(0, """
if (%s >= 0x%x) goto <skip>; if (%s >= 0x%x) goto <skip>;
emu_swi(); emu_swi();
<skip> <skip>

View file

@ -299,6 +299,20 @@ public class DBTraceTimeViewport implements TraceTimeViewport {
} }
} }
@Override
public List<Lifespan> getReversedSpans() {
ArrayList<Lifespan> result = new ArrayList<>();
try (LockHold hold = trace.lockRead()) {
synchronized (ordered) {
ListIterator<Lifespan> it = ordered.listIterator(ordered.size());
while (it.hasPrevious()) {
result.add(it.previous());
}
}
}
return result;
}
public List<Lifespan> getOrderedSpans(long snap) { public List<Lifespan> getOrderedSpans(long snap) {
try (LockHold hold = trace.lockRead()) { try (LockHold hold = trace.lockRead()) {
setSnap(snap); setSnap(snap);

View file

@ -188,7 +188,7 @@ public class DBTraceBreakpoint
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.name = name; this.name = name;
update(NAME_COLUMN); update(NAME_COLUMN);
@ -199,14 +199,14 @@ public class DBTraceBreakpoint
} }
@Override @Override
public String getName() { public String getName(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return name; return name;
} }
} }
@Override @Override
public Set<TraceThread> getThreads() { public Set<TraceThread> getThreads(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
if (threadKeys.length == 0) { if (threadKeys.length == 0) {
return Set.of(); return Set.of();
@ -227,28 +227,28 @@ public class DBTraceBreakpoint
} }
@Override @Override
public AddressRange getRange() { public AddressRange getRange(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range; return range;
} }
} }
@Override @Override
public Address getMinAddress() { public Address getMinAddress(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getMinAddress(); return range.getMinAddress();
} }
} }
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getMaxAddress(); return range.getMaxAddress();
} }
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getLength(); return range.getLength();
} }
@ -272,79 +272,12 @@ public class DBTraceBreakpoint
} }
} }
@Override
public boolean isAlive(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap);
}
}
@Override
public long getPlacedSnap() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.lmin();
}
}
@Override
public void setClearedSnap(long clearedSnap) throws DuplicateNameException {
setLifespan(Lifespan.span(getPlacedSnap(), clearedSnap));
}
@Override
public long getClearedSnap() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.lmax();
}
}
protected DBTraceBreakpoint doCopy() { protected DBTraceBreakpoint doCopy() {
DBTraceBreakpoint breakpoint = space.breakpointMapSpace.put(this, null); DBTraceBreakpoint breakpoint = space.breakpointMapSpace.put(this, null);
breakpoint.set(path, name, threadKeys, flagsByte, comment); breakpoint.set(path, name, threadKeys, flagsByte, comment);
return breakpoint; return breakpoint;
} }
@Override
public DBTraceBreakpoint splitAndSet(long snap, boolean en,
Collection<TraceBreakpointKind> kinds) {
DBTraceBreakpoint that;
Lifespan oldLifespan = null;
Lifespan newLifespan = null;
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (!lifespan.contains(snap)) {
throw new IllegalArgumentException("snap = " + snap);
}
if (flagsByte == computeFlagsByte(en, kinds)) {
return this;
}
if (snap == getPlacedSnap()) {
this.doSetFlags(en, kinds);
that = this;
}
else {
that = doCopy();
that.doSetLifespan(Lifespan.span(snap, getClearedSnap()));
that.doSetFlags(en, kinds);
oldLifespan = lifespan;
newLifespan = Lifespan.span(getPlacedSnap(), snap - 1);
this.doSetLifespan(newLifespan);
}
}
if (that == this) {
space.trace.setChanged(
new TraceChangeRecord<>(TraceEvents.BREAKPOINT_CHANGED, space, this));
}
else {
// Yes, issue ADDED, before LIFESPAN_CHANGED, as noted in docs
space.trace
.setChanged(new TraceChangeRecord<>(TraceEvents.BREAKPOINT_ADDED, space, that));
space.trace.setChanged(
new TraceChangeRecord<>(TraceEvents.BREAKPOINT_LIFESPAN_CHANGED, space, this,
Objects.requireNonNull(oldLifespan), Objects.requireNonNull(newLifespan)));
}
return that;
}
protected static byte computeFlagsByte(boolean enabled, Collection<TraceBreakpointKind> kinds) { protected static byte computeFlagsByte(boolean enabled, Collection<TraceBreakpointKind> kinds) {
byte flags = 0; byte flags = 0;
for (TraceBreakpointKind k : kinds) { for (TraceBreakpointKind k : kinds) {
@ -405,7 +338,7 @@ public class DBTraceBreakpoint
} }
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(long snap, boolean enabled) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
doSetEnabled(enabled); doSetEnabled(enabled);
} }
@ -422,7 +355,7 @@ public class DBTraceBreakpoint
} }
@Override @Override
public void setEmuEnabled(boolean enabled) { public void setEmuEnabled(long snap, boolean enabled) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
doSetEmuEnabled(enabled); doSetEmuEnabled(enabled);
} }
@ -439,7 +372,7 @@ public class DBTraceBreakpoint
} }
@Override @Override
public void setKinds(Collection<TraceBreakpointKind> kinds) { public void setKinds(long snap, Collection<TraceBreakpointKind> kinds) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
doSetKinds(kinds); doSetKinds(kinds);
} }
@ -448,14 +381,14 @@ public class DBTraceBreakpoint
} }
@Override @Override
public Set<TraceBreakpointKind> getKinds() { public Set<TraceBreakpointKind> getKinds(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return kindsView; return kindsView;
} }
} }
@Override @Override
public void setComment(String comment) { public void setComment(long snap, String comment) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.comment = comment; this.comment = comment;
update(COMMENT_COLUMN); update(COMMENT_COLUMN);
@ -465,14 +398,14 @@ public class DBTraceBreakpoint
} }
@Override @Override
public String getComment() { public String getComment(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return comment; return comment;
} }
} }
@Override @Override
public void setEmuSleigh(String emuSleigh) { public void setEmuSleigh(long snap, String emuSleigh) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (emuSleigh == null || SleighUtils.UNCONDITIONAL_BREAK.equals(emuSleigh)) { if (emuSleigh == null || SleighUtils.UNCONDITIONAL_BREAK.equals(emuSleigh)) {
this.emuSleigh = null; this.emuSleigh = null;
@ -487,7 +420,7 @@ public class DBTraceBreakpoint
} }
@Override @Override
public String getEmuSleigh() { public String getEmuSleigh(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return emuSleigh == null || emuSleigh.isBlank() ? SleighUtils.UNCONDITIONAL_BREAK return emuSleigh == null || emuSleigh.isBlank() ? SleighUtils.UNCONDITIONAL_BREAK
: emuSleigh; : emuSleigh;
@ -499,10 +432,29 @@ public class DBTraceBreakpoint
space.deleteBreakpoint(this); space.deleteBreakpoint(this);
} }
@Override
public void remove(long snap) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (snap <= lifespan.lmin()) {
space.deleteBreakpoint(this);
}
else if (snap <= lifespan.lmax()) {
doSetLifespan(lifespan.withMax(snap - 1));
}
}
}
@Override @Override
public boolean isValid(long snap) { public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap); return lifespan.contains(snap);
} }
} }
@Override
public boolean isAlive(Lifespan span) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.intersects(span);
}
}
} }

View file

@ -83,7 +83,7 @@ public class DBTraceBreakpointManager
if (pc == ignore) { if (pc == ignore) {
continue; continue;
} }
if (!lifespan.intersects(pc.getLifespan())) { if (!pc.isAlive(lifespan)) {
continue; continue;
} }
throw new DuplicateNameException("A breakpoint having path '" + path + throw new DuplicateNameException("A breakpoint having path '" + path +
@ -129,7 +129,7 @@ public class DBTraceBreakpointManager
} }
try (LockHold hold = LockHold.lock(lock.readLock())) { try (LockHold hold = LockHold.lock(lock.readLock())) {
return getBreakpointsByPath(path).stream() return getBreakpointsByPath(path).stream()
.filter(b -> b.getLifespan().contains(snap)) .filter(b -> b.isValid(snap))
.findAny() .findAny()
.orElse(null); .orElse(null);
} }

View file

@ -33,7 +33,6 @@ import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.model.thread.*; import ghidra.trace.model.thread.*;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.DuplicateNameException;
public class DBTraceObjectBreakpointLocation public class DBTraceObjectBreakpointLocation
implements TraceObjectBreakpointLocation, DBTraceObjectInterface { implements TraceObjectBreakpointLocation, DBTraceObjectInterface {
@ -85,10 +84,6 @@ public class DBTraceObjectBreakpointLocation
private final DBTraceObject object; private final DBTraceObject object;
private final BreakpointChangeTranslator translator; private final BreakpointChangeTranslator translator;
// Keep copies here for when the object gets invalidated
private AddressRange range;
private Lifespan lifespan;
public DBTraceObjectBreakpointLocation(DBTraceObject object) { public DBTraceObjectBreakpointLocation(DBTraceObject object) {
this.object = object; this.object = object;
@ -111,17 +106,17 @@ public class DBTraceObjectBreakpointLocation
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setName(getLifespan(), name); setName(Lifespan.nowOn(snap), name);
} }
} }
@Override @Override
public String getName() { public String getName(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
String display = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), String display =
KEY_DISPLAY, String.class, null); TraceObjectInterfaceUtils.getValue(object, snap, KEY_DISPLAY, String.class, null);
if (display != null) { if (display != null) {
return display; return display;
} }
@ -143,118 +138,44 @@ public class DBTraceObjectBreakpointLocation
public void setRange(Lifespan lifespan, AddressRange range) { public void setRange(Lifespan lifespan, AddressRange range) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.setValue(lifespan, KEY_RANGE, range); object.setValue(lifespan, KEY_RANGE, range);
this.range = range;
} }
} }
@Override @Override
public AddressRange getRange() { public AddressRange getRange(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
if (object.getLife().isEmpty()) { return TraceObjectInterfaceUtils.getValue(object, snap, KEY_RANGE, AddressRange.class,
return range; null);
}
return range = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
KEY_RANGE, AddressRange.class, range);
} }
} }
@Override @Override
public Address getMinAddress() { public Address getMinAddress(long snap) {
AddressRange range = getRange(); AddressRange range = getRange(snap);
return range == null ? null : range.getMinAddress(); return range == null ? null : range.getMinAddress();
} }
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress(long snap) {
AddressRange range = getRange(); AddressRange range = getRange(snap);
return range == null ? null : range.getMaxAddress(); return range == null ? null : range.getMaxAddress();
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
AddressRange range = getRange(); AddressRange range = getRange(snap);
return range == null ? 0 : range.getLength(); return range == null ? 0 : range.getLength();
} }
@Override
public void setLifespan(Lifespan lifespan) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) {
TraceObjectInterfaceUtils.setLifespan(TraceObjectBreakpointLocation.class, object,
lifespan);
this.lifespan = lifespan;
}
}
@Override
public Lifespan getLifespan() {
try (LockHold hold = object.getTrace().lockRead()) {
Lifespan computed = computeSpan();
if (computed != null) {
lifespan = computed;
}
return lifespan;
}
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = object.getTrace().lockRead()) {
return object.isAlive(snap);
}
}
@Override
public Lifespan computeSpan() {
Lifespan span = TraceObjectBreakpointLocation.super.computeSpan();
if (span != null) {
return span;
}
return getSpecification().computeSpan();
}
@Override
public long getPlacedSnap() {
return getLifespan().lmin();
}
@Override
public void setClearedSnap(long clearedSnap) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) {
setLifespan(Lifespan.span(getPlacedSnap(), clearedSnap));
}
}
@Override
public long getClearedSnap() {
return getLifespan().lmax();
}
@Override
public TraceBreakpoint splitAndSet(long snap, boolean enabled,
Collection<TraceBreakpointKind> kinds) {
try (LockHold hold = object.getTrace().lockWrite()) {
if (enabled != isEnabled(snap)) {
object.setValue(Lifespan.span(snap, getClearedSnap()),
TraceObjectTogglable.KEY_ENABLED, enabled);
}
Set<TraceBreakpointKind> asSet =
kinds instanceof Set<TraceBreakpointKind> yes ? yes : Set.copyOf(kinds);
if (!Objects.equals(asSet, getKinds())) {
this.setKinds(Lifespan.span(snap, getClearedSnap()), asSet);
}
return this;
}
}
@Override @Override
public void setEnabled(Lifespan lifespan, boolean enabled) { public void setEnabled(Lifespan lifespan, boolean enabled) {
object.setValue(lifespan, TraceObjectTogglable.KEY_ENABLED, enabled); object.setValue(lifespan, TraceObjectTogglable.KEY_ENABLED, enabled);
} }
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(long snap, boolean enabled) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setEnabled(getLifespan(), enabled); setEnabled(Lifespan.nowOn(snap), enabled);
} }
} }
@ -282,37 +203,37 @@ public class DBTraceObjectBreakpointLocation
} }
@Override @Override
public void setKinds(Collection<TraceBreakpointKind> kinds) { public void setKinds(long snap, Collection<TraceBreakpointKind> kinds) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setKinds(getLifespan(), kinds); setKinds(Lifespan.nowOn(snap), kinds);
} }
} }
@Override @Override
public Set<TraceBreakpointKind> getKinds() { public Set<TraceBreakpointKind> getKinds(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
return getSpecification().getKinds(); return getSpecification().getKinds(snap);
} }
} }
@Override @Override
public Set<TraceThread> getThreads() { public Set<TraceThread> getThreads(long snap) {
// TODO: Delete this? It's sort of deprecated out the gate anyway.... // TODO: Delete this? It's sort of deprecated out the gate anyway....
DBTraceObjectManager manager = object.getManager(); DBTraceObjectManager manager = object.getManager();
TraceObjectSchema schema = manager.getRootSchema(); TraceObjectSchema schema = manager.getRootSchema();
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
Set<TraceThread> threads = Set<TraceThread> threads =
object.queryAncestorsInterface(getLifespan(), TraceObjectThread.class) object.queryAncestorsInterface(Lifespan.at(snap), TraceObjectThread.class)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (!threads.isEmpty()) { if (!threads.isEmpty()) {
return threads; return threads;
} }
PathFilter procFilter = schema.searchFor(TraceObjectProcess.class, false); PathFilter procFilter = schema.searchFor(TraceObjectProcess.class, false);
return object.getAncestorsRoot(getLifespan(), procFilter) Lifespan lifespan = Lifespan.at(snap);
return object.getAncestorsRoot(lifespan, procFilter)
.flatMap(proc -> proc.getSource(object) .flatMap(proc -> proc.getSource(object)
.querySuccessorsInterface(getLifespan(), .querySuccessorsInterface(lifespan, TraceObjectThread.class, true))
TraceObjectThread.class, true))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
} }
@ -323,17 +244,17 @@ public class DBTraceObjectBreakpointLocation
} }
@Override @Override
public void setComment(String comment) { public void setComment(long snap, String comment) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setComment(getLifespan(), comment); setComment(Lifespan.nowOn(snap), comment);
} }
} }
@Override @Override
public String getComment() { public String getComment(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
String comment = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), String comment =
KEY_COMMENT, String.class, ""); TraceObjectInterfaceUtils.getValue(object, snap, KEY_COMMENT, String.class, "");
if (!comment.isBlank()) { if (!comment.isBlank()) {
return comment; return comment;
} }
@ -341,7 +262,7 @@ public class DBTraceObjectBreakpointLocation
if (spec == null) { if (spec == null) {
return ""; return "";
} }
return spec.getExpression(); return spec.getExpression(snap);
} }
} }
@ -351,17 +272,17 @@ public class DBTraceObjectBreakpointLocation
} }
@Override @Override
public void setEmuEnabled(boolean emuEnabled) { public void setEmuEnabled(long snap, boolean emuEnabled) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setEmuEnabled(getLifespan(), emuEnabled); setEmuEnabled(Lifespan.nowOn(snap), emuEnabled);
} }
} }
@Override @Override
public boolean isEmuEnabled(long snap) { public boolean isEmuEnabled(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_EMU_ENABLED, return TraceObjectInterfaceUtils.getValue(object, snap, KEY_EMU_ENABLED, Boolean.class,
Boolean.class, true); true);
} }
} }
@ -376,30 +297,42 @@ public class DBTraceObjectBreakpointLocation
} }
@Override @Override
public void setEmuSleigh(String sleigh) { public void setEmuSleigh(long snap, String sleigh) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setEmuSleigh(getLifespan(), sleigh); setEmuSleigh(Lifespan.nowOn(snap), sleigh);
} }
} }
@Override @Override
public String getEmuSleigh() { public String getEmuSleigh(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_EMU_SLEIGH, return TraceObjectInterfaceUtils.getValue(object, snap, KEY_EMU_SLEIGH, String.class,
String.class, SleighUtils.UNCONDITIONAL_BREAK); SleighUtils.UNCONDITIONAL_BREAK);
} }
} }
@Override @Override
public void delete() { public void delete() {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.removeTree(computeSpan()); object.removeTree(Lifespan.ALL);
}
}
@Override
public void remove(long snap) {
try (LockHold hold = object.getTrace().lockWrite()) {
object.removeTree(Lifespan.nowOn(snap));
} }
} }
@Override @Override
public boolean isValid(long snap) { public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null; return object.isAlive(snap);
}
@Override
public boolean isAlive(Lifespan span) {
return object.isAlive(span);
} }
@Override @Override
@ -420,8 +353,8 @@ public class DBTraceObjectBreakpointLocation
} }
} }
public TraceAddressSpace getTraceAddressSpace() { public TraceAddressSpace getTraceAddressSpace(long snap) {
return spaceForValue(computeMinSnap(), KEY_RANGE); return spaceForValue(snap, KEY_RANGE);
} }
@Override @Override

View file

@ -32,12 +32,10 @@ import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.iface.TraceObjectTogglable; import ghidra.trace.model.target.iface.TraceObjectTogglable;
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils; import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.schema.TraceObjectSchema; import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
public class DBTraceObjectBreakpointSpec public class DBTraceObjectBreakpointSpec
implements TraceObjectBreakpointSpec, DBTraceObjectInterface { implements TraceObjectBreakpointSpec, DBTraceObjectInterface {
@ -69,82 +67,42 @@ public class DBTraceObjectBreakpointSpec
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.setValue(getLifespan(), TraceObjectInterface.KEY_DISPLAY, name); object.setValue(Lifespan.nowOn(snap), TraceObjectInterface.KEY_DISPLAY, name);
} }
} }
@Override @Override
public String getName() { public String getName(long snap) {
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), return TraceObjectInterfaceUtils.getValue(object, snap, TraceObjectInterface.KEY_DISPLAY,
TraceObjectInterface.KEY_DISPLAY, String.class, ""); String.class, "");
} }
@Override @Override
public AddressRange getRange() { public AddressRange getRange(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public Address getMinAddress() { public Address getMinAddress(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public Lifespan getLifespan() { public void setEnabled(long snap, boolean enabled) {
return computeSpan();
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = object.getTrace().lockRead()) {
return object.isAlive(snap);
}
}
@Override
public long getPlacedSnap() {
return computeMinSnap();
}
@Override
public void setClearedSnap(long clearedSnap) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setLifespan(Lifespan.span(getPlacedSnap(), clearedSnap)); object.setValue(Lifespan.nowOn(snap), TraceObjectTogglable.KEY_ENABLED,
}
}
@Override
public long getClearedSnap() {
return computeMaxSnap();
}
@Override
public void setLifespan(Lifespan lifespan) throws DuplicateNameException {
TraceObjectInterfaceUtils.setLifespan(TraceObjectThread.class, object, lifespan);
}
@Override
public TraceBreakpoint splitAndSet(long snap, boolean enabled,
Collection<TraceBreakpointKind> kinds) {
throw new UnsupportedOperationException("Only used by default trace recorder");
}
@Override
public void setEnabled(boolean enabled) {
try (LockHold hold = object.getTrace().lockWrite()) {
object.setValue(getLifespan(), TraceObjectTogglable.KEY_ENABLED,
enabled ? true : null); enabled ? true : null);
} }
} }
@ -167,15 +125,15 @@ public class DBTraceObjectBreakpointSpec
} }
@Override @Override
public void setKinds(Collection<TraceBreakpointKind> kinds) { public void setKinds(long snap, Collection<TraceBreakpointKind> kinds) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setKinds(getLifespan(), kinds); setKinds(Lifespan.nowOn(snap), kinds);
} }
} }
@Override @Override
public Set<TraceBreakpointKind> getKinds() { public Set<TraceBreakpointKind> getKinds(long snap) {
String kindsStr = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), String kindsStr = TraceObjectInterfaceUtils.getValue(object, snap,
TraceObjectBreakpointSpec.KEY_KINDS, String.class, null); TraceObjectBreakpointSpec.KEY_KINDS, String.class, null);
if (kindsStr == null) { if (kindsStr == null) {
return kinds; return kinds;
@ -190,28 +148,28 @@ public class DBTraceObjectBreakpointSpec
} }
@Override @Override
public String getExpression() { public String getExpression(long snap) {
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), return TraceObjectInterfaceUtils.getValue(object, snap,
TraceObjectBreakpointSpec.KEY_EXPRESSION, String.class, null); TraceObjectBreakpointSpec.KEY_EXPRESSION, String.class, null);
} }
@Override @Override
public Set<TraceThread> getThreads() { public Set<TraceThread> getThreads(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public void setComment(String comment) { public void setComment(long snap, String comment) {
throw new UnsupportedOperationException("Set on a location instead"); throw new UnsupportedOperationException("Set on a location instead");
} }
@Override @Override
public String getComment() { public String getComment(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public void setEmuEnabled(boolean enabled) { public void setEmuEnabled(long snap, boolean enabled) {
throw new UnsupportedOperationException("Set on a location instead"); throw new UnsupportedOperationException("Set on a location instead");
} }
@ -221,25 +179,37 @@ public class DBTraceObjectBreakpointSpec
} }
@Override @Override
public void setEmuSleigh(String sleigh) { public void setEmuSleigh(long snap, String sleigh) {
throw new UnsupportedOperationException("Set on a location instead"); throw new UnsupportedOperationException("Set on a location instead");
} }
@Override @Override
public String getEmuSleigh() { public String getEmuSleigh(long snap) {
throw new UnsupportedOperationException("Ask a location instead"); throw new UnsupportedOperationException("Ask a location instead");
} }
@Override @Override
public void delete() { public void delete() {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.removeTree(computeSpan()); object.removeTree(Lifespan.ALL);
}
}
@Override
public void remove(long snap) {
try (LockHold hold = object.getTrace().lockWrite()) {
object.removeTree(Lifespan.nowOn(snap));
} }
} }
@Override @Override
public boolean isValid(long snap) { public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null; return object.isAlive(snap);
}
@Override
public boolean isAlive(Lifespan span) {
return object.isAlive(span);
} }
@Override @Override
@ -247,16 +217,18 @@ public class DBTraceObjectBreakpointSpec
return object; return object;
} }
@Override protected Collection<? extends TraceObjectBreakpointLocation> getLocations(Lifespan span) {
public Collection<? extends TraceObjectBreakpointLocation> getLocations() {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
return object return object.querySuccessorsInterface(span, TraceObjectBreakpointLocation.class, true)
.querySuccessorsInterface(getLifespan(), TraceObjectBreakpointLocation.class,
true)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
} }
@Override
public Collection<? extends TraceObjectBreakpointLocation> getLocations(long snap) {
return getLocations(Lifespan.at(snap));
}
@Override @Override
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) { public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
if (rec.getEventType() == TraceEvents.VALUE_CREATED) { if (rec.getEventType() == TraceEvents.VALUE_CREATED) {
@ -271,9 +243,9 @@ public class DBTraceObjectBreakpointSpec
if (object.getCanonicalParent(affected.getMaxSnap()) == null) { if (object.getCanonicalParent(affected.getMaxSnap()) == null) {
return null; // Incomplete object return null; // Incomplete object
} }
for (TraceObjectBreakpointLocation loc : getLocations()) { for (TraceObjectBreakpointLocation loc : getLocations(affected.getLifespan())) {
DBTraceObjectBreakpointLocation dbLoc = (DBTraceObjectBreakpointLocation) loc; DBTraceObjectBreakpointLocation dbLoc = (DBTraceObjectBreakpointLocation) loc;
TraceAddressSpace space = dbLoc.getTraceAddressSpace(); TraceAddressSpace space = dbLoc.getTraceAddressSpace(affected.getMinSnap());
TraceChangeRecord<?, ?> evt = TraceChangeRecord<?, ?> evt =
new TraceChangeRecord<>(TraceEvents.BREAKPOINT_CHANGED, space, loc, null, null); new TraceChangeRecord<>(TraceEvents.BREAKPOINT_CHANGED, space, loc, null, null);
object.getTrace().setChanged(evt); object.getTrace().setChanged(evt);

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import static ghidra.lifecycle.Unfinished.*; import static ghidra.lifecycle.Unfinished.TODO;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@ -83,7 +83,7 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
if (region == null) { if (region == null) {
return address.toString(showBlockName, pad); return address.toString(showBlockName, pad);
} }
return region.getName() + ":" + address.toString(false, pad); return region.getName(getStartSnap()) + ":" + address.toString(false, pad);
} }
} }

View file

@ -145,7 +145,7 @@ public class DBTraceMemoryRegion
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.name = name; this.name = name;
update(NAME_COLUMN); update(NAME_COLUMN);
@ -155,54 +155,14 @@ public class DBTraceMemoryRegion
} }
@Override @Override
public String getName() { public String getName(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return name; return name;
} }
} }
@Override @Override
public void setLifespan(Lifespan newLifespan) public void setRange(long snap, AddressRange newRange) throws TraceOverlappedRegionException {
throws TraceOverlappedRegionException, DuplicateNameException {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
checkOverlapConflicts(newLifespan, range);
checkPathConflicts(newLifespan, path);
Lifespan oldLifespan = getLifespan();
doSetLifespan(newLifespan);
space.trace.updateViewsChangeRegionBlockLifespan(this, oldLifespan, newLifespan);
space.trace.setChanged(new TraceChangeRecord<>(TraceEvents.REGION_LIFESPAN_CHANGED,
space, this, oldLifespan, newLifespan));
}
}
@Override
public void setCreationSnap(long creationSnap)
throws DuplicateNameException, TraceOverlappedRegionException {
setLifespan(Lifespan.span(creationSnap, getDestructionSnap()));
}
@Override
public long getCreationSnap() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.lmin();
}
}
@Override
public void setDestructionSnap(long destructionSnap)
throws DuplicateNameException, TraceOverlappedRegionException {
setLifespan(Lifespan.span(getCreationSnap(), destructionSnap));
}
@Override
public long getDestructionSnap() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.lmax();
}
}
@Override
public void setRange(AddressRange newRange) throws TraceOverlappedRegionException {
AddressRange oldRange; AddressRange oldRange;
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (range.equals(newRange)) { if (range.equals(newRange)) {
@ -217,51 +177,58 @@ public class DBTraceMemoryRegion
} }
@Override @Override
public void setMinAddress(Address min) throws TraceOverlappedRegionException { public AddressRange getRange(long snap) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
setRange(DBTraceUtils.toRange(min, range.getMaxAddress())); return range;
} }
} }
@Override @Override
public Address getMinAddress() { public void setMinAddress(long snap, Address min) throws TraceOverlappedRegionException {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
setRange(snap, DBTraceUtils.toRange(min, range.getMaxAddress()));
}
}
@Override
public Address getMinAddress(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getMinAddress(); return range.getMinAddress();
} }
} }
@Override @Override
public void setMaxAddress(Address max) throws TraceOverlappedRegionException { public void setMaxAddress(long snap, Address max) throws TraceOverlappedRegionException {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
setRange(DBTraceUtils.toRange(range.getMinAddress(), max)); setRange(snap, DBTraceUtils.toRange(range.getMinAddress(), max));
} }
} }
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getMaxAddress(); return range.getMaxAddress();
} }
} }
@Override @Override
public void setLength(long length) public void setLength(long snap, long length)
throws AddressOverflowException, TraceOverlappedRegionException { throws AddressOverflowException, TraceOverlappedRegionException {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
Address minAddress = range.getMinAddress(); Address minAddress = range.getMinAddress();
setRange(DBTraceUtils.toRange(minAddress, minAddress.addNoWrap(length - 1))); setRange(snap, DBTraceUtils.toRange(minAddress, minAddress.addNoWrap(length - 1)));
} }
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getLength(); return range.getLength();
} }
} }
@Override @Override
public void setFlags(Collection<TraceMemoryFlag> flags) { public void setFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.flagsByte = TraceMemoryFlag.toBits(flags); this.flagsByte = TraceMemoryFlag.toBits(flags);
this.flags.clear(); this.flags.clear();
@ -274,7 +241,7 @@ public class DBTraceMemoryRegion
@SuppressWarnings("hiding") @SuppressWarnings("hiding")
@Override @Override
public void addFlags(Collection<TraceMemoryFlag> flags) { public void addFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.flagsByte |= TraceMemoryFlag.toBits(flags); this.flagsByte |= TraceMemoryFlag.toBits(flags);
this.flags.addAll(flags); this.flags.addAll(flags);
@ -286,7 +253,7 @@ public class DBTraceMemoryRegion
@SuppressWarnings("hiding") @SuppressWarnings("hiding")
@Override @Override
public void clearFlags(Collection<TraceMemoryFlag> flags) { public void clearFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
this.flagsByte &= ~TraceMemoryFlag.toBits(flags); this.flagsByte &= ~TraceMemoryFlag.toBits(flags);
this.flags.removeAll(flags); this.flags.removeAll(flags);
@ -297,7 +264,7 @@ public class DBTraceMemoryRegion
} }
@Override @Override
public Set<TraceMemoryFlag> getFlags() { public Set<TraceMemoryFlag> getFlags(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return Set.copyOf(flags); return Set.copyOf(flags);
} }
@ -308,6 +275,18 @@ public class DBTraceMemoryRegion
space.deleteRegion(this); space.deleteRegion(this);
} }
@Override
public void remove(long snap) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (snap <= lifespan.lmin()) {
space.deleteRegion(this);
}
else if (snap <= lifespan.lmax()) {
doSetLifespan(lifespan.withMax(snap - 1));
}
}
}
@Override @Override
public boolean isValid(long snap) { public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {

View file

@ -32,7 +32,6 @@ import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.schema.TraceObjectSchema; import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.DuplicateNameException;
public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTraceObjectInterface { public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTraceObjectInterface {
@ -128,10 +127,6 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
private final DBTraceObject object; private final DBTraceObject object;
private final RegionChangeTranslator translator; private final RegionChangeTranslator translator;
// Keep copies here for when the object gets invalidated
private AddressRange range;
private Lifespan lifespan;
public DBTraceObjectMemoryRegion(DBTraceObject object) { public DBTraceObjectMemoryRegion(DBTraceObject object) {
this.object = object; this.object = object;
@ -154,101 +149,44 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setName(computeSpan(), name); setName(Lifespan.nowOn(snap), name);
} }
} }
@Override @Override
public String getName() { public String getName(long snap) {
TraceObjectValue value = TraceObjectValue value = object.getValue(snap, TraceObjectInterface.KEY_DISPLAY);
object.getValue(getCreationSnap(), TraceObjectInterface.KEY_DISPLAY);
return value == null ? "" : (String) value.getValue(); return value == null ? "" : (String) value.getValue();
} }
@Override
public void setLifespan(Lifespan newLifespan) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) {
TraceObjectInterfaceUtils.setLifespan(TraceObjectMemoryRegion.class, object,
newLifespan);
this.lifespan = newLifespan;
}
}
@Override
public Lifespan getLifespan() {
try (LockHold hold = object.getTrace().lockRead()) {
Lifespan computed = computeSpan();
if (computed != null) {
lifespan = computed;
}
return lifespan;
}
}
@Override
public void setCreationSnap(long creationSnap) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) {
setLifespan(Lifespan.span(creationSnap, getDestructionSnap()));
}
}
@Override
public long getCreationSnap() {
return computeMinSnap();
}
@Override
public void setDestructionSnap(long destructionSnap) throws DuplicateNameException {
try (LockHold hold = object.getTrace().lockWrite()) {
setLifespan(Lifespan.span(getCreationSnap(), destructionSnap));
}
}
@Override
public long getDestructionSnap() {
return computeMaxSnap();
}
@Override @Override
public void setRange(Lifespan lifespan, AddressRange newRange) { public void setRange(Lifespan lifespan, AddressRange newRange) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.setValue(lifespan, TraceObjectMemoryRegion.KEY_RANGE, newRange); object.setValue(lifespan, TraceObjectMemoryRegion.KEY_RANGE, newRange);
this.range = newRange;
} }
} }
@Override @Override
public void setRange(AddressRange newRange) { public void setRange(long snap, AddressRange newRange) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setRange(computeSpan(), newRange); setRange(Lifespan.nowOn(snap), newRange);
} }
} }
@Override @Override
public AddressRange getRange(long snap) { public AddressRange getRange(long snap) {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockRead()) {
// TODO: Caching without regard to snap seems bad return TraceObjectInterfaceUtils.getValue(object, snap,
return range = TraceObjectInterfaceUtils.getValue(object, snap, TraceObjectMemoryRegion.KEY_RANGE, AddressRange.class, null);
TraceObjectMemoryRegion.KEY_RANGE, AddressRange.class, range);
} }
} }
@Override @Override
public AddressRange getRange() { public void setMinAddress(long snap, Address min) {
try (LockHold hold = object.getTrace().lockRead()) {
if (object.getLife().isEmpty()) {
return range;
}
return getRange(getCreationSnap());
}
}
@Override
public void setMinAddress(Address min) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setRange(DBTraceUtils.toRange(min, getMaxAddress())); setRange(Lifespan.nowOn(snap), DBTraceUtils.toRange(min, getMaxAddress(snap)));
} }
} }
@ -259,15 +197,9 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
@Override @Override
public Address getMinAddress() { public void setMaxAddress(long snap, Address max) {
AddressRange range = getRange();
return range == null ? null : range.getMinAddress();
}
@Override
public void setMaxAddress(Address max) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setRange(DBTraceUtils.toRange(getMinAddress(), max)); setRange(Lifespan.nowOn(snap), DBTraceUtils.toRange(getMinAddress(snap), max));
} }
} }
@ -278,21 +210,16 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
@Override @Override
public Address getMaxAddress() { public void setLength(long snap, long length) throws AddressOverflowException {
AddressRange range = getRange();
return range == null ? null : range.getMaxAddress();
}
@Override
public void setLength(long length) throws AddressOverflowException {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setRange(new AddressRangeImpl(getMinAddress(), length)); setRange(Lifespan.nowOn(snap), new AddressRangeImpl(getMinAddress(snap), length));
} }
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
return getRange().getLength(); AddressRange range = getRange(snap);
return range == null ? 0 : range.getLength();
} }
protected static String keyForFlag(TraceMemoryFlag flag) { protected static String keyForFlag(TraceMemoryFlag flag) {
@ -333,23 +260,23 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
@Override @Override
public void setFlags(Collection<TraceMemoryFlag> flags) { public void setFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
setFlags(getLifespan(), flags); setFlags(Lifespan.nowOn(snap), flags);
} }
} }
@Override @Override
public void addFlags(Collection<TraceMemoryFlag> flags) { public void addFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
addFlags(getLifespan(), flags); addFlags(Lifespan.nowOn(snap), flags);
} }
} }
@Override @Override
public void clearFlags(Collection<TraceMemoryFlag> flags) { public void clearFlags(long snap, Collection<TraceMemoryFlag> flags) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
clearFlags(getLifespan(), flags); clearFlags(Lifespan.nowOn(snap), flags);
} }
} }
@ -366,22 +293,22 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
@Override @Override
public Set<TraceMemoryFlag> getFlags() { public void delete() {
try (LockHold hold = object.getTrace().lockRead()) { try (LockHold hold = object.getTrace().lockWrite()) {
return getFlags(getCreationSnap()); object.removeTree(Lifespan.ALL);
} }
} }
@Override @Override
public void delete() { public void remove(long snap) {
try (LockHold hold = object.getTrace().lockWrite()) { try (LockHold hold = object.getTrace().lockWrite()) {
object.removeTree(computeSpan()); object.removeTree(Lifespan.nowOn(snap));
} }
} }
@Override @Override
public boolean isValid(long snap) { public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null; return object.isAlive(snap);
} }
@Override @Override

View file

@ -56,14 +56,14 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
} }
@Override @Override
public int getBitLength() { public int getBitLength(long snap) {
return TraceObjectInterfaceUtils.getValue(object, computeMinSnap(), return TraceObjectInterfaceUtils.getValue(object, snap, TraceObjectRegister.KEY_BITLENGTH,
TraceObjectRegister.KEY_BITLENGTH, Integer.class, 0); Integer.class, 0);
} }
@Override @Override
public void setValue(Lifespan lifespan, byte[] value) { public void setValue(Lifespan lifespan, byte[] value) {
int length = getByteLength(); int length = getByteLength(lifespan.lmin());
if (length != 0 && value.length != length) { if (length != 0 && value.length != length) {
throw new IllegalArgumentException("Length must match the register"); throw new IllegalArgumentException("Length must match the register");
} }
@ -77,14 +77,14 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
return null; return null;
} }
Object val = ov.getValue(); Object val = ov.getValue();
if (val instanceof byte[]) { if (val instanceof byte[] arr) {
// TODO: Should I correct mismatched size? // TODO: Should I correct mismatched size?
return (byte[]) val; return arr;
} }
if (val instanceof String) { if (val instanceof String str) {
// Always base 16. Model API says byte array for register value is big endian. // Always base 16. Model API says byte array for register value is big endian.
BigInteger bigVal = new BigInteger((String) val, 16); BigInteger bigVal = new BigInteger(str, 16);
return Utils.bigIntegerToBytes(bigVal, getByteLength(), true); return Utils.bigIntegerToBytes(bigVal, getByteLength(snap), true);
} }
throw new ClassCastException("Cannot convert " + val + " to byte array for register value"); throw new ClassCastException("Cannot convert " + val + " to byte array for register value");
} }

View file

@ -15,7 +15,8 @@
*/ */
package ghidra.trace.database.module; package ghidra.trace.database.module;
import java.util.*; import java.util.Collection;
import java.util.Objects;
import db.DBRecord; import db.DBRecord;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -87,8 +88,8 @@ public class DBTraceModule extends AbstractDBTraceAddressSnapRangePropertyMapDat
} }
@Override @Override
public DBTraceSection addSection(String sectionPath, String sectionName, AddressRange range) public DBTraceSection addSection(long snap, String sectionPath, String sectionName,
throws DuplicateNameException { AddressRange range) throws DuplicateNameException {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) { try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
return space.manager.doAddSection(this, sectionPath, sectionName, range); return space.manager.doAddSection(this, sectionPath, sectionName, range);
} }
@ -102,7 +103,7 @@ public class DBTraceModule extends AbstractDBTraceAddressSnapRangePropertyMapDat
} }
@Override @Override
public void setName(String name) { public void setName(long snap, String name) {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) { try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
if (Objects.equals(this.name, name)) { if (Objects.equals(this.name, name)) {
return; return;
@ -114,14 +115,14 @@ public class DBTraceModule extends AbstractDBTraceAddressSnapRangePropertyMapDat
} }
@Override @Override
public String getName() { public String getName(long snap) {
try (LockHold hold = LockHold.lock(space.manager.readLock())) { try (LockHold hold = LockHold.lock(space.manager.readLock())) {
return name; return name;
} }
} }
@Override @Override
public void setRange(AddressRange range) { public void setRange(long snap, AddressRange range) {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) { try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
if (this.range.equals(range)) { if (this.range.equals(range)) {
return; return;
@ -132,106 +133,67 @@ public class DBTraceModule extends AbstractDBTraceAddressSnapRangePropertyMapDat
} }
@Override @Override
public void setBase(Address base) { public AddressRange getRange(long snap) {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
setRange(DBTraceUtils.toRange(base, range.getMaxAddress())); return range;
} }
} }
@Override @Override
public Address getBase() { public void setBase(long snap, Address base) {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
setRange(snap, DBTraceUtils.toRange(base, range.getMaxAddress()));
}
}
@Override
public Address getBase(long snap) {
try (LockHold hold = LockHold.lock(space.manager.readLock())) { try (LockHold hold = LockHold.lock(space.manager.readLock())) {
return range.getMinAddress(); return range.getMinAddress();
} }
} }
@Override @Override
public void setMaxAddress(Address max) { public void setMaxAddress(long snap, Address max) {
try (LockHold hold = LockHold.lock(space.manager.writeLock())) { try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
setRange(DBTraceUtils.toRange(range.getMinAddress(), max)); setRange(snap, DBTraceUtils.toRange(range.getMinAddress(), max));
} }
} }
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getMaxAddress(); return range.getMaxAddress();
} }
} }
@Override @Override
public void setLength(long length) throws AddressOverflowException { public void setLength(long snap, long length) throws AddressOverflowException {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
Address base = range.getMinAddress(); Address base = range.getMinAddress();
setRange(DBTraceUtils.toRange(base, base.addNoWrap(length - 1))); setRange(snap, DBTraceUtils.toRange(base, base.addNoWrap(length - 1)));
} }
} }
@Override @Override
public long getLength() { public long getLength(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return range.getLength(); return range.getLength();
} }
} }
@Override @Override
public void setLifespan(Lifespan newLifespan) throws DuplicateNameException { public Collection<? extends DBTraceSection> getSections(long snap) {
Lifespan oldLifespan; return getAllSections();
try (LockHold hold = LockHold.lock(space.manager.writeLock())) {
space.manager.checkModulePathConflicts(this, path, newLifespan);
ArrayList<? extends DBTraceSection> sections = new ArrayList<>(getSections());
for (DBTraceSection traceSection : sections) {
space.manager.checkSectionPathConflicts(traceSection, traceSection.getPath(),
newLifespan);
}
oldLifespan = this.lifespan;
doSetLifespan(newLifespan);
for (DBTraceSection traceSection : sections) {
traceSection.doSetLifespan(newLifespan);
}
}
space.trace.setChanged(new TraceChangeRecord<>(TraceEvents.MODULE_LIFESPAN_CHANGED,
null, this, oldLifespan, newLifespan));
} }
@Override @Override
public Lifespan getLifespan() { public Collection<? extends DBTraceSection> getAllSections() {
try (LockHold hold = LockHold.lock(space.manager.readLock())) {
return lifespan;
}
}
@Override
public void setLoadedSnap(long loadedSnap) throws DuplicateNameException {
setLifespan(Lifespan.span(loadedSnap, getUnloadedSnap()));
}
@Override
public long getLoadedSnap() {
try (LockHold hold = LockHold.lock(space.manager.readLock())) {
return lifespan.lmin();
}
}
@Override
public void setUnloadedSnap(long unloadedSnap) throws DuplicateNameException {
setLifespan(Lifespan.span(getLoadedSnap(), unloadedSnap));
}
@Override
public long getUnloadedSnap() {
try (LockHold hold = LockHold.lock(space.manager.readLock())) {
return lifespan.lmax();
}
}
@Override
public Collection<? extends DBTraceSection> getSections() {
return space.manager.doGetSectionsByModuleId(getKey()); return space.manager.doGetSectionsByModuleId(getKey());
} }
@Override @Override
public TraceSection getSectionByName(String sectionName) { public TraceSection getSectionByName(long snap, String sectionName) {
return space.manager.doGetSectionByName(getKey(), sectionName); return space.manager.doGetSectionByName(getKey(), sectionName);
} }
@ -239,4 +201,30 @@ public class DBTraceModule extends AbstractDBTraceAddressSnapRangePropertyMapDat
public void delete() { public void delete() {
space.manager.doDeleteModule(this); space.manager.doDeleteModule(this);
} }
@Override
public void remove(long snap) {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
if (snap <= lifespan.lmin()) {
space.manager.doDeleteModule(this);
}
else if (snap <= lifespan.lmax()) {
doSetLifespan(lifespan.withMax(snap - 1));
}
}
}
@Override
public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap);
}
}
@Override
public boolean isAlive(Lifespan span) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.intersects(span);
}
}
} }

View file

@ -65,7 +65,7 @@ public class DBTraceModuleManager extends AbstractDBTraceSpaceBasedManager<DBTra
if (pc == ignore) { if (pc == ignore) {
continue; continue;
} }
if (!pc.getLifespan().intersects(moduleLifespan)) { if (!pc.isAlive(moduleLifespan)) {
continue; continue;
} }
throw new DuplicateNameException( throw new DuplicateNameException(
@ -85,7 +85,7 @@ public class DBTraceModuleManager extends AbstractDBTraceSpaceBasedManager<DBTra
* so have the same lifespan, no? I suppose this logic is only true in objects mode..., * so have the same lifespan, no? I suppose this logic is only true in objects mode...,
* and there, the logic is performed by the value key duplicate check. * and there, the logic is performed by the value key duplicate check.
*/ */
if (!pc.getModule().getLifespan().intersects(moduleLifespan)) { if (!pc.getModule().isAlive(moduleLifespan)) {
continue; continue;
} }
throw new DuplicateNameException("Section with path '" + sectionPath + throw new DuplicateNameException("Section with path '" + sectionPath +
@ -131,7 +131,7 @@ public class DBTraceModuleManager extends AbstractDBTraceSpaceBasedManager<DBTra
} }
try (LockHold hold = LockHold.lock(lock.readLock())) { try (LockHold hold = LockHold.lock(lock.readLock())) {
return doGetModulesByPath(modulePath).stream() return doGetModulesByPath(modulePath).stream()
.filter(m -> m.getLifespan().contains(snap)) .filter(m -> m.isValid(snap))
.findAny() .findAny()
.orElse(null); .orElse(null);
} }
@ -272,7 +272,7 @@ public class DBTraceModuleManager extends AbstractDBTraceSpaceBasedManager<DBTra
} }
try (LockHold hold = LockHold.lock(lock.readLock())) { try (LockHold hold = LockHold.lock(lock.readLock())) {
return doGetSectionsByPath(sectionPath).stream() return doGetSectionsByPath(sectionPath).stream()
.filter(s -> s.getModule().getLifespan().contains(snap)) .filter(s -> s.getModule().isValid(snap))
.findAny() .findAny()
.orElse(null); .orElse(null);
} }

View file

@ -164,7 +164,8 @@ public class DBTraceModuleSpace implements TraceModuleSpace, DBTraceSpaceBased {
public DBTraceSection doGetSectionByName(long moduleKey, String sectionName) { public DBTraceSection doGetSectionByName(long moduleKey, String sectionName) {
for (DBTraceSection section : sectionsByModuleKey.get(moduleKey)) { for (DBTraceSection section : sectionsByModuleKey.get(moduleKey)) {
if (!Objects.equals(section.getName(), sectionName)) { // Legacy manager does not consider snap in name
if (!Objects.equals(section.getName(0), sectionName)) {
continue; continue;
} }
return section; return section;

Some files were not shown because too many files have changed in this diff Show more