mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 05:24:53 +08:00
Merge remote-tracking branch 'origin/GP-1711_Dan_traceDisassemblyConflicts--SQUASHED'
This commit is contained in:
+36
-8
@@ -20,12 +20,14 @@ import java.util.Map.Entry;
|
|||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.trace.database.*;
|
import ghidra.trace.database.DBTraceCacheForContainingQueries;
|
||||||
import ghidra.trace.database.DBTraceCacheForContainingQueries.GetKey;
|
import ghidra.trace.database.DBTraceCacheForContainingQueries.GetKey;
|
||||||
|
import ghidra.trace.database.DBTraceCacheForSequenceQueries;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapAddressSetView;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapAddressSetView;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||||
|
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.Trace.TraceCodeChangeType;
|
import ghidra.trace.model.Trace.TraceCodeChangeType;
|
||||||
import ghidra.trace.model.listing.TraceBaseDefinedUnitsView;
|
import ghidra.trace.model.listing.TraceBaseDefinedUnitsView;
|
||||||
@@ -359,20 +361,28 @@ public abstract class AbstractBaseDBTraceDefinedUnitsView<T extends AbstractDBTr
|
|||||||
* Select a sub-lifespan from that given so that the box does not overlap an existing unit
|
* Select a sub-lifespan from that given so that the box does not overlap an existing unit
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The selected lifespan will have the same start snap at that given. The box is the bounding
|
* The selected lifespan will have the same start snap as that given. The box is the bounding
|
||||||
* box of a unit the client is trying to create.
|
* box of a unit the client is trying to create.
|
||||||
*
|
*
|
||||||
* @param span the lifespan of the box
|
* @param span the lifespan of the box
|
||||||
|
* @param extending if applicable, the unit whose lifespan is being extended
|
||||||
* @param range the address range of the box
|
* @param range the address range of the box
|
||||||
* @return the selected sub-lifespan
|
* @return the selected sub-lifespan
|
||||||
* @throws CodeUnitInsertionException if the start snap is contained in an existing unit
|
* @throws CodeUnitInsertionException if the start snap is contained in an existing unit
|
||||||
*/
|
*/
|
||||||
protected Lifespan truncateSoonestDefined(Lifespan span, AddressRange range)
|
protected Lifespan truncateSoonestDefined(Lifespan span, AbstractDBTraceCodeUnit<?> extending,
|
||||||
throws CodeUnitInsertionException {
|
AddressRange range) throws CodeUnitInsertionException {
|
||||||
|
final Lifespan toScan;
|
||||||
|
if (extending == null) {
|
||||||
|
toScan = span;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert span.lmax() > extending.getEndSnap();
|
||||||
|
toScan = span.withMin(extending.getEndSnap() + 1);
|
||||||
|
}
|
||||||
T truncateBy =
|
T truncateBy =
|
||||||
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range, span)
|
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range, toScan)
|
||||||
.starting(
|
.starting(Rectangle2DDirection.BOTTOMMOST))
|
||||||
Rectangle2DDirection.BOTTOMMOST))
|
|
||||||
.firstValue();
|
.firstValue();
|
||||||
if (truncateBy == null) {
|
if (truncateBy == null) {
|
||||||
return span;
|
return span;
|
||||||
@@ -380,7 +390,25 @@ public abstract class AbstractBaseDBTraceDefinedUnitsView<T extends AbstractDBTr
|
|||||||
if (truncateBy.getStartSnap() <= span.lmin()) {
|
if (truncateBy.getStartSnap() <= span.lmin()) {
|
||||||
throw new CodeUnitInsertionException("Code units cannot overlap");
|
throw new CodeUnitInsertionException("Code units cannot overlap");
|
||||||
}
|
}
|
||||||
return Lifespan.span(span.lmin(), truncateBy.getStartSnap() - 1);
|
return span.withMax(truncateBy.getStartSnap() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long computeTruncatedMax(Lifespan lifespan, T extending, AddressRange range)
|
||||||
|
throws CodeUnitInsertionException {
|
||||||
|
// First, truncate lifespan to the next code unit when upper bound is max
|
||||||
|
if (!lifespan.maxIsFinite()) {
|
||||||
|
lifespan = space.instructions.truncateSoonestDefined(lifespan, extending, range);
|
||||||
|
lifespan = space.definedData.truncateSoonestDefined(lifespan, extending, range);
|
||||||
|
}
|
||||||
|
// Second, truncate lifespan to the next change of bytes in the range
|
||||||
|
DBTraceMemorySpace memSpace =
|
||||||
|
space.trace.getMemoryManager().getMemorySpace(space.space, true);
|
||||||
|
Lifespan fullSpan = extending == null ? lifespan : lifespan.bound(extending.getLifespan());
|
||||||
|
long endSnap = memSpace.getFirstChange(fullSpan, range);
|
||||||
|
if (endSnap == Long.MIN_VALUE) {
|
||||||
|
return lifespan.lmax();
|
||||||
|
}
|
||||||
|
return endSnap - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+7
-3
@@ -20,7 +20,6 @@ import java.nio.ByteBuffer;
|
|||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.trace.database.DBTrace;
|
import ghidra.trace.database.DBTrace;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||||
@@ -123,8 +122,13 @@ public abstract class AbstractDBTraceCodeUnit<T extends AbstractDBTraceCodeUnit<
|
|||||||
}
|
}
|
||||||
// Copy from the cache
|
// Copy from the cache
|
||||||
int toCopyFromCache =
|
int toCopyFromCache =
|
||||||
Math.max(0, Math.min(byteCache.position() - addressOffset, buffer.remaining()));
|
Math.min(byteCache.position() - addressOffset, buffer.remaining());
|
||||||
buffer.put(byteCache.array(), addressOffset, toCopyFromCache);
|
if (toCopyFromCache > 0) {
|
||||||
|
buffer.put(byteCache.array(), addressOffset, toCopyFromCache);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toCopyFromCache = 0;
|
||||||
|
}
|
||||||
if (byteCache.position() >= end) {
|
if (byteCache.position() >= end) {
|
||||||
return toCopyFromCache;
|
return toCopyFromCache;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -20,7 +20,8 @@ import java.io.IOException;
|
|||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.data.TypeDef;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.trace.database.DBTrace;
|
import ghidra.trace.database.DBTrace;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
|
|||||||
+5
-18
@@ -131,21 +131,8 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
|
|||||||
Address endAddress = address.addNoWrap(length - 1);
|
Address endAddress = address.addNoWrap(length - 1);
|
||||||
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||||
|
|
||||||
// First, truncate lifespan to the next code unit when upper bound is max
|
// Truncate, then check that against existing code units.
|
||||||
if (!lifespan.maxIsFinite()) {
|
long endSnap = computeTruncatedMax(lifespan, null, createdRange);
|
||||||
lifespan = space.instructions.truncateSoonestDefined(lifespan, createdRange);
|
|
||||||
lifespan = space.definedData.truncateSoonestDefined(lifespan, createdRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second, extend to the next change of bytes in the range within lifespan
|
|
||||||
// Then, check that against existing code units.
|
|
||||||
long endSnap = memSpace.getFirstChange(lifespan, createdRange);
|
|
||||||
if (endSnap == Long.MIN_VALUE) {
|
|
||||||
endSnap = lifespan.lmax();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
endSnap--;
|
|
||||||
}
|
|
||||||
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(createdRange,
|
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(createdRange,
|
||||||
Lifespan.span(startSnap, endSnap));
|
Lifespan.span(startSnap, endSnap));
|
||||||
if (!space.undefinedData.coversRange(tasr)) {
|
if (!space.undefinedData.coversRange(tasr)) {
|
||||||
@@ -158,13 +145,13 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
long dataTypeID = space.dataTypeManager.getResolvedID(dataType);
|
long dataTypeID = space.dataTypeManager.getResolvedID(dataType);
|
||||||
DBTraceData created = space.dataMapSpace.put(tasr, null);
|
DBTraceData created = mapSpace.put(tasr, null);
|
||||||
// TODO: data units with a guest platform
|
// TODO: data units with a guest platform
|
||||||
created.set(space.trace.getPlatformManager().getHostPlatform(), dataTypeID);
|
created.set(space.trace.getPlatformManager().getHostPlatform(), dataTypeID);
|
||||||
// TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
|
// TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
|
||||||
|
|
||||||
cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
|
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
cacheForSequence.notifyNewEntry(lifespan, createdRange, created);
|
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
space.undefinedData.invalidateCache();
|
space.undefinedData.invalidateCache();
|
||||||
|
|
||||||
if (dataType instanceof Composite || dataType instanceof Array ||
|
if (dataType instanceof Composite || dataType instanceof Array ||
|
||||||
|
|||||||
+122
-47
@@ -23,11 +23,11 @@ import ghidra.program.model.address.*;
|
|||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.Trace.TraceCodeChangeType;
|
import ghidra.trace.model.Trace.TraceCodeChangeType;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
@@ -59,6 +59,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
private final Address errorAddress;
|
private final Address errorAddress;
|
||||||
private final InstructionError conflict;
|
private final InstructionError conflict;
|
||||||
private final CodeUnit conflictCodeUnit;
|
private final CodeUnit conflictCodeUnit;
|
||||||
|
private final boolean overwrite;
|
||||||
|
|
||||||
protected int count = 0;
|
protected int count = 0;
|
||||||
|
|
||||||
@@ -75,10 +76,11 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
* @param errorAddress the address of the first error in the block, if any
|
* @param errorAddress the address of the first error in the block, if any
|
||||||
* @param conflict a description of the error, if any
|
* @param conflict a description of the error, if any
|
||||||
* @param conflictCodeUnit if a conflict, the code unit that already exists
|
* @param conflictCodeUnit if a conflict, the code unit that already exists
|
||||||
|
* @param overwrite true to overwrite existing defined units
|
||||||
*/
|
*/
|
||||||
private InstructionBlockAdder(Set<Address> skipDelaySlots, Lifespan lifespan,
|
private InstructionBlockAdder(Set<Address> skipDelaySlots, Lifespan lifespan,
|
||||||
InternalTracePlatform platform, InstructionBlock block, Address errorAddress,
|
InternalTracePlatform platform, InstructionBlock block, Address errorAddress,
|
||||||
InstructionError conflict, CodeUnit conflictCodeUnit) {
|
InstructionError conflict, CodeUnit conflictCodeUnit, boolean overwrite) {
|
||||||
this.skipDelaySlots = skipDelaySlots;
|
this.skipDelaySlots = skipDelaySlots;
|
||||||
this.lifespan = lifespan;
|
this.lifespan = lifespan;
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
@@ -86,20 +88,113 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
this.errorAddress = errorAddress;
|
this.errorAddress = errorAddress;
|
||||||
this.conflict = conflict;
|
this.conflict = conflict;
|
||||||
this.conflictCodeUnit = conflictCodeUnit;
|
this.conflictCodeUnit = conflictCodeUnit;
|
||||||
|
this.overwrite = overwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void truncateOrDelete(TraceInstruction exists) {
|
||||||
|
if (exists.getStartSnap() < lifespan.lmin()) {
|
||||||
|
exists.setEndSnap(lifespan.lmin());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exists.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
|
||||||
|
try {
|
||||||
|
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
|
||||||
|
Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
|
||||||
|
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
|
||||||
|
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
|
||||||
|
candidate.getFlowOverride() == protoInstr.getFlowOverride();
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
|
||||||
|
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
|
||||||
|
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
|
||||||
|
if (exists == null || exists.getLength() != protoInstr.getLength()) {
|
||||||
|
AddressRange range =
|
||||||
|
new AddressRangeImpl(protoInstr.getAddress(), protoInstr.getLength());
|
||||||
|
space.definedUnits.clear(lifespan, range, false, TaskMonitor.DUMMY);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isSuitable(exists, protoInstr)) {
|
||||||
|
truncateOrDelete(exists);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long curEnd = exists.getEndSnap();
|
||||||
|
if (curEnd < lifespan.lmax()) {
|
||||||
|
AddressRange range =
|
||||||
|
new AddressRangeImpl(protoInstr.getAddress(), protoInstr.getLength());
|
||||||
|
long endSnap = computeTruncatedMax(lifespan, exists, range);
|
||||||
|
exists.setEndSnap(endSnap);
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the preceding unit and see if it can be extended to "create" the desired one
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For overwrite, the caller should first use
|
||||||
|
* {@link #doAdjustExisting(Address, InstructionPrototype, Instruction)}.
|
||||||
|
*
|
||||||
|
* @param address the starting address of the instruction
|
||||||
|
* @param protoInstr the prototype instruction
|
||||||
|
* @return the extended instruction, if it was a match
|
||||||
|
* @throws AddressOverflowException should never
|
||||||
|
* @throws CodeUnitInsertionException if there's no room to extend the preceding unit
|
||||||
|
*/
|
||||||
|
protected Instruction doExtendPreceding(Address address, Instruction protoInstr)
|
||||||
|
throws AddressOverflowException, CodeUnitInsertionException {
|
||||||
|
if (!lifespan.minIsFinite()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
DBTraceInstruction prec = getAt(lifespan.lmin() - 1, address);
|
||||||
|
if (prec == null || prec.getLength() != protoInstr.getLength()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isSuitable(prec, protoInstr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AddressRange range =
|
||||||
|
new AddressRangeImpl(protoInstr.getAddress(), protoInstr.getLength());
|
||||||
|
long endSnap = computeTruncatedMax(lifespan, prec, range);
|
||||||
|
if (!lifespan.contains(endSnap)) {
|
||||||
|
// We can't extend it far enough
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
prec.setEndSnap(endSnap);
|
||||||
|
return prec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the given instruction in the database
|
* Store the given instruction in the database
|
||||||
*
|
*
|
||||||
* @param address the address of the instruction
|
* @param address the address of the instruction
|
||||||
* @param prototype the instruction prototype
|
|
||||||
* @param protoInstr the parsed (usually pseudo) instruction
|
* @param protoInstr the parsed (usually pseudo) instruction
|
||||||
* @return the created instruction
|
* @return the created instruction
|
||||||
*/
|
*/
|
||||||
protected Instruction doCreateInstruction(Address address,
|
protected Instruction doCreateInstruction(Address address, Instruction protoInstr) {
|
||||||
InstructionPrototype prototype, Instruction protoInstr) {
|
|
||||||
try {
|
try {
|
||||||
Instruction created = doCreate(lifespan, address, platform, prototype, protoInstr);
|
if (overwrite) {
|
||||||
|
Instruction exists = doAdjustExisting(address, protoInstr);
|
||||||
|
if (exists != null) {
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction prec = doExtendPreceding(address, protoInstr);
|
||||||
|
if (prec != null) {
|
||||||
|
return prec;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction created =
|
||||||
|
doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr);
|
||||||
// copy override settings to replacement instruction
|
// copy override settings to replacement instruction
|
||||||
if (protoInstr.isFallThroughOverridden()) {
|
if (protoInstr.isFallThroughOverridden()) {
|
||||||
created.setFallThrough(protoInstr.getFallThrough());
|
created.setFallThrough(protoInstr.getFallThrough());
|
||||||
@@ -110,9 +205,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
}
|
}
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
catch (CodeUnitInsertionException | AddressOverflowException e) {
|
catch (AddressOverflowException | // End address already checked via protoInstr
|
||||||
// End address already computed when protoInstr created.
|
CodeUnitInsertionException | // We've already checked for conflicts
|
||||||
// We've also already checked for conflicts
|
CancelledException e) { // There's no monitor to cancel
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,8 +255,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skipDelaySlots.contains(startAddress)) {
|
if (!skipDelaySlots.contains(startAddress)) {
|
||||||
InstructionPrototype prototype = protoInstr.getPrototype();
|
if (!areDelaySlots && protoInstr.getPrototype().hasDelaySlots()) {
|
||||||
if (!areDelaySlots && prototype.hasDelaySlots()) {
|
|
||||||
// Reverse their order then add them. This ensures pcode can be generated
|
// Reverse their order then add them. This ensures pcode can be generated
|
||||||
// for the delay-slotted instruction upon its creation.
|
// for the delay-slotted instruction upon its creation.
|
||||||
Deque<Instruction> delayed =
|
Deque<Instruction> delayed =
|
||||||
@@ -172,8 +266,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
lastInstruction = replaceIfNotNull(lastInstruction,
|
lastInstruction = replaceIfNotNull(lastInstruction,
|
||||||
doAddInstructions(delayed.iterator(), true));
|
doAddInstructions(delayed.iterator(), true));
|
||||||
}
|
}
|
||||||
lastInstruction =
|
lastInstruction = doCreateInstruction(startAddress, protoInstr);
|
||||||
doCreateInstruction(startAddress, prototype, protoInstr);
|
|
||||||
}
|
}
|
||||||
if (errorAddress != null && conflictCodeUnit == null &&
|
if (errorAddress != null && conflictCodeUnit == null &&
|
||||||
errorAddress.compareTo(startAddress) <= 0) {
|
errorAddress.compareTo(startAddress) <= 0) {
|
||||||
@@ -259,23 +352,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
Address endAddress = address.addNoWrap(prototype.getLength() - 1);
|
Address endAddress = address.addNoWrap(prototype.getLength() - 1);
|
||||||
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||||
|
|
||||||
// First, truncate lifespan to the next code unit when upper bound is max
|
// Truncate, then check that against existing code units.
|
||||||
if (!lifespan.maxIsFinite()) {
|
long endSnap = computeTruncatedMax(lifespan, null, createdRange);
|
||||||
lifespan = space.instructions.truncateSoonestDefined(lifespan, createdRange);
|
|
||||||
lifespan = space.definedData.truncateSoonestDefined(lifespan, createdRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second, truncate lifespan to the next change of bytes in the range
|
|
||||||
// Then, check that against existing code units.
|
|
||||||
DBTraceMemorySpace memSpace =
|
|
||||||
space.trace.getMemoryManager().getMemorySpace(space.space, true);
|
|
||||||
long endSnap = memSpace.getFirstChange(lifespan, createdRange);
|
|
||||||
if (endSnap == Long.MIN_VALUE) {
|
|
||||||
endSnap = lifespan.lmax();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
endSnap--;
|
|
||||||
}
|
|
||||||
TraceAddressSnapRange tasr =
|
TraceAddressSnapRange tasr =
|
||||||
new ImmutableTraceAddressSnapRange(createdRange, lifespan.withMax(endSnap));
|
new ImmutableTraceAddressSnapRange(createdRange, lifespan.withMax(endSnap));
|
||||||
|
|
||||||
@@ -289,8 +367,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||||
created.set(platform, prototype, context);
|
created.set(platform, prototype, context);
|
||||||
|
|
||||||
cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
|
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
cacheForSequence.notifyNewEntry(lifespan, createdRange, created);
|
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
space.undefinedData.invalidateCache();
|
space.undefinedData.invalidateCache();
|
||||||
|
|
||||||
// TODO: Save the context register into the context manager? Flow it?
|
// TODO: Save the context register into the context manager? Flow it?
|
||||||
@@ -348,14 +426,19 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
* @param skipDelaySlots the addresses of delay-slotted instructions to skip
|
* @param skipDelaySlots the addresses of delay-slotted instructions to skip
|
||||||
* @param platform the instructions' platform (language, compiler)
|
* @param platform the instructions' platform (language, compiler)
|
||||||
* @param block the block of instructions to add
|
* @param block the block of instructions to add
|
||||||
|
* @param overwrite true to overwrite existing defined units
|
||||||
* @return the adder, or null
|
* @return the adder, or null
|
||||||
*/
|
*/
|
||||||
protected InstructionBlockAdder startAddingBlock(Lifespan lifespan,
|
protected InstructionBlockAdder startAddingBlock(Lifespan lifespan, Set<Address> skipDelaySlots,
|
||||||
Set<Address> skipDelaySlots, InternalTracePlatform platform, InstructionBlock block) {
|
InternalTracePlatform platform, InstructionBlock block, boolean overwrite) {
|
||||||
|
if (overwrite) {
|
||||||
|
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
|
||||||
|
null, overwrite);
|
||||||
|
}
|
||||||
InstructionError conflict = block.getInstructionConflict();
|
InstructionError conflict = block.getInstructionConflict();
|
||||||
if (conflict == null) {
|
if (conflict == null) {
|
||||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
|
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
|
||||||
null);
|
null, overwrite);
|
||||||
}
|
}
|
||||||
Address errorAddress = conflict.getInstructionAddress();
|
Address errorAddress = conflict.getInstructionAddress();
|
||||||
if (errorAddress == null) {
|
if (errorAddress == null) {
|
||||||
@@ -363,13 +446,13 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
}
|
}
|
||||||
if (!conflict.getInstructionErrorType().isConflict) {
|
if (!conflict.getInstructionErrorType().isConflict) {
|
||||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block,
|
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block,
|
||||||
errorAddress, conflict, null);
|
errorAddress, conflict, null, overwrite);
|
||||||
}
|
}
|
||||||
long startSnap = lifespan.lmin();
|
long startSnap = lifespan.lmin();
|
||||||
CodeUnit conflictCodeUnit =
|
CodeUnit conflictCodeUnit =
|
||||||
space.definedUnits.getAt(startSnap, conflict.getConflictAddress());
|
space.definedUnits.getAt(startSnap, conflict.getConflictAddress());
|
||||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, errorAddress,
|
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, errorAddress,
|
||||||
conflict, conflictCodeUnit);
|
conflict, conflictCodeUnit, overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -468,19 +551,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||||
long startSnap = lifespan.lmin();
|
long startSnap = lifespan.lmin();
|
||||||
Set<Address> skipDelaySlots = new HashSet<>();
|
Set<Address> skipDelaySlots = new HashSet<>();
|
||||||
if (overwrite) {
|
if (!overwrite) {
|
||||||
for (AddressRange range : instructionSet.getAddressSet()) {
|
|
||||||
space.definedUnits.clear(lifespan, range, false, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
checkInstructionSet(startSnap, instructionSet, skipDelaySlots);
|
checkInstructionSet(startSnap, instructionSet, skipDelaySlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add blocks
|
// Add blocks
|
||||||
for (InstructionBlock block : instructionSet) {
|
for (InstructionBlock block : instructionSet) {
|
||||||
InstructionBlockAdder adder =
|
InstructionBlockAdder adder =
|
||||||
startAddingBlock(lifespan, skipDelaySlots, dbPlatform, block);
|
startAddingBlock(lifespan, skipDelaySlots, dbPlatform, block, overwrite);
|
||||||
if (adder == null) {
|
if (adder == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -496,9 +574,6 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
|
||||||
throw new AssertionError(e); // No actual monitor
|
|
||||||
}
|
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
// Better have skipped any delay-slotted instructions whose delays overflowed
|
// Better have skipped any delay-slotted instructions whose delays overflowed
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
|
|||||||
+11
-4
@@ -299,21 +299,28 @@ public class DBTraceAddressSnapRangePropertyMapTree<T, DR extends AbstractDBTrac
|
|||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "hiding" })
|
@SuppressWarnings("unchecked")
|
||||||
protected void doSetRange(AddressRange range) {
|
protected void doSetRange(AddressRange range) {
|
||||||
|
long newMinOffset = tree.mapSpace.assertInSpace(range.getMinAddress());
|
||||||
|
long newMaxOffset = range.getMaxAddress().getOffset();
|
||||||
|
if (minOffset == newMinOffset && maxOffset == newMaxOffset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
DBTraceAddressSnapRangePropertyMapTree tree = this.tree;
|
DBTraceAddressSnapRangePropertyMapTree tree = this.tree;
|
||||||
long newMinOffset = tree.mapSpace.assertInSpace(range.getMinAddress());
|
|
||||||
tree.doUnparentEntry(this);
|
tree.doUnparentEntry(this);
|
||||||
minOffset = newMinOffset;
|
minOffset = newMinOffset;
|
||||||
maxOffset = range.getMaxAddress().getOffset();
|
maxOffset = newMaxOffset;
|
||||||
update(MIN_ADDRESS_COLUMN, MAX_ADDRESS_COLUMN);
|
update(MIN_ADDRESS_COLUMN, MAX_ADDRESS_COLUMN);
|
||||||
this.range = range;
|
this.range = range;
|
||||||
tree.doInsertDataEntry(this);
|
tree.doInsertDataEntry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "hiding" })
|
@SuppressWarnings("unchecked")
|
||||||
protected void doSetLifespan(Lifespan lifespan) {
|
protected void doSetLifespan(Lifespan lifespan) {
|
||||||
|
if (minSnap == lifespan.lmin() && maxSnap == lifespan.lmax()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
DBTraceAddressSnapRangePropertyMapTree tree = this.tree;
|
DBTraceAddressSnapRangePropertyMapTree tree = this.tree;
|
||||||
tree.doUnparentEntry(this);
|
tree.doUnparentEntry(this);
|
||||||
|
|||||||
+2
-1
@@ -634,7 +634,8 @@ public class DBTraceMemorySpace
|
|||||||
if (!remaining.isEmpty()) {
|
if (!remaining.isEmpty()) {
|
||||||
lastSnap.snap = Long.MAX_VALUE;
|
lastSnap.snap = Long.MAX_VALUE;
|
||||||
for (AddressRange rng : remaining) {
|
for (AddressRange rng : remaining) {
|
||||||
changed.add(new ImmutableTraceAddressSnapRange(rng, Lifespan.nowOn(loc.snap)));
|
changed.add(
|
||||||
|
new ImmutableTraceAddressSnapRange(rng, Lifespan.nowOnMaybeScratch(loc.snap)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.position(pos);
|
buf.position(pos);
|
||||||
|
|||||||
+74
-33
@@ -17,11 +17,9 @@ package ghidra.trace.database.space;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import db.DBHandle;
|
import db.DBHandle;
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import generic.CatenatedCollection;
|
import generic.CatenatedCollection;
|
||||||
@@ -89,6 +87,19 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record Frame(TraceThread thread, int level) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TabledSpace(DBTraceSpaceEntry entry, AddressSpace space, TraceThread thread) {
|
||||||
|
private boolean isRegisterSpace() {
|
||||||
|
return space.isRegisterSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Frame frame() {
|
||||||
|
return new Frame(thread, entry.frameLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected final String name;
|
protected final String name;
|
||||||
protected final DBHandle dbh;
|
protected final DBHandle dbh;
|
||||||
protected final ReadWriteLock lock;
|
protected final ReadWriteLock lock;
|
||||||
@@ -100,7 +111,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
// Note: use tree map so traversal is ordered by address space
|
// Note: use tree map so traversal is ordered by address space
|
||||||
protected final Map<AddressSpace, M> memSpaces = new TreeMap<>();
|
protected final Map<AddressSpace, M> memSpaces = new TreeMap<>();
|
||||||
// Note: can use hash map here. I see no need to order these spaces
|
// Note: can use hash map here. I see no need to order these spaces
|
||||||
protected final Map<Pair<TraceThread, Integer>, M> regSpaces = new HashMap<>();
|
protected final Map<Frame, M> regSpaces = new HashMap<>();
|
||||||
protected final Map<TraceObject, M> regSpacesByObject = new HashMap<>();
|
protected final Map<TraceObject, M> regSpacesByObject = new HashMap<>();
|
||||||
|
|
||||||
protected final Collection<M> memSpacesView =
|
protected final Collection<M> memSpacesView =
|
||||||
@@ -130,47 +141,61 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
return DBTraceUtils.tableName(name, space, threadKey, frameLevel);
|
return DBTraceUtils.tableName(name, space, threadKey, frameLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected void loadSpaces() throws VersionException, IOException {
|
protected void loadSpaces() throws VersionException, IOException {
|
||||||
for (DBTraceSpaceEntry ent : spaceStore.asMap().values()) {
|
Map<Frame, TabledSpace> newRegSpaces = new HashMap<>();
|
||||||
AddressFactory addressFactory = trace.getBaseAddressFactory();
|
Map<AddressSpace, TabledSpace> newMemSpaces = new HashMap<>();
|
||||||
AddressSpace space;
|
for (TabledSpace ts : getTabledSpaces()) {
|
||||||
if (NO_ADDRESS_SPACE.getName().equals(ent.spaceName)) {
|
if (ts.isRegisterSpace()) {
|
||||||
space = NO_ADDRESS_SPACE;
|
newRegSpaces.put(ts.frame(), ts);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
space = addressFactory.getAddressSpace(ent.spaceName);
|
newMemSpaces.put(ts.space(), ts);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
regSpaces.keySet().retainAll(newRegSpaces.keySet());
|
||||||
|
memSpaces.keySet().retainAll(newMemSpaces.keySet());
|
||||||
|
for (Entry<Frame, TabledSpace> ent : newRegSpaces.entrySet()) {
|
||||||
|
if (!regSpaces.containsKey(ent.getKey())) {
|
||||||
|
regSpaces.put(ent.getKey(), createRegisterSpace(ent.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Entry<AddressSpace, TabledSpace> ent : newMemSpaces.entrySet()) {
|
||||||
|
if (!memSpaces.containsKey(ent.getKey())) {
|
||||||
|
memSpaces.put(ent.getKey(), createSpace(ent.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AddressSpace getSpaceByName(AddressFactory factory, String name) {
|
||||||
|
if (NO_ADDRESS_SPACE.getName().equals(name)) {
|
||||||
|
return NO_ADDRESS_SPACE;
|
||||||
|
}
|
||||||
|
return factory.getAddressSpace(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<TabledSpace> getTabledSpaces() {
|
||||||
|
AddressFactory factory = trace.getBaseAddressFactory();
|
||||||
|
List<TabledSpace> result = new ArrayList<>();
|
||||||
|
for (DBTraceSpaceEntry ent : spaceStore.asMap().values()) {
|
||||||
|
AddressSpace space = getSpaceByName(factory, ent.spaceName);
|
||||||
if (space == null) {
|
if (space == null) {
|
||||||
Msg.error(this, "Space " + ent.spaceName + " does not exist in trace (language=" +
|
Msg.error(this, "Space " + ent.spaceName + " does not exist in trace (language=" +
|
||||||
baseLanguage + ").");
|
baseLanguage + ").");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (space.isRegisterSpace()) {
|
if (space.isRegisterSpace()) {
|
||||||
if (threadManager == null) {
|
if (threadManager == null) {
|
||||||
Msg.error(this, "Register spaces are not allowed without a thread manager.");
|
Msg.error(this, "Register spaces are not allowed without a thread manager.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TraceThread thread = threadManager.getThread(ent.threadKey);
|
TraceThread thread = threadManager.getThread(ent.threadKey);
|
||||||
M regSpace;
|
result.add(new TabledSpace(ent, space, thread));
|
||||||
if (ent.space == null) {
|
|
||||||
regSpace = createRegisterSpace(space, thread, ent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
regSpace = (M) ent.space;
|
|
||||||
}
|
|
||||||
regSpaces.put(ImmutablePair.of(thread, ent.getFrameLevel()), regSpace);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
M memSpace;
|
result.add(new TabledSpace(ent, space, null));
|
||||||
if (ent.space == null) {
|
|
||||||
memSpace = createSpace(space, ent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memSpace = (M) ent.space;
|
|
||||||
}
|
|
||||||
memSpaces.put(space, memSpace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected M getForSpace(AddressSpace space, boolean createIfAbsent) {
|
protected M getForSpace(AddressSpace space, boolean createIfAbsent) {
|
||||||
@@ -210,7 +235,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
return getForRegisterSpaceObjectThread((TraceObjectThread) thread, frameLevel,
|
return getForRegisterSpaceObjectThread((TraceObjectThread) thread, frameLevel,
|
||||||
createIfAbsent);
|
createIfAbsent);
|
||||||
}
|
}
|
||||||
Pair<TraceThread, Integer> frame = ImmutablePair.of(thread, frameLevel);
|
Frame frame = new Frame(thread, frameLevel);
|
||||||
if (!createIfAbsent) {
|
if (!createIfAbsent) {
|
||||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||||
return regSpaces.get(frame);
|
return regSpaces.get(frame);
|
||||||
@@ -340,6 +365,26 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
protected abstract M createRegisterSpace(AddressSpace space, TraceThread thread,
|
protected abstract M createRegisterSpace(AddressSpace space, TraceThread thread,
|
||||||
DBTraceSpaceEntry ent) throws VersionException, IOException;
|
DBTraceSpaceEntry ent) throws VersionException, IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private M createSpace(TabledSpace ts) throws VersionException, IOException {
|
||||||
|
if (ts.entry.space != null) {
|
||||||
|
return (M) ts.entry.space;
|
||||||
|
}
|
||||||
|
M space = createSpace(ts.space, ts.entry);
|
||||||
|
ts.entry.space = space;
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private M createRegisterSpace(TabledSpace ts) throws VersionException, IOException {
|
||||||
|
if (ts.entry.space != null) {
|
||||||
|
return (M) ts.entry.space;
|
||||||
|
}
|
||||||
|
M space = createRegisterSpace(ts.space, ts.thread, ts.entry);
|
||||||
|
ts.entry.space = space;
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dbError(IOException e) {
|
public void dbError(IOException e) {
|
||||||
trace.dbError(e);
|
trace.dbError(e);
|
||||||
@@ -349,10 +394,6 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||||||
public void invalidateCache(boolean all) {
|
public void invalidateCache(boolean all) {
|
||||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||||
spaceStore.invalidateCache();
|
spaceStore.invalidateCache();
|
||||||
// TODO: Need to do a real delta here, not blow away and remake
|
|
||||||
// Currently, object identities are not preserved by this operation
|
|
||||||
memSpaces.clear();
|
|
||||||
regSpaces.clear();
|
|
||||||
loadSpaces();
|
loadSpaces();
|
||||||
for (M m : memSpaces.values()) {
|
for (M m : memSpaces.values()) {
|
||||||
m.invalidateCache();
|
m.invalidateCache();
|
||||||
|
|||||||
+2
-11
@@ -20,17 +20,8 @@ import ghidra.trace.model.thread.TraceThread;
|
|||||||
import ghidra.trace.util.TraceAddressSpace;
|
import ghidra.trace.util.TraceAddressSpace;
|
||||||
|
|
||||||
public interface DBTraceSpaceKey extends TraceAddressSpace {
|
public interface DBTraceSpaceKey extends TraceAddressSpace {
|
||||||
static class DefaultDBTraceSpaceKey implements DBTraceSpaceKey {
|
record DefaultDBTraceSpaceKey(TraceThread thread, AddressSpace space, int frameLevel)
|
||||||
private final TraceThread thread;
|
implements DBTraceSpaceKey {
|
||||||
private final AddressSpace space;
|
|
||||||
private final int frameLevel;
|
|
||||||
|
|
||||||
private DefaultDBTraceSpaceKey(TraceThread thread, AddressSpace space, int frameLevel) {
|
|
||||||
this.thread = thread;
|
|
||||||
this.space = space;
|
|
||||||
this.frameLevel = frameLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSpace getAddressSpace() {
|
public AddressSpace getAddressSpace() {
|
||||||
return space;
|
return space;
|
||||||
|
|||||||
+124
-1
@@ -22,6 +22,7 @@ import java.io.IOException;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
@@ -171,6 +172,23 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPutBytesInScratchLeavesStaticDataUntouched() throws CodeUnitInsertionException {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
TraceData d0at4000 =
|
||||||
|
b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4));
|
||||||
|
assertEquals(Lifespan.nowOn(0), d0at4000.getLifespan());
|
||||||
|
assertEquals(new Scalar(32, 0x01020304), d0at4000.getValue());
|
||||||
|
|
||||||
|
b.trace.getMemoryManager().putBytes(-10, b.addr(0x4000), b.buf(5, 6, 7, 8));
|
||||||
|
TraceData d10at4000 =
|
||||||
|
b.trace.getCodeManager().definedData().getContaining(10, b.addr(0x4000));
|
||||||
|
assertSame(d0at4000, d10at4000);
|
||||||
|
assertEquals(Lifespan.nowOn(0), d10at4000.getLifespan());
|
||||||
|
assertEquals(new Scalar(32, 0x01020304), d10at4000.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPutBytesDeletesDynamicData() throws CodeUnitInsertionException {
|
public void testPutBytesDeletesDynamicData() throws CodeUnitInsertionException {
|
||||||
try (UndoableTransaction tid = b.startTransaction()) {
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
@@ -292,6 +310,110 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverlapErrorsMultithreaded() throws Throwable {
|
||||||
|
ArrayList<CompletableFuture<Integer>> creators = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
creators.add(CompletableFuture.supplyAsync(() -> {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
CompletableFuture.allOf(creators.toArray(CompletableFuture[]::new)).get();
|
||||||
|
assertEquals(9, creators.stream()
|
||||||
|
.mapToInt(c -> c.getNow(null))
|
||||||
|
.reduce(Integer::sum)
|
||||||
|
.orElse(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverlapAllowedAfterAbort() throws Throwable {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
tid.abort();
|
||||||
|
}
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverlapErrAfterInvalidate() throws Throwable {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
}
|
||||||
|
b.trace.undo();
|
||||||
|
b.trace.redo();
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test is interesting because the pointer type def causes an update to the data type
|
||||||
|
* settings <em>while the unit is still being created</em>. This will invalidate the trace's
|
||||||
|
* caches. All of them, including the defined data units, which can become the cause of many
|
||||||
|
* timing issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOverlapErrWithDataTypeSettings() throws Throwable {
|
||||||
|
AddressSpace space = b.trace.getBaseAddressFactory().getDefaultAddressSpace();
|
||||||
|
PointerTypedef type = new PointerTypedef(null, VoidDataType.dataType, 8, null, space);
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), type);
|
||||||
|
}
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), type);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverlapErrAfterSetEndSnap() throws Throwable {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
DBTraceDataAdapter data = b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
assertEquals(Lifespan.before(0), data.getLifespan());
|
||||||
|
data.setEndSnap(-10);
|
||||||
|
assertEquals(Lifespan.before(-9), data.getLifespan());
|
||||||
|
}
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
b.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Lifespan.ALL, b.addr(0x4000), IntegerDataType.dataType);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void assertAllNullFunc(Function<TraceBaseCodeUnitsView<?>, TraceCodeUnit> func) {
|
protected void assertAllNullFunc(Function<TraceBaseCodeUnitsView<?>, TraceCodeUnit> func) {
|
||||||
assertNull(func.apply(manager.codeUnits()));
|
assertNull(func.apply(manager.codeUnits()));
|
||||||
assertNull(func.apply(manager.data()));
|
assertNull(func.apply(manager.data()));
|
||||||
@@ -1538,7 +1660,8 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||||||
coversTwoWays(manager.definedData(), Lifespan.span(0, 5), b.range(0x4000, 0x4007)));
|
coversTwoWays(manager.definedData(), Lifespan.span(0, 5), b.range(0x4000, 0x4007)));
|
||||||
assertTrue(
|
assertTrue(
|
||||||
coversTwoWays(manager.definedData(), Lifespan.span(0, 9), b.range(0x4001, 0x4003)));
|
coversTwoWays(manager.definedData(), Lifespan.span(0, 9), b.range(0x4001, 0x4003)));
|
||||||
assertFalse(intersectsTwoWays(manager.definedData(), Lifespan.ALL, b.range(0x0000, 0x3fff)));
|
assertFalse(
|
||||||
|
intersectsTwoWays(manager.definedData(), Lifespan.ALL, b.range(0x0000, 0x3fff)));
|
||||||
assertFalse(
|
assertFalse(
|
||||||
intersectsTwoWays(manager.definedData(), Lifespan.ALL, b.range(0x4008, -0x0001)));
|
intersectsTwoWays(manager.definedData(), Lifespan.ALL, b.range(0x4008, -0x0001)));
|
||||||
assertFalse(intersectsTwoWays(manager.definedData(), Lifespan.toNow(-1), all));
|
assertFalse(intersectsTwoWays(manager.definedData(), Lifespan.toNow(-1), all));
|
||||||
|
|||||||
+12
@@ -761,6 +761,18 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||||||
assertEquals(4, data.getBytes(buf, 0));
|
assertEquals(4, data.getBytes(buf, 0));
|
||||||
assertArrayEquals(b.arr(1, 2, 3, 4), buf.array());
|
assertArrayEquals(b.arr(1, 2, 3, 4), buf.array());
|
||||||
|
|
||||||
|
buf = ByteBuffer.allocate(1);
|
||||||
|
assertEquals(1, data.getBytes(buf, 4));
|
||||||
|
assertArrayEquals(b.arr(0xeb), buf.array());
|
||||||
|
|
||||||
|
buf = ByteBuffer.allocate(1);
|
||||||
|
assertEquals(1, data.getBytes(buf, 5));
|
||||||
|
assertArrayEquals(b.arr(0xfe), buf.array());
|
||||||
|
|
||||||
|
buf = ByteBuffer.allocate(4);
|
||||||
|
assertEquals(4, data.getBytes(buf, 2));
|
||||||
|
assertArrayEquals(b.arr(3, 4, 0xeb, 0xfe), buf.array());
|
||||||
|
|
||||||
assertArrayEquals(b.arr(1, 2, 3, 4), data.getBytes());
|
assertArrayEquals(b.arr(1, 2, 3, 4), data.getBytes());
|
||||||
|
|
||||||
byte[] arr = new byte[6];
|
byte[] arr = new byte[6];
|
||||||
|
|||||||
+2
-6
@@ -15,9 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.trace.database.module;
|
package ghidra.trace.database.module;
|
||||||
|
|
||||||
import static ghidra.lifecycle.Unfinished.TODO;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -334,7 +332,6 @@ public class DBTraceModuleManagerTest extends AbstractGhidraHeadlessIntegrationT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("GP-479")
|
|
||||||
public void testUndoIdentitiesPreserved() throws Exception {
|
public void testUndoIdentitiesPreserved() throws Exception {
|
||||||
TraceModule mod1;
|
TraceModule mod1;
|
||||||
try (UndoableTransaction tid = b.startTransaction()) {
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
@@ -351,8 +348,7 @@ public class DBTraceModuleManagerTest extends AbstractGhidraHeadlessIntegrationT
|
|||||||
|
|
||||||
b.trace.undo();
|
b.trace.undo();
|
||||||
|
|
||||||
assertEquals(mod1, assertOne(moduleManager.getModulesByPath("Modules[first]")));
|
assertSame(mod1, assertOne(moduleManager.getModulesByPath("Modules[first]")));
|
||||||
TODO(); // TODO: mod1 should still be identical to that in database
|
|
||||||
assertTrue(moduleManager.getModulesByPath("Modules[second]").isEmpty());
|
assertTrue(moduleManager.getModulesByPath("Modules[second]").isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+140
-3
@@ -19,15 +19,19 @@ import static org.junit.Assert.assertEquals;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import ghidra.app.cmd.disassemble.*;
|
import ghidra.app.cmd.disassemble.*;
|
||||||
|
import ghidra.app.plugin.assembler.*;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
import ghidra.program.disassemble.Disassembler;
|
||||||
import ghidra.program.model.address.AddressOverflowException;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.address.AddressSet;
|
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
@@ -37,7 +41,8 @@ import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
|||||||
import ghidra.trace.database.listing.*;
|
import ghidra.trace.database.listing.*;
|
||||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||||
import ghidra.trace.model.Lifespan;
|
import ghidra.trace.model.*;
|
||||||
|
import ghidra.trace.model.listing.TraceCodeUnit;
|
||||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||||
import ghidra.trace.model.memory.TraceOverlappedRegionException;
|
import ghidra.trace.model.memory.TraceOverlappedRegionException;
|
||||||
import ghidra.trace.util.LanguageTestWatcher;
|
import ghidra.trace.util.LanguageTestWatcher;
|
||||||
@@ -306,4 +311,136 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn
|
|||||||
assertEquals("MOV ECX,EAX", cu2.toString());
|
assertEquals("MOV ECX,EAX", cu2.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record Repetition(Lifespan lifespan, boolean overwrite) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> List<T> toList(Iterable<? extends T> it) {
|
||||||
|
return StreamSupport.stream(it.spliterator(), false).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runTestCoalesceInstructions(List<Repetition> repetitions) throws Exception {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
DBTraceMemoryManager memory = b.trace.getMemoryManager();
|
||||||
|
DBTraceCodeManager code = b.trace.getCodeManager();
|
||||||
|
|
||||||
|
memory.createRegion(".text", 0, b.range(0x00400000, 0x00400fff));
|
||||||
|
Assembler asm = Assemblers.getAssembler(b.language);
|
||||||
|
Address entry = b.addr(0x00400000);
|
||||||
|
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||||
|
buf.assemble("imm r0, #123");
|
||||||
|
buf.assemble("mov r1, r0");
|
||||||
|
buf.assemble("ret");
|
||||||
|
|
||||||
|
long snap = Lifespan.isScratch(repetitions.get(0).lifespan.lmin()) ? Long.MIN_VALUE : 0;
|
||||||
|
memory.putBytes(snap, entry, ByteBuffer.wrap(buf.getBytes()));
|
||||||
|
|
||||||
|
AddressFactory factory = b.trace.getBaseAddressFactory();
|
||||||
|
Disassembler dis =
|
||||||
|
Disassembler.getDisassembler(b.language, factory, TaskMonitor.DUMMY, null);
|
||||||
|
InstructionSet set = new InstructionSet(factory);
|
||||||
|
set.addBlock(dis.pseudoDisassembleBlock(memory.getBufferAt(snap, entry), null, 10));
|
||||||
|
|
||||||
|
List<TraceCodeUnit> units = null;
|
||||||
|
TraceAddressSnapRange all =
|
||||||
|
new ImmutableTraceAddressSnapRange(b.range(0, -1), Lifespan.ALL);
|
||||||
|
for (Repetition rep : repetitions) {
|
||||||
|
code.instructions().addInstructionSet(rep.lifespan, set, rep.overwrite);
|
||||||
|
if (units == null) {
|
||||||
|
units = toList(code.definedUnits().getIntersecting(all));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Technically, getIntersecting makes no guarantee regarding order.
|
||||||
|
* Nevertheless, the structure shouldn't be perturbed, so I think it's fair to
|
||||||
|
* expect the same order.
|
||||||
|
*/
|
||||||
|
assertEquals(units, toList(code.definedUnits().getIntersecting(all)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsMinTwiceNoOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.nowOn(Long.MIN_VALUE), false),
|
||||||
|
new Repetition(Lifespan.nowOn(Long.MIN_VALUE), false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsMinTwiceYesOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.nowOn(Long.MIN_VALUE), true),
|
||||||
|
new Repetition(Lifespan.nowOn(Long.MIN_VALUE), true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsZeroTwiceYesOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.nowOn(0), true),
|
||||||
|
new Repetition(Lifespan.nowOn(0), true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsZeroThenOneYesOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.nowOn(0), true),
|
||||||
|
new Repetition(Lifespan.nowOn(1), true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsZeroOnlyThenOneNoOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.at(0), false),
|
||||||
|
new Repetition(Lifespan.nowOn(1), false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestLanguage(ProgramBuilder._TOY64_BE)
|
||||||
|
public void testCoalesceInstructionsZeroOnlyThenOneYesOverwrite() throws Exception {
|
||||||
|
runTestCoalesceInstructions(List.of(
|
||||||
|
new Repetition(Lifespan.at(0), true),
|
||||||
|
new Repetition(Lifespan.nowOn(1), true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoCoalesceAcrossByteChanges() throws Exception {
|
||||||
|
try (UndoableTransaction tid = b.startTransaction()) {
|
||||||
|
DBTraceMemoryManager memory = b.trace.getMemoryManager();
|
||||||
|
DBTraceCodeManager code = b.trace.getCodeManager();
|
||||||
|
|
||||||
|
memory.createRegion(".text", 0, b.range(0x00400000, 0x00400fff));
|
||||||
|
Assembler asm = Assemblers.getAssembler(b.language);
|
||||||
|
Address entry = b.addr(0x00400000);
|
||||||
|
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||||
|
buf.assemble("imm r0, #123");
|
||||||
|
buf.assemble("mov r1, r0");
|
||||||
|
buf.assemble("ret");
|
||||||
|
|
||||||
|
memory.putBytes(-1, entry, ByteBuffer.wrap(buf.getBytes()));
|
||||||
|
|
||||||
|
AddressFactory factory = b.trace.getBaseAddressFactory();
|
||||||
|
Disassembler dis =
|
||||||
|
Disassembler.getDisassembler(b.language, factory, TaskMonitor.DUMMY, null);
|
||||||
|
InstructionSet set = new InstructionSet(factory);
|
||||||
|
set.addBlock(dis.pseudoDisassembleBlock(memory.getBufferAt(-1, entry), null, 10));
|
||||||
|
|
||||||
|
TraceAddressSnapRange all =
|
||||||
|
new ImmutableTraceAddressSnapRange(b.range(0, -1), Lifespan.ALL);
|
||||||
|
code.instructions().addInstructionSet(Lifespan.nowOn(-1), set, true);
|
||||||
|
/**
|
||||||
|
* This is already a bogus sort of operation: The prototypes may not match the bytes. In
|
||||||
|
* any case, we should not expect coalescing.
|
||||||
|
*/
|
||||||
|
code.instructions().addInstructionSet(Lifespan.nowOn(0), set, true);
|
||||||
|
List<TraceCodeUnit> units = toList(code.definedUnits().getIntersecting(all));
|
||||||
|
assertEquals(6, units.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ public class PcodeExecutor<T> {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new PcodeExecutionException("Exception during pcode execution", frame, e);
|
throw new PcodeExecutionException(e.getMessage(), frame, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -326,4 +326,8 @@ public class DBAnnotatedObject extends DatabaseObject {
|
|||||||
public boolean isDeleted() {
|
public boolean isDeleted() {
|
||||||
return super.isDeleted(adapter.getLock());
|
return super.isDeleted(adapter.getLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTableName() {
|
||||||
|
return store.getTableName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1275,6 +1275,15 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the table backing this store
|
||||||
|
*
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
public String getTableName() {
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate this store's cache
|
* Invalidate this store's cache
|
||||||
*
|
*
|
||||||
|
|||||||
+6
-4
@@ -82,7 +82,7 @@ public abstract class AbstractConstraintsTree< //
|
|||||||
protected abstract NR createNodeEntry(DBCachedObjectStore<NR> store, DBRecord record);
|
protected abstract NR createNodeEntry(DBCachedObjectStore<NR> store, DBRecord record);
|
||||||
|
|
||||||
protected void init() {
|
protected void init() {
|
||||||
assert root == null;
|
// assert root == null;
|
||||||
root = getOrCreateRoot();
|
root = getOrCreateRoot();
|
||||||
leafLevel = computeLeafLevel();
|
leafLevel = computeLeafLevel();
|
||||||
}
|
}
|
||||||
@@ -843,6 +843,7 @@ public abstract class AbstractConstraintsTree< //
|
|||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
"Leaf node " + n + " cannot contain nodes " + nodeChildren);
|
"Leaf node " + n + " cannot contain nodes " + nodeChildren);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that child count matches by counting over iterator
|
// Check that child count matches by counting over iterator
|
||||||
@@ -911,9 +912,6 @@ public abstract class AbstractConstraintsTree< //
|
|||||||
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (leafLevel != computeLeafLevel()) {
|
|
||||||
throw new AssertionError("Leaf level is incorrect");
|
|
||||||
}
|
|
||||||
visit(null, new TreeRecordVisitor() {
|
visit(null, new TreeRecordVisitor() {
|
||||||
@Override
|
@Override
|
||||||
protected VisitResult beginNode(NR parent, NR n, QueryInclusion inclusion) {
|
protected VisitResult beginNode(NR parent, NR n, QueryInclusion inclusion) {
|
||||||
@@ -927,6 +925,9 @@ public abstract class AbstractConstraintsTree< //
|
|||||||
return VisitResult.NEXT;
|
return VisitResult.NEXT;
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
|
if (leafLevel != computeLeafLevel()) {
|
||||||
|
throw new AssertionError("Leaf level is incorrect");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract AbstractConstraintsTreeSpatialMap<DS, DR, NS, T, Q> asSpatialMap();
|
public abstract AbstractConstraintsTreeSpatialMap<DS, DR, NS, T, Q> asSpatialMap();
|
||||||
@@ -945,6 +946,7 @@ public abstract class AbstractConstraintsTree< //
|
|||||||
cachedNodeChildren.clear();
|
cachedNodeChildren.clear();
|
||||||
dataStore.invalidateCache();
|
dataStore.invalidateCache();
|
||||||
nodeStore.invalidateCache();
|
nodeStore.invalidateCache();
|
||||||
|
init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user