getDisplayedOffers() {
+ return List.copyOf(offerTableFilterPanel.getTableFilterModel().getModelData());
+ }
+ }
+
+ private final OfferPanel offerPanel = new OfferPanel();
+
+ private boolean isCancelled = false;
+
+ protected DebuggerSelectPlatformOfferDialog() {
+ super(DebuggerResources.NAME_CHOOSE_PLATFORM, Set.of(Opt.MODAL, Opt.INCLUDE_BUTTONS));
+
+ populateComponents();
+ }
+
+ protected void populateComponents() {
+ offerPanel.setBorder(BorderFactory.createTitledBorder(" Select Platform "));
+ addWorkPanel(offerPanel);
+ addOKButton();
+ addCancelButton();
+
+ setDefaultButton(okButton);
+ setOkEnabled(false);
+
+ // TODO: Separate this a bit
+ offerPanel.offerTable.getSelectionModel().addListSelectionListener(e -> {
+ setOkEnabled(getSelectedOffer() != null);
+ });
+ }
+
+ /**
+ * Set the preferred language and compiler spec IDs, typically from the current program.
+ *
+ *
+ * This must be called before {@link #setOffers(Collection)}.
+ *
+ * @param langID the preferred language
+ * @param csID the preferred compiler spec (ABI)
+ */
+ public void setPreferredIDs(LanguageID langID, CompilerSpecID csID) {
+ offerPanel.setPreferredIDs(langID, csID);
+ }
+
+ public void setOffers(Collection offers) {
+ offerPanel.setOffers(offers);
+ }
+
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+
+ public void setSelectedOffer(DebuggerPlatformOffer offer) {
+ offerPanel.setSelectedOffer(offer);
+ }
+
+ public DebuggerPlatformOffer getSelectedOffer() {
+ return offerPanel.getSelectedOffer();
+ }
+
+ // For tests
+ protected List getDisplayedOffers() {
+ return offerPanel.getDisplayedOffers();
+ }
+
+ protected void setFilterRecommended(boolean recommendedOnly) {
+ offerPanel.setFilterRecommended(recommendedOnly);
+ }
+
+ @Override
+ protected void cancelCallback() {
+ isCancelled = true;
+ super.cancelCallback();
+ }
+
+ @Override
+ protected void okCallback() {
+ if (getSelectedOffer() != null) {
+ isCancelled = false;
+ close();
+ }
+ // Do nothing. Should be disabled anyway
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
index b1da0a65fe..32fcd97de4 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
@@ -64,8 +64,6 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
return Set.of();
}
- protected abstract CompilerSpec getCompilerSpec(TraceObject object);
-
@Override
public DisassemblyResult disassemble(TraceThread thread, TraceObject object,
Address start, AddressSetView restricted, long snap, TaskMonitor monitor) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java
index 0e190f1514..638de62e07 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java
@@ -15,15 +15,21 @@
*/
package ghidra.app.plugin.core.debug.mapping;
+import java.util.Objects;
+
import ghidra.program.model.lang.CompilerSpec;
public abstract class AbstractDebuggerPlatformOffer implements DebuggerPlatformOffer {
private final String description;
protected final CompilerSpec cSpec;
+ private final int hash;
+
public AbstractDebuggerPlatformOffer(String description, CompilerSpec cSpec) {
this.description = description;
this.cSpec = cSpec;
+
+ this.hash = Objects.hash(description, cSpec);
}
@Override
@@ -35,4 +41,27 @@ public abstract class AbstractDebuggerPlatformOffer implements DebuggerPlatformO
public CompilerSpec getCompilerSpec() {
return cSpec;
}
+
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (this.getClass() != obj.getClass()) {
+ return false;
+ }
+ AbstractDebuggerPlatformOffer that = (AbstractDebuggerPlatformOffer) obj;
+ if (!Objects.equals(this.description, that.description)) {
+ return false;
+ }
+ if (!Objects.equals(this.cSpec, that.cSpec)) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java
index 6a9234f164..653f68c6bd 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java
@@ -24,19 +24,20 @@ import ghidra.trace.model.target.TraceObject;
public abstract class AbstractDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
protected abstract Set getOffers(TraceObject object, long snap,
- TraceObject env, String debugger, String arch, String os, Endian endian);
+ TraceObject env, String debugger, String arch, String os, Endian endian,
+ boolean includeOverrides);
@Override
public Set getOffers(Trace trace, TraceObject object, long snap,
boolean includeOverrides) {
TraceObject env = DebuggerPlatformOpinion.getEnvironment(object, snap);
if (env == null) {
- return getOffers(object, snap, env, null, null, null, null);
+ return getOffers(object, snap, env, null, null, null, null, includeOverrides);
}
String debugger = DebuggerPlatformOpinion.getDebugggerFromEnv(env, snap);
String arch = DebuggerPlatformOpinion.getArchitectureFromEnv(env, snap);
String os = DebuggerPlatformOpinion.getOperatingSystemFromEnv(env, snap);
Endian endian = DebuggerPlatformOpinion.getEndianFromEnv(env, snap);
- return getOffers(object, snap, env, debugger, arch, os, endian);
+ return getOffers(object, snap, env, debugger, arch, os, endian, includeOverrides);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java
index 730d66cf46..92f3268c03 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.mapping;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.lang.CompilerSpec;
+import ghidra.program.model.lang.Language;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.task.TaskMonitor;
@@ -25,6 +27,26 @@ import ghidra.util.task.TaskMonitor;
* An object for interpreting a trace according to a chosen platform
*/
public interface DebuggerPlatformMapper {
+
+ /**
+ * Get the compiler for a given object
+ *
+ * @param object the object
+ * @return the compiler spec
+ */
+ CompilerSpec getCompilerSpec(TraceObject object);
+
+ /**
+ * Get the language for a given object
+ *
+ * @param object the object
+ * @return the language
+ */
+ default Language getLangauge(TraceObject object) {
+ CompilerSpec cSpec = getCompilerSpec(object);
+ return cSpec == null ? null : cSpec.getLanguage();
+ }
+
/**
* Prepare the given trace for interpretation under this mapper
*
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java
index 714bb427b7..c836929c1f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java
@@ -62,13 +62,23 @@ public interface DebuggerPlatformOffer {
/**
* Get the language to which this offer can map
*
- * @return the langauge
+ * @return the language
*/
default Language getLanguage() {
CompilerSpec cSpec = getCompilerSpec();
return cSpec == null ? null : cSpec.getLanguage();
}
+ /**
+ * Get the language ID to which this offer can map
+ *
+ * @return the language ID
+ */
+ default LanguageID getLanguageID() {
+ Language language = getLanguage();
+ return language == null ? null : language.getLanguageID();
+ }
+
/**
* Get the compiler to which this offer can map
*
@@ -76,6 +86,24 @@ public interface DebuggerPlatformOffer {
*/
CompilerSpec getCompilerSpec();
+ /**
+ * Get the compiler spec ID to which this offer can map
+ *
+ * @return the language ID
+ */
+ default CompilerSpecID getCompilerSpecID() {
+ CompilerSpec cSpec = getCompilerSpec();
+ return cSpec == null ? null : cSpec.getCompilerSpecID();
+ }
+
+ /**
+ * Load a compiler spec from the language service given the language and cspec IDs
+ *
+ * @param langID the langauge ID
+ * @param cSpecID the compiler spec ID
+ * @return the compiler spec
+ * @throws AssertionError if either the language or the compiler spec is not found
+ */
default CompilerSpec getCompilerSpec(LanguageID langID, CompilerSpecID cSpecID) {
try {
LanguageService langServ = DefaultLanguageService.getLanguageService();
@@ -96,4 +124,12 @@ public interface DebuggerPlatformOffer {
* @return the mapper
*/
DebuggerPlatformMapper take(PluginTool tool, Trace trace);
+
+ /**
+ * Check if this or an equivalent offer was the creator of the given mapper
+ *
+ * @param mapper the mapper
+ * @return true if this offer could be the mapper's creator
+ */
+ boolean isCreatorOf(DebuggerPlatformMapper mapper);
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java
index e2d607933d..c78382eac4 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java
@@ -20,8 +20,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
-import ghidra.trace.model.guest.TraceGuestPlatform;
-import ghidra.trace.model.guest.TracePlatformManager;
+import ghidra.trace.model.guest.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.util.MathUtilities;
import ghidra.util.database.UndoableTransaction;
@@ -49,7 +48,7 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
}
@Override
- protected CompilerSpec getCompilerSpec(TraceObject object) {
+ public CompilerSpec getCompilerSpec(TraceObject object) {
return cSpec;
}
@@ -59,11 +58,11 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(),
true)) {
TracePlatformManager platformManager = trace.getPlatformManager();
- TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
- if (platform == null) {
- return; // It's the host compiler spec
+ TracePlatform platform = platformManager.getOrAddPlatform(cSpec);
+ if (platform.isHost()) {
+ return;
}
- addMappedRanges(platform);
+ addMappedRanges((TraceGuestPlatform) platform);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java
index ce4834f6b4..bd9481fba6 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java
@@ -40,7 +40,7 @@ public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
}
@Override
- protected CompilerSpec getCompilerSpec(TraceObject object) {
+ public CompilerSpec getCompilerSpec(TraceObject object) {
return trace.getBaseCompilerSpec();
}
@@ -86,6 +86,11 @@ public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new LegacyDebuggerPlatformMapper(tool, trace);
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == LegacyDebuggerPlatformMapper.class;
+ }
};
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/OverrideDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/OverrideDebuggerPlatformOpinion.java
new file mode 100644
index 0000000000..a5eb05c5a3
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/OverrideDebuggerPlatformOpinion.java
@@ -0,0 +1,126 @@
+/* ###
+ * 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.platform;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import ghidra.app.plugin.core.debug.mapping.*;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.lang.*;
+import ghidra.program.util.DefaultLanguageService;
+import ghidra.trace.model.Trace;
+import ghidra.trace.model.target.TraceObject;
+
+/**
+ * An "opinion" which offers every known compiler, but with only default mapping logic.
+ */
+public class OverrideDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
+ private static final LanguageCompilerSpecQuery ALL_SPECS =
+ new LanguageCompilerSpecQuery(null, null, null, null, null);
+ private static final Map> CACHE = new HashMap<>();
+
+ protected static class OverridePlatformOffer implements DebuggerPlatformOffer {
+
+ private final String description;
+ private final LanguageID languageID;
+ private final CompilerSpecID cSpecID;
+ private final int confidence;
+
+ public OverridePlatformOffer(String description, LanguageID languageID,
+ CompilerSpecID cSpecID, int confidence) {
+ this.description = description;
+ this.languageID = languageID;
+ this.cSpecID = cSpecID;
+ this.confidence = confidence;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public LanguageID getLanguageID() {
+ return languageID;
+ }
+
+ @Override
+ public CompilerSpecID getCompilerSpecID() {
+ return cSpecID;
+ }
+
+ @Override
+ public int getConfidence() {
+ return confidence;
+ }
+
+ @Override
+ public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
+ return new OverrideDebuggerPlatformMapper(tool, trace, getCompilerSpec());
+ }
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == OverrideDebuggerPlatformMapper.class;
+ }
+
+ @Override
+ public CompilerSpec getCompilerSpec() {
+ return getCompilerSpec(languageID, cSpecID);
+ }
+ }
+
+ protected static class OverrideDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
+ public OverrideDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
+ super(tool, trace, cSpec);
+ }
+ }
+
+ protected DebuggerPlatformOffer computeOfferForEndianAndLCSP(Endian endian,
+ LanguageCompilerSpecPair lcsp) {
+ try {
+ LanguageDescription ldesc = lcsp.getLanguageDescription();
+ return new OverridePlatformOffer("Override to " + lcsp,
+ ldesc.getLanguageID(),
+ lcsp.getCompilerSpecDescription().getCompilerSpecID(),
+ ldesc.getEndian() == endian ? -10 : -20);
+ }
+ catch (LanguageNotFoundException | CompilerSpecNotFoundException e) {
+ // It couldn't have been generated unless it existed
+ throw new AssertionError(e);
+ }
+ }
+
+ protected Set computeOffersForEndian(Endian endian) {
+ LanguageService langServ = DefaultLanguageService.getLanguageService();
+ return langServ.getLanguageCompilerSpecPairs(ALL_SPECS)
+ .stream()
+ .map(lcsp -> computeOfferForEndianAndLCSP(endian, lcsp))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ protected Set getOffers(TraceObject object, long snap, TraceObject env,
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
+ if (!includeOverrides) {
+ return Set.of();
+ }
+ synchronized (CACHE) {
+ return CACHE.computeIfAbsent(endian, this::computeOffersForEndian);
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
index 176f88a033..95b4dc606e 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
@@ -65,12 +65,17 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new DbgengX64DebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == DbgengX64DebuggerPlatformMapper.class;
+ }
};
}
@Override
protected Set getOffers(TraceObject object, long snap, TraceObject env,
- String debugger, String arch, String os, Endian endian) {
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
if (debugger == null || arch == null || !debugger.toLowerCase().contains("dbg")) {
return Set.of();
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java
index 1ff3247f48..0bc8f55c10 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java
@@ -79,11 +79,16 @@ public class FridaDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinio
// TODO: May need these per offer
return new FridaDebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == FridaDebuggerPlatformMapper.class;
+ }
}
@Override
protected Set getOffers(TraceObject object, long snap, TraceObject env,
- String debugger, String arch, String os, Endian endian) {
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
if (debugger == null || arch == null ||
os == null | !debugger.toLowerCase().contains("frida")) {
return Set.of();
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java
index f9bdbe240b..842dfa338f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java
@@ -66,6 +66,11 @@ public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new GdbDebuggerPlatformMapper(tool, trace, cSpec);
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == GdbDebuggerPlatformMapper.class;
+ }
}
protected static class GdbDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
@@ -78,12 +83,12 @@ public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
protected Set offersForLanguageAndCSpec(String arch, Endian endian,
LanguageCompilerSpecPair lcsp)
throws CompilerSpecNotFoundException, LanguageNotFoundException {
- return Set.of(GdbDebuggerPlatformOffer.fromArchLCSP("Default GDB for " + arch, lcsp));
+ return Set.of(GdbDebuggerPlatformOffer.fromArchLCSP(arch, lcsp));
}
@Override
protected Set getOffers(TraceObject object, long snap, TraceObject env,
- String debugger, String arch, String os, Endian endian) {
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
if (debugger == null || !"gdb".equals(debugger.toLowerCase())) {
return Set.of();
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java
index a3a1345164..a2fc91c6a6 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java
@@ -29,7 +29,6 @@ public class JdiDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default");
protected static class JdiDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
- // TODO: Delete this class?
public JdiDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
super(tool, trace, cSpec);
}
@@ -68,11 +67,16 @@ public class JdiDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new JdiDebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == JdiDebuggerPlatformMapper.class;
+ }
}
@Override
protected Set getOffers(TraceObject object, long snap, TraceObject env,
- String debugger, String arch, String os, Endian endian) {
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
if (debugger == null || arch == null || !debugger.contains("Java Debug Interface")) {
return Set.of();
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java
index 4548ba78fe..626477fd98 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java
@@ -31,7 +31,7 @@ public class LldbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc");
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows");
- protected static class LldbDebuggerPlatformMapper
+ protected static final class LldbDebuggerPlatformMapper
extends DefaultDebuggerPlatformMapper {
public LldbDebuggerPlatformMapper(PluginTool tool, Trace trace,
CompilerSpec cSpec) {
@@ -79,11 +79,16 @@ public class LldbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
// TODO: May need these per offer
return new LldbDebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == LldbDebuggerPlatformMapper.class;
+ }
}
@Override
protected Set getOffers(TraceObject object, long snap, TraceObject env,
- String debugger, String arch, String os, Endian endian) {
+ String debugger, String arch, String os, Endian endian, boolean includeOverrides) {
if (debugger == null || arch == null ||
os == null | !debugger.toLowerCase().contains("lldb")) {
return Set.of();
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java
index fb64b1e1cc..18c2da95d9 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java
@@ -15,21 +15,18 @@
*/
package ghidra.app.plugin.core.debug.service.platform;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
-import docking.action.builder.ActionBuilder;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
+import ghidra.app.plugin.core.debug.event.DebuggerPlatformPluginEvent;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
-import ghidra.app.plugin.core.debug.gui.DebuggerResources.ChoosePlatformAction;
import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.app.services.DebuggerPlatformService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
-import ghidra.lifecycle.Unfinished;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
@@ -55,8 +52,6 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
- ActionBuilder actionChoosePlatform;
-
private final Map mappersByTrace = new HashMap<>();
public DebuggerPlatformServicePlugin(PluginTool tool) {
@@ -65,13 +60,10 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla
}
@Override
- protected void init() {
- super.init();
- createActions();
- }
-
- protected void createActions() {
- actionChoosePlatform = ChoosePlatformAction.builder(this); // TODO:
+ public DebuggerPlatformMapper getCurrentMapperFor(Trace trace) {
+ synchronized (mappersByTrace) {
+ return mappersByTrace.get(trace);
+ }
}
@Override
@@ -93,7 +85,7 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla
mappersByTrace.put(trace, mapper);
}
mapper.addToTrace(snap);
- // TODO: Fire a listener
+ firePluginEvent(new DebuggerPlatformPluginEvent(getName(), trace, mapper));
return mapper;
}
@@ -109,13 +101,10 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla
return null;
}
- @Override
- public DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap) {
- return Unfinished.TODO();
- }
-
@Override
public void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap) {
+ Objects.requireNonNull(trace);
+ Objects.requireNonNull(mapper);
if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalArgumentException("Trace is not opened in this tool");
}
@@ -123,7 +112,7 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla
mappersByTrace.put(trace, mapper);
}
mapper.addToTrace(snap);
- // TODO: Fire a listener
+ firePluginEvent(new DebuggerPlatformPluginEvent(getName(), trace, mapper));
}
@Override
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java
index 4366d1a03f..cf41855161 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java
@@ -51,6 +51,9 @@ import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.stack.TraceStackFrame;
+import ghidra.trace.model.target.TraceObject;
+import ghidra.trace.model.target.TraceObjectKeyPath;
+import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule;
@@ -404,6 +407,13 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return focus == null ? null : recorder.getTraceThreadForSuccessor(focus);
}
+ protected TraceObject objectFromTargetFocus(TraceRecorder recorder, TargetObject focus) {
+ return focus == null ? null
+ : recorder.getTrace()
+ .getObjectManager()
+ .getObjectByCanonicalPath(TraceObjectKeyPath.of(focus.getPath()));
+ }
+
protected TraceStackFrame frameFromTargetFocus(TraceRecorder recorder, TargetObject focus) {
return focus == null ? null : recorder.getTraceStackFrameForSuccessor(focus);
}
@@ -439,6 +449,32 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
// NOTE, if still null without focus support,
// we will take the eldest live thread at the resolved snap
}
+ TraceObject object = coordinates.getObject();
+ if (object == null) {
+ if (supportsFocus(recorder)) {
+ object = objectFromTargetFocus(recorder, focus);
+ }
+ if (object /*still*/ == null) { // either no focus support, or focus is not recorded
+ object = lastForTrace == null ? null : lastForTrace.getObject();
+ if (object != null) {
+ TraceObjectThread objThread =
+ object.queryCanonicalAncestorsInterface(TraceObjectThread.class)
+ .findFirst()
+ .orElse(null);
+ if (objThread != thread) {
+ object = null; // Abandon remembered object
+ }
+ }
+ }
+ if (object /*still*/ == null && thread instanceof TraceObjectThread objThread) {
+ // TODO: Seek the frame out?
+ object = objThread.getObject();
+ }
+ if (object /*still*/ == null) {
+ object = trace.getObjectManager().getRootObject();
+ // Could still be null, but that means no objects in the trace at all
+ }
+ }
/**
* Only select a default thread if the trace is not live. If it is live, and the model
* supports focus, then we should expect the debugger to control thread/frame focus.
@@ -500,7 +536,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
frame = 0;
}
return DebuggerCoordinates.all(trace, recorder, thread, view, Objects.requireNonNull(time),
- Objects.requireNonNull(frame));
+ Objects.requireNonNull(frame), object);
}
protected DebuggerCoordinates doSetCurrent(DebuggerCoordinates newCurrent) {
@@ -557,11 +593,12 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
}
TraceThread thread = threadFromTargetFocus(recorder, obj);
+ TraceObject object = objectFromTargetFocus(recorder, obj);
long snap = recorder.getSnap();
TraceStackFrame traceFrame = frameFromTargetFocus(recorder, obj);
Integer frame = traceFrame == null ? null : traceFrame.getLevel();
activateNoFocus(DebuggerCoordinates.all(trace, recorder, thread, null,
- TraceSchedule.snap(snap), frame));
+ TraceSchedule.snap(snap), frame, object));
return true;
}
@@ -667,6 +704,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return current.getFrame();
}
+ @Override
+ public TraceObject getCurrentObject() {
+ return current.getObject();
+ }
+
public Long findSnapshot(DebuggerCoordinates coordinates) {
if (coordinates.getTime().isSnapOnly()) {
return coordinates.getSnap();
@@ -985,6 +1027,16 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return null;
}
TraceRecorder recorder = resolved.getRecorder();
+ if (!Objects.equals(prev.getObject(), resolved.getObject())) {
+ TraceObject obj = resolved.getObject();
+ if (obj != null) {
+ TargetObject object =
+ recorder.getTarget().getSuccessor(obj.getCanonicalPath().getKeyList());
+ if (object != null) {
+ return object;
+ }
+ }
+ }
if (!Objects.equals(prev.getFrame(), resolved.getFrame())) {
TargetStackFrame frame =
recorder.getTargetStackFrame(resolved.getThread(), resolved.getFrame());
@@ -1066,6 +1118,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
activate(DebuggerCoordinates.frame(frameLevel));
}
+ @Override
+ public void activateObject(TraceObject object) {
+ activate(DebuggerCoordinates.object(object));
+ }
+
@Override
public void setAutoActivatePresent(boolean enabled) {
autoActivatePresent.set(enabled, null);
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java
index 7dab95f9b4..b5527c35f9 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java
@@ -16,6 +16,7 @@
package ghidra.app.services;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
+import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
@@ -23,6 +24,15 @@ import ghidra.trace.model.target.TraceObject;
* A service to manage the current mapper for active traces
*/
public interface DebuggerPlatformService {
+
+ /**
+ * Get the current mapper for the given trace
+ *
+ * @param trace the trace
+ * @return the mapper, or null
+ */
+ DebuggerPlatformMapper getCurrentMapperFor(Trace trace);
+
/**
* Get a mapper applicable to the given object
*
@@ -37,8 +47,7 @@ public interface DebuggerPlatformService {
* @param snap the snap, usually the current snap
* @return the mapper, or null if no offer was provided
*/
- DebuggerPlatformMapper getMapper(Trace trace, TraceObject object,
- long snap);
+ DebuggerPlatformMapper getMapper(Trace trace, TraceObject object, long snap);
/**
* Get a new mapper for the given object, ignoring the trace's current mapper
@@ -54,15 +63,6 @@ public interface DebuggerPlatformService {
*/
DebuggerPlatformMapper getNewMapper(Trace trace, TraceObject object, long snap);
- /**
- * Display a dialog for the user to manually select a mapper for the given object
- *
- * @param object the object for which a mapper is desired
- * @param snap the snap, usually the current snap
- * @return the mapper, or null if the dialog was cancelled
- */
- DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap);
-
/**
* Set the current mapper for the trace and initialize the trace for the mapper
*
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerTraceManagerService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerTraceManagerService.java
index a6e4f6ffeb..0f94111f81 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerTraceManagerService.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerTraceManagerService.java
@@ -26,6 +26,7 @@ import ghidra.framework.plugintool.ServiceInfo;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
+import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.TriConsumer;
@@ -141,6 +142,13 @@ public interface DebuggerTraceManagerService {
*/
int getCurrentFrame();
+ /**
+ * Get the active object
+ *
+ * @return the active object, or null
+ */
+ TraceObject getCurrentObject();
+
/**
* Open a trace
*
@@ -282,6 +290,13 @@ public interface DebuggerTraceManagerService {
*/
void activateFrame(int frameLevel);
+ /**
+ * Activate the given object
+ *
+ * @param object the desired object
+ */
+ void activateObject(TraceObject object);
+
/**
* Control whether the trace manager automatically activates the "present snapshot"
*
diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginScreenShots.java
new file mode 100644
index 0000000000..947566be71
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginScreenShots.java
@@ -0,0 +1,63 @@
+/* ###
+ * 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.gui.platform;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
+import ghidra.app.services.DebuggerTraceManagerService;
+import ghidra.dbg.target.schema.SchemaContext;
+import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
+import ghidra.dbg.target.schema.XmlSchemaContext;
+import ghidra.trace.database.ToyDBTraceBuilder;
+import ghidra.trace.database.target.DBTraceObjectManagerTest;
+import ghidra.util.database.UndoableTransaction;
+import help.screenshot.GhidraScreenShotGenerator;
+
+public class DebuggerPlatformPluginScreenShots extends GhidraScreenShotGenerator {
+
+ DebuggerTraceManagerService traceManager;
+ DebuggerPlatformPlugin platformPlugin;
+
+ @Before
+ public void setUpMine() throws Throwable {
+ traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
+ platformPlugin = addPlugin(tool, DebuggerPlatformPlugin.class);
+ }
+
+ @Test
+ public void testCaptureDebuggerSelectPlatformOfferDialog() throws Throwable {
+ SchemaContext ctx = XmlSchemaContext.deserialize(DBTraceObjectManagerTest.XML_CTX);
+ try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("echo", "DATA:BE:64:default")) {
+ try (UndoableTransaction tid = tb.startTransaction()) {
+ tb.trace.getObjectManager()
+ .createRootObject(ctx.getSchema(new SchemaName("Session")));
+ }
+ traceManager.openTrace(tb.trace);
+ traceManager.activateTrace(tb.trace);
+ waitForSwing();
+
+ performAction(platformPlugin.actionMore, false);
+ DebuggerSelectPlatformOfferDialog dialog =
+ waitForDialogComponent(DebuggerSelectPlatformOfferDialog.class);
+ dialog.setFilterRecommended(false);
+ waitForSwing();
+
+ captureDialog(dialog);
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java
new file mode 100644
index 0000000000..5bf700b8d5
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java
@@ -0,0 +1,102 @@
+/* ###
+ * 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.gui.platform;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import docking.action.ToggleDockingActionIf;
+import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
+import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
+import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer;
+import ghidra.app.services.DebuggerPlatformService;
+import ghidra.program.model.lang.LanguageID;
+import ghidra.trace.database.ToyDBTraceBuilder;
+
+public class DebuggerPlatformPluginTest extends AbstractGhidraHeadedDebuggerGUITest {
+ DebuggerPlatformPlugin platformPlugin;
+ DebuggerPlatformService platformService;
+
+ protected List getPlatformActions() {
+ return tool.getAllActions()
+ .stream()
+ .filter(a -> a.getOwner().equals(platformPlugin.getName()))
+ .filter(a -> a instanceof ToggleDockingActionIf)
+ .filter(a -> a != platformPlugin.actionMore)
+ .map(a -> (ToggleDockingActionIf) a)
+ .collect(Collectors.toList());
+ }
+
+ @Before
+ public void setUpPlatformTest() throws Throwable {
+ platformPlugin = addPlugin(tool, DebuggerPlatformPlugin.class);
+ platformService = tool.getService(DebuggerPlatformService.class);
+ }
+
+ protected void chooseLanguageIDViaMore(LanguageID langID) {
+ performAction(platformPlugin.actionMore, false);
+ DebuggerSelectPlatformOfferDialog dialog =
+ waitForDialogComponent(DebuggerSelectPlatformOfferDialog.class);
+ dialog.setFilterRecommended(false);
+ waitForSwing();
+
+ List offers = runSwing(() -> dialog.getDisplayedOffers());
+ DebuggerPlatformOffer toyOffer = offers.stream()
+ .filter(o -> Objects.equals(langID, o.getLanguageID()))
+ .findFirst()
+ .orElseThrow();
+ runSwing(() -> dialog.setSelectedOffer(toyOffer));
+ runSwing(() -> dialog.okCallback());
+ waitForSwing();
+ }
+
+ @Test
+ public void testActionMore() throws Throwable {
+ createAndOpenTrace("DATA:BE:64:default");
+ traceManager.activateTrace(tb.trace);
+
+ chooseLanguageIDViaMore(new LanguageID("Toy:BE:64:default"));
+ DebuggerPlatformMapper mapper = platformService.getCurrentMapperFor(tb.trace);
+ assertEquals(new LanguageID("Toy:BE:64:default"), mapper.getLangauge(null).getLanguageID());
+ }
+
+ @Test
+ public void testRemembersChosenOffer() throws Throwable {
+ createAndOpenTrace("DATA:BE:64:default");
+ try (ToyDBTraceBuilder tb2 =
+ new ToyDBTraceBuilder("second-" + name.getMethodName(), "DATA:BE:64:default")) {
+ traceManager.openTrace(tb2.trace);
+ traceManager.activateTrace(tb2.trace);
+
+ chooseLanguageIDViaMore(new LanguageID("Toy:BE:64:default"));
+ assertEquals(2, getPlatformActions().size());
+
+ traceManager.activateTrace(tb.trace);
+ waitForSwing();
+ assertEquals(1, getPlatformActions().size());
+
+ traceManager.activateTrace(tb2.trace);
+ waitForSwing();
+ assertEquals(2, getPlatformActions().size());
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
index 64aed3469f..a5956da67e 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
@@ -24,6 +24,12 @@ import ghidra.trace.model.target.TraceObject;
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
+ protected static class TestDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
+ public TestDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
+ super(tool, trace, cSpec);
+ }
+ }
+
enum Offers implements DebuggerPlatformOffer {
ARM_V8_LE("Test armv8le", "ARM:LE:32:v8", "default"),
X86_64("Test x86-64", "x86:LE:64:default", "gcc");
@@ -58,13 +64,19 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
@Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
- return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
+ return new TestDebuggerPlatformMapper(tool, trace, getCompilerSpec());
+ }
+
+ @Override
+ public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
+ return mapper.getClass() == TestDebuggerPlatformMapper.class;
}
}
@Override
protected Set getOffers(TraceObject object, long snap,
- TraceObject env, String debugger, String arch, String os, Endian endian) {
+ TraceObject env, String debugger, String arch, String os, Endian endian,
+ boolean includeOverrides) {
if (!"test".equals(debugger)) {
return Set.of();
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java
index 473ce0e94e..7ac389202c 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java
@@ -145,7 +145,8 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
TraceThread thread = tb.getOrAddThread("Threads[0]", 0);
AsyncPcodeExecutor executor =
TracePcodeUtils.executorForCoordinates(
- DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0));
+ DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0,
+ null));
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
@@ -182,7 +183,8 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
thread = tb.getOrAddThread("Threads[0]", 0);
AsyncPcodeExecutor executor =
TracePcodeUtils.executorForCoordinates(
- DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0));
+ DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0,
+ null));
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java
index 1e0bca2d3d..abf3ff2d30 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java
@@ -17,8 +17,7 @@ package ghidra.app.plugin.core.debug.service.tracemgr;
import static org.junit.Assert.*;
-import java.util.Collection;
-import java.util.Set;
+import java.util.*;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -30,9 +29,17 @@ import ghidra.app.services.ActionSource;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.model.TestTargetStack;
import ghidra.dbg.model.TestTargetStackFrameHasRegisterBank;
+import ghidra.dbg.target.schema.SchemaContext;
+import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
+import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.framework.model.DomainFile;
+import ghidra.trace.database.target.DBTraceObjectManager;
+import ghidra.trace.database.target.DBTraceObjectManagerTest;
import ghidra.trace.model.Trace;
import ghidra.trace.model.stack.TraceStack;
+import ghidra.trace.model.target.TraceObject;
+import ghidra.trace.model.target.TraceObjectKeyPath;
+import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.database.UndoableTransaction;
@@ -194,6 +201,48 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
assertEquals(0, traceManager.getCurrentFrame());
}
+ @Test
+ public void testGetCurrentObject() throws Exception {
+ assertEquals(null, traceManager.getCurrentObject());
+
+ createTrace();
+ waitForDomainObject(tb.trace);
+
+ assertEquals(null, traceManager.getCurrentObject());
+
+ traceManager.openTrace(tb.trace);
+ waitForSwing();
+
+ assertEquals(null, traceManager.getCurrentObject());
+
+ traceManager.activateTrace(tb.trace);
+ waitForSwing();
+
+ assertEquals(null, traceManager.getCurrentObject());
+
+ SchemaContext ctx = XmlSchemaContext.deserialize(DBTraceObjectManagerTest.XML_CTX);
+ TraceObject objThread0;
+ try (UndoableTransaction tid = tb.startTransaction()) {
+ DBTraceObjectManager objectManager = tb.trace.getObjectManager();
+ objectManager.createRootObject(ctx.getSchema(new SchemaName("Session"))).getChild();
+ objThread0 =
+ objectManager.createObject(TraceObjectKeyPath.parse("Targets[0].Threads[0]"));
+ }
+ TraceThread thread =
+ Objects.requireNonNull(objThread0.queryInterface(TraceObjectThread.class));
+
+ traceManager.activateObject(objThread0);
+ waitForSwing();
+
+ assertEquals(objThread0, traceManager.getCurrentObject());
+ assertEquals(thread, traceManager.getCurrentThread());
+
+ traceManager.activateTrace(null);
+ waitForSwing();
+
+ assertEquals(null, traceManager.getCurrentObject());
+ }
+
@Test
public void testOpenTrace() throws Exception {
createTrace();
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java
index d5e6325645..3f3eb0b025 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java
@@ -33,7 +33,9 @@ import ghidra.program.util.DefaultLanguageService;
import ghidra.trace.database.DBTraceUtils.CompilerSpecIDDBFieldCodec;
import ghidra.trace.database.DBTraceUtils.LanguageIDDBFieldCodec;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.Trace.TracePlatformChangeType;
import ghidra.trace.model.guest.TraceGuestPlatform;
+import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
@@ -184,6 +186,8 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
hostAddressSet.delete(hostRange);
guestAddressSet.delete(guestRange);
}
+ manager.trace.setChanged(new TraceChangeRecord<>(TracePlatformChangeType.MAPPING_DELETED,
+ null, this, range, null));
}
@Override
@@ -211,6 +215,7 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
@Override
public DBTraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart,
long length) throws AddressOverflowException {
+ DBTraceGuestPlatformMappedRange mappedRange;
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
Address hostEnd = hostStart.addWrap(length - 1);
if (hostAddressSet.intersects(hostStart, hostEnd)) {
@@ -222,14 +227,16 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
if (guestAddressSet.intersects(guestStart, guestEnd)) {
throw new IllegalArgumentException("Range overlaps existing guest mapped range(s)");
}
- DBTraceGuestPlatformMappedRange mappedRange = manager.rangeMappingStore.create();
+ mappedRange = manager.rangeMappingStore.create();
mappedRange.set(hostStart, this, guestStart, length);
rangesByHostAddress.put(hostStart, mappedRange);
rangesByGuestAddress.put(guestStart, mappedRange);
hostAddressSet.add(mappedRange.getHostRange());
guestAddressSet.add(mappedRange.getGuestRange());
- return mappedRange;
}
+ manager.trace.setChanged(new TraceChangeRecord<>(TracePlatformChangeType.MAPPING_ADDED,
+ null, this, null, mappedRange));
+ return mappedRange;
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java
index ea5b72fbfe..29d17c8df5 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java
@@ -161,6 +161,6 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
@Override
public void delete(TaskMonitor monitor) throws CancelledException {
- manager.platformStore.getObjectAt(guestPlatformKey).deleteMappedRange(this, monitor);
+ platform.deleteMappedRange(this, monitor);
}
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java
index cf3156f53a..f9a05376be 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java
@@ -29,7 +29,9 @@ import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceManager;
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.Trace.TracePlatformChangeType;
import ghidra.trace.model.guest.*;
+import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.database.*;
import ghidra.util.exception.CancelledException;
@@ -261,6 +263,7 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
platformsByCompiler.remove(platform.getCompilerSpec());
platformStore.delete(platform);
}
+ trace.setChanged(new TraceChangeRecord<>(TracePlatformChangeType.DELETED, null, platform));
}
@Override
@@ -281,9 +284,12 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
throw new IllegalArgumentException(
"Base compiler spec cannot be a guest compiler spec");
}
+ DBTraceGuestPlatform platform;
try (LockHold hold = LockHold.lock(lock.writeLock())) {
- return doAddGuestPlatform(compilerSpec);
+ platform = doAddGuestPlatform(compilerSpec);
}
+ trace.setChanged(new TraceChangeRecord<>(TracePlatformChangeType.ADDED, null, platform));
+ return platform;
}
@Override
@@ -297,18 +303,21 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
}
@Override
- public DBTraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec) {
+ public InternalTracePlatform getOrAddPlatform(CompilerSpec compilerSpec) {
if (compilerSpec.getCompilerSpecID()
.equals(trace.getBaseCompilerSpec().getCompilerSpecID())) {
- throw new IllegalArgumentException("Base language cannot be a guest language");
+ return hostPlatform;
}
+ DBTraceGuestPlatform platform;
try (LockHold hold = LockHold.lock(lock.writeLock())) {
DBTraceGuestPlatform exists = platformsByCompiler.get(compilerSpec);
if (exists != null) {
return exists;
}
- return doAddGuestPlatform(compilerSpec);
+ platform = doAddGuestPlatform(compilerSpec);
}
+ trace.setChanged(new TraceChangeRecord<>(TracePlatformChangeType.ADDED, null, platform));
+ return platform;
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
index fd3f31b9e9..454e4cdcbe 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
@@ -32,7 +32,7 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointManager;
import ghidra.trace.model.context.TraceRegisterContextManager;
import ghidra.trace.model.data.TraceBasedDataTypeManager;
-import ghidra.trace.model.guest.TracePlatformManager;
+import ghidra.trace.model.guest.*;
import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.*;
@@ -379,6 +379,16 @@ public interface Trace extends DataTypeManagerDomainObject {
public static final TraceSnapshotChangeType DELETED = new TraceSnapshotChangeType<>();
}
+ public static final class TracePlatformChangeType
+ extends DefaultTraceChangeType {
+ public static final TracePlatformChangeType ADDED = new TracePlatformChangeType<>();
+ public static final TracePlatformChangeType DELETED = new TracePlatformChangeType<>();
+ public static final TracePlatformChangeType MAPPING_ADDED =
+ new TracePlatformChangeType<>();
+ public static final TracePlatformChangeType MAPPING_DELETED =
+ new TracePlatformChangeType<>();
+ }
+
public interface TraceProgramViewListener {
void viewCreated(TraceProgramView view);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java
index 2a471245fd..1582732f3d 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java
@@ -33,6 +33,13 @@ public interface TracePlatformManager {
*/
TracePlatform getHostPlatform();
+ /**
+ * Get all guest platforms
+ *
+ * @return the collection of platforms
+ */
+ Collection getGuestPlatforms();
+
/**
* Add a guest platform
*
@@ -53,14 +60,7 @@ public interface TracePlatformManager {
* Get or add a platform for the given compiler spec
*
* @param compilerSpec the compiler spec
- * @return the new or existing platform, or null if compiler spec is the base compiler spec
+ * @return the new or existing platform
*/
- TraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec);
-
- /**
- * Get all guest platforms
- *
- * @return the collection of platforms
- */
- Collection getGuestPlatforms();
+ TracePlatform getOrAddPlatform(CompilerSpec compilerSpec);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/target/DBTraceObjectManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/target/DBTraceObjectManagerTest.java
index 12e11aed86..35e89715b7 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/target/DBTraceObjectManagerTest.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/target/DBTraceObjectManagerTest.java
@@ -40,6 +40,30 @@ import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.util.database.*;
public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationTest {
+ public static final String XML_CTX = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """;
protected ToyDBTraceBuilder b;
protected DBTraceObjectManager manager;
@@ -54,29 +78,7 @@ public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationT
b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default");
manager = b.trace.getObjectManager();
- ctx = XmlSchemaContext.deserialize("" + //
- "" + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- " " + //
- "");
+ ctx = XmlSchemaContext.deserialize(XML_CTX);
}
protected void populateModel(int targetCount) {
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java
index 67922c58fc..0f262fff87 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java
@@ -45,6 +45,10 @@ import utility.function.Callback;
public class DialogComponentProvider
implements ActionContextProvider, StatusListener, TaskListener {
+ protected enum Opt {
+ MODAL, INCLUDE_STATUS, INCLUDE_BUTTONS, CAN_RUN_TASKS;
+ }
+
private static final Color WARNING_COLOR = new Color(0xff9900);
private final static int DEFAULT_DELAY = 750;
@@ -99,6 +103,17 @@ public class DialogComponentProvider
private Dimension defaultSize;
+ /**
+ * Constructor for GhidraDialogComponent using option set instead of booleans
+ * @param title the dialog title
+ * @param options the options. See {@link Option} and
+ * {{@link #DialogComponentProvider(String, boolean, boolean, boolean, boolean)}
+ */
+ protected DialogComponentProvider(String title, Set options) {
+ this(title, options.contains(Opt.MODAL), options.contains(Opt.INCLUDE_STATUS),
+ options.contains(Opt.INCLUDE_BUTTONS), options.contains(Opt.CAN_RUN_TASKS));
+ }
+
/**
* Constructor for a GhidraDialogComponent that will be modal and will include a status line and
* a button panel. Its title will be the same as its name.