Merge remote-tracking branch 'origin/GP-6071_ghidra1_VTMatchSet'

This commit is contained in:
ghidra1
2025-11-05 14:17:01 -05:00
5 changed files with 95 additions and 67 deletions
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -301,11 +301,6 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
return true; return true;
} }
@Override
public boolean isInvalid() {
return session.getMatchSetRecord(key) == null;
}
private VTMatch getMatchForRecord(DBRecord matchRecord) { private VTMatch getMatchForRecord(DBRecord matchRecord) {
try { try {
lock.acquire(); lock.acquire();
@@ -346,14 +341,8 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
return matchTableAdapter; return matchTableAdapter;
} }
public void invalidateCache() { void invalidateCache() {
lock.acquire(); matchCache.invalidate();
try {
matchCache.invalidate();
}
finally {
lock.release();
}
} }
@Override @Override
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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, static VTMatchTableDBAdapter getAdapter(DBHandle dbHandle, long tableID, OpenMode openMode,
TaskMonitor monitor) throws VersionException { 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, public abstract DBRecord insertMatchRecord(VTMatchInfo info, VTMatchSetDB matchSet,
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,30 +16,25 @@
package ghidra.feature.vt.api.db; package ghidra.feature.vt.api.db;
import static ghidra.feature.vt.api.db.VTMatchTableDBAdapter.ColumnDescription.*; 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 java.io.IOException;
import db.*; import db.*;
import ghidra.feature.vt.api.main.VTMatchInfo;
import ghidra.framework.data.OpenMode;
import ghidra.util.exception.VersionException;
public class VTMatchTableDBAdapterV0 extends VTMatchTableDBAdapter { public class VTMatchTableDBAdapterV0 extends VTMatchTableDBAdapter {
private Table table; private Table table;
private final DBHandle dbHandle;
public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID) throws IOException { public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID) throws IOException {
this.dbHandle = dbHandle; table = dbHandle.createTable(TABLE_NAME + tableID, TABLE_SCHEMA,
table = new int[] { ASSOCIATION_COL.column() });
dbHandle.createTable(TABLE_NAME + tableID, TABLE_SCHEMA,
new int[] { ASSOCIATION_COL.column() });
} }
public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID, OpenMode openMode, public VTMatchTableDBAdapterV0(DBHandle dbHandle, long tableID, OpenMode openMode)
TaskMonitor monitor) throws VersionException { throws VersionException {
this.dbHandle = dbHandle;
table = dbHandle.getTable(TABLE_NAME + tableID); table = dbHandle.getTable(TABLE_NAME + tableID);
if (table == null) { if (table == null) {
throw new VersionException("Missing Table: " + TABLE_NAME); throw new VersionException("Missing Table: " + TABLE_NAME);
@@ -57,8 +52,10 @@ public class VTMatchTableDBAdapterV0 extends VTMatchTableDBAdapter {
DBRecord record = TABLE_SCHEMA.createRecord(table.getKey()); DBRecord record = TABLE_SCHEMA.createRecord(table.getKey());
record.setLongValue(TAG_KEY_COL.column(), (tag == null) ? -1 : tag.getKey()); record.setLongValue(TAG_KEY_COL.column(), (tag == null) ? -1 : tag.getKey());
record.setString(SIMILARITY_SCORE_COL.column(), info.getSimilarityScore().toStorageString()); record.setString(SIMILARITY_SCORE_COL.column(),
record.setString(CONFIDENCE_SCORE_COL.column(), info.getConfidenceScore().toStorageString()); info.getSimilarityScore().toStorageString());
record.setString(CONFIDENCE_SCORE_COL.column(),
info.getConfidenceScore().toStorageString());
record.setLongValue(ASSOCIATION_COL.column(), association.getKey()); record.setLongValue(ASSOCIATION_COL.column(), association.getKey());
record.setIntValue(SOURCE_LENGTH_COL.column(), info.getSourceLength()); record.setIntValue(SOURCE_LENGTH_COL.column(), info.getSourceLength());
record.setIntValue(DESTINATION_LENGTH_COL.column(), info.getDestinationLength()); record.setIntValue(DESTINATION_LENGTH_COL.column(), info.getDestinationLength());
@@ -17,7 +17,6 @@ package ghidra.feature.vt.api.db;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import db.*; import db.*;
import ghidra.app.util.task.OpenProgramRequest; import ghidra.app.util.task.OpenProgramRequest;
@@ -93,7 +92,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
private Program sourceProgram; private Program sourceProgram;
private Program destinationProgram; private Program destinationProgram;
private List<VTMatchSetDB> matchSets = new CopyOnWriteArrayList<>(); private Map<Long, VTMatchSetDB> matchSetMap = new HashMap<>();
private VTMatchSet manualMatchSet; private VTMatchSet manualMatchSet;
private VTMatchSet impliedMatchSet; private VTMatchSet impliedMatchSet;
@@ -140,10 +139,12 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
initializePrograms(sourceProgram, destinationProgram, true); initializePrograms(sourceProgram, destinationProgram, true);
createMatchSet(new ManualMatchProgramCorrelator(sourceProgram, destinationProgram), manualMatchSet =
MANUAL_MATCH_SET_ID); createMatchSet(new ManualMatchProgramCorrelator(sourceProgram, destinationProgram),
createMatchSet(new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram), MANUAL_MATCH_SET_ID);
IMPLIED_MATCH_SET_ID); impliedMatchSet =
createMatchSet(new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram),
IMPLIED_MATCH_SET_ID);
updateVersion(); updateVersion();
} }
@@ -439,17 +440,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
super.clearCache(all); super.clearCache(all);
associationManager.invalidateCache(); associationManager.invalidateCache();
tagCache.invalidate(); tagCache.invalidate();
reconcileCachedMatchSets();
List<VTMatchSetDB> temp = new ArrayList<>();
for (VTMatchSetDB matchSet : matchSets) {
if (!matchSet.isInvalid()) {
matchSet.invalidateCache();
temp.add(matchSet);
}
}
matchSets.retainAll(temp);
} }
finally { finally {
lock.release(); lock.release();
@@ -471,11 +462,44 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
RecordIterator recordIterator = matchSetTableAdapter.getRecords(); RecordIterator recordIterator = matchSetTableAdapter.getRecords();
while (recordIterator.hasNext()) { while (recordIterator.hasNext()) {
DBRecord record = recordIterator.next(); DBRecord record = recordIterator.next();
matchSets.add( matchSetMap.put(record.getKey(),
VTMatchSetDB.getMatchSetDB(record, this, getDBHandle(), openMode, monitor, lock)); VTMatchSetDB.getMatchSetDB(record, this, getDBHandle(), openMode, monitor, lock));
} }
} }
private void reconcileCachedMatchSets() {
try {
Set<Long> 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 @Override
public Program getSourceProgram() { public Program getSourceProgram() {
return sourceProgram; return sourceProgram;
@@ -503,7 +527,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
DBRecord record = matchSetTableAdapter.createMatchSetRecord(id, correlator); DBRecord record = matchSetTableAdapter.createMatchSetRecord(id, correlator);
VTMatchSetDB matchSet = VTMatchSetDB matchSet =
VTMatchSetDB.createMatchSetDB(record, this, getDBHandle(), lock); VTMatchSetDB.createMatchSetDB(record, this, getDBHandle(), lock);
matchSets.add(matchSet); matchSetMap.put(matchSet.getKey(), matchSet);
changeSetsModified = true; // signal endTransaction to clear undo stack changeSetsModified = true; // signal endTransaction to clear undo stack
setObjectChanged(VTEvent.MATCH_SET_ADDED, matchSet, null, matchSet); setObjectChanged(VTEvent.MATCH_SET_ADDED, matchSet, null, matchSet);
@@ -564,7 +588,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
@Override @Override
public List<VTMatchSet> getMatchSets() { public List<VTMatchSet> getMatchSets() {
return new ArrayList<>(matchSets); return new ArrayList<>(matchSetMap.values());
} }
AddressSet getSourceAddressSet(DBRecord record) throws IOException { AddressSet getSourceAddressSet(DBRecord record) throws IOException {
@@ -625,11 +649,17 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
@Override @Override
public List<VTMatch> getMatches(VTAssociation association) { public List<VTMatch> getMatches(VTAssociation association) {
List<VTMatch> matches = new ArrayList<>(); try {
for (VTMatchSet matchSet : matchSets) { lock.acquire();
matches.addAll(matchSet.getMatches(association)); List<VTMatch> 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 @Override
public VTMatchSet getManualMatchSet() { public VTMatchSet getManualMatchSet() {
if (manualMatchSet == null) { try {
manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName()); lock.acquire();
if (manualMatchSet == null) {
manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName());
}
return manualMatchSet;
}
finally {
lock.release();
} }
return manualMatchSet;
} }
@Override @Override
public VTMatchSet getImpliedMatchSet() { public VTMatchSet getImpliedMatchSet() {
if (impliedMatchSet == null) { try {
impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName()); lock.acquire();
if (impliedMatchSet == null) {
impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName());
}
return impliedMatchSet;
}
finally {
lock.release();
} }
return impliedMatchSet;
} }
private VTMatchSet findMatchSet(String correlatorClassName) { private VTMatchSet findMatchSet(String correlatorClassName) {
for (VTMatchSet matchSet : matchSets) { for (VTMatchSet matchSet : matchSetMap.values()) {
VTProgramCorrelatorInfo info = matchSet.getProgramCorrelatorInfo(); VTProgramCorrelatorInfo info = matchSet.getProgramCorrelatorInfo();
String matchSetCorrelatorClassName = info.getCorrelatorClassName(); String matchSetCorrelatorClassName = info.getCorrelatorClassName();
if (correlatorClassName.equals(matchSetCorrelatorClassName)) { if (correlatorClassName.equals(matchSetCorrelatorClassName)) {
@@ -771,8 +813,8 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
} }
public VTMatchTag getMatchTag(long key) { public VTMatchTag getMatchTag(long key) {
lock.acquire();
try { try {
lock.acquire();
VTMatchTagDB matchTagDB = tagCache.get(key); VTMatchTagDB matchTagDB = tagCache.get(key);
if (matchTagDB != null) { if (matchTagDB != null) {
return matchTagDB; return matchTagDB;
@@ -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 * @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. * is currently valid which may include a deleted state.
*/ */
protected boolean isInvalid() { protected final boolean isInvalid() {
return !deleted && invalidateCount != getCurrentValidationCount(); return !deleted && invalidateCount != getCurrentValidationCount();
} }