diff --git a/src/main/java/ai/reveng/toolkit/ghidra/core/tasks/StartAnalysisTask.java b/src/main/java/ai/reveng/toolkit/ghidra/core/tasks/StartAnalysisTask.java index 8e4d41a5..8296937f 100644 --- a/src/main/java/ai/reveng/toolkit/ghidra/core/tasks/StartAnalysisTask.java +++ b/src/main/java/ai/reveng/toolkit/ghidra/core/tasks/StartAnalysisTask.java @@ -6,8 +6,11 @@ import ai.reveng.toolkit.ghidra.core.services.api.AnalysisOptionsBuilder; import ai.reveng.toolkit.ghidra.core.services.api.GhidraRevengService; import ai.reveng.toolkit.ghidra.core.services.api.types.AnalysisStatus; +import ai.reveng.toolkit.ghidra.core.services.logging.ReaiLoggingService; +import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Program; +import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.Task; import ghidra.util.task.TaskMonitor; @@ -42,20 +45,35 @@ public StartAnalysisTask(Program program, @Override public void run(TaskMonitor monitor) throws CancelledException { + ReaiLoggingService loggingService = tool.getService(ReaiLoggingService.class); + monitor.setMessage("Uploading Binary"); - reService.upload(program); - monitor.setMessage("Exporting Function Boundaries"); + try { + reService.upload(program); + } catch (RuntimeException e) { + reportFailure(monitor, loggingService, "Failed to upload binary", e); + return; + } monitor.setMessage("Sending Analysis Request"); - GhidraRevengService.ProgramWithID programWithID; try { - programWithID = reService.startAnalysis(program, options); + programWithID = reService.startAnalysis(program, options); } catch (ApiException e) { - monitor.setMessage("Analysis Request Failed"); + reportFailure(monitor, loggingService, "Analysis request was rejected by the server", e); + return; + } catch (RuntimeException e) { + reportFailure(monitor, loggingService, "Failed to start analysis", e); return; } + String successMessage = "Analysis started successfully (analysis ID %s)" + .formatted(programWithID.analysisID().id()); + monitor.setMessage(successMessage); + if (loggingService != null) { + loggingService.info(successMessage); + } + tool.firePluginEvent(new RevEngAIAnalysisStatusChangedEvent( "StartAnalysisTask", programWithID, @@ -63,6 +81,34 @@ public void run(TaskMonitor monitor) throws CancelledException { ); } + private void reportFailure(TaskMonitor monitor, + ReaiLoggingService loggingService, + String context, + Throwable error) { + String message = context + ": " + describe(error); + monitor.setMessage("Analysis Request Failed"); + if (loggingService != null) { + loggingService.error(message); + } + Msg.error(this, message, error); + Msg.showError(this, null, + ReaiPluginPackage.WINDOW_PREFIX + "Failed to start analysis", + message, error); + } + + private static String describe(Throwable error) { + Throwable cause = error; + if (!(cause instanceof ApiException) && cause.getCause() instanceof ApiException) { + cause = cause.getCause(); + } + if (cause instanceof ApiException apiException) { + String body = apiException.getResponseBody(); + String detail = (body != null && !body.isBlank()) ? body : apiException.getMessage(); + return "HTTP " + apiException.getCode() + " — " + detail; + } + return cause.getMessage() != null ? cause.getMessage() : cause.toString(); + } + @Override public boolean getWaitForTaskCompleted() { return super.getWaitForTaskCompleted(); diff --git a/src/main/java/ai/reveng/toolkit/ghidra/plugins/AnalysisManagementPlugin.java b/src/main/java/ai/reveng/toolkit/ghidra/plugins/AnalysisManagementPlugin.java index 2573bf71..cda6bea0 100644 --- a/src/main/java/ai/reveng/toolkit/ghidra/plugins/AnalysisManagementPlugin.java +++ b/src/main/java/ai/reveng/toolkit/ghidra/plugins/AnalysisManagementPlugin.java @@ -151,9 +151,10 @@ private void setupActions() { var builder = TaskBuilder.withTask(task); analysisLogComponent.setVisible(true); builder.launchInBackground(analysisLogComponent.getTaskMonitor()); - // The task will fire the RevEngAIAnalysisStatusChangedEvent event when done - // which is then picked up by the AnalysisManagementPlugin and forwarded to the AnalysisLogComponent - tool.getService(ReaiLoggingService.class).info("Started analysis: "); + // The task runs in the background and logs the actual outcome (success or failure) + // itself. On success it fires the RevEngAIAnalysisStatusChangedEvent, which is picked + // up by the AnalysisManagementPlugin and forwarded to the AnalysisLogComponent. + tool.getService(ReaiLoggingService.class).info("Launching analysis task in background"); } else { // User clicked Cancel tool.getService(ReaiLoggingService.class).info("Create new analysis dialog cancelled by user");