diff --git a/Ghidra/Features/VersionTracking/build.gradle b/Ghidra/Features/VersionTracking/build.gradle index 05d0966b84..ce6bd58d83 100644 --- a/Ghidra/Features/VersionTracking/build.gradle +++ b/Ghidra/Features/VersionTracking/build.gradle @@ -31,10 +31,4 @@ dependencies { testImplementation "org.jmockit:jmockit:1.44" testImplementation project(path: ':Project', configuration: 'testArtifacts') -} -integrationTest { - // tests to slow to run during automated testing - excludes = [ - '**/VTAutoVersionTrackingTest*', - ] -} +} \ No newline at end of file diff --git a/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java b/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java index f1a798c2c1..69d9ad1939 100644 --- a/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java +++ b/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java @@ -23,12 +23,13 @@ import java.util.List; import ghidra.app.script.GhidraScript; import ghidra.feature.vt.api.db.VTSessionDB; import ghidra.feature.vt.api.main.VTSession; -import ghidra.feature.vt.gui.actions.AutoVersionTrackingCommand; +import ghidra.feature.vt.gui.actions.AutoVersionTrackingTask; import ghidra.feature.vt.gui.plugin.*; import ghidra.framework.model.DomainFolder; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Program; +import ghidra.util.task.TaskLauncher; public class AutoVersionTrackingScript extends GhidraScript { @Override @@ -72,15 +73,10 @@ public class AutoVersionTrackingScript extends GhidraScript { //String description = "AutoVTScript"; - AutoVersionTrackingCommand autoVTcmd = - new AutoVersionTrackingCommand(controller, session, 1.0, 10.0); - - controller.getTool().executeBackgroundCommand(autoVTcmd, session); - //destinationProgram.save(description, monitor); - - //session.save(description, monitor); - //session.release(this); + AutoVersionTrackingTask autoVtTask = + new AutoVersionTrackingTask(controller, session, 1.0, 10.0); + TaskLauncher.launch(autoVtTask); } public static T getPlugin(PluginTool tool, Class c) { diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingAction.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingAction.java index dd04e025e0..6906cf6e7b 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingAction.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingAction.java @@ -25,10 +25,11 @@ import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTPlugin; import ghidra.util.HTMLUtilities; import ghidra.util.HelpLocation; +import ghidra.util.task.TaskLauncher; import resources.ResourceManager; /** - * This action runs the {@link AutoVersionTrackingCommand} + * This action runs the {@link AutoVersionTrackingTask} */ public class AutoVersionTrackingAction extends DockingAction { public static Icon AUTO_VT_ICON = ResourceManager.loadImage("images/wizard.png"); @@ -59,18 +60,16 @@ public class AutoVersionTrackingAction extends DockingAction { VTSession session = controller.getSession(); - // In the future we might want to make these user options so the user can change them - // I don't want to make this change until the confidence option in the reference - // correlators is changed to make more sense to the user - currently the confidence has - // to be entered as the value before the log 10 is computed but the table shows log 10 value - + // In the future we might want to make these user options so the user can change them. + // We don't want to make this change until the confidence option in the reference + // correlators is changed to make more sense to the user - currently the confidence has + // to be entered as the value before the log 10 is computed but the table shows log 10 value. + // // The current passed values for score and confidence (.95 and 10.0) // get you accepted matches with similarity scores >= .95 and // confidence (log 10) scores 2.0 and up - AutoVersionTrackingCommand command = - new AutoVersionTrackingCommand(controller, session, 0.95, 10.0); - - controller.getTool().executeBackgroundCommand(command, session); + AutoVersionTrackingTask task = new AutoVersionTrackingTask(controller, session, 0.95, 10.0); + TaskLauncher.launch(task); } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingCommand.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingTask.java similarity index 76% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingCommand.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingTask.java index 92eed54d3d..2ee3ade962 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingCommand.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AutoVersionTrackingTask.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. @@ -17,6 +17,8 @@ package ghidra.feature.vt.gui.actions; import java.util.*; +import javax.swing.SwingConstants; + import ghidra.feature.vt.api.correlator.program.*; import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.util.VTAssociationStatusException; @@ -24,8 +26,6 @@ import ghidra.feature.vt.api.util.VTOptions; import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.task.ApplyMarkupItemTask; import ghidra.feature.vt.gui.util.MatchInfo; -import ghidra.framework.cmd.BackgroundCommand; -import ghidra.framework.model.DomainObject; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; @@ -36,12 +36,12 @@ import ghidra.program.util.ListingDiff; import ghidra.util.Msg; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; +import ghidra.util.task.*; import util.CollectionUtils; /** * This command runs all of the exact {@link VTProgramCorrelator}s that return - * unique matches (ie only one of each match is found in each program): + * unique matches (i.e., only one of each match is found in each program): *
    *
  1. Exact Symbol Name correlator
  2. *
  3. Exact Data correlator
  4. @@ -65,8 +65,9 @@ import util.CollectionUtils; *

    As more techniques get developed, more automation will be added to this command. * */ -public class AutoVersionTrackingCommand extends BackgroundCommand { +public class AutoVersionTrackingTask extends Task { + private static final String NAME = "Auto Version Tracking Command"; private VTSession session; private Program sourceProgram; private Program destinationProgram; @@ -78,7 +79,7 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { private double minCombinedReferenceCorrelatorConfidence; private final ToolOptions applyOptions; private String statusMsg = null; - private static int NUM_CORRELATORS = 7; + private static int NUM_CORRELATORS = 8; /** * Constructor for AutoVersionTrackingCommand @@ -92,9 +93,10 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { * @param minCombinedReferenceCorrelatorConfidence The minimum confidence used to limit matches * created by the Combined Reference Correlator. */ - public AutoVersionTrackingCommand(VTController controller, VTSession session, + public AutoVersionTrackingTask(VTController controller, VTSession session, double minCombinedReferenceCorrelatorScore, double minCombinedReferenceCorrelatorConfidence) { + super(NAME, true, true, true); this.session = session; this.sourceProgram = session.getSourceProgram(); this.destinationProgram = session.getDestinationProgram(); @@ -103,119 +105,160 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { this.minCombinedReferenceCorrelatorScore = minCombinedReferenceCorrelatorScore; this.minCombinedReferenceCorrelatorConfidence = minCombinedReferenceCorrelatorConfidence; this.applyOptions = controller.getOptions(); + } @Override - public boolean applyTo(DomainObject obj, TaskMonitor monitor) { + public int getStatusTextAlignment() { + return SwingConstants.LEADING; + } + + @Override + public void run(TaskMonitor monitor) throws CancelledException { + + boolean error = true; + int id = session.startTransaction(NAME); + try { + session.setEventsEnabled(false); + doRun(monitor); + error = false; + } + catch (CancelledException e) { + error = false; // allow work performed so far to be saved + } + finally { + session.setEventsEnabled(true); + session.endTransaction(id, !error); + } + } + + private void doRun(TaskMonitor realMonitor) throws CancelledException { + + SubTaskMonitor monitor = new SubTaskMonitor(realMonitor); boolean hasApplyErrors = false; sourceAddressSet = sourceProgram.getMemory().getLoadedAndInitializedAddressSet(); destinationAddressSet = destinationProgram.getMemory().getLoadedAndInitializedAddressSet(); - try { - monitor.setMessage("Running Auto Version Tracking"); - monitor.setCancelEnabled(true); - monitor.initialize(NUM_CORRELATORS); - // Use default options for all of the "exact" correlators and passed in options for - // the others. - VTOptions options; + int count = 0; + monitor.doInitialize(NUM_CORRELATORS); - // Run the correlators in the following order: - // Do this one first because we don't want it to find ones that get markup - // applied by later correlators - VTProgramCorrelatorFactory factory = new SymbolNameProgramCorrelatorFactory(); + // Use default options for all of the "exact" correlators; passed in options for the others + VTOptions options; + + // Run the correlators in the following order: + // Do this one first because we don't want it to find ones that get markup applied by later + // correlators + VTProgramCorrelatorFactory factory = new SymbolNameProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + String prefix = "%s correlation (%d of " + NUM_CORRELATORS + ") - "; + monitor.setPrefix(String.format(prefix, "Symbol Name", ++count)); + hasApplyErrors = correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + factory = new ExactDataMatchProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + monitor.setPrefix(String.format(prefix, "Exact Data", ++count)); + hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + factory = new ExactMatchBytesProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + monitor.setPrefix(String.format(prefix, "Exact Bytes", ++count)); + hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + factory = new ExactMatchInstructionsProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + monitor.setPrefix(String.format(prefix, "Exact Instructions", ++count)); + hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + factory = new ExactMatchMnemonicsProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + monitor.setPrefix(String.format(prefix, "Exact Mnemonic", ++count)); + hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + // This is the first of the "speculative" post-correlator match algorithm. The correlator + // returns all duplicate function instruction matches so there will always be more + // than one possible match for each function. The compare mechanism used by the + // function compare window determines matches based on matching operand values. + // Given that each function must contains the same instructions to even become a match, + // and the compare function mechanism has been very well tested, the mechanism for + // finding the correct match is very accurate. + factory = new DuplicateFunctionMatchProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + + monitor.setPrefix(String.format(prefix, "Duplicate Function", ++count)); + hasApplyErrors |= correlateAndPossiblyApplyDuplicateFunctions(factory, options, monitor); + monitor.doIncrementProgress(); + + // The rest are mores speculative matching algorithms because they depend on our + // choosing the correct score/confidence pair to determine very probable matches. These + // values were chosen based on what has been seen so far but this needs to be tested + // further on more programs and possibly add options for users to give their own thresholds. + + // Get the names of the confidence and similarity score thresholds that + // are used by all of the "reference" correlators + String confidenceOption = VTAbstractReferenceProgramCorrelatorFactory.CONFIDENCE_THRESHOLD; + String scoreOption = VTAbstractReferenceProgramCorrelatorFactory.SIMILARITY_THRESHOLD; + + // Get the number of data and function matches + int numDataMatches = getNumberOfDataMatches(monitor); + int numFunctionMatches = getNumberOfFunctionMatches(monitor); + + // Run the DataReferenceCorrelator if there are accepted data matches but no accepted + // function matches + if (numDataMatches > 0 && numFunctionMatches == 0) { + factory = new DataReferenceProgramCorrelatorFactory(); options = factory.createDefaultOptions(); - hasApplyErrors = correlateAndPossiblyApply(factory, options, monitor); + options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); + options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); - factory = new ExactDataMatchProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); + monitor.setPrefix(String.format(prefix, "Data Reference", ++count)); + hasApplyErrors = hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); - factory = new ExactMatchBytesProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); - - factory = new ExactMatchInstructionsProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); - - factory = new ExactMatchMnemonicsProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - hasApplyErrors |= correlateAndPossiblyApply(factory, options, monitor); - - // This is the first of the "speculative" post-correlator match algorithm. The correlator - // returns all duplicate function instruction matches so there will always be more - // than one possible match for each function. The compare mechanism used by the - // function compare window determines matches based on matching operand values. - // Given that each function must contains the same instructions to even become a match, - // and the compare function mechanism has been very well tested, the mechanism for - // finding the correct match is very accurate. - factory = new DuplicateFunctionMatchProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - hasApplyErrors |= - correlateAndPossiblyApplyDuplicateFunctions(factory, options, monitor); - - // The rest are mores speculative matching algorithms because they depend on our - // choosing the correct score/confidence pair to determine very probable matches. These - // values were chosen based on what has been seen so far but this needs to be tested - // further on more programs and possibly add options for users to - // give their own thresholds. - - // Get the names of the confidence and similarity score thresholds that - // are used by all of the "reference" correlators - String confidenceOption = - VTAbstractReferenceProgramCorrelatorFactory.CONFIDENCE_THRESHOLD; - String scoreOption = VTAbstractReferenceProgramCorrelatorFactory.SIMILARITY_THRESHOLD; - - // Get the number of data and function matches - int numDataMatches = getNumberOfDataMatches(monitor); - int numFunctionMatches = getNumberOfFunctionMatches(monitor); - - // Run the DataReferenceCorrelator if there are accepted data matches but no accepted - // function matches - if (numDataMatches > 0 && numFunctionMatches == 0) { - factory = new DataReferenceProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); - options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); - hasApplyErrors = - hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); - - // Get the number of data and function matches again if this correlator ran - numDataMatches = getNumberOfDataMatches(monitor); - numFunctionMatches = getNumberOfFunctionMatches(monitor); - } - - // Run the FunctionReferenceCorrelator if there are accepted function matches but - // no accepted data matches - if (numDataMatches > 0 && numFunctionMatches == 0) { - factory = new FunctionReferenceProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); - options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); - factory = new FunctionReferenceProgramCorrelatorFactory(); - hasApplyErrors = - hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); - - // Get the number of data and function matches again if this correlator ran - numDataMatches = getNumberOfDataMatches(monitor); - numFunctionMatches = getNumberOfFunctionMatches(monitor); - } - - // Run the CombinedDataAndFunctionReferenceCorrelator if there are both accepted function matches but - // and data matches - if (numDataMatches > 0 && numFunctionMatches > 0) { - factory = new CombinedFunctionAndDataReferenceProgramCorrelatorFactory(); - options = factory.createDefaultOptions(); - options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); - options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); - hasApplyErrors = - hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); - } + // Get the number of data and function matches again if this correlator ran + numDataMatches = getNumberOfDataMatches(monitor); + numFunctionMatches = getNumberOfFunctionMatches(monitor); } - catch (CancelledException e) { - statusMsg = getName() + " was cancelled."; - return false; + + // Run the FunctionReferenceCorrelator if there are accepted function matches but no + // accepted data matches + if (numDataMatches > 0 && numFunctionMatches == 0) { + factory = new FunctionReferenceProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); + options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); + factory = new FunctionReferenceProgramCorrelatorFactory(); + + monitor.setPrefix(String.format(prefix, "Function Reference", ++count)); + hasApplyErrors = hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); + + // Get the number of data and function matches again if this correlator ran + numDataMatches = getNumberOfDataMatches(monitor); + numFunctionMatches = getNumberOfFunctionMatches(monitor); + } + + // Run the CombinedDataAndFunctionReferenceCorrelator if there are both accepted function + // matches but and data matches + if (numDataMatches > 0 && numFunctionMatches > 0) { + factory = new CombinedFunctionAndDataReferenceProgramCorrelatorFactory(); + options = factory.createDefaultOptions(); + options.setDouble(confidenceOption, minCombinedReferenceCorrelatorConfidence); + options.setDouble(scoreOption, minCombinedReferenceCorrelatorScore); + + monitor.setPrefix(String.format(prefix, "Function and Data", ++count)); + hasApplyErrors = hasApplyErrors | correlateAndPossiblyApply(factory, options, monitor); + monitor.doIncrementProgress(); } String applyMarkupStatus = " with no apply markup errors."; @@ -223,13 +266,9 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { applyMarkupStatus = " with some apply markup errors. See the log or the markup table for more details"; } - statusMsg = - - getName() + " completed successfully" + applyMarkupStatus; + statusMsg = NAME + " completed successfully" + applyMarkupStatus; controller.getTool().setStatusInfo(statusMsg); - - return true; } private int getNumberOfDataMatches(TaskMonitor monitor) throws CancelledException { @@ -288,7 +327,7 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { sourceAddressSet, destinationProgram, destinationAddressSet, options); VTMatchSet results = correlator.correlate(session, monitor); - + monitor.initialize(results.getMatchCount()); boolean hasMarkupErrors = applyMatches(results.getMatches(), correlator.getName(), monitor); monitor.incrementProgress(1); @@ -318,6 +357,7 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { sourceAddressSet, destinationProgram, destinationAddressSet, options); VTMatchSet results = correlator.correlate(session, monitor); + monitor.initialize(results.getMatchCount()); boolean hasMarkupErrors = applyDuplicateFunctionMatches(results.getMatches(), monitor); monitor.incrementProgress(1); @@ -363,6 +403,7 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { ApplyMarkupItemTask markupTask = new ApplyMarkupItemTask(controller.getSession(), markupItems, applyOptions); + markupTask.run(monitor); boolean currentMatchHasErrors = markupTask.hasErrors(); if (currentMatchHasErrors) { @@ -387,19 +428,19 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { return true; } catch (VTAssociationStatusException e) { - Msg.warn(AutoVersionTrackingCommand.class, + Msg.warn(AutoVersionTrackingTask.class, "Could not set match accepted for " + association, e); return false; } } /** - * Accept matches and apply markup for duplicate function instruction matches with matching operands - * if they are a unique match within their associated set. + * Accept matches and apply markup for duplicate function instruction matches with matching + * operands if they are a unique match within their associated set. * @param matches A collection of version tracking matches from the duplicate instruction - * matcher. + * matcher * @param monitor Allows user to cancel - * @return true if any markup errors, false if no markup errors. + * @return true if any markup errors, false if no markup errors * @throws CancelledException if cancelled */ private boolean applyDuplicateFunctionMatches(Collection matches, TaskMonitor monitor) @@ -409,19 +450,22 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { boolean someMatchesHaveMarkupErrors = false; Set copyOfMatches = new HashSet<>(matches); - // Process matches in related sets of matches - for (VTMatch match : matches) { + String message = "Processing match %d of %d..."; + int n = matches.size(); + Iterator it = matches.iterator(); + for (int i = 0; it.hasNext(); i++) { monitor.checkCanceled(); + monitor.setMessage(String.format(message, i, n)); - // if match has already been removed (ie it was in a set that was already processed) - // then skip it + VTMatch match = it.next(); + + // skip if match has already been removed (it was in a set that was already processed) if (!copyOfMatches.contains(match)) { continue; } - // get a set of related matches from the set of all matches - // ie these all have the same instructions as each other but not necessarily - // the same operands. + // Get a set of related matches from the set of all matches. These all have the same + // instructions as each other but not necessarily the same operands. Set relatedMatches = getRelatedMatches(match, matches, monitor); // remove related matches from the set of matches to process next time @@ -440,19 +484,19 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { Set

    uniqueDestFunctionAddresses = dedupMatchingFunctions(destinationProgram, destAddresses, monitor); - // Keep only matches containing the unique sources and destination functions determined above + // Keep only matches containing unique source and destination functions from above Set dedupedMatches = getMatches(relatedMatches, uniqueSourceFunctionAddresses, uniqueDestFunctionAddresses, monitor); - // Loop through all the source functions + // loop through all the source functions for (Address sourceAddress : uniqueSourceFunctionAddresses) { monitor.checkCanceled(); - // Find all destination functions with equivalent operands to current source function + // find all destination functions with equivalent operands to source function Set matchesWithEquivalentOperands = getMatchesWithEquivalentOperands( dedupedMatches, sourceAddress, uniqueDestFunctionAddresses, monitor); - // If there is just one equivalent match try to accept the match and apply markup + // if there is just one equivalent match try to accept the match and apply markup if (matchesWithEquivalentOperands.size() == 1) { VTMatch theMatch = CollectionUtils.any(matchesWithEquivalentOperands); someMatchesHaveMarkupErrors |= @@ -814,7 +858,7 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { * @return Set of addresses of deduped function bytes * @throws CancelledException if cancelled */ - public Set
    dedupMatchingFunctions(Program program, Set
    addresses, + private Set
    dedupMatchingFunctions(Program program, Set
    addresses, TaskMonitor monitor) throws CancelledException { FunctionManager functionManager = program.getFunctionManager(); @@ -859,13 +903,54 @@ public class AutoVersionTrackingCommand extends BackgroundCommand { return uniqueFunctionAddresses; } - @Override public String getStatusMsg() { return statusMsg; } - @Override - public String getName() { - return "Auto Version Tracking Command"; + /** A task monitor that allows us to control the message content and the progress */ + private class SubTaskMonitor extends WrappingTaskMonitor { + + private String prefix; + + SubTaskMonitor(TaskMonitor delegate) { + super(delegate); + } + + void setPrefix(String prefix) { + this.prefix = prefix; + } + + void doIncrementProgress() { + super.incrementProgress(1); + } + + void doInitialize(long max) { + super.initialize(max); + } + + @Override + public void setMessage(String message) { + super.setMessage(prefix + message); + } + + @Override + public void initialize(long max) { + // we control the max value + } + + @Override + public synchronized void setMaximum(long max) { + // we control the max value + } + + @Override + public void setProgress(long value) { + // we control the progress + } + + @Override + public void incrementProgress(long incrementAmount) { + // we control progress + } } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java index c10fdc572a..872078b8b1 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java @@ -82,7 +82,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter private SelectionOverrideMemento selectionMemento; private boolean filteringFrozen; - // a selection we may have to set later, after the table has finished loading + // a selection we may have to set later, after the table has finished loading private VTMatch pendingMatchSelection; private SwingUpdateManager selectMatchUpdateManager; private MatchTableSelectionAction tableSelectionStateAction; @@ -182,8 +182,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter int filteredCount = matchesTableModel.getRowCount(); int unfilteredCount = matchesTableModel.getUnfilteredRowCount(); int filteredOutCount = unfilteredCount - filteredCount; - ancillaryFilterButton.setToolTipText( - "More Filters - " + filteredOutCount + " item(s) hidden"); + ancillaryFilterButton + .setToolTipText("More Filters - " + filteredOutCount + " item(s) hidden"); } else { ancillaryFilterButton.setToolTipText("More Filters - no active filters"); @@ -233,9 +233,9 @@ public class VTMatchTableProvider extends ComponentProviderAdapter int unfilteredCount = matchesTableModel.getUnfilteredRowCount(); String sessionName = controller.getVersionTrackingSessionName(); - StringBuffer buffy = new StringBuffer(); - buffy.append("[Session: ").append(sessionName).append("] "); - buffy.append('-').append(filteredCount).append(" matches"); + StringBuilder buffy = new StringBuilder(); + buffy.append("[Session: ").append(sessionName).append("] - "); + buffy.append(filteredCount).append(" matches"); if (filteredCount != unfilteredCount) { buffy.append(" (of ").append(unfilteredCount).append(')'); } @@ -289,7 +289,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter selectMatchUpdateManager.updateLater(); } else { - // for any selection that is not handled by the match changing we want to + // for any selection that is not handled by the match changing we want to // notify that context has changed so that actions properly update notifyContextChanged(); } @@ -307,14 +307,14 @@ public class VTMatchTableProvider extends ComponentProviderAdapter int sourceLabelColumnIndex = matchesTableModel.getColumnIndex(SourceLabelTableColumn.class); TableColumn sourceLabelColumn = columnModel.getColumn(sourceLabelColumnIndex); - sourceLabelColumn.setCellRenderer( - new VTSymbolRenderer(controller.getServiceProvider(), table)); + sourceLabelColumn + .setCellRenderer(new VTSymbolRenderer(controller.getServiceProvider(), table)); int destinationLabelColumnIndex = matchesTableModel.getColumnIndex(DestinationLabelTableColumn.class); TableColumn destinationLabelColumn = columnModel.getColumn(destinationLabelColumnIndex); - destinationLabelColumn.setCellRenderer( - new VTSymbolRenderer(controller.getServiceProvider(), table)); + destinationLabelColumn + .setCellRenderer(new VTSymbolRenderer(controller.getServiceProvider(), table)); int statusColumnIndex = matchesTableModel.getColumnIndex(StatusTableColumn.class); TableColumn statusColumn = columnModel.getColumn(statusColumnIndex); @@ -352,8 +352,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter innerPanel.add(lengthFilterPanel); ancillaryFilterButton = new JButton(UNFILTERED_ICON); - ancillaryFilterButton.addActionListener( - e -> tool.showDialog(ancillaryFilterDialog, component)); + ancillaryFilterButton + .addActionListener(e -> tool.showDialog(ancillaryFilterDialog, component)); ancillaryFilterButton.setToolTipText("Filters Dialog"); HelpService helpService = DockingWindowManager.getHelpService(); HelpLocation filterHelpLocation = @@ -405,7 +405,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter int row = matchesTableModel.getRowIndex(match); if (row < 0) { pendingMatchSelection = match; - // this happen while reloading. If so, save the match and listen for + // this happen while reloading. If so, save the match and listen for // the table data changed and restore the selection at that point return; } @@ -517,7 +517,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter } if (matchesContextChanged) { - // Now that all records have been processed, + // Now that all records have been processed, // since the match table changed perform a reload to apply filters. reload(); @@ -695,8 +695,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter "should become ignored by applying a match."); vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME) - .registerOptionsEditor( - new ApplyMarkupPropertyEditor(controller)); + .registerOptionsEditor(new ApplyMarkupPropertyEditor(controller)); vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS) .setOptionsHelpLocation( new HelpLocation("VersionTracking", "Apply Markup Options")); @@ -709,17 +708,15 @@ public class VTMatchTableProvider extends ComponentProviderAdapter vtOptions.setOptionsHelpLocation(applyOptionsHelpLocation); vtOptions.getOptions(ACCEPT_MATCH_OPTIONS_NAME) - .setOptionsHelpLocation( - applyMatchOptionsHelpLocation); + .setOptionsHelpLocation(applyMatchOptionsHelpLocation); vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME) - .setOptionsHelpLocation( - applyMatchOptionsHelpLocation); + .setOptionsHelpLocation(applyMatchOptionsHelpLocation); } //================================================================================================== // FilterDialogModel Methods -//================================================================================================== +//================================================================================================== @Override public void addFilter(Filter filter) { @@ -728,7 +725,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter matchesTableModel.addFilter(filter); } - /** + /** * Forces a refilter, even though filtering operations may be disabled. The reload * is necessary since the model contents may have changed */ @@ -794,11 +791,11 @@ public class VTMatchTableProvider extends ComponentProviderAdapter /** * A class meant to override the default table selection behavior in special situations. *

    - * Issue 1: Accepting or applying a match can trigger the match to be filtered out + * Issue 1: Accepting or applying a match can trigger the match to be filtered out * of the table. The default SelectionManager does not restore the selection for that item, * as it knows that the item is gone. *

    - * Issue 2: Accepting or applying a match can trigger the match to be moved due to a + * Issue 2: Accepting or applying a match can trigger the match to be moved due to a * sort operation after the edit. *

    * Desired Behavior: Have the selection restored to the previous location, even if the @@ -847,18 +844,18 @@ public class VTMatchTableProvider extends ComponentProviderAdapter ListSelectionModel selectionModel = matchesTable.getSelectionModel(); int rowToSelect = row; if (row > matchesTableModel.getRowCount()) { - // The model has shrunk. Not sure what the best action is? + // The model has shrunk. Not sure what the best action is? tryToSelectMatch(selectionModel);// this only works if we are tracking by match and not index return; } - // At this point the selection model may still believe that its selection is the + // At this point the selection model may still believe that its selection is the // value we are setting. Calling clearSelection() will kick the model. Without the // kick, the setSelectionInterval() call we make may ultimately have no effect. selectionModel.clearSelection(); if (tableSelectionState == MAINTAIN_SELECTED_ROW_INDEX) { - // In this state we are tracking row selection, so just select the previously + // In this state we are tracking row selection, so just select the previously // selected row. selectionModel.setSelectionInterval(rowToSelect, rowToSelect); matchesTable.scrollToSelectedRow(); @@ -874,7 +871,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter } private void tryToSelectMatch(ListSelectionModel selectionModel) { - // In this state we are tracking the value that was selected and we want to + // In this state we are tracking the value that was selected and we want to // reselect that value. int matchRow = matchesTableModel.getRowIndex(match); if (matchRow >= 0 && matchRow < matchesTableModel.getRowCount()) { @@ -890,8 +887,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter } /** - * Override the built-in SelectionManager so that we can respond to the current table - * selection mode. + * Override the built-in SelectionManager so that we can respond to the current table + * selection mode. */ private class VTMatchTableSelectionManager extends RowObjectSelectionManager { VTMatchTableSelectionManager(JTable table, AbstractSortedTableModel tableModel) { diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/VtTask.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/VtTask.java index 7c8ffcf136..57de59b521 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/VtTask.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/VtTask.java @@ -41,11 +41,8 @@ public abstract class VtTask extends Task { @Override public final void run(TaskMonitor monitor) { - boolean restoreEvents = false; - if (session != null && shouldSuspendSessionEvents()) { - session.setEventsEnabled(false); - restoreEvents = true; - } + boolean restoreEvents = suspendEvents(); + try { success = doWork(monitor); } @@ -62,6 +59,24 @@ public abstract class VtTask extends Task { } } + private boolean suspendEvents() { + + if (session == null) { + return false; // no events to suspend + } + + if (!shouldSuspendSessionEvents()) { + return false; // this task has chosen not to suspend events + } + + if (!session.isSendingEvents()) { + return false; // someone external to this task is managing events + } + + session.setEventsEnabled(false); + return true; + } + /** * Determine if session events should be suspended during task execution. * This can improve performance during task execution at the expense of bulk diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAutoVersionTrackingTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAutoVersionTrackingTest.java index a1c844b7f8..f32f740005 100644 --- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAutoVersionTrackingTest.java +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAutoVersionTrackingTest.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. @@ -19,7 +19,6 @@ import static ghidra.feature.vt.db.VTTestUtils.*; import static org.junit.Assert.*; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.*; @@ -29,7 +28,7 @@ import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.util.VTAssociationStatusException; import ghidra.feature.vt.db.VTTestUtils; import ghidra.feature.vt.gui.VTTestEnv; -import ghidra.feature.vt.gui.actions.AutoVersionTrackingCommand; +import ghidra.feature.vt.gui.actions.AutoVersionTrackingTask; import ghidra.feature.vt.gui.plugin.VTController; import ghidra.framework.options.Options; import ghidra.program.database.ProgramDB; @@ -42,7 +41,7 @@ import ghidra.program.model.symbol.SourceType; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.util.Msg; import ghidra.util.exception.InvalidInputException; -import ghidra.util.task.TaskMonitor; +import ghidra.util.task.TaskLauncher; public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTest { @@ -84,8 +83,7 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe controller = env.getVTController(); // Score .999999 and confidence 10.0 (log10 confidence 2.0) and up - boolean success = runAutoVTCommand(0.999999999, 10.0); - assertTrue("Auto Version Tracking Command failed to run", success); + runAutoVTCommand(0.999999999, 10.0); // verify that the default options are what we expect // if this assert fails then the follow-on tests will probably fail @@ -146,8 +144,7 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe // Score 0.5 and conf threshold 1.0 allow similarity scores of higher than 0.5 for combined // reference correlator and 1.0 and higher for the log 10 confidence score - boolean success = runAutoVTCommand(0.5, 1.0); - assertTrue("Auto Version Tracking Command failed to run", success); + runAutoVTCommand(0.5, 1.0); // verify that the default options are what we expect // if this assert fails then the follow-on tests will probably fail @@ -613,8 +610,7 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe sourceProgram.endTransaction(startTransaction, true); // run Auto VT - boolean success = runAutoVTCommand(1.0, 10.0); - assertTrue("Auto Version Tracking Command failed to run", success); + runAutoVTCommand(1.0, 10.0); // Check that the match we are interested in got accepted String correlator = "Combined Function and Data Reference Match"; @@ -670,8 +666,7 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe sourceProgram.endTransaction(startTransaction, true); // run Auto VT - boolean success = runAutoVTCommand(1.0, 10.0); - assertTrue("Auto Version Tracking Command failed to run", success); + runAutoVTCommand(1.0, 10.0); // Check that the match we are interested in got accepted String correlator = "Duplicate Function Instructions Match"; @@ -735,8 +730,7 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe // Now run the AutoVT command with lower confidence thresholds to allow the match we want to // test in as a match - boolean success = runAutoVTCommand(0.5, 1.0); - assertTrue("Auto Version Tracking Command failed to run", success); + runAutoVTCommand(0.5, 1.0); // Check that the match we are interested in got accepted String correlator = "Combined Function and Data Reference Match"; @@ -829,20 +823,18 @@ public class VTAutoVersionTrackingTest extends AbstractGhidraHeadedIntegrationTe assertEquals(expectedAcceptedMatchCount, getNumAcceptedMatches(vtSession, correlatorName)); } - private boolean runAutoVTCommand(double minReferenceCorrelatorScore, + private void runAutoVTCommand(double minReferenceCorrelatorScore, double minReferenceCorrelatorConfidence) { - AtomicBoolean result = new AtomicBoolean(); - runSwing(() -> { - String transactionName = "Auto Version Tracking Test"; - int startTransaction = session.startTransaction(transactionName); - AutoVersionTrackingCommand vtCommand = new AutoVersionTrackingCommand(controller, - session, minReferenceCorrelatorScore, minReferenceCorrelatorConfidence); - result.set(vtCommand.applyTo(session, TaskMonitor.DUMMY)); + AutoVersionTrackingTask task = new AutoVersionTrackingTask(controller, session, + minReferenceCorrelatorScore, minReferenceCorrelatorConfidence); + TaskLauncher.launch(task); + waitForSession(); + } - session.endTransaction(startTransaction, result.get()); - }); - return result.get(); + private void waitForSession() { + session.flushEvents(); + waitForSwing(); } private VTMatchSet getVTMatchSet(VTSession vtSession, String correlatorName) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java index 978289edb2..282941cea6 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java @@ -74,6 +74,7 @@ public interface DomainObject { /** * Returns whether the object has changed. + * @return whether the object has changed. */ public boolean isChanged(); @@ -87,17 +88,19 @@ public interface DomainObject { /** * Returns true if this object has been marked as Temporary. + * @return true if this object has been marked as Temporary. */ public boolean isTemporary(); /** * Returns true if changes are permitted. + * @return true if changes are permitted. */ public boolean isChangeable(); /** - * Returns true if this object can be saved; a read-only file - * cannot be saved. + * Returns true if this object can be saved; a read-only file cannot be saved. + * @return true if this object can be saved */ public boolean canSave(); @@ -118,8 +121,8 @@ public interface DomainObject { * Saves (i.e., serializes) the current content to a packed file. * @param outputFile packed output file * @param monitor progress monitor - * @throws IOException - * @throws CancelledException + * @throws IOException if an exception occurs + * @throws CancelledException if the user cancels * @throws UnsupportedOperationException if not supported by object implementation */ public void saveToPackedFile(File outputFile, TaskMonitor monitor) @@ -127,9 +130,9 @@ public interface DomainObject { /** * Notify the domain object that the specified consumer is no longer using it. - * When the last consumer invokes this method, the domain object will be closed + * When the last consumer invokes this method, the domain object will be closed * and will become invalid. - * @param consumer the consumer (e.g., tool, plugin, etc) of the domain object + * @param consumer the consumer (e.g., tool, plugin, etc) of the domain object * previously established with the addConsumer method. */ public void release(Object consumer); @@ -149,14 +152,14 @@ public interface DomainObject { /** * Adds a listener that will be notified when this DomainObject is closed. This is meant * for clients to have a chance to cleanup, such as reference removal. - * + * * @param listener the reference to add */ public void addCloseListener(DomainObjectClosedListener listener); /** * Removes the given close listener. - * + * * @param listener the listener to remove. */ public void removeCloseListener(DomainObjectClosedListener listener); @@ -179,11 +182,13 @@ public interface DomainObject { /** * Returns a word or short phrase that best describes or categorizes * the object in terms that a user will understand. + * @return the description */ public String getDescription(); /** * Get the name of this domain object. + * @return the name */ public String getName(); @@ -195,7 +200,7 @@ public interface DomainObject { /** * Get the domain file for this domain object. - * @return the associated domain file + * @return the associated domain file */ public DomainFile getDomainFile(); @@ -207,13 +212,13 @@ public interface DomainObject { */ public boolean addConsumer(Object consumer); - /** + /** * Returns the list of consumers on this domainObject * @return the list of consumers. */ public ArrayList getConsumerList(); - /** + /** * Returns true if the given consumer is using (has open) this domain object. * @param consumer the object to test to see if it is a consumer of this domain object. * @return true if the given consumer is using (has open) this domain object; @@ -229,8 +234,8 @@ public interface DomainObject { *

    * NOTE: when re-enabling events, an event will be sent to the system to signal that * every listener should update. - * - * + * + * * @param enabled true means to enable events */ public void setEventsEnabled(boolean enabled); @@ -238,7 +243,8 @@ public interface DomainObject { /** * Returns true if this object is sending out events as it is changed. The default is * true. You can change this value by calling {@link #setEventsEnabled(boolean)}. - * + * + * @return true if sending events * @see #setEventsEnabled(boolean) */ public boolean isSendingEvents(); @@ -258,26 +264,27 @@ public interface DomainObject { * Returns true if a modification lock can be obtained on this * domain object. Care should be taken with using this method since * this will not prevent another thread from modifying the domain object. + * @return true if can lock */ public boolean canLock(); /** - * Returns true if the domain object currenly has a modification lock enabled. + * Returns true if the domain object currently has a modification lock enabled. + * @return true if locked */ public boolean isLocked(); /** - * Attempt to obtain a modification lock on the domain object. Multiple locks - * may be granted on this domain object, although all lock owners must release their - * lock in a timely fashion. + * Attempt to obtain a modification lock on the domain object. Multiple locks may be granted + * on this domain object, although all lock owners must release their lock in a timely fashion. * @param reason very short reason for requesting lock - * @return true if lock obtained successfully, else false which indicates that a - * modification is in process. + * @return true if lock obtained successfully, else false which indicates that a modification + * is in process. */ public boolean lock(String reason); /** - * Cancels any previous lock and aquires it. + * Cancels any previous lock and acquires it. * @param rollback if true, any changes in made with the previous lock should be discarded. * @param reason very short reason for requesting lock */ @@ -290,7 +297,7 @@ public interface DomainObject { /** * Returns all properties lists contained by this domain object. - * + * * @return all property lists contained by this domain object. */ public List getOptionsNames(); @@ -298,17 +305,20 @@ public interface DomainObject { /** * Get the property list for the given name. * @param propertyListName name of property list + * @return the options */ public Options getOptions(String propertyListName); /** * Returns true if this domain object has been closed as a result of the last release + * @return true if closed */ public boolean isClosed(); /** * Returns true if the user has exclusive access to the domain object. Exclusive access means * either the object is not shared or the user has an exclusive checkout on the object. + * @return true if has exclusive access */ public boolean hasExclusiveAccess();