mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 17:59:56 +08:00
GP-1678: Create experimental object-based recorder and opinion
This commit is contained in:
+1
-1
@@ -78,6 +78,6 @@ public abstract class AbstractModelForDbgengProcessActivationTest
|
|||||||
.collect(Collectors.toList())).trim();
|
.collect(Collectors.toList())).trim();
|
||||||
String procId = getIdFromCapture(line);
|
String procId = getIdFromCapture(line);
|
||||||
assertEquals(expected.getPath(),
|
assertEquals(expected.getPath(),
|
||||||
getProcessPattern().applyIndices(procId).getSingletonPath());
|
getProcessPattern().applyKeys(procId).getSingletonPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -83,6 +83,6 @@ public abstract class AbstractModelForGdbFrameActivationTest
|
|||||||
assertFalse(line.contains("\n"));
|
assertFalse(line.contains("\n"));
|
||||||
assertTrue(line.startsWith("#"));
|
assertTrue(line.startsWith("#"));
|
||||||
String frameLevel = line.substring(1).split("\\s+")[0];
|
String frameLevel = line.substring(1).split("\\s+")[0];
|
||||||
assertEquals(expected.getPath(), STACK_PATTERN.applyIndices(frameLevel).getSingletonPath());
|
assertEquals(expected.getPath(), STACK_PATTERN.applyKeys(frameLevel).getSingletonPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -74,6 +74,6 @@ public abstract class AbstractModelForGdbInferiorActivationTest
|
|||||||
.filter(l -> l.trim().startsWith("*"))
|
.filter(l -> l.trim().startsWith("*"))
|
||||||
.collect(Collectors.toList())).trim();
|
.collect(Collectors.toList())).trim();
|
||||||
String inferiorId = line.split("\\s+")[1];
|
String inferiorId = line.split("\\s+")[1];
|
||||||
assertEquals(expected.getPath(), INF_PATTERN.applyIndices(inferiorId).getSingletonPath());
|
assertEquals(expected.getPath(), INF_PATTERN.applyKeys(inferiorId).getSingletonPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -87,6 +87,6 @@ public abstract class AbstractModelForGdbThreadActivationTest
|
|||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
String threadGid = line.split("\\s+")[2];
|
String threadGid = line.split("\\s+")[2];
|
||||||
assertEquals(expected.getPath(),
|
assertEquals(expected.getPath(),
|
||||||
THREAD_PATTERN.applyIndices(threadGid, threadGid).getSingletonPath());
|
THREAD_PATTERN.applyKeys(threadGid, threadGid).getSingletonPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ public abstract class AbstractModelForLldbScenarioX64RegistersTest
|
|||||||
for (String name : toWrite.keySet()) {
|
for (String name : toWrite.keySet()) {
|
||||||
for (TargetRegisterBank bank : banks.values()) {
|
for (TargetRegisterBank bank : banks.values()) {
|
||||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||||
for (TargetRegister reg : regs.values()) {
|
for (TargetRegister reg : regs.values()) {
|
||||||
waitOn(bank.writeRegister(reg, toWrite.get(name)));
|
waitOn(bank.writeRegister(reg, toWrite.get(name)));
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -81,7 +81,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
|||||||
for (Entry<String, byte[]> ent : getRegisterWrites().entrySet()) {
|
for (Entry<String, byte[]> ent : getRegisterWrites().entrySet()) {
|
||||||
String regName = ent.getKey();
|
String regName = ent.getKey();
|
||||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||||
path, pred -> pred.applyIndices(regName), false);
|
path, pred -> pred.applyKeys(regName), false);
|
||||||
for (TargetRegister reg : regs.values()) {
|
for (TargetRegister reg : regs.values()) {
|
||||||
assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8);
|
assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
|||||||
for (TargetRegisterBank bank : banks.values()) {
|
for (TargetRegisterBank bank : banks.values()) {
|
||||||
for (String name : exp.keySet()) {
|
for (String name : exp.keySet()) {
|
||||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||||
for (TargetRegister reg : regs.values()) {
|
for (TargetRegister reg : regs.values()) {
|
||||||
byte[] bytes = waitOn(bank.readRegister(reg));
|
byte[] bytes = waitOn(bank.readRegister(reg));
|
||||||
read.put(name, bytes);
|
read.put(name, bytes);
|
||||||
@@ -149,7 +149,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
|||||||
for (TargetRegisterBank bank : banks.values()) {
|
for (TargetRegisterBank bank : banks.values()) {
|
||||||
for (String name : write.keySet()) {
|
for (String name : write.keySet()) {
|
||||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||||
for (TargetRegister reg : regs.values()) {
|
for (TargetRegister reg : regs.values()) {
|
||||||
waitOn(bank.writeRegister(reg, write.get(name)));
|
waitOn(bank.writeRegister(reg, write.get(name)));
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
|||||||
Trace trace = current.getTrace();
|
Trace trace = current.getTrace();
|
||||||
TraceRecorder recorder = current.getRecorder();
|
TraceRecorder recorder = current.getRecorder();
|
||||||
BackgroundUtils.async(tool, trace, NAME, true, true, false,
|
BackgroundUtils.async(tool, trace, NAME, true, true, false,
|
||||||
(__, monitor) -> recorder.captureProcessMemory(selection, monitor, false));
|
(__, monitor) -> recorder.readMemoryBlocks(selection, monitor, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,7 +78,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
|||||||
}
|
}
|
||||||
TraceRecorder recorder = current.getRecorder();
|
TraceRecorder recorder = current.getRecorder();
|
||||||
// TODO: Either allow partial, or provide action to intersect with accessible
|
// TODO: Either allow partial, or provide action to intersect with accessible
|
||||||
if (!recorder.getAccessibleProcessMemory().contains(selection)) {
|
if (!recorder.getAccessibleMemory().contains(selection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ public class PCLocationTrackingSpec implements RegisterLocationTrackingSpec {
|
|||||||
if (frame == null) {
|
if (frame == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return frame.getProgramCounter();
|
return frame.getProgramCounter(snap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+2
-2
@@ -56,7 +56,7 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||||||
}
|
}
|
||||||
TraceRecorder recorder = coordinates.getRecorder();
|
TraceRecorder recorder = coordinates.getRecorder();
|
||||||
AddressSet visibleAccessible =
|
AddressSet visibleAccessible =
|
||||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
recorder.getAccessibleMemory().intersect(visible);
|
||||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||||
AddressSetView alreadyKnown =
|
AddressSetView alreadyKnown =
|
||||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||||
@@ -67,6 +67,6 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||||||
return AsyncUtils.NIL;
|
return AsyncUtils.NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return recorder.captureProcessMemory(toRead, TaskMonitor.DUMMY, false);
|
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -58,7 +58,7 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||||||
}
|
}
|
||||||
TraceRecorder recorder = coordinates.getRecorder();
|
TraceRecorder recorder = coordinates.getRecorder();
|
||||||
AddressSet visibleAccessible =
|
AddressSet visibleAccessible =
|
||||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
recorder.getAccessibleMemory().intersect(visible);
|
||||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||||
AddressSetView alreadyKnown =
|
AddressSetView alreadyKnown =
|
||||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||||
@@ -92,6 +92,6 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||||||
return AsyncUtils.NIL;
|
return AsyncUtils.NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return recorder.captureProcessMemory(toRead, TaskMonitor.DUMMY, false);
|
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -810,7 +810,7 @@ public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
this.captureTask = recorder.captureProcessMemory(new AddressSet(range), monitor, false);
|
this.captureTask = recorder.readMemoryBlocks(new AddressSet(range), monitor, false);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
captureTask.get(); // Not a fan, but whatever.
|
captureTask.get(); // Not a fan, but whatever.
|
||||||
|
|||||||
+16
-3
@@ -854,6 +854,14 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRoot(ActionContext context) {
|
||||||
|
TargetObject object = this.getObjectFromContext(context);
|
||||||
|
if (object == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return object.isRoot();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isInstance(ActionContext context, Class<? extends TargetObject> clazz) {
|
public boolean isInstance(ActionContext context, Class<? extends TargetObject> clazz) {
|
||||||
TargetObject object = this.getObjectFromContext(context);
|
TargetObject object = this.getObjectFromContext(context);
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
@@ -1128,8 +1136,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||||||
.popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex)
|
.popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex)
|
||||||
.popupMenuIcon(AbstractRecordAction.ICON)
|
.popupMenuIcon(AbstractRecordAction.ICON)
|
||||||
.helpLocation(new HelpLocation(plugin.getName(), "record"))
|
.helpLocation(new HelpLocation(plugin.getName(), "record"))
|
||||||
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||||
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||||
.onAction(ctx -> performStartRecording(ctx))
|
.onAction(ctx -> performStartRecording(ctx))
|
||||||
.enabled(true)
|
.enabled(true)
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
@@ -1592,7 +1600,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startRecording(TargetProcess targetObject, boolean prompt) {
|
public void startRecording(TargetObject targetObject, boolean prompt) {
|
||||||
TraceRecorder rec = modelService.getRecorder(targetObject);
|
TraceRecorder rec = modelService.getRecorder(targetObject);
|
||||||
if (rec != null) {
|
if (rec != null) {
|
||||||
return; // Already being recorded
|
return; // Already being recorded
|
||||||
@@ -1630,6 +1638,11 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void performStartRecording(ActionContext context) {
|
public void performStartRecording(ActionContext context) {
|
||||||
|
TargetObject maybeRoot = getObjectFromContext(context);
|
||||||
|
if (maybeRoot.isRoot()) {
|
||||||
|
startRecording(maybeRoot, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
performAction(context, false, TargetProcess.class, proc -> {
|
performAction(context, false, TargetProcess.class, proc -> {
|
||||||
TargetProcess valid = DebugModelConventions.liveProcessOrNull(proc);
|
TargetProcess valid = DebugModelConventions.liveProcessOrNull(proc);
|
||||||
if (valid != null) {
|
if (valid != null) {
|
||||||
|
|||||||
+1
@@ -410,6 +410,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentStack == stack) {
|
if (currentStack == stack) {
|
||||||
|
stackTableModel.fireTableDataChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentStack = stack;
|
currentStack = stack;
|
||||||
|
|||||||
+17
-8
@@ -28,41 +28,50 @@ import ghidra.util.database.UndoableTransaction;
|
|||||||
|
|
||||||
public class StackFrameRow {
|
public class StackFrameRow {
|
||||||
public static class Synthetic extends StackFrameRow {
|
public static class Synthetic extends StackFrameRow {
|
||||||
|
private Address pc;
|
||||||
|
|
||||||
public Synthetic(DebuggerStackProvider provider, Address pc) {
|
public Synthetic(DebuggerStackProvider provider, Address pc) {
|
||||||
super(provider, pc);
|
super(provider);
|
||||||
|
this.pc = pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgramCounter(Address pc) {
|
public void updateProgramCounter(Address pc) {
|
||||||
this.pc = pc;
|
this.pc = pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getProgramCounter() {
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final DebuggerStackProvider provider;
|
private final DebuggerStackProvider provider;
|
||||||
|
|
||||||
final TraceStackFrame frame;
|
final TraceStackFrame frame;
|
||||||
private int level;
|
private int level;
|
||||||
Address pc;
|
|
||||||
|
|
||||||
public StackFrameRow(DebuggerStackProvider provider, TraceStackFrame frame) {
|
public StackFrameRow(DebuggerStackProvider provider, TraceStackFrame frame) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.frame = frame;
|
this.frame = frame;
|
||||||
this.level = frame.getLevel();
|
this.level = frame.getLevel();
|
||||||
this.pc = frame.getProgramCounter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private StackFrameRow(DebuggerStackProvider provider, Address pc) {
|
private StackFrameRow(DebuggerStackProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.frame = null;
|
this.frame = null;
|
||||||
this.level = 0;
|
this.level = 0;
|
||||||
this.pc = pc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFrameLevel() {
|
public int getFrameLevel() {
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getSnap() {
|
||||||
|
return provider.current.getSnap();
|
||||||
|
}
|
||||||
|
|
||||||
public Address getProgramCounter() {
|
public Address getProgramCounter() {
|
||||||
return pc;
|
return frame.getProgramCounter(getSnap());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getComment() {
|
public String getComment() {
|
||||||
@@ -88,11 +97,12 @@ public class StackFrameRow {
|
|||||||
if (curThread == null) {
|
if (curThread == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Address pc = getProgramCounter();
|
||||||
if (pc == null) {
|
if (pc == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
|
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
|
||||||
curThread, Range.singleton(provider.current.getSnap()), pc);
|
curThread, Range.singleton(getSnap()), pc);
|
||||||
ProgramLocation sloc = provider.mappingService.getOpenMappedLocation(dloc);
|
ProgramLocation sloc = provider.mappingService.getOpenMappedLocation(dloc);
|
||||||
if (sloc == null) {
|
if (sloc == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -103,6 +113,5 @@ public class StackFrameRow {
|
|||||||
protected void update() {
|
protected void update() {
|
||||||
assert frame != null; // Should never update a synthetic stack
|
assert frame != null; // Should never update a synthetic stack
|
||||||
level = frame.getLevel();
|
level = frame.getLevel();
|
||||||
pc = frame.getProgramCounter();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -578,11 +578,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
||||||
|
if (someLoc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
TraceProgramView view = current.getView();
|
TraceProgramView view = current.getView();
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (someLoc.getProgram() instanceof TraceProgramView) {
|
Program program = someLoc.getProgram();
|
||||||
|
if (program == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (program instanceof TraceProgramView) {
|
||||||
return someLoc;
|
return someLoc;
|
||||||
}
|
}
|
||||||
return mappingService.getDynamicLocationFromStatic(view, someLoc);
|
return mappingService.getDynamicLocationFromStatic(view, someLoc);
|
||||||
|
|||||||
+4
-3
@@ -101,6 +101,7 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
|||||||
*/
|
*/
|
||||||
public default Set<DebuggerMappingOffer> getOffers(TargetObject target,
|
public default Set<DebuggerMappingOffer> getOffers(TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
// TODO: Remove this check?
|
||||||
if (!(target instanceof TargetProcess)) {
|
if (!(target instanceof TargetProcess)) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
@@ -117,13 +118,13 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce this opinion's offers for the given environment and target process
|
* Produce this opinion's offers for the given environment and target
|
||||||
*
|
*
|
||||||
* @param env the environment associated with the target
|
* @param env the environment associated with the target
|
||||||
* @param process the target process
|
* @param target the target (usually a process)
|
||||||
* @param includeOverrides true to include override offers, i.e., those with negative confidence
|
* @param includeOverrides true to include override offers, i.e., those with negative confidence
|
||||||
* @return the offers, possibly empty, but never null
|
* @return the offers, possibly empty, but never null
|
||||||
*/
|
*/
|
||||||
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides);
|
boolean includeOverrides);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-4
@@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.mapping;
|
package ghidra.app.plugin.core.debug.mapping;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.address.AddressRange;
|
|
||||||
|
|
||||||
public interface DebuggerMemoryMapper {
|
public interface DebuggerMemoryMapper {
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +32,10 @@ public interface DebuggerMemoryMapper {
|
|||||||
* @param traceRange the range in the view's address space
|
* @param traceRange the range in the view's address space
|
||||||
* @return the "same range" in the target's address space
|
* @return the "same range" in the target's address space
|
||||||
*/
|
*/
|
||||||
AddressRange traceToTarget(AddressRange traceRange);
|
default AddressRange traceToTarget(AddressRange traceRange) {
|
||||||
|
return new AddressRangeImpl(traceToTarget(traceRange.getMinAddress()),
|
||||||
|
traceToTarget(traceRange.getMaxAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map the given address from the target process into the trace
|
* Map the given address from the target process into the trace
|
||||||
@@ -49,5 +51,8 @@ public interface DebuggerMemoryMapper {
|
|||||||
* @param targetRange the range in the target's address space
|
* @param targetRange the range in the target's address space
|
||||||
* @return the "same range" in the trace's address space
|
* @return the "same range" in the trace's address space
|
||||||
*/
|
*/
|
||||||
AddressRange targetToTrace(AddressRange targetRange);
|
default AddressRange targetToTrace(AddressRange targetRange) {
|
||||||
|
return new AddressRangeImpl(targetToTrace(targetRange.getMinAddress()),
|
||||||
|
targetToTrace(targetRange.getMaxAddress()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-12
@@ -45,12 +45,6 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
|||||||
public Address traceToTarget(Address traceAddr) {
|
public Address traceToTarget(Address traceAddr) {
|
||||||
assert isInFactory(traceAddr, traceAddressFactory);
|
assert isInFactory(traceAddr, traceAddressFactory);
|
||||||
return toSameNamedSpace(traceAddr, targetAddressFactory);
|
return toSameNamedSpace(traceAddr, targetAddressFactory);
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AddressRange traceToTarget(AddressRange traceRange) {
|
|
||||||
return new AddressRangeImpl(traceToTarget(traceRange.getMinAddress()),
|
|
||||||
traceToTarget(traceRange.getMaxAddress()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,10 +59,4 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
|||||||
assert isInFactory(targetAddr, targetAddressFactory);
|
assert isInFactory(targetAddr, targetAddressFactory);
|
||||||
return toSameNamedSpace(targetAddr, traceAddressFactory);
|
return toSameNamedSpace(targetAddr, traceAddressFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AddressRange targetToTrace(AddressRange targetRange) {
|
|
||||||
return new AddressRangeImpl(targetToTrace(targetRange.getMinAddress()),
|
|
||||||
targetToTrace(targetRange.getMaxAddress()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -35,12 +35,12 @@ public class DefaultDebuggerTargetTraceMapper implements DebuggerTargetTraceMapp
|
|||||||
protected final Set<String> extraRegNames;
|
protected final Set<String> extraRegNames;
|
||||||
|
|
||||||
public DefaultDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
public DefaultDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
||||||
CompilerSpecID csId, Collection<String> extraRegNames)
|
CompilerSpecID csID, Collection<String> extraRegNames)
|
||||||
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||||
this.target = target;
|
this.target = target;
|
||||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||||
this.language = langServ.getLanguage(langID);
|
this.language = langServ.getLanguage(langID);
|
||||||
this.cSpec = language.getCompilerSpecByID(csId);
|
this.cSpec = language.getCompilerSpecByID(csID);
|
||||||
|
|
||||||
this.extraRegNames = Set.copyOf(extraRegNames);
|
this.extraRegNames = Set.copyOf(extraRegNames);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.mapping;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ghidra.dbg.target.TargetObject;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
|
||||||
|
public class ObjectBasedDebuggerMappingOffer extends DefaultDebuggerMappingOffer {
|
||||||
|
private static final String DESCRIPTION =
|
||||||
|
"EXPERIMENTAL: Object-based recording, deferred mapping";
|
||||||
|
private static final int CONFIDENCE = -100; // TODO: Increase this when it becomes preferred
|
||||||
|
protected static final LanguageID LANGID_DATA64 = new LanguageID("DATA:BE:64:default");
|
||||||
|
protected static final CompilerSpecID CSID_PTR64 = new CompilerSpecID("pointer64");
|
||||||
|
|
||||||
|
public ObjectBasedDebuggerMappingOffer(TargetObject target) {
|
||||||
|
// TODO: Is extraRegNames relevant?
|
||||||
|
super(target, CONFIDENCE, DESCRIPTION, LANGID_DATA64, CSID_PTR64, Set.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DebuggerTargetTraceMapper createMapper()
|
||||||
|
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||||
|
return new ObjectBasedDebuggerTargetTraceMapper(target, langID, csID, extraRegNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.mapping;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ghidra.dbg.target.TargetEnvironment;
|
||||||
|
import ghidra.dbg.target.TargetObject;
|
||||||
|
|
||||||
|
public class ObjectBasedDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<DebuggerMappingOffer> getOffers(TargetObject target, boolean includeOverrides) {
|
||||||
|
// TODO: Remove this check
|
||||||
|
if (!includeOverrides) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
// TODO: Do I want to require it to record the whole model?
|
||||||
|
// If not, I need to figure out how to locate object dependencies and still record them.
|
||||||
|
if (!target.isRoot()) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return Set.of(new ObjectBasedDebuggerMappingOffer(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
|
boolean includeOverrides) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.mapping;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.trace.model.Trace;
|
||||||
|
import ghidra.util.database.UndoableTransaction;
|
||||||
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
|
public class ObjectBasedDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
||||||
|
protected final Trace trace;
|
||||||
|
protected final AddressSpace base;
|
||||||
|
|
||||||
|
protected final Map<Integer, AddressSpace> targetToTraceSpaces = new HashMap<>();
|
||||||
|
protected final Map<Integer, AddressSpace> traceToTargetSpaces = new HashMap<>();
|
||||||
|
|
||||||
|
public ObjectBasedDebuggerMemoryMapper(Trace trace) {
|
||||||
|
this.trace = trace;
|
||||||
|
this.base = trace.getBaseAddressFactory().getDefaultAddressSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address traceToTarget(Address traceAddr) {
|
||||||
|
AddressSpace traceSpace = traceAddr.getAddressSpace();
|
||||||
|
int traceIdHash = System.identityHashCode(traceSpace);
|
||||||
|
AddressSpace targetSpace;
|
||||||
|
synchronized (traceToTargetSpaces) {
|
||||||
|
targetSpace = traceToTargetSpaces.get(traceIdHash);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Can only be null if space is the default space or some non-physical space. In that case,
|
||||||
|
* the target hasn't defined a space with that name, so no mapping.
|
||||||
|
*/
|
||||||
|
if (targetSpace == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return targetSpace.getAddress(traceAddr.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address targetToTrace(Address targetAddr) {
|
||||||
|
AddressSpace targetSpace = targetAddr.getAddressSpace();
|
||||||
|
int targetIdHash = System.identityHashCode(targetSpace);
|
||||||
|
AddressSpace traceSpace;
|
||||||
|
synchronized (traceToTargetSpaces) {
|
||||||
|
traceSpace = targetToTraceSpaces.get(targetIdHash);
|
||||||
|
if (traceSpace == null) {
|
||||||
|
traceSpace = createSpace(targetSpace.getName());
|
||||||
|
targetToTraceSpaces.put(targetIdHash, traceSpace);
|
||||||
|
traceToTargetSpaces.put(System.identityHashCode(traceSpace),
|
||||||
|
targetSpace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return traceSpace.getAddress(targetAddr.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AddressSpace createSpace(String name) {
|
||||||
|
try (UndoableTransaction tid =
|
||||||
|
UndoableTransaction.start(trace, "Create space for mapping", true)) {
|
||||||
|
AddressFactory factory = trace.getBaseAddressFactory();
|
||||||
|
AddressSpace space = factory.getAddressSpace(name);
|
||||||
|
if (space == null) {
|
||||||
|
return trace.getMemoryManager().createOverlayAddressSpace(name, base);
|
||||||
|
}
|
||||||
|
// Let the default space suffice for its own name
|
||||||
|
// NB. if overlay already exists, we've already issued a warning
|
||||||
|
if (space == base || space.isOverlaySpace()) {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
// Otherwise, do not allow non-physical spaces to be used by accident.
|
||||||
|
return trace.getMemoryManager().createOverlayAddressSpace('_' + name, base);
|
||||||
|
}
|
||||||
|
catch (DuplicateNameException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.mapping;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.record.ObjectBasedTraceRecorder;
|
||||||
|
import ghidra.app.services.TraceRecorder;
|
||||||
|
import ghidra.dbg.target.*;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.trace.model.Trace;
|
||||||
|
|
||||||
|
public class ObjectBasedDebuggerTargetTraceMapper extends DefaultDebuggerTargetTraceMapper {
|
||||||
|
|
||||||
|
protected ObjectBasedDebuggerMemoryMapper memoryMapper;
|
||||||
|
|
||||||
|
public ObjectBasedDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
||||||
|
CompilerSpecID csID, Collection<String> extraRegNames)
|
||||||
|
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||||
|
super(target, langID, csID, extraRegNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DebuggerMemoryMapper createMemoryMapper(TargetMemory memory) {
|
||||||
|
// TODO: Validate regions to not overlap?
|
||||||
|
// Could probably do that in unit testing of model instead
|
||||||
|
return memoryMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DebuggerRegisterMapper createRegisterMapper(TargetRegisterContainer registers) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TraceRecorder startRecording(DebuggerModelServicePlugin service, Trace trace) {
|
||||||
|
this.memoryMapper = new ObjectBasedDebuggerMemoryMapper(trace);
|
||||||
|
return new ObjectBasedTraceRecorder(service, trace, target, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-3
@@ -19,7 +19,8 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.*;
|
import ghidra.dbg.target.TargetEnvironment;
|
||||||
|
import ghidra.dbg.target.TargetObject;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
if (!includeOverrides) {
|
if (!includeOverrides) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
@@ -65,7 +66,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
// ALL THE SPECS!!!
|
// ALL THE SPECS!!!
|
||||||
new LanguageCompilerSpecQuery(null, null, null, null, null))
|
new LanguageCompilerSpecQuery(null, null, null, null, null))
|
||||||
.stream()
|
.stream()
|
||||||
.map(lcsp -> offerForLanguageAndCSpec(process, endian, lcsp))
|
.map(lcsp -> offerForLanguageAndCSpec(target, endian, lcsp))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-4
@@ -49,24 +49,28 @@ public class FridaArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
|
|
||||||
protected static class FridaAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
protected static class FridaAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||||
public FridaAarch64MacosOffer(TargetProcess process) {
|
public FridaAarch64MacosOffer(TargetProcess process) {
|
||||||
super(process, 50, "AARCH64/Frida on macos", LANG_ID_AARCH64,COMP_ID_DEFAULT,
|
super(process, 50, "AARCH64/Frida on macos", LANG_ID_AARCH64, COMP_ID_DEFAULT,
|
||||||
Set.of("cpsr"));
|
Set.of("cpsr"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includesOverrides) {
|
boolean includesOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
String arch = env.getArchitecture();
|
String arch = env.getArchitecture();
|
||||||
boolean is64Bit = arch.contains("AARCH64") || arch.contains("arm64") || arch.contains("arm");
|
boolean is64Bit =
|
||||||
|
arch.contains("AARCH64") || arch.contains("arm64") || arch.contains("arm");
|
||||||
String os = env.getOperatingSystem();
|
String os = env.getOperatingSystem();
|
||||||
if (os.contains("macos")) {
|
if (os.contains("macos")) {
|
||||||
if (is64Bit) {
|
if (is64Bit) {
|
||||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||||
return Set.of(new FridaAarch64MacosOffer(process));
|
return Set.of(new FridaAarch64MacosOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Set.of();
|
return Set.of();
|
||||||
|
|||||||
+6
-3
@@ -49,14 +49,17 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
|
|
||||||
protected static class LldbAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
protected static class LldbAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||||
public LldbAarch64MacosOffer(TargetProcess process) {
|
public LldbAarch64MacosOffer(TargetProcess process) {
|
||||||
super(process, 50, "AARCH64/LLDB on macos", LANG_ID_AARCH64,COMP_ID_DEFAULT,
|
super(process, 50, "AARCH64/LLDB on macos", LANG_ID_AARCH64, COMP_ID_DEFAULT,
|
||||||
Set.of("cpsr"));
|
Set.of("cpsr"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includesOverrides) {
|
boolean includesOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
@@ -66,7 +69,7 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
if (os.contains("macos")) {
|
if (os.contains("macos")) {
|
||||||
if (is64Bit) {
|
if (is64Bit) {
|
||||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||||
return Set.of(new LldbAarch64MacosOffer(process));
|
return Set.of(new LldbAarch64MacosOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Set.of();
|
return Set.of();
|
||||||
|
|||||||
+5
-2
@@ -75,15 +75,18 @@ public class DbgengX64DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (env == null || !env.getDebugger().toLowerCase().contains("dbg")) {
|
if (env == null || !env.getDebugger().toLowerCase().contains("dbg")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
boolean is64Bit =
|
boolean is64Bit =
|
||||||
env.getArchitecture().contains("x86_64") || env.getArchitecture().contains("x64_32");
|
env.getArchitecture().contains("x86_64") || env.getArchitecture().contains("x64_32");
|
||||||
if (is64Bit) {
|
if (is64Bit) {
|
||||||
return Set.of(new DbgI386X86_64WindowsOffer(process));
|
return Set.of(new DbgI386X86_64WindowsOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -96,7 +96,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
|||||||
try {
|
try {
|
||||||
// This is on its own task thread, so whatever.
|
// This is on its own task thread, so whatever.
|
||||||
// Just don't hang it indefinitely.
|
// Just don't hang it indefinitely.
|
||||||
recorder.captureProcessMemory(set, TaskMonitor.DUMMY, false)
|
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, false)
|
||||||
.get(1000, TimeUnit.MILLISECONDS);
|
.get(1000, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
|||||||
+12
-7
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.frida;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.TargetEnvironment;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetProcess;
|
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.lang.LanguageID;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -71,16 +70,22 @@ public class FridaX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
TargetProcess process = (TargetProcess) target;
|
||||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
String arch = env.getArchitecture();
|
String arch = env.getArchitecture();
|
||||||
boolean is32Bit = arch.contains("ia32") ||arch.contains("x86-32") || arch.contains("i386") ||
|
boolean is32Bit =
|
||||||
arch.contains("x86_32");
|
arch.contains("ia32") || arch.contains("x86-32") || arch.contains("i386") ||
|
||||||
boolean is64Bit = arch.contains("x64") ||arch.contains("x86-64") || arch.contains("x64-32") ||
|
arch.contains("x86_32");
|
||||||
arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686");
|
boolean is64Bit =
|
||||||
|
arch.contains("x64") || arch.contains("x86-64") || arch.contains("x64-32") ||
|
||||||
|
arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686");
|
||||||
String os = env.getOperatingSystem();
|
String os = env.getOperatingSystem();
|
||||||
if (os.contains("darwin")) {
|
if (os.contains("darwin")) {
|
||||||
if (is64Bit) {
|
if (is64Bit) {
|
||||||
|
|||||||
+4
-3
@@ -21,7 +21,8 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.*;
|
import ghidra.dbg.target.TargetEnvironment;
|
||||||
|
import ghidra.dbg.target.TargetObject;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
if (!isGdb(env)) {
|
if (!isGdb(env)) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
@@ -90,7 +91,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
|||||||
String arch = env.getArchitecture();
|
String arch = env.getArchitecture();
|
||||||
|
|
||||||
return getCompilerSpecsForGnu(arch, endian).stream()
|
return getCompilerSpecsForGnu(arch, endian).stream()
|
||||||
.flatMap(lcsp -> offersForLanguageAndCSpec(process, arch, endian, lcsp).stream())
|
.flatMap(lcsp -> offersForLanguageAndCSpec(target, arch, endian, lcsp).stream())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.TargetEnvironment;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetProcess;
|
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
|
||||||
@@ -39,8 +38,11 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,7 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
String arch = env.getArchitecture();
|
String arch = env.getArchitecture();
|
||||||
if (arch.startsWith("m68k")) {
|
if (arch.startsWith("m68k")) {
|
||||||
return Set.of(new GdbM68kBELinux32DefOffer(process));
|
return Set.of(new GdbM68kBELinux32DefOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.TargetEnvironment;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetProcess;
|
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
|
||||||
@@ -79,8 +78,12 @@ public class GdbMipsDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
TargetProcess process = (TargetProcess) target;
|
||||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.TargetEnvironment;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetProcess;
|
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
|
||||||
@@ -65,8 +64,12 @@ public class GdbPowerPCDebuggerMappingOpinion implements DebuggerMappingOpinion
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
TargetProcess process = (TargetProcess) target;
|
||||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -100,8 +100,12 @@ public class GdbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
TargetProcess process = (TargetProcess) target;
|
||||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -64,8 +64,11 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
@@ -73,6 +76,6 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
// NOTE: Not worried about JRE version
|
// NOTE: Not worried about JRE version
|
||||||
return Set.of(new DalvikDebuggerMappingOffer(process));
|
return Set.of(new DalvikDebuggerMappingOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -64,8 +64,11 @@ public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
@@ -73,6 +76,6 @@ public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
// NOTE: Not worried about JRE version
|
// NOTE: Not worried about JRE version
|
||||||
return Set.of(new JavaDebuggerMappingOffer(process));
|
return Set.of(new JavaDebuggerMappingOffer((TargetProcess) target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.lldb;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.dbg.target.TargetEnvironment;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetProcess;
|
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.lang.LanguageID;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -71,8 +70,12 @@ public class LldbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||||
boolean includeOverrides) {
|
boolean includeOverrides) {
|
||||||
|
if (!(target instanceof TargetProcess)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
TargetProcess process = (TargetProcess) target;
|
||||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -250,6 +250,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
|||||||
Msg.info(this, "Ignoring " + breakpoint +
|
Msg.info(this, "Ignoring " + breakpoint +
|
||||||
" changed until service has finished loading its trace");
|
" changed until service has finished loading its trace");
|
||||||
}
|
}
|
||||||
|
catch (NoSuchElementException e) {
|
||||||
|
// TODO: This catch clause should not be necessary.
|
||||||
|
Msg.error(this,
|
||||||
|
"!!!! Object-based breakpoint emitted event without a spec: " + breakpoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void breakpointLifespanChanged(TraceAddressSpace spaceIsNull,
|
private void breakpointLifespanChanged(TraceAddressSpace spaceIsNull,
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ public class ReadsTargetMemoryPcodeExecutorState
|
|||||||
if (!isLive()) {
|
if (!isLive()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
waitTimeout(recorder.captureProcessMemory(unknown, TaskMonitor.DUMMY, false));
|
waitTimeout(recorder.readMemoryBlocks(unknown, TaskMonitor.DUMMY, false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-56
@@ -21,11 +21,11 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedMemoryRecorder;
|
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedMemoryRecorder;
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.app.plugin.core.debug.service.model.record.RecorderUtils;
|
||||||
import ghidra.async.TypeSpec;
|
|
||||||
import ghidra.dbg.target.TargetMemory;
|
import ghidra.dbg.target.TargetMemory;
|
||||||
import ghidra.dbg.target.TargetMemoryRegion;
|
import ghidra.dbg.target.TargetMemoryRegion;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.memory.*;
|
import ghidra.trace.model.memory.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -35,20 +35,7 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
||||||
|
|
||||||
// For large memory captures
|
// For large memory captures
|
||||||
private static final int BLOCK_SIZE = 4096;
|
private static final int BLOCK_BITS = 12; // 4096 bytes
|
||||||
private static final long BLOCK_MASK = -1L << 12;
|
|
||||||
|
|
||||||
protected static AddressSetView expandToBlocks(AddressSetView asv) {
|
|
||||||
AddressSet result = new AddressSet();
|
|
||||||
// Not terribly efficient, but this is one range most of the time
|
|
||||||
for (AddressRange range : asv) {
|
|
||||||
AddressSpace space = range.getAddressSpace();
|
|
||||||
Address min = space.getAddress(range.getMinAddress().getOffset() & BLOCK_MASK);
|
|
||||||
Address max = space.getAddress(range.getMaxAddress().getOffset() | ~BLOCK_MASK);
|
|
||||||
result.add(new AddressRangeImpl(min, max));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DefaultTraceRecorder recorder;
|
private final DefaultTraceRecorder recorder;
|
||||||
private final Trace trace;
|
private final Trace trace;
|
||||||
@@ -62,45 +49,7 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
|||||||
|
|
||||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||||
TaskMonitor monitor, boolean toMap) {
|
TaskMonitor monitor, boolean toMap) {
|
||||||
// TODO: Figure out how to display/select per-thread memory.
|
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor, toMap);
|
||||||
// Probably need a thread parameter passed in then?
|
|
||||||
// NOTE: That thread memory will already be chained to process memory. Good.
|
|
||||||
|
|
||||||
// NOTE: I don't intend to warn about the number of requests.
|
|
||||||
// They're delivered in serial, and there's a cancel button that works
|
|
||||||
|
|
||||||
int total = 0;
|
|
||||||
AddressSetView expSet = expandToBlocks(set)
|
|
||||||
.intersect(trace.getMemoryManager().getRegionsAddressSet(recorder.getSnap()));
|
|
||||||
for (AddressRange r : expSet) {
|
|
||||||
total += Long.divideUnsigned(r.getLength() + BLOCK_SIZE - 1, BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
monitor.initialize(total);
|
|
||||||
monitor.setMessage("Capturing memory");
|
|
||||||
// TODO: Read blocks in parallel? Probably NO. Tends to overload the agent.
|
|
||||||
NavigableMap<Address, byte[]> result = toMap ? new TreeMap<>() : null;
|
|
||||||
return AsyncUtils.each(TypeSpec.VOID, expSet.iterator(), (r, loop) -> {
|
|
||||||
AddressRangeChunker blocks = new AddressRangeChunker(r, BLOCK_SIZE);
|
|
||||||
AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (vBlk, inner) -> {
|
|
||||||
// The listener in the recorder will copy to the Trace.
|
|
||||||
monitor.incrementProgress(1);
|
|
||||||
AddressRange tBlk = recorder.getMemoryMapper().traceToTarget(vBlk);
|
|
||||||
recorder.getProcessMemory()
|
|
||||||
.readMemory(tBlk.getMinAddress(), (int) tBlk.getLength())
|
|
||||||
.thenAccept(data -> {
|
|
||||||
if (toMap) {
|
|
||||||
result.put(tBlk.getMinAddress(), data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exceptionally(e -> {
|
|
||||||
Msg.error(this, "Error reading block " + tBlk + ": " + e);
|
|
||||||
// NOTE: Above may double log, since recorder listens for errors, too
|
|
||||||
return null; // Continue looping on errors
|
|
||||||
})
|
|
||||||
.thenApply(__ -> !monitor.isCancelled())
|
|
||||||
.handle(inner::repeatWhile);
|
|
||||||
}).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile);
|
|
||||||
}).thenApply(__ -> result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ public class DefaultStackRecorder implements ManagedStackRecorder {
|
|||||||
|
|
||||||
public void doRecordFrame(TraceStack traceStack, int frameLevel, Address pc) {
|
public void doRecordFrame(TraceStack traceStack, int frameLevel, Address pc) {
|
||||||
TraceStackFrame traceFrame = traceStack.getFrame(frameLevel, true);
|
TraceStackFrame traceFrame = traceStack.getFrame(frameLevel, true);
|
||||||
traceFrame.setProgramCounter(pc);
|
traceFrame.setProgramCounter(null, pc); // Not object-based, so span=null
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordFrame(TargetStackFrame frame) {
|
public void recordFrame(TargetStackFrame frame) {
|
||||||
|
|||||||
+11
-14
@@ -22,6 +22,8 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
|||||||
|
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.app.plugin.core.debug.service.model.interfaces.*;
|
import ghidra.app.plugin.core.debug.service.model.interfaces.*;
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.record.DataTypeRecorder;
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.record.SymbolRecorder;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
import ghidra.app.services.TraceRecorderListener;
|
import ghidra.app.services.TraceRecorderListener;
|
||||||
import ghidra.async.AsyncLazyValue;
|
import ghidra.async.AsyncLazyValue;
|
||||||
@@ -64,11 +66,11 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
TraceObjectManager objectManager;
|
TraceObjectManager objectManager;
|
||||||
|
|
||||||
DefaultBreakpointRecorder breakpointRecorder;
|
DefaultBreakpointRecorder breakpointRecorder;
|
||||||
DefaultDataTypeRecorder datatypeRecorder;
|
DataTypeRecorder datatypeRecorder;
|
||||||
DefaultMemoryRecorder memoryRecorder;
|
DefaultMemoryRecorder memoryRecorder;
|
||||||
DefaultModuleRecorder moduleRecorder;
|
DefaultModuleRecorder moduleRecorder;
|
||||||
DefaultProcessRecorder processRecorder;
|
DefaultProcessRecorder processRecorder;
|
||||||
DefaultSymbolRecorder symbolRecorder;
|
SymbolRecorder symbolRecorder;
|
||||||
DefaultTimeRecorder timeRecorder;
|
DefaultTimeRecorder timeRecorder;
|
||||||
|
|
||||||
//protected final PermanentTransactionExecutor seqTx;
|
//protected final PermanentTransactionExecutor seqTx;
|
||||||
@@ -94,10 +96,10 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
|
|
||||||
this.processRecorder = new DefaultProcessRecorder(this);
|
this.processRecorder = new DefaultProcessRecorder(this);
|
||||||
this.breakpointRecorder = new DefaultBreakpointRecorder(this);
|
this.breakpointRecorder = new DefaultBreakpointRecorder(this);
|
||||||
this.datatypeRecorder = new DefaultDataTypeRecorder(this);
|
this.datatypeRecorder = new DataTypeRecorder(this);
|
||||||
this.memoryRecorder = new DefaultMemoryRecorder(this);
|
this.memoryRecorder = new DefaultMemoryRecorder(this);
|
||||||
this.moduleRecorder = new DefaultModuleRecorder(this);
|
this.moduleRecorder = new DefaultModuleRecorder(this);
|
||||||
this.symbolRecorder = new DefaultSymbolRecorder(this);
|
this.symbolRecorder = new SymbolRecorder(this);
|
||||||
this.timeRecorder = new DefaultTimeRecorder(this);
|
this.timeRecorder = new DefaultTimeRecorder(this);
|
||||||
this.objectManager = new TraceObjectManager(target, mapper, this);
|
this.objectManager = new TraceObjectManager(target, mapper, this);
|
||||||
}
|
}
|
||||||
@@ -266,7 +268,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
/*---------------- CAPTURE METHODS -------------------*/
|
/*---------------- CAPTURE METHODS -------------------*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set,
|
||||||
TaskMonitor monitor, boolean toMap) {
|
TaskMonitor monitor, boolean toMap) {
|
||||||
if (set.isEmpty()) {
|
if (set.isEmpty()) {
|
||||||
return CompletableFuture.completedFuture(new TreeMap<>());
|
return CompletableFuture.completedFuture(new TreeMap<>());
|
||||||
@@ -499,12 +501,6 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
|
|
||||||
/*---------------- LISTENER METHODS -------------------*/
|
/*---------------- LISTENER METHODS -------------------*/
|
||||||
|
|
||||||
// UNUSED?
|
|
||||||
@Override
|
|
||||||
public TraceEventListener getListenerForRecord() {
|
|
||||||
return objectManager.getEventListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListenerSet<TraceRecorderListener> getListeners() {
|
public ListenerSet<TraceRecorderListener> getListeners() {
|
||||||
return objectManager.getListeners();
|
return objectManager.getListeners();
|
||||||
}
|
}
|
||||||
@@ -526,17 +522,17 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSetView getAccessibleProcessMemory() {
|
public AddressSetView getAccessibleMemory() {
|
||||||
return processRecorder.getAccessibleProcessMemory();
|
return processRecorder.getAccessibleProcessMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<byte[]> readProcessMemory(Address start, int length) {
|
public CompletableFuture<byte[]> readMemory(Address start, int length) {
|
||||||
return processRecorder.readProcessMemory(start, length);
|
return processRecorder.readProcessMemory(start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> writeProcessMemory(Address start, byte[] data) {
|
public CompletableFuture<Void> writeMemory(Address start, byte[] data) {
|
||||||
return processRecorder.writeProcessMemory(start, data);
|
return processRecorder.writeProcessMemory(start, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,6 +562,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UNUSED?
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> flushTransactions() {
|
public CompletableFuture<Void> flushTransactions() {
|
||||||
return parTx.flush();
|
return parTx.flush();
|
||||||
|
|||||||
+16
-2
@@ -24,20 +24,22 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
|||||||
import ghidra.app.plugin.core.debug.utils.DefaultTransactionCoalescer;
|
import ghidra.app.plugin.core.debug.utils.DefaultTransactionCoalescer;
|
||||||
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer;
|
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer;
|
||||||
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer.CoalescedTx;
|
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer.CoalescedTx;
|
||||||
|
import ghidra.framework.model.DomainObjectException;
|
||||||
import ghidra.framework.model.UndoableDomainObject;
|
import ghidra.framework.model.UndoableDomainObject;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.ClosedException;
|
||||||
|
|
||||||
public class PermanentTransactionExecutor {
|
public class PermanentTransactionExecutor {
|
||||||
|
|
||||||
private final TransactionCoalescer txc;
|
private final TransactionCoalescer txc;
|
||||||
private final Executor[] threads;
|
private final ExecutorService[] threads;
|
||||||
private final UndoableDomainObject obj;
|
private final UndoableDomainObject obj;
|
||||||
|
|
||||||
public PermanentTransactionExecutor(UndoableDomainObject obj, String name, int threadCount,
|
public PermanentTransactionExecutor(UndoableDomainObject obj, String name, int threadCount,
|
||||||
int delayMs) {
|
int delayMs) {
|
||||||
this.obj = obj;
|
this.obj = obj;
|
||||||
txc = new DefaultTransactionCoalescer<>(obj, RecorderPermanentTransaction::start, delayMs);
|
txc = new DefaultTransactionCoalescer<>(obj, RecorderPermanentTransaction::start, delayMs);
|
||||||
this.threads = new Executor[threadCount];
|
this.threads = new ExecutorService[threadCount];
|
||||||
for (int i = 0; i < threadCount; i++) {
|
for (int i = 0; i < threadCount; i++) {
|
||||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||||
.namingPattern(name + "thread-" + i + "-%d")
|
.namingPattern(name + "thread-" + i + "-%d")
|
||||||
@@ -46,6 +48,12 @@ public class PermanentTransactionExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void shutdownNow() {
|
||||||
|
for (ExecutorService t : threads) {
|
||||||
|
t.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This hash is borrowed from {@link HashMap}, except for the power-of-two masking, since I
|
* This hash is borrowed from {@link HashMap}, except for the power-of-two masking, since I
|
||||||
* don't want to force the thread count to be a power of two (though it probably is). In the
|
* don't want to force the thread count to be a power of two (though it probably is). In the
|
||||||
@@ -70,6 +78,12 @@ public class PermanentTransactionExecutor {
|
|||||||
try (CoalescedTx tx = txc.start(description)) {
|
try (CoalescedTx tx = txc.start(description)) {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
|
catch (DomainObjectException e) {
|
||||||
|
if (e.getCause() instanceof ClosedException) {
|
||||||
|
Msg.info(this, obj + " is closed. Shutting down transaction executor.");
|
||||||
|
shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
}, selectThread(sel)).exceptionally(e -> {
|
}, selectThread(sel)).exceptionally(e -> {
|
||||||
Msg.error(this, "Trouble recording " + description, e);
|
Msg.error(this, "Trouble recording " + description, e);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ import ghidra.util.database.UndoableTransaction;
|
|||||||
|
|
||||||
public class RecorderPermanentTransaction implements AutoCloseable {
|
public class RecorderPermanentTransaction implements AutoCloseable {
|
||||||
|
|
||||||
static RecorderPermanentTransaction start(UndoableDomainObject obj, String description) {
|
public static RecorderPermanentTransaction start(UndoableDomainObject obj, String description) {
|
||||||
UndoableTransaction tid = UndoableTransaction.start(obj, description, true);
|
UndoableTransaction tid = UndoableTransaction.start(obj, description, true);
|
||||||
return new RecorderPermanentTransaction(obj, tid);
|
return new RecorderPermanentTransaction(obj, tid);
|
||||||
}
|
}
|
||||||
|
|||||||
-7
@@ -79,13 +79,6 @@ public class RecorderSimpleMemory implements AbstractRecorderMemory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get accessible memory, as viewed in the trace
|
|
||||||
*
|
|
||||||
* @param pred an additional predicate applied via "AND" with accessibility
|
|
||||||
* @param memMapper target-to-trace mapping utility
|
|
||||||
* @return the computed set
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||||
DebuggerMemoryMapper memMapper) {
|
DebuggerMemoryMapper memMapper) {
|
||||||
|
|||||||
+7
@@ -32,6 +32,13 @@ public interface AbstractRecorderMemory {
|
|||||||
|
|
||||||
public CompletableFuture<Void> writeMemory(Address address, byte[] data);
|
public CompletableFuture<Void> writeMemory(Address address, byte[] data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get accessible memory, as viewed in the trace
|
||||||
|
*
|
||||||
|
* @param pred an additional predicate applied via "AND" with accessibility
|
||||||
|
* @param memMapper target-to-trace mapping utility
|
||||||
|
* @return the computed set
|
||||||
|
*/
|
||||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||||
DebuggerMemoryMapper memMapper);
|
DebuggerMemoryMapper memMapper);
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -13,11 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.service.model;
|
package ghidra.app.plugin.core.debug.service.model.record;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.RecorderPermanentTransaction;
|
||||||
|
import ghidra.app.services.TraceRecorder;
|
||||||
import ghidra.async.AsyncFence;
|
import ghidra.async.AsyncFence;
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
import ghidra.dbg.target.*;
|
import ghidra.dbg.target.*;
|
||||||
@@ -27,13 +29,13 @@ import ghidra.program.model.data.*;
|
|||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class DefaultDataTypeRecorder {
|
public class DataTypeRecorder {
|
||||||
|
|
||||||
//private DefaultTraceRecorder recorder;
|
//private TraceRecorder recorder;
|
||||||
private Trace trace;
|
private final Trace trace;
|
||||||
private final TargetDataTypeConverter typeConverter;
|
private final TargetDataTypeConverter typeConverter;
|
||||||
|
|
||||||
public DefaultDataTypeRecorder(DefaultTraceRecorder recorder) {
|
public DataTypeRecorder(TraceRecorder recorder) {
|
||||||
//this.recorder = recorder;
|
//this.recorder = recorder;
|
||||||
this.trace = recorder.getTrace();
|
this.trace = recorder.getTrace();
|
||||||
this.typeConverter = new TargetDataTypeConverter(trace.getDataTypeManager());
|
this.typeConverter = new TargetDataTypeConverter(trace.getDataTypeManager());
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.service.model.record;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.mapping.DebuggerRegisterMapper;
|
||||||
|
import ghidra.app.plugin.core.debug.register.RegisterTypeInfo;
|
||||||
|
import ghidra.dbg.target.TargetRegister;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
|
||||||
|
public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
||||||
|
@Override
|
||||||
|
public TargetRegister getTargetRegister(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register getTraceRegister(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetRegister traceToTarget(Register register) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register targetToTrace(TargetRegister tReg) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisterTypeInfo getDefaultTypeInfo(Register lReg) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Register> getRegistersOnTarget() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void targetRegisterAdded(TargetRegister register) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void targetRegisterRemoved(TargetRegister register) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.service.model.record;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.*;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||||
|
import ghidra.dbg.target.TargetMemory;
|
||||||
|
import ghidra.dbg.target.TargetMemoryRegion;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.trace.model.memory.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import utilities.util.IDKeyed;
|
||||||
|
|
||||||
|
class MemoryRecorder {
|
||||||
|
protected final ObjectBasedTraceRecorder recorder;
|
||||||
|
protected final TraceMemoryManager memoryManager;
|
||||||
|
|
||||||
|
protected final Map<IDKeyed<AddressSpace>, TargetMemory> memoriesByTargetSpace =
|
||||||
|
new HashMap<>();
|
||||||
|
protected final Map<IDKeyed<TargetMemoryRegion>, AddressRange> regions = new HashMap<>();
|
||||||
|
|
||||||
|
protected MemoryRecorder(ObjectBasedTraceRecorder recorder) {
|
||||||
|
this.recorder = recorder;
|
||||||
|
this.memoryManager = recorder.trace.getMemoryManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetMemory getMemoryForSpace(AddressSpace space) {
|
||||||
|
return memoriesByTargetSpace.get(new IDKeyed<>(space));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMemoryForSpace(AddressSpace targetSpace, TargetMemory memory) {
|
||||||
|
TargetMemory exists =
|
||||||
|
memoriesByTargetSpace.put(new IDKeyed<>(targetSpace), memory);
|
||||||
|
if (exists != null && exists != memory) {
|
||||||
|
Msg.warn(this,
|
||||||
|
"Address space duplicated between memories: " + exists + " and " + memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addRegionMemory(TargetMemoryRegion region, TargetMemory memory) {
|
||||||
|
addMemoryForSpace(region.getRange().getMinAddress().getAddressSpace(), memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void adjustRegionRange(TargetMemoryRegion region, AddressRange range) {
|
||||||
|
synchronized (regions) {
|
||||||
|
AddressRange tRange = recorder.memoryMapper.targetToTrace(range);
|
||||||
|
if (tRange == null) {
|
||||||
|
regions.remove(new IDKeyed<>(region));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
regions.put(new IDKeyed<>(region), tRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeMemory(TargetMemory memory) {
|
||||||
|
while (memoriesByTargetSpace.values().remove(memory))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeRegion(TargetMemoryRegion region) {
|
||||||
|
synchronized (regions) {
|
||||||
|
regions.remove(new IDKeyed<>(region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CompletableFuture<byte[]> read(Address start, int length) {
|
||||||
|
Address tStart = recorder.memoryMapper.traceToTarget(start);
|
||||||
|
if (tStart == null) {
|
||||||
|
return CompletableFuture.completedFuture(new byte[] {});
|
||||||
|
}
|
||||||
|
TargetMemory memory = getMemoryForSpace(tStart.getAddressSpace());
|
||||||
|
if (memory == null) {
|
||||||
|
return CompletableFuture.completedFuture(new byte[] {});
|
||||||
|
}
|
||||||
|
return memory.readMemory(tStart, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CompletableFuture<Void> write(Address start, byte[] data) {
|
||||||
|
Address tStart = recorder.memoryMapper.traceToTarget(start);
|
||||||
|
if (tStart == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Address space " + start.getAddressSpace() + " not defined on the target");
|
||||||
|
}
|
||||||
|
TargetMemory memory = getMemoryForSpace(tStart.getAddressSpace());
|
||||||
|
if (memory == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Address space " + tStart.getAddressSpace() +
|
||||||
|
" cannot be found in target memory");
|
||||||
|
}
|
||||||
|
return memory.writeMemory(tStart, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void invalidate(TargetMemory memory, long snap) {
|
||||||
|
Set<AddressSpace> targetSpaces = memoriesByTargetSpace.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getValue() == memory)
|
||||||
|
.map(e -> e.getKey().obj)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
for (AddressSpace targetSpace : targetSpaces) {
|
||||||
|
Address traceMin = recorder.memoryMapper.targetToTrace(targetSpace.getMinAddress());
|
||||||
|
Address traceMax = traceMin.getAddressSpace().getMaxAddress();
|
||||||
|
memoryManager.setState(snap, traceMin, traceMax, TraceMemoryState.UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void recordMemory(long snap, Address start, byte[] data) {
|
||||||
|
memoryManager.putBytes(snap, start, ByteBuffer.wrap(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordError(long snap, Address tMin, DebuggerMemoryAccessException e) {
|
||||||
|
// TODO: Bookmark to describe error?
|
||||||
|
memoryManager.setState(snap, tMin, TraceMemoryState.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isAccessible(TraceMemoryRegion r) {
|
||||||
|
// TODO: Perhaps a bit aggressive, but haven't really been checking anyway.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Collector<AddressRange, AddressSet, AddressSet> toAddressSet() {
|
||||||
|
return new Collector<>() {
|
||||||
|
@Override
|
||||||
|
public Supplier<AddressSet> supplier() {
|
||||||
|
return AddressSet::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiConsumer<AddressSet, AddressRange> accumulator() {
|
||||||
|
return AddressSet::add;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryOperator<AddressSet> combiner() {
|
||||||
|
return (s1, s2) -> {
|
||||||
|
s1.add(s2);
|
||||||
|
return s1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<AddressSet, AddressSet> finisher() {
|
||||||
|
return Function.identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Characteristics> characteristics() {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressSetView getAccessible() {
|
||||||
|
synchronized (regions) {
|
||||||
|
return regions.values()
|
||||||
|
.stream()
|
||||||
|
.collect(toAddressSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+641
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user