mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 13:16:48 +08:00
GP-6236: Emulate from nearest snapshot. Avoid UI hang in Registers Panel.
This commit is contained in:
@@ -24,7 +24,6 @@ import javax.swing.Icon;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.services.DebuggerEmulationService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
||||
import ghidra.debug.api.target.Target;
|
||||
@@ -45,8 +44,6 @@ import ghidra.trace.model.time.schedule.PatchStep;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule.ScheduleForm;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* The control / state editing modes
|
||||
@@ -332,22 +329,7 @@ public enum ControlMode {
|
||||
DebuggerTraceManagerService traceManager =
|
||||
Objects.requireNonNull(tool.getService(DebuggerTraceManagerService.class),
|
||||
"No trace manager service");
|
||||
Long found = traceManager.findSnapshot(withTime);
|
||||
// Materialize it on the same thread (even if swing)
|
||||
// It shouldn't take long, since we're only appending one step.
|
||||
if (found == null) {
|
||||
// TODO: Could still do it async on another thread, no?
|
||||
// Not sure it buys anything, since program view will call .get on swing thread
|
||||
DebuggerEmulationService emulationService = Objects.requireNonNull(
|
||||
tool.getService(DebuggerEmulationService.class), "No emulation service");
|
||||
try {
|
||||
emulationService.emulate(coordinates.getPlatform(), time,
|
||||
TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return traceManager.activateAndNotify(withTime, ActivationCause.EMU_STATE_EDIT);
|
||||
}
|
||||
|
||||
|
||||
+26
-8
@@ -687,9 +687,16 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
||||
TracePlatform platform = key.platform;
|
||||
TraceSchedule time = key.time;
|
||||
|
||||
Map.Entry<CacheKey, CachedEmulator> ancestor = findNearestPrefix(key);
|
||||
if (ancestor != null) {
|
||||
CacheKey prevKey = ancestor.getKey();
|
||||
TraceSnapshot tracePrefix = trace.getTimeManager().findSnapshotWithNearestPrefix(time);
|
||||
if (tracePrefix.getSchedule().isSnapOnly()) {
|
||||
tracePrefix = null;
|
||||
}
|
||||
Map.Entry<CacheKey, CachedEmulator> cachePrefix = findNearestPrefix(key);
|
||||
if (cachePrefix != null && (tracePrefix == null ||
|
||||
cachePrefix.getKey().time.compareTo(tracePrefix.getSchedule()) >= 0)) {
|
||||
CacheKey prevKey = cachePrefix.getKey();
|
||||
|
||||
Msg.debug(this, "Using cached emulator at %s".formatted(prevKey.time));
|
||||
|
||||
synchronized (cache) {
|
||||
cache.remove(prevKey);
|
||||
@@ -698,7 +705,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
||||
|
||||
// TODO: Handle errors, and add to proper place in cache?
|
||||
// TODO: Finish partially-executed instructions?
|
||||
try (BusyEmu be = new BusyEmu(ancestor.getValue())) {
|
||||
try (BusyEmu be = new BusyEmu(cachePrefix.getValue())) {
|
||||
PcodeMachine<?> emu = be.ce.emulator();
|
||||
|
||||
emu.clearAllInjects();
|
||||
@@ -713,18 +720,28 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
||||
return be.dup();
|
||||
}
|
||||
}
|
||||
|
||||
Target target = targetService == null ? null : targetService.getTarget(trace);
|
||||
DefaultPcodeDebuggerAccess from =
|
||||
new DefaultPcodeDebuggerAccess(tool, target, platform, time.getSnap());
|
||||
DefaultPcodeDebuggerAccess from = new DefaultPcodeDebuggerAccess(tool, target, platform,
|
||||
tracePrefix != null ? tracePrefix.getKey() : time.getSnap(), time.getSnap());
|
||||
Writer writer = DebuggerEmulationIntegration.bytesDelayedWriteTrace(from);
|
||||
|
||||
PcodeMachine<?> emu = emulatorFactory.create(from, writer);
|
||||
try (BusyEmu be = new BusyEmu(new CachedEmulator(key.trace, emu, writer))) {
|
||||
installBreakpoints(key.trace, key.time.getSnap(), be.ce.emulator());
|
||||
monitor.initialize(time.totalTickCount());
|
||||
monitor.initialize(time.totalTickCount() -
|
||||
(tracePrefix != null ? tracePrefix.getSchedule().totalTickCount() : 0));
|
||||
createRegisterSpaces(trace, time, monitor);
|
||||
monitor.setMessage("Emulating");
|
||||
time.execute(trace, emu, monitor);
|
||||
if (tracePrefix != null) {
|
||||
Msg.debug(this, "Using new emulator from scratch snapshot %s"
|
||||
.formatted(tracePrefix.getScheduleString()));
|
||||
time.finish(trace, tracePrefix.getSchedule(), emu, monitor);
|
||||
}
|
||||
else {
|
||||
Msg.debug(this, "Using new emulator from snap %d".formatted(time.getSnap()));
|
||||
time.execute(trace, emu, monitor);
|
||||
}
|
||||
return be.dup();
|
||||
}
|
||||
}
|
||||
@@ -753,6 +770,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
||||
ce.writer().writeDown(into);
|
||||
TraceThread lastThread = key.time.getLastThread(key.trace);
|
||||
destSnap.setEventThread(lastThread);
|
||||
destSnap.setVersion(key.trace.getEmulatorCacheVersion());
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Msg.showError(this, null, "Emulate",
|
||||
|
||||
+90
-3
@@ -16,8 +16,7 @@
|
||||
package ghidra.trace.database.time;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
@@ -31,7 +30,7 @@ import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.TraceTimeManager;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.*;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule.TimeRadix;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
@@ -157,6 +156,94 @@ public class DBTraceTimeManager implements TraceTimeManager, DBTraceManager {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
protected DBTraceSnapshot doGetValidSnapshotBySchedule(String key, long version) {
|
||||
for (DBTraceSnapshot snapshot : snapshotsBySchedule.get(key)) {
|
||||
if (snapshot.getVersion() >= version) {
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected DBTraceSnapshot doFindNearest(TraceSchedule schedule, long version) {
|
||||
// Base case
|
||||
if (schedule.isSnapOnly()) {
|
||||
return snapshotStore.getObjectAt(schedule.getSnap()); // may be null
|
||||
}
|
||||
|
||||
// Inductive case
|
||||
DBTraceSnapshot best = null;
|
||||
TraceSchedule dropped = schedule.dropLastStep();
|
||||
String strDropped = dropped.toString();
|
||||
for (Iterator<String> it =
|
||||
snapshotsBySchedule.tail(strDropped, true).keys().iterator(); it.hasNext();) {
|
||||
String key = it.next();
|
||||
if (!key.startsWith(strDropped)) {
|
||||
break;
|
||||
}
|
||||
TraceSchedule candidateSchedule = TraceSchedule.parse(key);
|
||||
// We're in a chunk of too-advanced (and probably unrelated) schedules
|
||||
if (candidateSchedule.stepCount() > schedule.stepCount()) {
|
||||
TraceSchedule candidateTrunc =
|
||||
candidateSchedule.truncateToSteps(schedule.stepCount());
|
||||
Step candidateStep = candidateTrunc.lastStep().step();
|
||||
TraceSchedule candidateDropped = candidateTrunc.dropLastStep();
|
||||
// Hack the lexicographic indexing
|
||||
String extra = switch (candidateStep) {
|
||||
case PatchStep s -> "t%d-%c".formatted(s.getThreadKey(), '{' + 1);
|
||||
default -> "%s%c".formatted(candidateStep, ';' + 1);
|
||||
};
|
||||
String newTailKey = (candidateDropped.isSnapOnly() ? "%s:%s" : "%s;%s")
|
||||
.formatted(candidateDropped, extra);
|
||||
it = snapshotsBySchedule.tail(newTailKey, true).keys().iterator();
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a potential nearest. Must be related and less than, but better than best
|
||||
CompareResult cmp = candidateSchedule.compareSchedule(schedule);
|
||||
if (!cmp.related || cmp.compareTo > 0) {
|
||||
continue;
|
||||
}
|
||||
if (best != null && best.getSchedule().compareTo(candidateSchedule) >= 0) {
|
||||
continue;
|
||||
}
|
||||
// Checking validity requires loading the record. Do this filter last.
|
||||
DBTraceSnapshot valid = doGetValidSnapshotBySchedule(key, version);
|
||||
if (valid != null) {
|
||||
best = valid;
|
||||
}
|
||||
}
|
||||
|
||||
if (best != null) {
|
||||
return best;
|
||||
}
|
||||
|
||||
return doFindNearest(dropped, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @implNote Because the index is lexicographic, we have to hack a bit. Consider that 20 would
|
||||
* come before 3 in the index. That said, all the steps leading up to the last would
|
||||
* have to be equal for it to be a prefix, so I don't think any weird lexicographic
|
||||
* stuff comes into play except in the final step.
|
||||
* @implNote Even if the index were numeric, we have to worry about non-related schedules
|
||||
* appearing between related ones, e.g., {@code 0:t0-2, 0:t0-3;t1-1}, when searching
|
||||
* for {@code 0:t0-4}.
|
||||
*/
|
||||
@Override
|
||||
public TraceSnapshot findSnapshotWithNearestPrefix(TraceSchedule schedule) {
|
||||
long version = trace.getEmulatorCacheVersion();
|
||||
Optional<? extends TraceSnapshot> exists = getSnapshotsWithSchedule(schedule).stream()
|
||||
.filter(s -> s.getVersion() >= version)
|
||||
.findFirst();
|
||||
if (exists.isPresent()) {
|
||||
return exists.get();
|
||||
}
|
||||
return doFindNearest(schedule.dropPSteps(), version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceSnapshot> getAllSnapshots() {
|
||||
return Collections.unmodifiableCollection(snapshotStore.asMap().values());
|
||||
|
||||
+18
-1
@@ -17,6 +17,7 @@ package ghidra.trace.model.time;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule.TimeRadix;
|
||||
|
||||
@@ -76,6 +77,23 @@ public interface TraceTimeManager {
|
||||
*/
|
||||
TraceSnapshot findScratchSnapshot(TraceSchedule schedule);
|
||||
|
||||
/**
|
||||
* Find the nearest related snapshot whose schedule is a prefix of the given schedule
|
||||
*
|
||||
* <p>
|
||||
* This finds a snapshot that can be used as the initial state of an emulator to materialize the
|
||||
* state at the given schedule. The one it returns is the one that would require the fewest
|
||||
* instruction steps. Note that since an emulator cannot be initialized into the middle of an
|
||||
* instruction, snapshots whose schedules contain p-code op steps are ignored. Additionally,
|
||||
* this will ignore any snapshots whose version is less than the emulator cache version.
|
||||
*
|
||||
* @param schedule the desired schedule
|
||||
* @param allowOpSteps whether to include snapshots with p-code op steps
|
||||
* @return the found snapshot, or null
|
||||
* @see Trace#getEmulatorCacheVersion()
|
||||
*/
|
||||
TraceSnapshot findSnapshotWithNearestPrefix(TraceSchedule schedule);
|
||||
|
||||
/**
|
||||
* List all snapshots in the trace
|
||||
*
|
||||
@@ -129,5 +147,4 @@ public interface TraceTimeManager {
|
||||
* @return radix the radix
|
||||
*/
|
||||
TimeRadix getTimeRadix();
|
||||
|
||||
}
|
||||
|
||||
+48
-17
@@ -200,6 +200,35 @@ public class Sequence implements Comparable<Sequence> {
|
||||
return Long.max(0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the last step from this sequence
|
||||
*
|
||||
* @return the sequence with the last step removed
|
||||
*/
|
||||
public Sequence dropLast() {
|
||||
if (steps.isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return new Sequence(new ArrayList<>(steps.subList(0, steps.size() - 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the last step}
|
||||
*/
|
||||
public Step last() {
|
||||
return steps.getLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate this sequence to the first count steps
|
||||
*
|
||||
* @param count the count
|
||||
* @return the new sequence
|
||||
*/
|
||||
public Sequence truncate(int count) {
|
||||
return new Sequence(new ArrayList<>(steps.subList(0, count)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sequence clone() {
|
||||
return new Sequence(
|
||||
@@ -280,24 +309,22 @@ public class Sequence implements Comparable<Sequence> {
|
||||
Step s2 = that.steps.get(i);
|
||||
result = s1.compareStep(s2);
|
||||
switch (result) {
|
||||
case UNREL_LT:
|
||||
case UNREL_GT:
|
||||
case UNREL_LT, UNREL_GT -> {
|
||||
return result;
|
||||
case REL_LT:
|
||||
if (i + 1 == this.steps.size()) {
|
||||
return CompareResult.REL_LT;
|
||||
}
|
||||
else {
|
||||
return CompareResult.UNREL_LT;
|
||||
}
|
||||
case REL_GT:
|
||||
if (i + 1 == that.steps.size()) {
|
||||
return CompareResult.REL_GT;
|
||||
}
|
||||
else {
|
||||
return CompareResult.UNREL_GT;
|
||||
}
|
||||
default: // EQUALS, next step
|
||||
}
|
||||
case REL_LT -> {
|
||||
return i + 1 == this.steps.size()
|
||||
? CompareResult.REL_LT
|
||||
: CompareResult.UNREL_LT;
|
||||
}
|
||||
case REL_GT -> {
|
||||
return i + 1 == that.steps.size()
|
||||
? CompareResult.REL_GT
|
||||
: CompareResult.UNREL_GT;
|
||||
}
|
||||
default -> {
|
||||
// EQUALS, next step
|
||||
}
|
||||
}
|
||||
}
|
||||
if (that.steps.size() > min) {
|
||||
@@ -360,6 +387,10 @@ public class Sequence implements Comparable<Sequence> {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return steps.size();
|
||||
}
|
||||
|
||||
public long totalSkipCount() {
|
||||
long count = 0;
|
||||
for (Step step : steps) {
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.time.schedule;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
|
||||
public enum StepKind implements Stepper {
|
||||
INSTRUCTION {
|
||||
@Override
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepInstruction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipInstruction();
|
||||
}
|
||||
},
|
||||
PCODE {
|
||||
@Override
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepPcodeOp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipPcodeOp();
|
||||
}
|
||||
};
|
||||
}
|
||||
+4
-29
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -18,37 +18,12 @@ package ghidra.trace.model.time.schedule;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
|
||||
public interface Stepper {
|
||||
enum Enum implements Stepper {
|
||||
INSTRUCTION {
|
||||
@Override
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepInstruction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipInstruction();
|
||||
}
|
||||
},
|
||||
PCODE {
|
||||
@Override
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepPcodeOp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipPcodeOp();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Stepper instruction() {
|
||||
return Enum.INSTRUCTION;
|
||||
return StepKind.INSTRUCTION;
|
||||
}
|
||||
|
||||
static Stepper pcode() {
|
||||
return Enum.PCODE;
|
||||
return StepKind.PCODE;
|
||||
}
|
||||
|
||||
void tick(PcodeThread<?> thread);
|
||||
|
||||
+63
@@ -486,6 +486,15 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
||||
return !steps.isNop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this schedule has p-code steps
|
||||
*
|
||||
* @return true if this indicates at least one instruction step
|
||||
*/
|
||||
public boolean hasPSteps() {
|
||||
return !pSteps.isNop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source snapshot
|
||||
*
|
||||
@@ -580,6 +589,15 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
||||
return steps.totalTickCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of steps, excluding p-code steps
|
||||
*
|
||||
* @return the number of steps
|
||||
*/
|
||||
public int stepCount() {
|
||||
return steps.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of patches, excluding p-code patches
|
||||
*
|
||||
@@ -884,6 +902,51 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
||||
return new TraceSchedule(this.snap, this.steps, new Sequence());
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the last step
|
||||
*
|
||||
* <p>
|
||||
* If there are p-code steps, this drops the last step there. Otherwise, this drops the last
|
||||
* step from the instruction steps. A step includes all ticks in the step, e.g.,
|
||||
* {@code 0:t0-20;t1-5} becomes {@code 0:t0-20}. To remove a specific number of ticks, see
|
||||
* {@link TraceSchedule#steppedBackward(Trace, long)}.
|
||||
*
|
||||
* @return the schedule with the last step removed
|
||||
* @throws NoSuchElementException If there are neither instruction nor p-code steps.
|
||||
*/
|
||||
public TraceSchedule dropLastStep() {
|
||||
if (!this.pSteps.isNop()) {
|
||||
return new TraceSchedule(this.snap, this.steps, this.pSteps.dropLast());
|
||||
}
|
||||
return new TraceSchedule(this.snap, this.steps.dropLast(), new Sequence());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a step and which kind (instruction or p-code)
|
||||
*/
|
||||
public record StepAndKind(StepKind kind, Step step) {}
|
||||
|
||||
/**
|
||||
* {@return the last step of the schedule}
|
||||
*/
|
||||
public StepAndKind lastStep() {
|
||||
if (!this.pSteps.isNop()) {
|
||||
return new StepAndKind(StepKind.PCODE, pSteps.last());
|
||||
}
|
||||
return new StepAndKind(StepKind.INSTRUCTION, steps.last());
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all p-code steps, if any, and enough instruction steps, such that {@link #stepCount()}
|
||||
* returns the given count.
|
||||
*
|
||||
* @param count the desired step count
|
||||
* @return the new schedule
|
||||
*/
|
||||
public TraceSchedule truncateToSteps(int count) {
|
||||
return new TraceSchedule(this.snap, this.steps.truncate(count), new Sequence());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the threads involved in the schedule
|
||||
*
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.time;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
|
||||
public class DBTraceTimeManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
ToyDBTraceBuilder b;
|
||||
DBTraceTimeManager timeManager;
|
||||
|
||||
@Before
|
||||
public void setUpTimeManagerTest() throws Exception {
|
||||
b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default");
|
||||
timeManager = b.trace.getTimeManager();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownTimeManagerTest() throws Exception {
|
||||
b.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindSnapshotWithNearestPrefix() throws Exception {
|
||||
try (Transaction tx = b.startTransaction()) {
|
||||
assertNotNull(timeManager.findScratchSnapshot(TraceSchedule.parse("0:t0-2")));
|
||||
assertNotNull(timeManager.findScratchSnapshot(TraceSchedule.parse("0:t0-20;t1-9")));
|
||||
assertNotNull(timeManager.findScratchSnapshot(TraceSchedule.parse("0:t0-20;t1-10")));
|
||||
assertNotNull(timeManager.findScratchSnapshot(TraceSchedule.parse("0:t0-3;t1-10")));
|
||||
assertNotNull(timeManager.findScratchSnapshot(TraceSchedule.parse("0:t0-4")));
|
||||
}
|
||||
|
||||
assertEquals("0:t0-20;t1-10",
|
||||
timeManager.findSnapshotWithNearestPrefix(TraceSchedule.parse("0:t0-20;t1-10"))
|
||||
.getScheduleString());
|
||||
assertEquals("0:t0-20;t1-10",
|
||||
timeManager.findSnapshotWithNearestPrefix(TraceSchedule.parse("0:t0-20;t1-11"))
|
||||
.getScheduleString());
|
||||
assertEquals("0:t0-2",
|
||||
timeManager.findSnapshotWithNearestPrefix(TraceSchedule.parse("0:t0-3"))
|
||||
.getScheduleString());
|
||||
assertEquals("0:t0-4",
|
||||
timeManager.findSnapshotWithNearestPrefix(TraceSchedule.parse("0:t0-5"))
|
||||
.getScheduleString());
|
||||
|
||||
assertNull(timeManager.findSnapshotWithNearestPrefix(TraceSchedule.parse("1:t0-1")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user