diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java index 28557181d7..54eeaec75d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java @@ -26,6 +26,7 @@ import com.google.common.cache.RemovalNotification; import db.DBHandle; import generic.depends.DependentService; import generic.depends.err.ServiceConstructionException; +import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.framework.options.Options; import ghidra.lifecycle.Internal; import ghidra.program.model.address.*; @@ -139,6 +140,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace protected boolean recordChanges = false; protected Set viewports = new WeakHashCowSet<>(); + protected ListenerSet directListeners = + new ListenerSet<>(DBTraceDirectChangeListener.class); protected DBTraceVariableSnapProgramView programView; protected Set programViews = new WeakHashCowSet<>(); protected Set programViewsView = Collections.unmodifiableSet(programViews); @@ -585,6 +588,23 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace fireEvent(event); } + @Override + public void fireEvent(DomainObjectChangeRecord ev) { + super.fireEvent(ev); + if (directListeners != null) { + // Some events fire during construction + directListeners.fire.changed(ev); + } + } + + public void addDirectChangeListener(DBTraceDirectChangeListener listener) { + directListeners.add(listener); + } + + public void removeDirectChangeListener(DBTraceDirectChangeListener listener) { + directListeners.remove(listener); + } + @Override public DBTraceProgramView getFixedProgramView(long snap) { // NOTE: The new viewport will need to read from the time manager during init diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceDirectChangeListener.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceDirectChangeListener.java new file mode 100644 index 0000000000..967240abfe --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceDirectChangeListener.java @@ -0,0 +1,22 @@ +/* ### + * 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.trace.database; + +import ghidra.framework.model.DomainObjectChangeRecord; + +public interface DBTraceDirectChangeListener { + void changed(DomainObjectChangeRecord rec); +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java index 876e9dc23f..710047621e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java @@ -42,8 +42,7 @@ import ghidra.program.model.util.AddressSetPropertyMap; import ghidra.program.model.util.PropertyMapManager; import ghidra.program.util.ChangeManager; import ghidra.program.util.ProgramChangeRecord; -import ghidra.trace.database.DBTrace; -import ghidra.trace.database.DBTraceTimeViewport; +import ghidra.trace.database.*; import ghidra.trace.database.listing.DBTraceCodeSpace; import ghidra.trace.database.listing.DBTraceDefinedUnitsView; import ghidra.trace.database.memory.DBTraceMemorySpace; @@ -82,7 +81,8 @@ public class DBTraceProgramView implements TraceProgramView { public static final int TIME_INTERVAL = 100; public static final int BUF_SIZE = 1000; - protected class EventTranslator extends TraceDomainObjectListener { + protected class EventTranslator extends TypedEventDispatcher + implements DBTraceDirectChangeListener { public EventTranslator() { listenForUntyped(DomainObject.DO_OBJECT_SAVED, this::eventPassthrough); listenForUntyped(DomainObject.DO_DOMAIN_FILE_CHANGED, this::eventPassthrough); @@ -172,6 +172,11 @@ public class DBTraceProgramView implements TraceProgramView { listenFor(TraceSymbolChangeType.DELETED, this::symbolDeleted); } + @Override + public void changed(DomainObjectChangeRecord event) { + handleChangeRecord(event); + } + private void eventPassthrough(DomainObjectChangeRecord rec) { fireEventAllViews(rec); } @@ -1376,7 +1381,7 @@ public class DBTraceProgramView implements TraceProgramView { protected synchronized EventTranslator getEventTranslator() { if (eventTranslator == null) { eventTranslator = new EventTranslator(); - trace.addListener(eventTranslator); + trace.addDirectChangeListener(eventTranslator); } return eventTranslator; } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceDomainObjectListener.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceDomainObjectListener.java index 6e057af99b..fd859a47fe 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceDomainObjectListener.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceDomainObjectListener.java @@ -15,165 +15,12 @@ */ package ghidra.trace.model; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - import ghidra.framework.model.*; -import ghidra.trace.util.*; import ghidra.util.TimedMsg; -public class TraceDomainObjectListener implements DomainObjectListener { +public class TraceDomainObjectListener extends TypedEventDispatcher + implements DomainObjectListener { - public interface EventRecordHandler { - void handle(TraceChangeRecord record); - } - - public interface FullEventRecordHandler extends EventRecordHandler { - void handle(TraceAddressSpace space, T affectedObject, U oldValue, U newValue); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getSpace(), record.getAffectedObject(), record.getOldValue(), - record.getNewValue()); - } - } - - public interface AffectedObjectHandler extends EventRecordHandler { - void handle(TraceAddressSpace space, T affectedObject); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getSpace(), record.getAffectedObject()); - } - } - - public interface AffectedObjectOnlyHandler extends EventRecordHandler { - void handle(T affectedObject); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getAffectedObject()); - } - } - - public interface AffectedAndValuesOnlyHandler extends EventRecordHandler { - void handle(T affectedObject, U oldValue, U newValue); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getAffectedObject(), record.getOldValue(), record.getNewValue()); - } - } - - public interface SpaceValuesHandler extends EventRecordHandler { - void handle(TraceAddressSpace space, U oldValue, U newValue); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getSpace(), record.getOldValue(), record.getNewValue()); - } - } - - public interface ValuesOnlyHandler extends EventRecordHandler { - void handle(U oldValue, U newValue); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getOldValue(), record.getNewValue()); - } - } - - public interface IgnoreValuesHandler extends EventRecordHandler { - void handle(TraceAddressSpace space); - - @Override - default void handle(TraceChangeRecord record) { - handle(record.getSpace()); - } - } - - public interface IgnoreAllHandler extends EventRecordHandler { - void handle(); - - @Override - default void handle(TraceChangeRecord record) { - handle(); - } - } - - private Map, EventRecordHandler> typedMap = new HashMap<>(); - private Map> untypedMap = new HashMap<>(); - private Consumer restoredHandler = null; - - protected void listenFor(TraceChangeType type, EventRecordHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, - FullEventRecordHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, - AffectedObjectHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, - AffectedObjectOnlyHandler handler) { - typedMap.put(type, handler); - } - - /** - * Listen for the given event, taking the affected object, the old value, and the new value - * - * @param the type of the affected object - * @param the type of the values - * @param type the event type - * @param handler the handler - */ - protected void listenFor(TraceChangeType type, - AffectedAndValuesOnlyHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, - ValuesOnlyHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, - SpaceValuesHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, IgnoreValuesHandler handler) { - typedMap.put(type, handler); - } - - protected void listenFor(TraceChangeType type, IgnoreAllHandler handler) { - typedMap.put(type, handler); - } - - protected void listenForUntyped(int type, Consumer handler) { - if (type == DomainObject.DO_OBJECT_RESTORED) { - restoredHandler = handler; - } - else { - untypedMap.put(type, handler); - } - } - - @SuppressWarnings("unchecked") - public void handleTraceChangeRecord(TraceChangeRecord rec) { - @SuppressWarnings("rawtypes") - EventRecordHandler handler = typedMap.get(rec.getType()); - if (handler != null) { - handler.handle(rec); - } - } - @Override public void domainObjectChanged(DomainObjectChangedEvent ev) { //TimedMsg.info(this, "Handing (" + this + "): " + ev); @@ -189,23 +36,8 @@ public class TraceDomainObjectListener implements DomainObjectListener { } //Map CountsByType = new TreeMap<>(); for (DomainObjectChangeRecord rec : ev) { - //String typeName = DefaultTraceChangeType.getName(rec.getEventType()); - //CountsByType.compute(typeName, (k, v) -> v == null ? 1 : v + 1); - if (rec instanceof TraceChangeRecord) { - handleTraceChangeRecord((TraceChangeRecord) rec); - continue; - } - Consumer handler; - if (null != (handler = untypedMap.get(rec.getEventType()))) { - handler.accept(rec); - continue; - } - unhandled(rec); + handleChangeRecord(rec); } //TimedMsg.info(this, " Done: " + CountsByType); } - - protected void unhandled(DomainObjectChangeRecord rec) { - // Extension point - } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TypedEventDispatcher.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TypedEventDispatcher.java new file mode 100644 index 0000000000..c3ba4cef2d --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TypedEventDispatcher.java @@ -0,0 +1,195 @@ +/* ### + * 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.trace.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import ghidra.framework.model.DomainObject; +import ghidra.framework.model.DomainObjectChangeRecord; +import ghidra.trace.util.*; + +public class TypedEventDispatcher { + + public interface EventRecordHandler { + void handle(TraceChangeRecord record); + } + + public interface FullEventRecordHandler extends EventRecordHandler { + void handle(TraceAddressSpace space, T affectedObject, U oldValue, U newValue); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getSpace(), record.getAffectedObject(), record.getOldValue(), + record.getNewValue()); + } + } + + public interface AffectedObjectHandler extends EventRecordHandler { + void handle(TraceAddressSpace space, T affectedObject); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getSpace(), record.getAffectedObject()); + } + } + + public interface AffectedObjectOnlyHandler extends EventRecordHandler { + void handle(T affectedObject); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getAffectedObject()); + } + } + + public interface AffectedAndValuesOnlyHandler extends EventRecordHandler { + void handle(T affectedObject, U oldValue, U newValue); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getAffectedObject(), record.getOldValue(), record.getNewValue()); + } + } + + public interface SpaceValuesHandler extends EventRecordHandler { + void handle(TraceAddressSpace space, U oldValue, U newValue); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getSpace(), record.getOldValue(), record.getNewValue()); + } + } + + public interface ValuesOnlyHandler extends EventRecordHandler { + void handle(U oldValue, U newValue); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getOldValue(), record.getNewValue()); + } + } + + public interface IgnoreValuesHandler extends EventRecordHandler { + void handle(TraceAddressSpace space); + + @Override + default void handle(TraceChangeRecord record) { + handle(record.getSpace()); + } + } + + public interface IgnoreAllHandler extends EventRecordHandler { + void handle(); + + @Override + default void handle(TraceChangeRecord record) { + handle(); + } + } + + private Map, EventRecordHandler> typedMap = new HashMap<>(); + private Map> untypedMap = new HashMap<>(); + protected Consumer restoredHandler = null; + + protected void listenFor(TraceChangeType type, EventRecordHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, + FullEventRecordHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, + AffectedObjectHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, + AffectedObjectOnlyHandler handler) { + typedMap.put(type, handler); + } + + /** + * Listen for the given event, taking the affected object, the old value, and the new value + * + * @param the type of the affected object + * @param the type of the values + * @param type the event type + * @param handler the handler + */ + protected void listenFor(TraceChangeType type, + AffectedAndValuesOnlyHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, + ValuesOnlyHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, + SpaceValuesHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, IgnoreValuesHandler handler) { + typedMap.put(type, handler); + } + + protected void listenFor(TraceChangeType type, IgnoreAllHandler handler) { + typedMap.put(type, handler); + } + + protected void listenForUntyped(int type, Consumer handler) { + if (type == DomainObject.DO_OBJECT_RESTORED) { + restoredHandler = handler; + } + else { + untypedMap.put(type, handler); + } + } + + public void handleChangeRecord(DomainObjectChangeRecord rec) { + //String typeName = DefaultTraceChangeType.getName(rec.getEventType()); + //CountsByType.compute(typeName, (k, v) -> v == null ? 1 : v + 1); + if (rec instanceof TraceChangeRecord) { + handleTraceChangeRecord((TraceChangeRecord) rec); + return; + } + Consumer handler; + if (null != (handler = untypedMap.get(rec.getEventType()))) { + handler.accept(rec); + return; + } + unhandled(rec); + } + + @SuppressWarnings("unchecked") + public void handleTraceChangeRecord(TraceChangeRecord rec) { + @SuppressWarnings("rawtypes") + EventRecordHandler handler = typedMap.get(rec.getType()); + if (handler != null) { + handler.handle(rec); + } + } + + protected void unhandled(DomainObjectChangeRecord rec) { + // Extension point + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java index b409502420..0765002182 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java @@ -65,7 +65,7 @@ class DomainObjectChangeSupport { this.timer = GhidraTimerFactory.getGhidraTimer(timeInterval, timeInterval, this::sendEventNow); timer.setInitialDelay(25); - timer.setDelay(500); + timer.setDelay(timeInterval); timer.setRepeats(true); }