From 9b77c004425424a3c7c0906a3a81b52fbad04994 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:47:57 -0400 Subject: [PATCH] GP-6073 - FID - Implemented delayed DB loading to improve tool startup. --- .../feature/fid/analyzer/FidAnalyzer.java | 16 ++-- .../ghidra/feature/fid/db/FidFileManager.java | 90 ++++++++++++++----- .../ghidra/feature/fid/plugin/FidPlugin.java | 30 ++++++- 3 files changed, 105 insertions(+), 31 deletions(-) diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java index ce1cf27eb9..82b92c58be 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.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. @@ -114,19 +114,25 @@ public class FidAnalyzer extends AbstractAnalyzer { @Override public boolean getDefaultEnablement(Program program) { - return service.canProcess(program.getLanguage()); + // Loading Fid db files can be slow. For now, signal that we can analyze and check later. + return true; + //return service.canProcess(program.getLanguage()); } @Override public boolean canAnalyze(Program program) { - return service.canProcess(program.getLanguage()); + // Loading Fid db files can be slow. For now, signal that we can analyze and check later. + return true; + //return service.canProcess(program.getLanguage()); } @Override public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { + if (!service.canProcess(program.getLanguage())) { - Msg.warn(this, "No FID Libraries apply for language " + program.getLanguageID()); + // This can now happen, since we no longer check in canAnalyze() + Msg.debug(this, "No FID Libraries apply for language " + program.getLanguageID()); return false; } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FidFileManager.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FidFileManager.java index f731a3b4f1..f989de0220 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FidFileManager.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FidFileManager.java @@ -27,14 +27,18 @@ import generic.util.Path; import ghidra.framework.Application; import ghidra.framework.preferences.Preferences; import ghidra.program.model.lang.Language; +import ghidra.util.Msg; import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskLauncher; +import ghidra.util.task.TaskMonitor; /** * Manages the set of FidFiles for the application. This uses the singleton pattern and * all users of Fid databases must use this to get open Fid databases. This ensures that - * there is only one updateable Fid database open for any given FidFile. + * there is only one updatable Fid database open for any given FidFile. */ public class FidFileManager { @@ -48,7 +52,7 @@ public class FidFileManager { private WeakSet listeners; /** - * Returns the singleton instance of the FidFileManager. + * {@return the singleton instance of the FidFileManager.} */ public static FidFileManager getInstance() { if (THE_FID_FILE_MANAGER == null) { @@ -59,14 +63,23 @@ public class FidFileManager { private FidFileManager() { listeners = WeakDataStructureFactory.createCopyOnWriteWeakSet(); - // findDeliveredFidFiles(); - too slow - // restoreFromPreferences(); } private Set loadFidFiles() { if (fidFiles == null) { - findDeliveredFidFiles(); - restoreFromPreferences(); + + TaskLauncher.launchModal("Loading Fid Files", monitor -> { + + fidFiles = new CopyOnWriteArraySet<>(); + try { + findDeliveredFidFiles(monitor); + restoreFromPreferences(monitor); + } + catch (CancelledException ce) { + Msg.showWarn(this, null, "Fid Loading Cancelled", "User cancelled Fid Db " + + "loading. To load all Fid Db files, the tool must be restarted."); + } + }); } return fidFiles; @@ -74,7 +87,7 @@ public class FidFileManager { /** * Add user FidDb file - * @param file + * @param file the file * @return FidFile or null if invalid */ public FidFile addUserFidFile(File file) { @@ -105,7 +118,7 @@ public class FidFileManager { } /** - * Returns a list of all the FidFiles know to the application. + * {@return a list of all the FidFiles know to the application.} */ public List getFidFiles() { loadFidFiles(); @@ -114,13 +127,30 @@ public class FidFileManager { return files; } - public boolean hasFidFiles() { - loadFidFiles(); - return !fidFiles.isEmpty(); + /** + * Triggers a load of the Fid db files, if not already loaded. + */ + public void load() { + if (fidFiles == null) { + loadFidFiles(); + } } + /** + * {@return true if Fid db files have been loaded and files have been found.} + */ + public boolean hasFidFiles() { + return fidFiles != null && !fidFiles.isEmpty(); + } + + /** + * {@return true if Fid db files have been loaded and user Fid files have been found.} + */ public boolean hasUserFidFiles() { - loadFidFiles(); + if (fidFiles == null) { + return false; + } + for (FidFile fidFile : fidFiles) { if (!fidFile.isInstalled()) { return true; @@ -130,8 +160,15 @@ public class FidFileManager { } /** - * Returns a list of all the user added (non installation) Fid files. This will - * be files containing packed databases. + * {@return true if the Fid db files have been loaded.} + */ + public boolean hasLoadedFidFiles() { + return fidFiles != null; + } + + /** + * {@return a list of all the user added (non installation) Fid files. This will + * be files containing packed databases.} */ public List getUserAddedFiles() { loadFidFiles(); @@ -251,17 +288,20 @@ public class FidFileManager { return list; } - private void restoreFromPreferences() { + private void restoreFromPreferences(TaskMonitor monitor) throws CancelledException { Set userAddedFiles = getFilesFromPreference(USER_ADDED_FILES); - addUserFidFiles(userAddedFiles); + monitor.initialize(userAddedFiles.size(), "Adding user Fid files..."); + doAddUserFidFiles(userAddedFiles, monitor); Set excludedFiles = getFilesFromPreference(INACTIVE_FID_FILES); - excludeFidFiles(excludedFiles); + monitor.initialize(excludedFiles.size(), "Removing inactive user Fid files..."); + doExcludeFidFiles(excludedFiles, monitor); } - private void addUserFidFiles(Set userAddedFiles) { - loadFidFiles(); + private void doAddUserFidFiles(Set userAddedFiles, TaskMonitor monitor) + throws CancelledException { for (File file : userAddedFiles) { + monitor.increment(); FidFile fidFile = new FidFile(this, file, false); if (fidFile.isValidFile()) { fidFiles.add(fidFile); @@ -269,9 +309,10 @@ public class FidFileManager { } } - private void excludeFidFiles(Set excludedFiles) { - loadFidFiles(); + private void doExcludeFidFiles(Set excludedFiles, TaskMonitor monitor) + throws CancelledException { for (FidFile fidFile : fidFiles) { + monitor.increment(); if (excludedFiles.contains(fidFile.getFile())) { fidFile.setActive(false); } @@ -296,11 +337,14 @@ public class FidFileManager { return set; } - private void findDeliveredFidFiles() { - fidFiles = new CopyOnWriteArraySet<>(); + private void findDeliveredFidFiles(TaskMonitor monitor) throws CancelledException { + List foundFiles = Application.findFilesByExtensionInApplication(FidFile.FID_RAW_DATABASE_FILE_EXTENSION); + + monitor.initialize(foundFiles.size(), "Processing included Fid files..."); for (ResourceFile resourceFile : foundFiles) { + monitor.increment(); File file = resourceFile.getFile(true); FidFile fidFile = new FidFile(this, file, true); if (fidFile.isValidFile()) { diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java index 67ce059209..34c6d14bd0 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java @@ -94,7 +94,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { */ private void createStandardActions() { new ActionBuilder("Choose Active FidDbs", getName()) - .enabledWhen(ac -> fidFileManager.hasFidFiles()) + .enabledWhen(ac -> enabledForAnyFidFiles()) .onAction(ac -> chooseActiveFidDbs()) .menuPath(ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Choose active FidDbs...") .menuGroup(MENU_GROUP_1, "1") @@ -121,7 +121,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { .buildAndInstall(tool); new ActionBuilder("Detach attached FidDb", getName()) - .enabledWhen(ac -> fidFileManager.hasUserFidFiles()) + .enabledWhen(ac -> enabledForUserFidFiles()) .onAction(ac -> removeFidFile()) .menuPath(ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Detach attached FidDb...") .menuGroup(MENU_GROUP_1, "4") @@ -130,8 +130,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { .buildAndInstall(tool); new ActionBuilder("Populate FidDb from programs", getName()) - .enabledWhen(ac -> fidFileManager.hasUserFidFiles()) + .enabledWhen(ac -> enabledForUserFidFiles()) .onAction(ac -> { + fidFileManager.load(); PopulateFidDialog populateFidDialog = new PopulateFidDialog(tool, service); tool.showDialog(populateFidDialog); }) @@ -143,11 +144,30 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { .buildAndInstall(tool); } + private boolean enabledForAnyFidFiles() { + if (!fidFileManager.hasLoadedFidFiles()) { + // We haven't loaded Fid files yet. Since we don't know if we can enable, return true + // so users can at least try to perform the action. + return true; + } + return fidFileManager.hasFidFiles(); + } + + private boolean enabledForUserFidFiles() { + if (!fidFileManager.hasLoadedFidFiles()) { + // We haven't loaded Fid files yet. Since we don't know if we can enable, return true + // so users can at least try to perform the action. + return true; + } + return fidFileManager.hasUserFidFiles(); + } + /** * Method to select which known FID databases are currently active * during search. */ private synchronized void chooseActiveFidDbs() { + fidFileManager.load(); ActiveFidConfigureDialog dialog = new ActiveFidConfigureDialog(fidFileManager.getFidFiles()); tool.showDialog(dialog); @@ -159,6 +179,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { * extension (.fidb). If they don't, we will add it for them. */ private void createFidDb() { + fidFileManager.load(); File dbFile = askFile("Create new FidDb file", "Create"); if (dbFile == null) { return; @@ -186,6 +207,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { * Method to attach an already-created (but heretofore unknown) database. */ private void attachFidDb() { + fidFileManager.load(); File dbFile = askFile("Attach existing FidDb file", "Attach"); if (dbFile != null) { fidFileManager.addUserFidFile(dbFile); @@ -196,6 +218,8 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { * Method to "forget" about (close and stop trying to re-open next session) a FID database. */ private void removeFidFile() { + fidFileManager.load(); + FidFile fidFile = askChoice("Choose FidDb to detach", "Please choose the FidDb to detach", fidFileManager.getUserAddedFiles(), null); if (fidFile != null) {