diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetDB.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetDB.java index fb97a0f858..a263a1e049 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetDB.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetDB.java @@ -4,9 +4,9 @@ * 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. @@ -301,11 +301,6 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet { return true; } - @Override - public boolean isInvalid() { - return session.getMatchSetRecord(key) == null; - } - private VTMatch getMatchForRecord(DBRecord matchRecord) { try { lock.acquire(); @@ -346,14 +341,8 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet { return matchTableAdapter; } - public void invalidateCache() { - lock.acquire(); - try { - matchCache.invalidate(); - } - finally { - lock.release(); - } + void invalidateCache() { + matchCache.invalidate(); } @Override diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java index 68cf6c9d16..c04b93681e 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java @@ -4,9 +4,9 @@ * 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. @@ -80,7 +80,7 @@ public abstract class VTMatchTableDBAdapter { static VTMatchTableDBAdapter getAdapter(DBHandle dbHandle, long tableID, OpenMode openMode, TaskMonitor monitor) throws VersionException { - return new VTMatchTableDBAdapterV0(dbHandle, tableID, openMode, monitor); + return new VTMatchTableDBAdapterV0(dbHandle, tableID, openMode); } public abstract DBRecord insertMatchRecord(VTMatchInfo info, VTMatchSetDB matchSet, diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapterV0.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapterV0.java index 7c6c50800a..e4c891d9a2 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapterV0.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapterV0.java @@ -4,9 +4,9 @@ * 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. @@ -16,30 +16,25 @@ package ghidra.feature.vt.api.db; import static ghidra.feature.vt.api.db.VTMatchTableDBAdapter.ColumnDescription.*; -import ghidra.feature.vt.api.main.VTMatchInfo; -import ghidra.framework.data.OpenMode; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; import java.io.IOException; import db.*; +import ghidra.feature.vt.api.main.VTMatchInfo; +import ghidra.framework.data.OpenMode; +import ghidra.util.exception.VersionException; public class VTMatchTableDBAdapterV0 extends VTMatchTableDBAdapter { private Table table; - private final DBHandle dbHandle; public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID) throws IOException { - this.dbHandle = dbHandle; - table = - dbHandle.createTable(TABLE_NAME + tableID, TABLE_SCHEMA, - new int[] { ASSOCIATION_COL.column() }); + table = dbHandle.createTable(TABLE_NAME + tableID, TABLE_SCHEMA, + new int[] { ASSOCIATION_COL.column() }); } - public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID, OpenMode openMode, - TaskMonitor monitor) throws VersionException { - this.dbHandle = dbHandle; + public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID, OpenMode openMode) + throws VersionException { table = dbHandle.getTable(TABLE_NAME + tableID); if (table == null) { throw new VersionException("Missing Table: " + TABLE_NAME); @@ -57,8 +52,10 @@ public class VTMatchTableDBAdapterV0 extends VTMatchTableDBAdapter { DBRecord record = TABLE_SCHEMA.createRecord(table.getKey()); record.setLongValue(TAG_KEY_COL.column(), (tag == null) ? -1 : tag.getKey()); - record.setString(SIMILARITY_SCORE_COL.column(), info.getSimilarityScore().toStorageString()); - record.setString(CONFIDENCE_SCORE_COL.column(), info.getConfidenceScore().toStorageString()); + record.setString(SIMILARITY_SCORE_COL.column(), + info.getSimilarityScore().toStorageString()); + record.setString(CONFIDENCE_SCORE_COL.column(), + info.getConfidenceScore().toStorageString()); record.setLongValue(ASSOCIATION_COL.column(), association.getKey()); record.setIntValue(SOURCE_LENGTH_COL.column(), info.getSourceLength()); record.setIntValue(DESTINATION_LENGTH_COL.column(), info.getDestinationLength()); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java index edf1778e71..3e08dbd370 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java @@ -17,7 +17,6 @@ package ghidra.feature.vt.api.db; import java.io.IOException; import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; import db.*; import ghidra.app.util.task.OpenProgramRequest; @@ -93,7 +92,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { private Program sourceProgram; private Program destinationProgram; - private List matchSets = new CopyOnWriteArrayList<>(); + private Map matchSetMap = new HashMap<>(); private VTMatchSet manualMatchSet; private VTMatchSet impliedMatchSet; @@ -140,10 +139,12 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { initializePrograms(sourceProgram, destinationProgram, true); - createMatchSet(new ManualMatchProgramCorrelator(sourceProgram, destinationProgram), - MANUAL_MATCH_SET_ID); - createMatchSet(new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram), - IMPLIED_MATCH_SET_ID); + manualMatchSet = + createMatchSet(new ManualMatchProgramCorrelator(sourceProgram, destinationProgram), + MANUAL_MATCH_SET_ID); + impliedMatchSet = + createMatchSet(new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram), + IMPLIED_MATCH_SET_ID); updateVersion(); } @@ -439,17 +440,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { super.clearCache(all); associationManager.invalidateCache(); tagCache.invalidate(); - - List temp = new ArrayList<>(); - - for (VTMatchSetDB matchSet : matchSets) { - if (!matchSet.isInvalid()) { - matchSet.invalidateCache(); - temp.add(matchSet); - } - } - - matchSets.retainAll(temp); + reconcileCachedMatchSets(); } finally { lock.release(); @@ -471,11 +462,44 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { RecordIterator recordIterator = matchSetTableAdapter.getRecords(); while (recordIterator.hasNext()) { DBRecord record = recordIterator.next(); - matchSets.add( + matchSetMap.put(record.getKey(), VTMatchSetDB.getMatchSetDB(record, this, getDBHandle(), openMode, monitor, lock)); } } + private void reconcileCachedMatchSets() { + + try { + Set cachedKeySet = new HashSet<>(matchSetMap.keySet()); + + RecordIterator recordIterator = matchSetTableAdapter.getRecords(); + while (recordIterator.hasNext()) { + DBRecord record = recordIterator.next(); + long key = record.getKey(); + if (cachedKeySet.remove(key)) { + // Invalidate cached MatchSet + matchSetMap.get(key).invalidateCache(); + } + else { + // Add missing MatchSet to cache + matchSetMap.put(key, VTMatchSetDB.getMatchSetDB(record, this, getDBHandle(), + OpenMode.UPDATE, TaskMonitor.DUMMY, lock)); + } + } + + // Remove obsolete/invalid MatchSets whose record was not found + for (long key : cachedKeySet) { + matchSetMap.remove(key); + } + } + catch (VersionException e) { + throw new RuntimeException("Unexpected exception", e); + } + catch (IOException e) { + dbError(e); + } + } + @Override public Program getSourceProgram() { return sourceProgram; @@ -503,7 +527,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { DBRecord record = matchSetTableAdapter.createMatchSetRecord(id, correlator); VTMatchSetDB matchSet = VTMatchSetDB.createMatchSetDB(record, this, getDBHandle(), lock); - matchSets.add(matchSet); + matchSetMap.put(matchSet.getKey(), matchSet); changeSetsModified = true; // signal endTransaction to clear undo stack setObjectChanged(VTEvent.MATCH_SET_ADDED, matchSet, null, matchSet); @@ -564,7 +588,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { @Override public List getMatchSets() { - return new ArrayList<>(matchSets); + return new ArrayList<>(matchSetMap.values()); } AddressSet getSourceAddressSet(DBRecord record) throws IOException { @@ -625,11 +649,17 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { @Override public List getMatches(VTAssociation association) { - List matches = new ArrayList<>(); - for (VTMatchSet matchSet : matchSets) { - matches.addAll(matchSet.getMatches(association)); + try { + lock.acquire(); + List matches = new ArrayList<>(); + for (VTMatchSet matchSet : matchSetMap.values()) { + matches.addAll(matchSet.getMatches(association)); + } + return matches; + } + finally { + lock.release(); } - return matches; } /** @@ -649,22 +679,34 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { @Override public VTMatchSet getManualMatchSet() { - if (manualMatchSet == null) { - manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName()); + try { + lock.acquire(); + if (manualMatchSet == null) { + manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName()); + } + return manualMatchSet; + } + finally { + lock.release(); } - return manualMatchSet; } @Override public VTMatchSet getImpliedMatchSet() { - if (impliedMatchSet == null) { - impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName()); + try { + lock.acquire(); + if (impliedMatchSet == null) { + impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName()); + } + return impliedMatchSet; + } + finally { + lock.release(); } - return impliedMatchSet; } private VTMatchSet findMatchSet(String correlatorClassName) { - for (VTMatchSet matchSet : matchSets) { + for (VTMatchSet matchSet : matchSetMap.values()) { VTProgramCorrelatorInfo info = matchSet.getProgramCorrelatorInfo(); String matchSetCorrelatorClassName = info.getCorrelatorClassName(); if (correlatorClassName.equals(matchSetCorrelatorClassName)) { @@ -771,8 +813,8 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession { } public VTMatchTag getMatchTag(long key) { - lock.acquire(); try { + lock.acquire(); VTMatchTagDB matchTagDB = tagCache.get(key); if (matchTagDB != null) { return matchTagDB; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java index 5062576c2a..9f959e950a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java @@ -102,7 +102,7 @@ public abstract class DatabaseObject { * @return true if this object is invalid and must be re-validated, else false if object state * is currently valid which may include a deleted state. */ - protected boolean isInvalid() { + protected final boolean isInvalid() { return !deleted && invalidateCount != getCurrentValidationCount(); }