mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 14:50:08 +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();
|
||||
String procId = getIdFromCapture(line);
|
||||
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"));
|
||||
assertTrue(line.startsWith("#"));
|
||||
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("*"))
|
||||
.collect(Collectors.toList())).trim();
|
||||
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()));
|
||||
String threadGid = line.split("\\s+")[2];
|
||||
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 (TargetRegisterBank bank : banks.values()) {
|
||||
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()) {
|
||||
waitOn(bank.writeRegister(reg, toWrite.get(name)));
|
||||
}
|
||||
|
||||
+3
-3
@@ -81,7 +81,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (Entry<String, byte[]> ent : getRegisterWrites().entrySet()) {
|
||||
String regName = ent.getKey();
|
||||
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()) {
|
||||
assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : exp.keySet()) {
|
||||
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()) {
|
||||
byte[] bytes = waitOn(bank.readRegister(reg));
|
||||
read.put(name, bytes);
|
||||
@@ -149,7 +149,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : write.keySet()) {
|
||||
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()) {
|
||||
waitOn(bank.writeRegister(reg, write.get(name)));
|
||||
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||
Trace trace = current.getTrace();
|
||||
TraceRecorder recorder = current.getRecorder();
|
||||
BackgroundUtils.async(tool, trace, NAME, true, true, false,
|
||||
(__, monitor) -> recorder.captureProcessMemory(selection, monitor, false));
|
||||
(__, monitor) -> recorder.readMemoryBlocks(selection, monitor, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +78,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||
}
|
||||
TraceRecorder recorder = current.getRecorder();
|
||||
// TODO: Either allow partial, or provide action to intersect with accessible
|
||||
if (!recorder.getAccessibleProcessMemory().contains(selection)) {
|
||||
if (!recorder.getAccessibleMemory().contains(selection)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ public class PCLocationTrackingSpec implements RegisterLocationTrackingSpec {
|
||||
if (frame == null) {
|
||||
return null;
|
||||
}
|
||||
return frame.getProgramCounter();
|
||||
return frame.getProgramCounter(snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -56,7 +56,7 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
}
|
||||
TraceRecorder recorder = coordinates.getRecorder();
|
||||
AddressSet visibleAccessible =
|
||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
||||
recorder.getAccessibleMemory().intersect(visible);
|
||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||
AddressSetView alreadyKnown =
|
||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||
@@ -67,6 +67,6 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
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();
|
||||
AddressSet visibleAccessible =
|
||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
||||
recorder.getAccessibleMemory().intersect(visible);
|
||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||
AddressSetView alreadyKnown =
|
||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||
@@ -92,6 +92,6 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
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 {
|
||||
synchronized (this) {
|
||||
monitor.checkCanceled();
|
||||
this.captureTask = recorder.captureProcessMemory(new AddressSet(range), monitor, false);
|
||||
this.captureTask = recorder.readMemoryBlocks(new AddressSet(range), monitor, false);
|
||||
}
|
||||
try {
|
||||
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) {
|
||||
TargetObject object = this.getObjectFromContext(context);
|
||||
if (object == null) {
|
||||
@@ -1128,8 +1136,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
.popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex)
|
||||
.popupMenuIcon(AbstractRecordAction.ICON)
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "record"))
|
||||
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
||||
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
||||
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||
.onAction(ctx -> performStartRecording(ctx))
|
||||
.enabled(true)
|
||||
.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);
|
||||
if (rec != null) {
|
||||
return; // Already being recorded
|
||||
@@ -1630,6 +1638,11 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
}
|
||||
|
||||
public void performStartRecording(ActionContext context) {
|
||||
TargetObject maybeRoot = getObjectFromContext(context);
|
||||
if (maybeRoot.isRoot()) {
|
||||
startRecording(maybeRoot, true);
|
||||
return;
|
||||
}
|
||||
performAction(context, false, TargetProcess.class, proc -> {
|
||||
TargetProcess valid = DebugModelConventions.liveProcessOrNull(proc);
|
||||
if (valid != null) {
|
||||
|
||||
+1
@@ -410,6 +410,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
||||
return;
|
||||
}
|
||||
if (currentStack == stack) {
|
||||
stackTableModel.fireTableDataChanged();
|
||||
return;
|
||||
}
|
||||
currentStack = stack;
|
||||
|
||||
+17
-8
@@ -28,41 +28,50 @@ import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class StackFrameRow {
|
||||
public static class Synthetic extends StackFrameRow {
|
||||
private Address pc;
|
||||
|
||||
public Synthetic(DebuggerStackProvider provider, Address pc) {
|
||||
super(provider, pc);
|
||||
super(provider);
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
public void updateProgramCounter(Address pc) {
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getProgramCounter() {
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
|
||||
private final DebuggerStackProvider provider;
|
||||
|
||||
final TraceStackFrame frame;
|
||||
private int level;
|
||||
Address pc;
|
||||
|
||||
public StackFrameRow(DebuggerStackProvider provider, TraceStackFrame frame) {
|
||||
this.provider = provider;
|
||||
this.frame = frame;
|
||||
this.level = frame.getLevel();
|
||||
this.pc = frame.getProgramCounter();
|
||||
}
|
||||
|
||||
private StackFrameRow(DebuggerStackProvider provider, Address pc) {
|
||||
private StackFrameRow(DebuggerStackProvider provider) {
|
||||
this.provider = provider;
|
||||
this.frame = null;
|
||||
this.level = 0;
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
public int getFrameLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public long getSnap() {
|
||||
return provider.current.getSnap();
|
||||
}
|
||||
|
||||
public Address getProgramCounter() {
|
||||
return pc;
|
||||
return frame.getProgramCounter(getSnap());
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
@@ -88,11 +97,12 @@ public class StackFrameRow {
|
||||
if (curThread == null) {
|
||||
return null;
|
||||
}
|
||||
Address pc = getProgramCounter();
|
||||
if (pc == null) {
|
||||
return null;
|
||||
}
|
||||
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
|
||||
curThread, Range.singleton(provider.current.getSnap()), pc);
|
||||
curThread, Range.singleton(getSnap()), pc);
|
||||
ProgramLocation sloc = provider.mappingService.getOpenMappedLocation(dloc);
|
||||
if (sloc == null) {
|
||||
return null;
|
||||
@@ -103,6 +113,5 @@ public class StackFrameRow {
|
||||
protected void update() {
|
||||
assert frame != null; // Should never update a synthetic stack
|
||||
level = frame.getLevel();
|
||||
pc = frame.getProgramCounter();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -578,11 +578,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
||||
if (someLoc == null) {
|
||||
return null;
|
||||
}
|
||||
TraceProgramView view = current.getView();
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
if (someLoc.getProgram() instanceof TraceProgramView) {
|
||||
Program program = someLoc.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
if (program instanceof TraceProgramView) {
|
||||
return someLoc;
|
||||
}
|
||||
return mappingService.getDynamicLocationFromStatic(view, someLoc);
|
||||
|
||||
+4
-3
@@ -101,6 +101,7 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
||||
*/
|
||||
public default Set<DebuggerMappingOffer> getOffers(TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
// TODO: Remove this check?
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
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 process the target process
|
||||
* @param target the target (usually a process)
|
||||
* @param includeOverrides true to include override offers, i.e., those with negative confidence
|
||||
* @return the offers, possibly empty, but never null
|
||||
*/
|
||||
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides);
|
||||
}
|
||||
|
||||
+9
-4
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.mapping;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public interface DebuggerMemoryMapper {
|
||||
/**
|
||||
@@ -33,7 +32,10 @@ public interface DebuggerMemoryMapper {
|
||||
* @param traceRange the range in the view'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
|
||||
@@ -49,5 +51,8 @@ public interface DebuggerMemoryMapper {
|
||||
* @param targetRange the range in the target'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) {
|
||||
assert isInFactory(traceAddr, traceAddressFactory);
|
||||
return toSameNamedSpace(traceAddr, targetAddressFactory);
|
||||
};
|
||||
|
||||
@Override
|
||||
public AddressRange traceToTarget(AddressRange traceRange) {
|
||||
return new AddressRangeImpl(traceToTarget(traceRange.getMinAddress()),
|
||||
traceToTarget(traceRange.getMaxAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +59,4 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
||||
assert isInFactory(targetAddr, targetAddressFactory);
|
||||
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;
|
||||
|
||||
public DefaultDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
||||
CompilerSpecID csId, Collection<String> extraRegNames)
|
||||
CompilerSpecID csID, Collection<String> extraRegNames)
|
||||
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||
this.target = target;
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
this.language = langServ.getLanguage(langID);
|
||||
this.cSpec = language.getCompilerSpecByID(csId);
|
||||
this.cSpec = language.getCompilerSpecByID(csID);
|
||||
|
||||
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 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.util.DefaultLanguageService;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!includeOverrides) {
|
||||
return Set.of();
|
||||
@@ -65,7 +66,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
// ALL THE SPECS!!!
|
||||
new LanguageCompilerSpecQuery(null, null, null, null, null))
|
||||
.stream()
|
||||
.map(lcsp -> offerForLanguageAndCSpec(process, endian, lcsp))
|
||||
.map(lcsp -> offerForLanguageAndCSpec(target, endian, lcsp))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -49,24 +49,28 @@ public class FridaArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
|
||||
protected static class FridaAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includesOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||
return Set.of();
|
||||
}
|
||||
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();
|
||||
if (os.contains("macos")) {
|
||||
if (is64Bit) {
|
||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||
return Set.of(new FridaAarch64MacosOffer(process));
|
||||
return Set.of(new FridaAarch64MacosOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
|
||||
+6
-3
@@ -49,14 +49,17 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
|
||||
protected static class LldbAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includesOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -66,7 +69,7 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
if (os.contains("macos")) {
|
||||
if (is64Bit) {
|
||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||
return Set.of(new LldbAarch64MacosOffer(process));
|
||||
return Set.of(new LldbAarch64MacosOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
|
||||
+5
-2
@@ -75,15 +75,18 @@ public class DbgengX64DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (env == null || !env.getDebugger().toLowerCase().contains("dbg")) {
|
||||
return Set.of();
|
||||
}
|
||||
boolean is64Bit =
|
||||
env.getArchitecture().contains("x86_64") || env.getArchitecture().contains("x64_32");
|
||||
if (is64Bit) {
|
||||
return Set.of(new DbgI386X86_64WindowsOffer(process));
|
||||
return Set.of(new DbgI386X86_64WindowsOffer((TargetProcess) target));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
try {
|
||||
// This is on its own task thread, so whatever.
|
||||
// Just don't hang it indefinitely.
|
||||
recorder.captureProcessMemory(set, TaskMonitor.DUMMY, false)
|
||||
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, false)
|
||||
.get(1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
|
||||
+12
-7
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.frida;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.util.Msg;
|
||||
@@ -71,16 +70,22 @@ public class FridaX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||
return Set.of();
|
||||
}
|
||||
String arch = env.getArchitecture();
|
||||
boolean is32Bit = arch.contains("ia32") ||arch.contains("x86-32") || arch.contains("i386") ||
|
||||
arch.contains("x86_32");
|
||||
boolean is64Bit = arch.contains("x64") ||arch.contains("x86-64") || arch.contains("x64-32") ||
|
||||
arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686");
|
||||
boolean is32Bit =
|
||||
arch.contains("ia32") || arch.contains("x86-32") || arch.contains("i386") ||
|
||||
arch.contains("x86_32");
|
||||
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();
|
||||
if (os.contains("darwin")) {
|
||||
if (is64Bit) {
|
||||
|
||||
+4
-3
@@ -21,7 +21,8 @@ import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
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.util.DefaultLanguageService;
|
||||
|
||||
@@ -81,7 +82,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!isGdb(env)) {
|
||||
return Set.of();
|
||||
@@ -90,7 +91,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
String arch = env.getArchitecture();
|
||||
|
||||
return getCompilerSpecsForGnu(arch, endian).stream()
|
||||
.flatMap(lcsp -> offersForLanguageAndCSpec(process, arch, endian, lcsp).stream())
|
||||
.flatMap(lcsp -> offersForLanguageAndCSpec(target, arch, endian, lcsp).stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -39,8 +38,11 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -54,7 +56,7 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
String arch = env.getArchitecture();
|
||||
if (arch.startsWith("m68k")) {
|
||||
return Set.of(new GdbM68kBELinux32DefOffer(process));
|
||||
return Set.of(new GdbM68kBELinux32DefOffer((TargetProcess) target));
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -79,8 +78,12 @@ public class GdbMipsDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -65,8 +64,12 @@ public class GdbPowerPCDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
-1
@@ -100,8 +100,12 @@ public class GdbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
-2
@@ -64,8 +64,11 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -73,6 +76,6 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
return Set.of();
|
||||
}
|
||||
// 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
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -73,6 +76,6 @@ public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
return Set.of();
|
||||
}
|
||||
// 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 ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.util.Msg;
|
||||
@@ -71,8 +70,12 @@ public class LldbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
@@ -250,6 +250,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||
Msg.info(this, "Ignoring " + breakpoint +
|
||||
" 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,
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class ReadsTargetMemoryPcodeExecutorState
|
||||
if (!isLive()) {
|
||||
return false;
|
||||
}
|
||||
waitTimeout(recorder.captureProcessMemory(unknown, TaskMonitor.DUMMY, false));
|
||||
waitTimeout(recorder.readMemoryBlocks(unknown, TaskMonitor.DUMMY, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+5
-56
@@ -21,11 +21,11 @@ import java.util.concurrent.CompletableFuture;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedMemoryRecorder;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.app.plugin.core.debug.service.model.record.RecorderUtils;
|
||||
import ghidra.dbg.target.TargetMemory;
|
||||
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.memory.*;
|
||||
import ghidra.util.Msg;
|
||||
@@ -35,20 +35,7 @@ import ghidra.util.task.TaskMonitor;
|
||||
public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
||||
|
||||
// For large memory captures
|
||||
private static final int BLOCK_SIZE = 4096;
|
||||
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 static final int BLOCK_BITS = 12; // 4096 bytes
|
||||
|
||||
private final DefaultTraceRecorder recorder;
|
||||
private final Trace trace;
|
||||
@@ -62,45 +49,7 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
||||
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
// TODO: Figure out how to display/select per-thread memory.
|
||||
// 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);
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor, toMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public class DefaultStackRecorder implements ManagedStackRecorder {
|
||||
|
||||
public void doRecordFrame(TraceStack traceStack, int frameLevel, Address pc) {
|
||||
TraceStackFrame traceFrame = traceStack.getFrame(frameLevel, true);
|
||||
traceFrame.setProgramCounter(pc);
|
||||
traceFrame.setProgramCounter(null, pc); // Not object-based, so span=null
|
||||
}
|
||||
|
||||
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.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.TraceRecorderListener;
|
||||
import ghidra.async.AsyncLazyValue;
|
||||
@@ -64,11 +66,11 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
TraceObjectManager objectManager;
|
||||
|
||||
DefaultBreakpointRecorder breakpointRecorder;
|
||||
DefaultDataTypeRecorder datatypeRecorder;
|
||||
DataTypeRecorder datatypeRecorder;
|
||||
DefaultMemoryRecorder memoryRecorder;
|
||||
DefaultModuleRecorder moduleRecorder;
|
||||
DefaultProcessRecorder processRecorder;
|
||||
DefaultSymbolRecorder symbolRecorder;
|
||||
SymbolRecorder symbolRecorder;
|
||||
DefaultTimeRecorder timeRecorder;
|
||||
|
||||
//protected final PermanentTransactionExecutor seqTx;
|
||||
@@ -94,10 +96,10 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
|
||||
this.processRecorder = new DefaultProcessRecorder(this);
|
||||
this.breakpointRecorder = new DefaultBreakpointRecorder(this);
|
||||
this.datatypeRecorder = new DefaultDataTypeRecorder(this);
|
||||
this.datatypeRecorder = new DataTypeRecorder(this);
|
||||
this.memoryRecorder = new DefaultMemoryRecorder(this);
|
||||
this.moduleRecorder = new DefaultModuleRecorder(this);
|
||||
this.symbolRecorder = new DefaultSymbolRecorder(this);
|
||||
this.symbolRecorder = new SymbolRecorder(this);
|
||||
this.timeRecorder = new DefaultTimeRecorder(this);
|
||||
this.objectManager = new TraceObjectManager(target, mapper, this);
|
||||
}
|
||||
@@ -266,7 +268,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
/*---------------- CAPTURE METHODS -------------------*/
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
if (set.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(new TreeMap<>());
|
||||
@@ -499,12 +501,6 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
|
||||
/*---------------- LISTENER METHODS -------------------*/
|
||||
|
||||
// UNUSED?
|
||||
@Override
|
||||
public TraceEventListener getListenerForRecord() {
|
||||
return objectManager.getEventListener();
|
||||
}
|
||||
|
||||
public ListenerSet<TraceRecorderListener> getListeners() {
|
||||
return objectManager.getListeners();
|
||||
}
|
||||
@@ -526,17 +522,17 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getAccessibleProcessMemory() {
|
||||
public AddressSetView getAccessibleMemory() {
|
||||
return processRecorder.getAccessibleProcessMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> readProcessMemory(Address start, int length) {
|
||||
public CompletableFuture<byte[]> readMemory(Address start, int length) {
|
||||
return processRecorder.readProcessMemory(start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeProcessMemory(Address start, byte[] data) {
|
||||
public CompletableFuture<Void> writeMemory(Address start, byte[] data) {
|
||||
return processRecorder.writeProcessMemory(start, data);
|
||||
}
|
||||
|
||||
@@ -566,6 +562,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
return true;
|
||||
}
|
||||
|
||||
// UNUSED?
|
||||
@Override
|
||||
public CompletableFuture<Void> flushTransactions() {
|
||||
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.TransactionCoalescer;
|
||||
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer.CoalescedTx;
|
||||
import ghidra.framework.model.DomainObjectException;
|
||||
import ghidra.framework.model.UndoableDomainObject;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.ClosedException;
|
||||
|
||||
public class PermanentTransactionExecutor {
|
||||
|
||||
private final TransactionCoalescer txc;
|
||||
private final Executor[] threads;
|
||||
private final ExecutorService[] threads;
|
||||
private final UndoableDomainObject obj;
|
||||
|
||||
public PermanentTransactionExecutor(UndoableDomainObject obj, String name, int threadCount,
|
||||
int delayMs) {
|
||||
this.obj = obj;
|
||||
txc = new DefaultTransactionCoalescer<>(obj, RecorderPermanentTransaction::start, delayMs);
|
||||
this.threads = new Executor[threadCount];
|
||||
this.threads = new ExecutorService[threadCount];
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.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
|
||||
* 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)) {
|
||||
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 -> {
|
||||
Msg.error(this, "Trouble recording " + description, e);
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
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);
|
||||
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
|
||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||
DebuggerMemoryMapper memMapper) {
|
||||
|
||||
+7
@@ -32,6 +32,13 @@ public interface AbstractRecorderMemory {
|
||||
|
||||
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,
|
||||
DebuggerMemoryMapper memMapper);
|
||||
|
||||
|
||||
+7
-5
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.RecorderPermanentTransaction;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.target.*;
|
||||
@@ -27,13 +29,13 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DefaultDataTypeRecorder {
|
||||
public class DataTypeRecorder {
|
||||
|
||||
//private DefaultTraceRecorder recorder;
|
||||
private Trace trace;
|
||||
//private TraceRecorder recorder;
|
||||
private final Trace trace;
|
||||
private final TargetDataTypeConverter typeConverter;
|
||||
|
||||
public DefaultDataTypeRecorder(DefaultTraceRecorder recorder) {
|
||||
public DataTypeRecorder(TraceRecorder recorder) {
|
||||
//this.recorder = recorder;
|
||||
this.trace = recorder.getTrace();
|
||||
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