Merge remote-tracking branch 'origin/GP-4483_Dan_staleEmuADDs--SQUASHED' into patch

This commit is contained in:
Ryan Kurtz 2024-10-17 10:41:51 -04:00
commit 9bd7487c91
11 changed files with 265 additions and 39 deletions

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.mapping;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject; import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
@ -27,7 +28,11 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Endian; import ghidra.program.model.lang.Endian;
import ghidra.trace.model.Trace; 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.listing.TraceInstruction;
import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
@ -61,7 +66,13 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
} }
protected boolean isCancelSilently(Address start, long snap) { protected boolean isCancelSilently(Address start, long snap) {
return trace.getCodeManager().instructions().getAt(snap, start) != null; TraceInstruction exists = trace.getCodeManager().instructions().getAt(snap, start);
if (exists == null) {
return false;
}
var states = trace.getMemoryManager().getStates(snap, exists.getRange());
return TraceMemoryOperations.isStateEntirely(exists.getRange(), states,
TraceMemoryState.KNOWN);
} }
protected Collection<DisassemblyInject> getDisassemblyInjections(TracePlatform platform) { protected Collection<DisassemblyInject> getDisassemblyInjections(TracePlatform platform) {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,6 +16,7 @@
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@ -29,9 +30,13 @@ 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.DBTraceMemoryManager;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -92,7 +97,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected void truncateOrDelete(TraceInstruction exists) { protected void truncateOrDelete(TraceInstruction exists) {
if (exists.getStartSnap() < lifespan.lmin()) { if (exists.getStartSnap() < lifespan.lmin()) {
exists.setEndSnap(lifespan.lmin()); exists.setEndSnap(lifespan.lmin() - 1);
} }
else { else {
exists.delete(); exists.delete();
@ -471,6 +476,15 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
conflict, conflictCodeUnit, overwrite); conflict, conflictCodeUnit, overwrite);
} }
protected boolean isKnown(DBTraceMemorySpace ms, long snap, CodeUnit cu) {
if (ms == null) {
return false;
}
AddressRange range = new AddressRangeImpl(cu.getMinAddress(), cu.getMaxAddress());
var states = ms.getStates(snap, range);
return TraceMemoryOperations.isStateEntirely(range, states, TraceMemoryState.KNOWN);
}
/** /**
* Checks the intended locations for conflicts with existing units. * Checks the intended locations for conflicts with existing units.
* *
@ -486,6 +500,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
Set<Address> skipDelaySlots) { Set<Address> skipDelaySlots) {
// NOTE: Partly derived from CodeManager#checkInstructionSet() // NOTE: Partly derived from CodeManager#checkInstructionSet()
// Attempted to factor more fluently // Attempted to factor more fluently
DBTraceMemoryManager mm = space.trace.getMemoryManager();
for (InstructionBlock block : instructionSet) { for (InstructionBlock block : instructionSet) {
// If block contains a known error, record its address, and do not proceed beyond it // If block contains a known error, record its address, and do not proceed beyond it
Address errorAddress = null; Address errorAddress = null;
@ -519,6 +534,12 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
lastProtoInstr = protoInstr; lastProtoInstr = protoInstr;
} }
CodeUnit existsCu = overlap.getRight(); CodeUnit existsCu = overlap.getRight();
DBTraceMemorySpace ms =
mm.getMemorySpace(existsCu.getAddress().getAddressSpace(), false);
if (!isKnown(ms, startSnap, existsCu) && existsCu instanceof TraceCodeUnit tcu) {
tcu.delete();
continue;
}
int cmp = existsCu.getMinAddress().compareTo(protoInstr.getMinAddress()); int cmp = existsCu.getMinAddress().compareTo(protoInstr.getMinAddress());
boolean existsIsInstruction = (existsCu instanceof TraceInstruction); boolean existsIsInstruction = (existsCu instanceof TraceInstruction);
if (cmp == 0 && existsIsInstruction) { if (cmp == 0 && existsIsInstruction) {
@ -552,7 +573,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
} }
// NOTE: existsIsInstruction implies cmp != 0, so record as off-cut conflict // NOTE: existsIsInstruction implies cmp != 0, so record as off-cut conflict
block.setCodeUnitConflict(existsCu.getAddress(), protoInstr.getAddress(), block.setCodeUnitConflict(existsCu.getAddress(), protoInstr.getAddress(),
flowFromAddress, existsIsInstruction, existsIsInstruction); flowFromAddress, existsIsInstruction, true);
} }
} }
} }

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -39,13 +39,17 @@ public class DBTraceAddressSnapRangePropertyMapAddressSetView<T> extends Abstrac
private final Predicate<? super T> predicate; private final Predicate<? super T> predicate;
/** /**
* TODO Document me * Construct an {@link AddressSetView} based on the given map of entries and predicate.
* *
* The caller must reduce the map if only a certain range is desired. * <p>
* The spatial map is a 2-dimensional collection of entries, but only the address dimension is
* considered. This set behaves as the union of address ranges for all entries whose values pass
* the predicate. Typically, the caller reduces the map first.
* *
* @param lock * @param space the address space of the given map
* @param map * @param lock a lock to ensure access to the underlying database is synchronized
* @param predicate * @param map the map whose entries to test
* @param predicate the predicate for testing entry values
*/ */
public DBTraceAddressSnapRangePropertyMapAddressSetView(AddressSpace space, ReadWriteLock lock, public DBTraceAddressSnapRangePropertyMapAddressSetView(AddressSpace space, ReadWriteLock lock,
SpatialMap<TraceAddressSnapRange, T, TraceAddressSnapRangeQuery> map, SpatialMap<TraceAddressSnapRange, T, TraceAddressSnapRangeQuery> map,

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -515,6 +515,12 @@ public class DBTraceAddressSnapRangePropertyMapTree<T, DR extends AbstractDBTrac
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new); Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
} }
public static TraceAddressSnapRangeQuery mostRecent(AddressRange range, Lifespan span) {
return intersecting(
new ImmutableTraceAddressSnapRange(range, span),
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
}
public static TraceAddressSnapRangeQuery equalTo(TraceAddressSnapRange shape) { public static TraceAddressSnapRangeQuery equalTo(TraceAddressSnapRange shape) {
return equalTo(shape, null, TraceAddressSnapRangeQuery::new); return equalTo(shape, null, TraceAddressSnapRangeQuery::new);
} }

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -279,6 +279,13 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager<DBTra
m -> m.getMostRecentStateEntry(snap, address)); m -> m.getMostRecentStateEntry(snap, address));
} }
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
AddressRange range, Predicate<TraceMemoryState> predicate) {
return delegateRead(range.getAddressSpace(),
m -> m.getViewMostRecentStateEntry(snap, range, predicate));
}
@Override @Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap, public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
Address address) { Address address) {

View File

@ -406,11 +406,17 @@ public class DBTraceMemorySpace
@Override @Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap, public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
Address address) { Address address) {
return getViewMostRecentStateEntry(snap, new AddressRangeImpl(address, address), s -> true);
}
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
AddressRange range, Predicate<TraceMemoryState> predicate) {
assertInSpace(range);
for (Lifespan span : viewport.getOrderedSpans(snap)) { for (Lifespan span : viewport.getOrderedSpans(snap)) {
Entry<TraceAddressSnapRange, TraceMemoryState> entry = var entry = stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(range, span))
stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(address, span)) .firstEntry();
.firstEntry(); if (entry != null && predicate.test(entry.getValue())) {
if (entry != null) {
return entry; return entry;
} }
} }

View File

@ -22,6 +22,7 @@ import org.apache.commons.collections4.IteratorUtils;
import generic.NestedIterator; import generic.NestedIterator;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.database.code.InstructionDB;
import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@ -41,6 +42,7 @@ import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewListing; import ghidra.trace.model.program.TraceProgramViewListing;
import ghidra.trace.model.property.TracePropertyMapOperations; import ghidra.trace.model.property.TracePropertyMapOperations;
@ -717,9 +719,21 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride) MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
// TODO: Why memBuf? Can it vary from program memory? int checkLengthOverride =
InstructionDB.checkLengthOverride(forcedLengthOverride, prototype);
int length = checkLengthOverride != 0 ? checkLengthOverride : prototype.getLength();
AddressRange range;
try {
range = new AddressRangeImpl(addr, length);
}
catch (AddressOverflowException e) {
throw new CodeUnitInsertionException("Code unit would extend beyond address space");
}
var mostRecent = program.memory.memoryManager.getViewMostRecentStateEntry(program.snap,
range, s -> s == TraceMemoryState.KNOWN);
long snap = mostRecent == null ? program.snap : mostRecent.getKey().getY2();
return codeOperations.instructions() return codeOperations.instructions()
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context, .create(Lifespan.nowOn(snap), addr, platform, prototype, context,
forcedLengthOverride); forcedLengthOverride);
} }

View File

@ -1635,6 +1635,17 @@ public class DBTraceProgramView implements TraceProgramView {
} }
protected boolean isCodeVisible(TraceCodeUnit cu, Lifespan lifespan) { protected boolean isCodeVisible(TraceCodeUnit cu, Lifespan lifespan) {
try {
byte[] cubytes = cu.getBytes();
byte[] mmbytes = new byte[cubytes.length];
memory.getBytes(cu.getAddress(), mmbytes);
if (!Arrays.equals(cubytes, mmbytes)) {
return false;
}
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
}
return viewport.isCompletelyVisible(cu.getRange(), lifespan, cu, return viewport.isCompletelyVisible(cu.getRange(), lifespan, cu,
getCodeOcclusion(cu.getTraceSpace())); getCodeOcclusion(cu.getTraceSpace()));
} }

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -48,7 +48,13 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
/** /**
* Create an instruction for the host platform * Create an instruction for the host platform
* *
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView) * @param lifespan the lifespan for the instruction unit
* @param address the starting address of the instruction
* @param prototype the instruction prototype
* @param context the input disassembly context for the instruction
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
* @return the new instruction
* @throws CodeUnitInsertionException if the instruction cannot be created
*/ */
default TraceInstruction create(Lifespan lifespan, Address address, default TraceInstruction create(Lifespan lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride) InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
@ -76,7 +82,14 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
/** /**
* Create several instructions for the host platform * Create several instructions for the host platform
* *
* @see #addInstructionSet(Lifespan, TracePlatform, InstructionSet, boolean) * <p>
* <b>NOTE:</b> This does not throw {@link CodeUnitInsertionException}. Conflicts are instead
* recorded in the {@code instructionSet}.
*
* @param lifespan the lifespan for all instruction units
* @param instructionSet the set of instructions to add
* @param overwrite true to replace conflicting instructions
* @return the (host) address set of instructions actually added
*/ */
default AddressSetView addInstructionSet(Lifespan lifespan, InstructionSet instructionSet, default AddressSetView addInstructionSet(Lifespan lifespan, InstructionSet instructionSet,
boolean overwrite) { boolean overwrite) {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -62,6 +62,38 @@ import ghidra.util.task.TaskMonitor;
* accidentally rely on implied temporal relationships in scratch space. * accidentally rely on implied temporal relationships in scratch space.
*/ */
public interface TraceMemoryOperations { public interface TraceMemoryOperations {
/**
* Check if the return value of {@link #getStates(long, AddressRange)} or similar represents a
* single entry of the given state.
*
* <p>
* This method returns false if there is not exactly one entry of the given state whose range
* covers the given range. As a special case, an empty collection will cause this method to
* return true iff state is {@link TraceMemoryState#UNKNOWN}.
*
* @param range the range to check, usually that passed to
* {@link #getStates(long, AddressRange)}.
* @param stateEntries the collection returned by {@link #getStates(long, AddressRange)}.
* @param state the expected state
* @return true if the state matches
*/
static boolean isStateEntirely(AddressRange range,
Collection<Entry<TraceAddressSnapRange, TraceMemoryState>> stateEntries,
TraceMemoryState state) {
if (stateEntries.isEmpty()) {
return state == TraceMemoryState.UNKNOWN;
}
if (stateEntries.size() != 1) {
return false;
}
Entry<TraceAddressSnapRange, TraceMemoryState> ent = stateEntries.iterator().next();
if (ent.getValue() != state) {
return false;
}
AddressRange entRange = ent.getKey().getRange();
return entRange.contains(range.getMinAddress()) && entRange.contains(range.getMaxAddress());
}
/** /**
* Get the trace to which the memory manager belongs * Get the trace to which the memory manager belongs
* *
@ -261,13 +293,25 @@ public interface TraceMemoryOperations {
* Get the entry recording the most recent state at the given snap and address, following * Get the entry recording the most recent state at the given snap and address, following
* schedule forks * schedule forks
* *
* @param snap the time * @param snap the latest time to consider
* @param address the location * @param address the address
* @return the state * @return the most-recent entry
*/ */
Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap, Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
Address address); Address address);
/**
* Get the entry recording the most recent state since the given snap within the given range
* that satisfies a given predicate, following schedule forks
*
* @param snap the latest time to consider
* @param range the range of addresses
* @param predicate a predicate on the state
* @return the most-recent entry
*/
Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
AddressRange range, Predicate<TraceMemoryState> predicate);
/** /**
* Get at least the subset of addresses having state satisfying the given predicate * Get at least the subset of addresses having state satisfying the given predicate
* *

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,13 +19,16 @@ import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import org.junit.*; import org.junit.*;
import db.Transaction; import db.Transaction;
import ghidra.app.plugin.assembler.*;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.AddressSet; import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@ -35,6 +38,13 @@ import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.listing.DBTraceCodeManager; import ghidra.trace.database.listing.DBTraceCodeManager;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.TraceTimeManager;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -48,9 +58,11 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
DBTraceCodeManager code; DBTraceCodeManager code;
protected static void assertUndefined(CodeUnit cu) { protected static void assertUndefined(CodeUnit cu) {
Data data = (Data) cu; if (cu instanceof Data data && DataType.DEFAULT.equals(data.getDataType()) &&
assertEquals(DataType.DEFAULT, data.getDataType()); !data.isDefined()) {
assertFalse(data.isDefined()); return;
}
fail("Expected undefined unit, but was '%s'".formatted(cu));
} }
protected <T> List<T> takeN(int n, Iterator<T> it) { protected <T> List<T> takeN(int n, Iterator<T> it) {
@ -896,4 +908,81 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertArrayEquals(b.arr(1), cu0.getBytes()); assertArrayEquals(b.arr(1), cu0.getBytes());
assertArrayEquals(b.arr(8), cu1.getBytes()); assertArrayEquals(b.arr(8), cu1.getBytes());
} }
@Test
public void testGetCodeUnitsInScratchView() throws Throwable {
TraceTimeManager tm = b.trace.getTimeManager();
Address entry = b.addr(0x00400000);
AddressSetView set = b.set(b.range(0x00400000, 0x00400003));
Assembler asm = Assemblers.getAssembler(b.language);
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
buf.assemble("imm r1, #234");
buf.assemble("add r1, r1");
final long snap;
try (Transaction tx = b.startTransaction()) {
TraceThread thread = b.getOrAddThread("Threads[1]", 0);
tm.getSnapshot(0, true);
memory.addRegion("Memory[test]", Lifespan.nowOn(0), b.range(0x00400000, 0x00400fff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceSnapshot scratch = tm.getSnapshot(Long.MIN_VALUE, true);
snap = scratch.getKey();
scratch.setSchedule(TraceSchedule.ZERO.steppedForward(thread, 1));
view.setSnap(snap);
Disassembler dis =
Disassembler.getDisassembler(view, TaskMonitor.DUMMY, msg -> Msg.error(this, msg));
AddressSetView result = dis.disassemble(entry, set);
assertEquals(set, result);
assertEquals(4, memory.putBytes(0, entry, ByteBuffer.wrap(buf.getBytes())));
// No disassembly at snap 0
}
byte[] arr = new byte[4];
view.getMemory().getBytes(entry, arr);
assertArrayEquals(buf.getBytes(), arr);
assertUndefined(listing.getCodeUnitAt(entry));
}
@Test
public void testCreateCodeUnitsInScratchViewAfterBytesChanged() throws Throwable {
TraceTimeManager tm = b.trace.getTimeManager();
Address entry = b.addr(0x00400000);
AddressSetView set = b.set(b.range(0x00400000, 0x00400003));
Assembler asm = Assemblers.getAssembler(b.language);
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
buf.assemble("imm r1, #234");
buf.assemble("add r1, r1");
final long snap;
try (Transaction tx = b.startTransaction()) {
TraceThread thread = b.getOrAddThread("Threads[1]", 0);
tm.getSnapshot(0, true);
memory.addRegion("Memory[test]", Lifespan.nowOn(0), b.range(0x00400000, 0x00400fff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceSnapshot scratch = tm.getSnapshot(Long.MIN_VALUE, true);
snap = scratch.getKey();
scratch.setSchedule(TraceSchedule.ZERO.steppedForward(thread, 1));
view.setSnap(snap);
Disassembler dis =
Disassembler.getDisassembler(view, TaskMonitor.DUMMY, msg -> Msg.error(this, msg));
AddressSetView result = dis.disassemble(entry, set);
assertEquals(set, result);
assertEquals(4, memory.putBytes(0, entry, ByteBuffer.wrap(buf.getBytes())));
// No disassembly at snap 0
// Attempt re-disassembly at scratch snap
result = dis.disassemble(entry, set);
assertEquals(set, result);
}
assertEquals("imm r1,#0xea", listing.getCodeUnitAt(entry).toString());
}
} }