GP-0: Fix javodocs. Fix tests. Fix streamSub.

This commit is contained in:
Dan 2024-03-04 09:43:29 -05:00
parent c2bb47d45a
commit ddea132049
20 changed files with 262 additions and 34 deletions

View file

@ -22,7 +22,6 @@ import db.Transaction;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.util.PathMatcher; import ghidra.dbg.util.PathMatcher;
import ghidra.dbg.util.PathPattern;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;

View file

@ -98,7 +98,7 @@ class TraceBreakpointSet {
/** /**
* Get the trace * Get the trace
* *
* @return * @return the trace
*/ */
public Trace getTrace() { public Trace getTrace() {
return trace; return trace;
@ -237,7 +237,7 @@ class TraceBreakpointSet {
* The caller should first call {@link #canMerge(TraceBreakpoint)} to check if the breakpoint * The caller should first call {@link #canMerge(TraceBreakpoint)} to check if the breakpoint
* "fits." * "fits."
* *
* @param bpt * @param bpt the breakpoint
* @return true if the set actually changed as a result * @return true if the set actually changed as a result
*/ */
public boolean add(TraceBreakpoint bpt) { public boolean add(TraceBreakpoint bpt) {

View file

@ -408,7 +408,7 @@ public class DBTraceBreakpoint
@Override @Override
public boolean isEnabled(long snap) { public boolean isEnabled(long snap) {
// NB. Only object mode support per-snap enablement // NB. Only object mode supports per-snap enablement
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return enabled; return enabled;
} }
@ -491,4 +491,11 @@ public class DBTraceBreakpoint
public void delete() { public void delete() {
space.deleteBreakpoint(this); space.deleteBreakpoint(this);
} }
@Override
public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap);
}
}
} }

View file

@ -369,6 +369,11 @@ public class DBTraceObjectBreakpointLocation
} }
} }
@Override
public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null;
}
@Override @Override
public TraceObject getObject() { public TraceObject getObject() {
return object; return object;

View file

@ -223,6 +223,11 @@ public class DBTraceObjectBreakpointSpec
} }
} }
@Override
public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null;
}
@Override @Override
public TraceObject getObject() { public TraceObject getObject() {
return object; return object;

View file

@ -307,4 +307,11 @@ public class DBTraceMemoryRegion
public void delete() { public void delete() {
space.deleteRegion(this); space.deleteRegion(this);
} }
@Override
public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap);
}
}
} }

View file

@ -386,6 +386,11 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
} }
} }
@Override
public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null;
}
@Override @Override
public TraceObject getObject() { public TraceObject getObject() {
return object; return object;

View file

@ -16,6 +16,7 @@
package ghidra.trace.database.target; package ghidra.trace.database.target;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -210,11 +211,15 @@ class DBTraceObjectValueWriteBehindCache {
private Stream<DBTraceObjectValueBehind> streamSub( private Stream<DBTraceObjectValueBehind> streamSub(
NavigableMap<Long, DBTraceObjectValueBehind> map, Lifespan span, boolean forward) { NavigableMap<Long, DBTraceObjectValueBehind> map, Lifespan span, boolean forward) {
Long floor = map.floorKey(span.min()); long min;
if (floor == null) { Entry<Long, DBTraceObjectValueBehind> floor = map.floorEntry(span.min());
floor = span.min(); if (floor != null && floor.getValue().getLifespan().contains(span.min())) {
min = floor.getKey();
} }
var sub = map.subMap(floor, true, span.max(), true); else {
min = span.min();
}
var sub = map.subMap(min, true, span.max(), true);
if (!forward) { if (!forward) {
sub = sub.descendingMap(); sub = sub.descendingMap();
} }

View file

@ -175,6 +175,11 @@ public class DBTraceObjectThread implements TraceObjectThread, DBTraceObjectInte
} }
} }
@Override
public boolean isValid(long snap) {
return object.getCanonicalParent(snap) != null;
}
@Override @Override
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) { public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
return translator.translate(rec); return translator.translate(rec);

View file

@ -184,4 +184,11 @@ public class DBTraceThread extends DBAnnotatedObject implements TraceThread {
public void delete() { public void delete() {
manager.deleteThread(this); manager.deleteThread(this);
} }
@Override
public boolean isValid(long snap) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return lifespan.contains(snap);
}
}
} }

View file

@ -18,6 +18,8 @@ package ghidra.trace.model.breakpoint;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
import ghidra.pcode.exec.SleighUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.*; import ghidra.trace.model.*;
@ -51,6 +53,8 @@ public interface TraceBreakpoint extends TraceUniqueObject {
* *
* <p> * <p>
* This should be a name suitable for display on the screen * This should be a name suitable for display on the screen
*
* @param name the new name
*/ */
void setName(String name); void setName(String name);
@ -75,12 +79,18 @@ public interface TraceBreakpoint extends TraceUniqueObject {
AddressRange getRange(); AddressRange getRange();
/** /**
* Get the minimum address in this breakpoint's range
*
* @see #getRange() * @see #getRange()
* @return the minimum address
*/ */
Address getMinAddress(); Address getMinAddress();
/** /**
* Get the maximum address in this breakpoint's range
*
* @see #getRange() * @see #getRange()
* @return the maximum address
*/ */
Address getMaxAddress(); Address getMaxAddress();
@ -109,6 +119,7 @@ public interface TraceBreakpoint extends TraceUniqueObject {
* Set the cleared snap of this breakpoint * Set the cleared snap of this breakpoint
* *
* @param clearedSnap the cleared snap, or {@link Long#MAX_VALUE} for "to the end of time" * @param clearedSnap the cleared snap, or {@link Long#MAX_VALUE} for "to the end of time"
* @throws DuplicateNameException if extending the lifespan would cause a naming collision
*/ */
void setClearedSnap(long clearedSnap) throws DuplicateNameException; void setClearedSnap(long clearedSnap) throws DuplicateNameException;
@ -236,21 +247,27 @@ public interface TraceBreakpoint extends TraceUniqueObject {
* Set Sleigh source to replace the breakpointed instruction in emulation * Set Sleigh source to replace the breakpointed instruction in emulation
* *
* <p> * <p>
* The default is simply "<code>{@link PcodeEmulationLibrary#emu_swi() emu_swi()}; * The default is simply:
* {@link PcodeEmulationLibrary#emu_exec_decoded() emu_exec_decoded()};</code>", effectively a * </p>
* non-conditional breakpoint followed by execution of the actual instruction. Modifying this *
* allows clients to create conditional breakpoints or simply override or inject additional * <pre>
* logic into an emulated target. * {@link PcodeEmulationLibrary#emu_swi() emu_swi()};
* {@link PcodeEmulationLibrary#emu_exec_decoded() emu_exec_decoded()};
* </pre>
* <p>
* That is effectively a non-conditional breakpoint followed by execution of the actual
* instruction. Modifying this allows clients to create conditional breakpoints or simply
* override or inject additional logic into an emulated target.
* *
* <p> * <p>
* <b>NOTE:</b> This current has no effect on access breakpoints, but only execution * <b>NOTE:</b> This currently has no effect on access breakpoints, but only execution
* breakpoints. * breakpoints.
* *
* <p> * <p>
* If the specified source fails to compile during emulator set-up, this will fall back to * If the specified source fails to compile during emulator set-up, this will fall back to
* {@link PcodeEmulationLibrary#emu_err * {@link PcodeEmulationLibrary#emu_swi()}
* *
* @see #DEFAULT_SLEIGH * @see SleighUtils#UNCONDITIONAL_BREAK
* @param sleigh the Sleigh source * @param sleigh the Sleigh source
*/ */
void setEmuSleigh(String sleigh); void setEmuSleigh(String sleigh);
@ -266,4 +283,18 @@ public interface TraceBreakpoint extends TraceUniqueObject {
* Delete this breakpoint from the trace * Delete this breakpoint from the trace
*/ */
void delete(); void delete();
/**
* Check if the breakpoint is valid at the given snapshot
*
* <p>
* In object mode, a breakpoint's life may be disjoint, so checking if the snap occurs between
* creation and destruction is not quite sufficient. This method encapsulates validity. In
* object mode, it checks that the breakpoint object has a canonical parent at the given
* snapshot. In table mode, it checks that the lifespan contains the snap.
*
* @param snap the snapshot key
* @return true if valid, false if not
*/
boolean isValid(long snap);
} }

View file

@ -89,6 +89,9 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
* *
* @param creationSnap the creation snap, or {@link Long#MIN_VALUE} for "since the beginning of * @param creationSnap the creation snap, or {@link Long#MIN_VALUE} for "since the beginning of
* time" * time"
* @throws DuplicateNameException if extending the region would cause a naming conflict
* @throws TraceOverlappedRegionException if extending the region would cause it to overlap
* another
*/ */
void setCreationSnap(long creationSnap) void setCreationSnap(long creationSnap)
throws DuplicateNameException, TraceOverlappedRegionException; throws DuplicateNameException, TraceOverlappedRegionException;
@ -105,6 +108,9 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
* *
* @param destructionSnap the destruction snap, or {@link Long#MAX_VALUE} for "to the end of * @param destructionSnap the destruction snap, or {@link Long#MAX_VALUE} for "to the end of
* time" * time"
* @throws DuplicateNameException if extending the region would cause a naming conflict
* @throws TraceOverlappedRegionException if extending the region would cause it to overlap
* another
*/ */
void setDestructionSnap(long destructionSnap) void setDestructionSnap(long destructionSnap)
throws DuplicateNameException, TraceOverlappedRegionException; throws DuplicateNameException, TraceOverlappedRegionException;
@ -138,22 +144,38 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
AddressRange getRange(); AddressRange getRange();
/** /**
* Set the minimum address of the range
*
* @see #setRange(AddressRange) * @see #setRange(AddressRange)
* @param min the new minimum
* @throws TraceOverlappedRegionException if extending the region would cause it to overlap
* another
*/ */
void setMinAddress(Address min) throws TraceOverlappedRegionException; void setMinAddress(Address min) throws TraceOverlappedRegionException;
/** /**
* Get the minimum address of the range
*
* @see #getRange() * @see #getRange()
* @return the minimum address
*/ */
Address getMinAddress(); Address getMinAddress();
/** /**
* Set the maximum address of the range
*
* @see #setRange(AddressRange) * @see #setRange(AddressRange)
* @param max the new minimum
* @throws TraceOverlappedRegionException if extending the region would cause it to overlap
* another
*/ */
void setMaxAddress(Address max) throws TraceOverlappedRegionException; void setMaxAddress(Address max) throws TraceOverlappedRegionException;
/** /**
* Get the maximum address of the range
*
* @see #getRange() * @see #getRange()
* @return the maximum address
*/ */
Address getMaxAddress(); Address getMaxAddress();
@ -164,6 +186,11 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
* This adjusts the max address of the range so that its length becomes that given * This adjusts the max address of the range so that its length becomes that given
* *
* @see #setRange(AddressRange) * @see #setRange(AddressRange)
* @param length the desired length of the range
* @throws AddressOverflowException if extending the range would cause the max address to
* overflow
* @throws TraceOverlappedRegionException if extending the region would cause it to overlap
* another
*/ */
void setLength(long length) throws AddressOverflowException, TraceOverlappedRegionException; void setLength(long length) throws AddressOverflowException, TraceOverlappedRegionException;
@ -182,7 +209,9 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
void setFlags(Collection<TraceMemoryFlag> flags); void setFlags(Collection<TraceMemoryFlag> flags);
/** /**
* @see #setFlags(Collection) * Set the flags, e.g., permissions, of this region
*
* @param flags the flags
*/ */
default void setFlags(TraceMemoryFlag... flags) { default void setFlags(TraceMemoryFlag... flags) {
setFlags(Arrays.asList(flags)); setFlags(Arrays.asList(flags));
@ -191,12 +220,14 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
/** /**
* Add the given flags, e.g., permissions, to this region * Add the given flags, e.g., permissions, to this region
* *
* @see #setFlags(Collection) * @param flags the flags
*/ */
void addFlags(Collection<TraceMemoryFlag> flags); void addFlags(Collection<TraceMemoryFlag> flags);
/** /**
* @see #addFlags(Collection) * Add the given flags, e.g., permissions, to this region
*
* @param flags the flags
*/ */
default void addFlags(TraceMemoryFlag... flags) { default void addFlags(TraceMemoryFlag... flags) {
addFlags(Arrays.asList(flags)); addFlags(Arrays.asList(flags));
@ -205,12 +236,14 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
/** /**
* Remove the given flags, e.g., permissions, from this region * Remove the given flags, e.g., permissions, from this region
* *
* @see #setFlags(Collection) * @param flags the flags
*/ */
void clearFlags(Collection<TraceMemoryFlag> flags); void clearFlags(Collection<TraceMemoryFlag> flags);
/** /**
* @see #clearFlags(Collection) * Remove the given flags, e.g., permissions, from this region
*
* @param flags the flags
*/ */
default void clearFlags(TraceMemoryFlag... flags) { default void clearFlags(TraceMemoryFlag... flags) {
clearFlags(Arrays.asList(flags)); clearFlags(Arrays.asList(flags));
@ -249,7 +282,7 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
/** /**
* Add or clear the {@link TraceMemoryFlag#WRITE} flag * Add or clear the {@link TraceMemoryFlag#WRITE} flag
* *
* @param read true to add, false to clear * @param write true to add, false to clear
*/ */
default void setWrite(boolean write) { default void setWrite(boolean write) {
if (write) { if (write) {
@ -272,7 +305,7 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
/** /**
* Add or clear the {@link TraceMemoryFlag#EXECUTE} flag * Add or clear the {@link TraceMemoryFlag#EXECUTE} flag
* *
* @param read true to add, false to clear * @param execute true to add, false to clear
*/ */
default void setExecute(boolean execute) { default void setExecute(boolean execute) {
if (execute) { if (execute) {
@ -295,7 +328,7 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
/** /**
* Add or clear the {@link TraceMemoryFlag#VOLATILE} flag * Add or clear the {@link TraceMemoryFlag#VOLATILE} flag
* *
* @param read true to add, false to clear * @param vol true to add, false to clear
*/ */
default void setVolatile(boolean vol) { default void setVolatile(boolean vol) {
if (vol) { if (vol) {
@ -319,4 +352,18 @@ public interface TraceMemoryRegion extends TraceUniqueObject {
* Delete this region from the trace * Delete this region from the trace
*/ */
void delete(); void delete();
/**
* Check if the region is valid at the given snapshot
*
* <p>
* In object mode, a region's life may be disjoint, so checking if the snap occurs between
* creation and destruction is not quite sufficient. This method encapsulates validity. In
* object mode, it checks that the region object has a canonical parent at the given snapshot.
* In table mode, it checks that the lifespan contains the snap.
*
* @param snap the snapshot key
* @return true if valid, false if not
*/
boolean isValid(long snap);
} }

View file

@ -66,6 +66,7 @@ public interface TraceThread extends TraceUniqueObject {
* *
* @param creationSnap the creation snap, or {@link Long#MIN_VALUE} for "since the beginning of * @param creationSnap the creation snap, or {@link Long#MIN_VALUE} for "since the beginning of
* time" * time"
* @throws DuplicateNameException if extending the thread's life would cause a naming conflict
*/ */
void setCreationSnap(long creationSnap) throws DuplicateNameException; void setCreationSnap(long creationSnap) throws DuplicateNameException;
@ -81,6 +82,7 @@ public interface TraceThread extends TraceUniqueObject {
* *
* @param destructionSnap the destruction snap, or {@link Long#MAX_VALUE} for "to the end of * @param destructionSnap the destruction snap, or {@link Long#MAX_VALUE} for "to the end of
* time" * time"
* @throws DuplicateNameException if extending the thread's life would cause a naming conflict
*/ */
void setDestructionSnap(long destructionSnap) throws DuplicateNameException; void setDestructionSnap(long destructionSnap) throws DuplicateNameException;
@ -144,4 +146,18 @@ public interface TraceThread extends TraceUniqueObject {
* Delete this thread from the trace * Delete this thread from the trace
*/ */
void delete(); void delete();
/**
* Check if the thread is valid at the given snapshot
*
* <p>
* In object mode, a thread's life may be disjoint, so checking if the snap occurs between
* creation and destruction is not quite sufficient. This method encapsulates validity. In
* object mode, it checks that the thread object has a canonical parent at the given snapshot.
* In table mode, it checks that the lifespan contains the snap.
*
* @param snap the snapshot key
* @return true if valid, false if not
*/
boolean isValid(long snap);
} }

View file

@ -20,6 +20,8 @@ import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.*; import org.junit.*;
import db.Transaction; import db.Transaction;
@ -257,14 +259,38 @@ public class DBTraceBreakpointManagerTest extends AbstractGhidraHeadlessIntegrat
assertEquals("WinMain", breakMain.getComment()); assertEquals("WinMain", breakMain.getComment());
} }
protected static class InvalidBreakpointMatcher extends BaseMatcher<TraceBreakpoint> {
private final long snap;
public InvalidBreakpointMatcher(long snap) {
this.snap = snap;
}
@Override
public boolean matches(Object actual) {
return actual == null || actual instanceof TraceBreakpoint bpt && !bpt.isValid(snap);
}
@Override
public void describeTo(Description description) {
description.appendText("An invalid or null breakpoint");
}
}
protected static InvalidBreakpointMatcher invalidBreakpoint(long snap) {
return new InvalidBreakpointMatcher(snap);
}
@Test @Test
public void testDelete() throws Exception { public void testDelete() throws Exception {
addBreakpoints(); addBreakpoints();
assertEquals(breakMain, breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]")); assertEquals(breakMain, breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]"));
try (Transaction tx = b.startTransaction()) { try (Transaction tx = b.startTransaction()) {
breakMain.delete(); breakMain.delete();
assertNull(breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]")); assertThat(breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]"),
invalidBreakpoint(0));
} }
assertNull(breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]")); assertThat(breakpointManager.getPlacedBreakpointByPath(0, "Breakpoints[0]"),
invalidBreakpoint(0));
} }
} }

View file

@ -15,12 +15,13 @@
*/ */
package ghidra.trace.database.memory; package ghidra.trace.database.memory;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertNull;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Set;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.*; import org.junit.*;
import db.Transaction; import db.Transaction;
@ -79,6 +80,29 @@ public abstract class AbstractDBTraceMemoryManagerRegionsTest
assertEquals(Set.of(region), Set.copyOf(memory.getAllRegions())); assertEquals(Set.of(region), Set.copyOf(memory.getAllRegions()));
} }
protected static class InvalidRegionMatcher extends BaseMatcher<TraceMemoryRegion> {
private final long snap;
public InvalidRegionMatcher(long snap) {
this.snap = snap;
}
@Override
public boolean matches(Object actual) {
return actual == null ||
actual instanceof TraceMemoryRegion region && !region.isValid(snap);
}
@Override
public void describeTo(Description description) {
description.appendText("An invalid or null region");
}
}
protected static InvalidRegionMatcher invalidRegion(long snap) {
return new InvalidRegionMatcher(snap);
}
@Test @Test
public void testGetLiveRegionByPath() throws Exception { public void testGetLiveRegionByPath() throws Exception {
assertNull(memory.getLiveRegionByPath(0, "Regions[0x1000]")); assertNull(memory.getLiveRegionByPath(0, "Regions[0x1000]"));
@ -90,8 +114,8 @@ public abstract class AbstractDBTraceMemoryManagerRegionsTest
} }
assertEquals(region, memory.getLiveRegionByPath(0, "Regions[0x1000]")); assertEquals(region, memory.getLiveRegionByPath(0, "Regions[0x1000]"));
assertNull(memory.getLiveRegionByPath(0, "Regions[0x1001]")); assertThat(memory.getLiveRegionByPath(0, "Regions[0x1001]"), invalidRegion(0));
assertNull(memory.getLiveRegionByPath(-1, "Regions[0x1000]")); assertThat(memory.getLiveRegionByPath(-1, "Regions[0x1000]"), invalidRegion(-1));
} }
@Test @Test

View file

@ -19,6 +19,8 @@ import static org.junit.Assert.*;
import java.util.Set; import java.util.Set;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.*; import org.junit.*;
import db.Transaction; import db.Transaction;
@ -86,6 +88,28 @@ public class DBTraceThreadManagerTest extends AbstractGhidraHeadlessIntegrationT
assertEquals(Set.of(thread2), Set.copyOf(threadManager.getThreadsByPath("Threads[2]"))); assertEquals(Set.of(thread2), Set.copyOf(threadManager.getThreadsByPath("Threads[2]")));
} }
protected static class InvalidThreadMatcher extends BaseMatcher<TraceThread> {
private final long snap;
public InvalidThreadMatcher(long snap) {
this.snap = snap;
}
@Override
public boolean matches(Object actual) {
return actual == null || actual instanceof TraceThread thread && !thread.isValid(snap);
}
@Override
public void describeTo(Description description) {
description.appendText("An invalid or null thread");
}
}
protected static InvalidThreadMatcher invalidThread(long snap) {
return new InvalidThreadMatcher(snap);
}
@Test @Test
public void testLiveThreadByPath() throws Exception { public void testLiveThreadByPath() throws Exception {
assertNull(threadManager.getLiveThreadByPath(0, "Threads[1]")); assertNull(threadManager.getLiveThreadByPath(0, "Threads[1]"));
@ -95,8 +119,8 @@ public class DBTraceThreadManagerTest extends AbstractGhidraHeadlessIntegrationT
assertEquals(thread2, threadManager.getLiveThreadByPath(0, "Threads[2]")); assertEquals(thread2, threadManager.getLiveThreadByPath(0, "Threads[2]"));
assertEquals(thread2, threadManager.getLiveThreadByPath(10, "Threads[2]")); assertEquals(thread2, threadManager.getLiveThreadByPath(10, "Threads[2]"));
assertNull(threadManager.getLiveThreadByPath(0, "Threads[3]")); assertNull(threadManager.getLiveThreadByPath(0, "Threads[3]"));
assertNull(threadManager.getLiveThreadByPath(-1, "Threads[2]")); assertThat(threadManager.getLiveThreadByPath(-1, "Threads[2]"), invalidThread(-1));
assertNull(threadManager.getLiveThreadByPath(11, "Threads[2]")); assertThat(threadManager.getLiveThreadByPath(11, "Threads[2]"), invalidThread(11));
} }
@Test @Test

View file

@ -38,6 +38,8 @@ import ghidra.util.classfinder.ClassSearcher;
* <p> * <p>
* For a complete example of a p-code emulator, see {@link PcodeEmulator}. For an alternative * For a complete example of a p-code emulator, see {@link PcodeEmulator}. For an alternative
* implementation incorporating an abstract piece, see the Taint Analyzer. * implementation incorporating an abstract piece, see the Taint Analyzer.
*
* @param <T> the type of objects in the machine's state
*/ */
public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> { public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {

View file

@ -198,7 +198,12 @@ public interface PcodeMachine<T> {
/** /**
* Set the suspension state of the machine * Set the suspension state of the machine
* *
* <p>
* This does not simply suspend all threads, but sets a machine-wide flag. A thread is suspended
* if either the thread's flag is set, or the machine's flag is set.
*
* @see PcodeThread#setSuspended(boolean) * @see PcodeThread#setSuspended(boolean)
* @param suspended true to suspend the machine, false to let it run
*/ */
void setSuspended(boolean suspended); void setSuspended(boolean suspended);
@ -206,6 +211,7 @@ public interface PcodeMachine<T> {
* Check the suspension state of the machine * Check the suspension state of the machine
* *
* @see PcodeThread#isSuspended() * @see PcodeThread#isSuspended()
* @return true if suspended
*/ */
boolean isSuspended(); boolean isSuspended();

View file

@ -15,8 +15,6 @@
*/ */
package ghidra.pcode.emu; package ghidra.pcode.emu;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary; import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
import ghidra.pcode.exec.*; import ghidra.pcode.exec.*;
@ -289,6 +287,9 @@ public interface PcodeThread<T> {
* reliable way to halt execution. Note the emulator may halt mid instruction. If this is not * reliable way to halt execution. Note the emulator may halt mid instruction. If this is not
* desired, then upon catching the exception, un-suspend the p-code thread and call * desired, then upon catching the exception, un-suspend the p-code thread and call
* {@link #finishInstruction()} or {@link #dropInstruction()}. * {@link #finishInstruction()} or {@link #dropInstruction()}.
*
* @see PcodeMachine#setSuspended(boolean)
* @param suspended true to suspend the machine, false to let it run
*/ */
void setSuspended(boolean suspended); void setSuspended(boolean suspended);
@ -341,6 +342,7 @@ public interface PcodeThread<T> {
* The memory part of this state is shared among all threads in the same machine. See * The memory part of this state is shared among all threads in the same machine. See
* {@link PcodeMachine#getSharedState()}. * {@link PcodeMachine#getSharedState()}.
* *
* @return the state
*/ */
ThreadPcodeExecutorState<T> getState(); ThreadPcodeExecutorState<T> getState();

View file

@ -40,6 +40,11 @@ public enum SleighUtils {
/** /**
* A Sleigh parsing error * A Sleigh parsing error
*
* @param header the header / title for the message
* @param message the detail message
* @param start the character position where the syntax error starts
* @param stop the character position where the syntax error ends
*/ */
public record SleighParseErrorEntry(String header, String message, int start, int stop) { public record SleighParseErrorEntry(String header, String message, int start, int stop) {
public String fullMessage() { public String fullMessage() {