mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-07-30 07:18:37 -09:00
Fix debugger module indexing startup hang
This commit is contained in:
@@ -100,13 +100,14 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
super(tool);
|
||||
this.autoWiring = AutoService.wireServicesProvidedAndConsumed(this);
|
||||
this.context = new DebuggerStaticMappingContext(executor);
|
||||
this.programModuleIndexer = new ProgramModuleIndexer(tool);
|
||||
this.programModuleIndexer = new ProgramModuleIndexer(tool, this::executeTask);
|
||||
this.moduleMapProposalGenerator = new ModuleMapProposalGenerator(programModuleIndexer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
tool.getProject().getProjectData().removeDomainFolderChangeListener(this);
|
||||
programModuleIndexer.dispose();
|
||||
executor.close();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -128,6 +129,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
}
|
||||
|
||||
private void checkTraceMapping(Trace trace, TaskMonitor monitor) throws CancelledException {
|
||||
programModuleIndexer.waitForInitialIndex(monitor);
|
||||
|
||||
DebuggerAutoMappingService autoMappingService =
|
||||
tool.getService(DebuggerAutoMappingService.class);
|
||||
if (autoMappingService == null ||
|
||||
@@ -188,6 +191,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
|
||||
private void checkAllTraceMappingsForProgram(Program program, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
programModuleIndexer.waitForInitialIndex(monitor);
|
||||
|
||||
DebuggerAutoMappingService autoMappingService =
|
||||
tool.getService(DebuggerAutoMappingService.class);
|
||||
if (autoMappingService == null ||
|
||||
|
||||
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.service.modules;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -31,6 +33,11 @@ import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.CancelledListener;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
// TODO: Consider making this a front-end plugin?
|
||||
public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
@@ -177,29 +184,103 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
private final Project project;
|
||||
private final ProjectData projectData;
|
||||
private volatile boolean disposed;
|
||||
private final CompletableFuture<Void> initialIndexFuture = new CompletableFuture<>();
|
||||
|
||||
private final Map<Program, ModuleChangeListener> openedForUpdate = new HashMap<>();
|
||||
private final Object indexLock = new Object();
|
||||
private final ModuleIndex index = new ModuleIndex();
|
||||
|
||||
public ProgramModuleIndexer(PluginTool tool) {
|
||||
this(tool, tool::execute);
|
||||
}
|
||||
|
||||
public ProgramModuleIndexer(PluginTool tool, Consumer<Task> taskExecutor) {
|
||||
this.project = tool.getProject();
|
||||
this.projectData = tool.getProject().getProjectData();
|
||||
this.projectData.addDomainFolderChangeListener(this);
|
||||
|
||||
indexFolder(projectData.getRootFolder());
|
||||
startInitialIndex(taskExecutor);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
disposed = true;
|
||||
projectData.removeDomainFolderChangeListener(this);
|
||||
initialIndexFuture.cancel(false);
|
||||
synchronized (openedForUpdate) {
|
||||
for (ModuleChangeListener listener : openedForUpdate.values()) {
|
||||
listener.dispose();
|
||||
}
|
||||
openedForUpdate.clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected void indexFolder(DomainFolder folder) {
|
||||
for (DomainFile file : folder.getFiles()) {
|
||||
addToIndex(file);
|
||||
protected void startInitialIndex(Consumer<Task> taskExecutor) {
|
||||
taskExecutor.accept(new Task("Index Program Modules", true, true, false) {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
indexProject(monitor);
|
||||
initialIndexFuture.complete(null);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
initialIndexFuture.cancel(false);
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
initialIndexFuture.completeExceptionally(e);
|
||||
Msg.error(this, "Failed to index program modules", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void indexProject(TaskMonitor monitor) throws CancelledException {
|
||||
int fileCount = projectData.getFileCount();
|
||||
if (fileCount < 0) {
|
||||
monitor.setIndeterminate(true);
|
||||
}
|
||||
for (DomainFolder sub : folder.getFolders()) {
|
||||
indexFolder(sub);
|
||||
else {
|
||||
monitor.initialize(fileCount);
|
||||
}
|
||||
|
||||
for (DomainFile file : ProjectDataUtils.descendantFiles(projectData.getRootFolder())) {
|
||||
if (disposed) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
monitor.setMessage("Indexing " + file.getPathname());
|
||||
addToIndex(file);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForInitialIndex(TaskMonitor monitor) throws CancelledException {
|
||||
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||
monitor.setMessage("Waiting for program module index");
|
||||
CompletableFuture<Void> cancelled = new CompletableFuture<>();
|
||||
CancelledListener listener = () -> cancelled.cancel(false);
|
||||
monitor.addCancelledListener(listener);
|
||||
try {
|
||||
monitor.checkCancelled();
|
||||
CompletableFuture.anyOf(cancelled, initialIndexFuture).get();
|
||||
initialIndexFuture.get();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new CancelledException();
|
||||
}
|
||||
catch (CancellationException e) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof CancellationException) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
Msg.error(this, "Failed to index program modules", cause);
|
||||
}
|
||||
finally {
|
||||
monitor.removeCancelledListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,28 +318,32 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
}
|
||||
String exeName = exePath == null ? null : new File(exePath).getName();
|
||||
|
||||
for (String modPath : getModulePaths(metadata)) {
|
||||
String modName = new File(modPath).getName();
|
||||
if (!modPath.equals(modName)) {
|
||||
index.addEntry(modPath, dfID, NameSource.MODULE_PATH);
|
||||
synchronized (indexLock) {
|
||||
for (String modPath : getModulePaths(metadata)) {
|
||||
String modName = new File(modPath).getName();
|
||||
if (!modPath.equals(modName)) {
|
||||
index.addEntry(modPath, dfID, NameSource.MODULE_PATH);
|
||||
}
|
||||
index.addEntry(modName, dfID, NameSource.MODULE_NAME);
|
||||
}
|
||||
index.addEntry(modName, dfID, NameSource.MODULE_NAME);
|
||||
}
|
||||
|
||||
index.addEntry(dfName, dfID, NameSource.DOMAIN_FILE_NAME);
|
||||
if (progName != null) {
|
||||
index.addEntry(progName, dfID, NameSource.DOMAIN_FILE_NAME);
|
||||
}
|
||||
if (exeName != null) {
|
||||
if (!exePath.equals(exeName)) {
|
||||
index.addEntry(exePath, dfID, NameSource.PROGRAM_EXECUTABLE_PATH);
|
||||
index.addEntry(dfName, dfID, NameSource.DOMAIN_FILE_NAME);
|
||||
if (progName != null) {
|
||||
index.addEntry(progName, dfID, NameSource.DOMAIN_FILE_NAME);
|
||||
}
|
||||
if (exeName != null) {
|
||||
if (!exePath.equals(exeName)) {
|
||||
index.addEntry(exePath, dfID, NameSource.PROGRAM_EXECUTABLE_PATH);
|
||||
}
|
||||
index.addEntry(exeName, dfID, NameSource.PROGRAM_EXECUTABLE_NAME);
|
||||
}
|
||||
index.addEntry(exeName, dfID, NameSource.PROGRAM_EXECUTABLE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeFromIndex(String fileID) {
|
||||
index.removeFile(fileID);
|
||||
synchronized (indexLock) {
|
||||
index.removeFile(fileID);
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshIndex(DomainFile file) {
|
||||
@@ -396,13 +481,15 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
|
||||
public List<IndexEntry> getBestEntries(TraceModule module, long snap) {
|
||||
String modulePathName = module.getName(snap).toLowerCase();
|
||||
List<IndexEntry> entries = new ArrayList<>(index.getByName(modulePathName));
|
||||
if (!entries.isEmpty()) {
|
||||
synchronized (indexLock) {
|
||||
List<IndexEntry> entries = new ArrayList<>(index.getByName(modulePathName));
|
||||
if (!entries.isEmpty()) {
|
||||
return entries;
|
||||
}
|
||||
String moduleFileName = new File(modulePathName).getName();
|
||||
entries.addAll(index.getByName(moduleFileName));
|
||||
return entries;
|
||||
}
|
||||
String moduleFileName = new File(modulePathName).getName();
|
||||
entries.addAll(index.getByName(moduleFileName));
|
||||
return entries;
|
||||
}
|
||||
|
||||
public DomainFile getBestMatch(AddressSpace space, TraceModule module, long snap,
|
||||
|
||||
Reference in New Issue
Block a user