diff --git a/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java b/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java index 447619c7c5..ead74f6b05 100644 --- a/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java +++ b/Ghidra/Debug/Debugger-importers/src/main/java/ghidra/app/util/opinion/TenetLoader.java @@ -52,6 +52,7 @@ import ghidra.trace.model.stack.TraceStackFrame; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject.ConflictResolution; import ghidra.trace.model.target.TraceObjectManager; +import ghidra.trace.model.target.TraceObjectManager.BypassWriteCache; import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.schema.*; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; @@ -151,10 +152,10 @@ public class TenetLoader implements Loader { TENET_SESSION_SCHEMA = SAMPLE_CTX.getSchema(new SchemaName("TenetSession")); } - private static AddressSpace defaultSpace; - private static Program program; + private AddressSpace defaultSpace; + private Program program; - private static boolean STORE_REG_ATTRS = false; + private static final boolean STORE_REG_ATTRS = false; /** * Create an address in the processor's default space. @@ -162,7 +163,7 @@ public class TenetLoader implements Loader { * @param offset the byte offset * @return the address */ - private static Address addr(final long offset) { + private Address addr(final long offset) { return defaultSpace.getAddress(offset); } @@ -205,11 +206,11 @@ public class TenetLoader implements Loader { * @param max the maximum (inclusive) byte offset * @return the range */ - private static AddressRange rng(final long min, final long max) { + private AddressRange rng(final long min, final long max) { return new AddressRangeImpl(addr(min), addr(max)); } - private static Address toAddr(final String addressString) { + private Address toAddr(final String addressString) { return AddressEvaluator.evaluate(program, addressString); } @@ -321,14 +322,16 @@ public class TenetLoader implements Loader { final Object consumer, final MessageLog log, final TaskMonitor monitor) throws LanguageNotFoundException, IOException, CancelledException { - TenetLoader.program = program; + this.program = program; final Language lang = program.getLanguage(); - TenetLoader.defaultSpace = lang.getAddressFactory().getDefaultAddressSpace(); + this.defaultSpace = lang.getAddressFactory().getDefaultAddressSpace(); final Trace trace = new DBTrace(name, program.getCompilerSpec(), consumer); + final TraceObjectManager om = trace.getObjectManager(); + + try (Transaction tx = trace.openTransaction("Import Tenet Trace: %s".formatted(name)); + BypassWriteCache bypass = om.withoutWriteCache()) { - try (Transaction tx = trace.openTransaction("Import Tenet Trace: %s".formatted(name))) { - final TraceObjectManager om = trace.getObjectManager(); om.createRootObject(TENET_SESSION_SCHEMA); final TraceThread traceThread = @@ -364,8 +367,9 @@ public class TenetLoader implements Loader { line = reader.readLine(); } - final TraceSnapshot snapshot = + TraceSnapshot snapshot = trace.getTimeManager().createSnapshot("Snapshot %d".formatted(snapNumber)); + snapshot.setEventThread(traceThread); long snap = snapshot.getKey(); snapNumber++; @@ -381,51 +385,33 @@ public class TenetLoader implements Loader { } final Matcher ipMatcher = ipPattern.matcher(line); - if (!ipMatcher.find()) { + if (ipMatcher.find()) { + curIp = Long.parseLong(ipMatcher.group(1), 16); + + if (parseRegisterOperations(snap, curIp, line, lineNumber, traceThread, + trace, log, monitor)) { + parseMemoryOperations(snap, curIp, line, trace, monitor); + } + else { + errorCount++; + } + } + else { log.appendMsg( "Line %d: Unable to find PC, skipping...".formatted(lineNumber)); errorCount++; - lineNumber++; - monitor.setProgress(lineNumber); - - line = reader.readLine(); - if (line != null) { - snap = trace.getTimeManager() - .createSnapshot("Snapshot %d".formatted(snapNumber)) - .getKey(); - snapNumber++; - } - continue; } - curIp = Long.parseLong(ipMatcher.group(1), 16); - - if (!parseRegisterOperations(snap, curIp, line, lineNumber, traceThread, - trace, log, monitor)) { - errorCount++; - lineNumber++; - monitor.setProgress(lineNumber); - - line = reader.readLine(); - if (line != null) { - snap = trace.getTimeManager() - .createSnapshot("Snapshot %d".formatted(snapNumber)) - .getKey(); - snapNumber++; - } - continue; - } - parseMemoryOperations(snap, curIp, line, trace, monitor); lineNumber++; monitor.setProgress(lineNumber); line = reader.readLine(); if (line != null) { - snap = trace.getTimeManager() - .createSnapshot("Snapshot %d".formatted(snapNumber)) - .getKey(); + snapshot = trace.getTimeManager() + .createSnapshot("Snapshot %d".formatted(snapNumber)); + snap = snapshot.getKey(); + snapshot.setEventThread(traceThread); snapNumber++; - } } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java index 473e7ae1aa..131721d4f3 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java @@ -189,6 +189,7 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager protected final DBTraceObjectValueRStarTree valueTree; protected final DBTraceObjectValueMap valueMap; protected final DBTraceObjectValueWriteBehindCache valueWbCache; + protected boolean wbCacheDisabled; protected final DBCachedObjectIndex objectsByPath; @@ -331,9 +332,10 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager protected DBTraceObjectValue doCreateValue(Lifespan lifespan, DBTraceObject parent, String key, Object value) { // Root is never in write-behind cache - DBTraceObjectValue entry = - parent == null ? doCreateValueData(lifespan, parent, key, value).getWrapper() - : valueWbCache.doCreateValue(lifespan, parent, key, value).getWrapper(); + boolean skipWb = wbCacheDisabled || parent == null; + DBTraceObjectValue entry = skipWb + ? doCreateValueData(lifespan, parent, key, value).getWrapper() + : valueWbCache.doCreateValue(lifespan, parent, key, value).getWrapper(); if (parent != null) { parent.notifyValueCreated(entry); } @@ -867,4 +869,15 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager public void waitWbWorkers() { valueWbCache.waitWorkers(); } + + @Override + public BypassWriteCache withoutWriteCache() { + wbCacheDisabled = true; + return new BypassWriteCache() { + @Override + public void close() { + wbCacheDisabled = false; + } + }; + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectManager.java index 5336ee9740..63302afc3c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectManager.java @@ -196,4 +196,26 @@ public interface TraceObjectManager { * database permits schema modification, but requires that the entire model be replaced. */ void clear(); + + /** + * A handle to automatically re-enable the write cache + */ + interface BypassWriteCache extends AutoCloseable { + @Override + void close(); + } + + /** + * Bypass the write cache, usually for an import operation. + *

+ * For live sessions, we typically want to complete object writes as promptly as possible, so we + * don't tie up the connection and/or the remote debugger. However, for import operations, we'd + * rather just have object writes go straight to the database. The importer will want to save, + * which requires flushing the cache anyway. Disabling the cache gives more honest progress + * reporting and assures any crashes or diagnostics occur during the import rather than during + * the flush. + * + * @return a handle to automatically re-enable the write cache + */ + BypassWriteCache withoutWriteCache(); }