[Retrace] Add fake stack entry with version to retrace exceptions

Bug: 171478943
Change-Id: I5c1ce93408da575976cc400cc2e4ae28e72faf22
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index bd537a6..90c1cca 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.retrace;
 
 import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR;
+import static com.android.tools.r8.utils.ExceptionUtils.failWithFakeEntry;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.DiagnosticsHandler;
@@ -234,7 +235,7 @@
     }
   }
 
-  public static void run(String[] args) {
+  public static void run(String[] args) throws RetraceFailedException {
     // To be compatible with standard retrace and remapper, we translate -arg into --arg.
     String[] mappedArgs = new String[args.length];
     boolean printInfo = false;
@@ -255,14 +256,23 @@
     }
     RetraceDiagnosticsHandler retraceDiagnosticsHandler =
         new RetraceDiagnosticsHandler(new DiagnosticsHandler() {}, printInfo);
-    Builder builder = parseArguments(mappedArgs, retraceDiagnosticsHandler);
+    try {
+      run(mappedArgs, retraceDiagnosticsHandler);
+    } catch (Throwable t) {
+      throw failWithFakeEntry(
+          retraceDiagnosticsHandler, t, RetraceFailedException::new, RetraceAbortException.class);
+    }
+  }
+
+  private static void run(String[] args, DiagnosticsHandler diagnosticsHandler) {
+    Builder builder = parseArguments(args, diagnosticsHandler);
     if (builder == null) {
       // --help or --version was an argument to list
-      if (Arrays.asList(mappedArgs).contains("--version")) {
+      if (Arrays.asList(args).contains("--version")) {
         System.out.println("Retrace " + Version.getVersionString());
         return;
       }
-      assert Arrays.asList(mappedArgs).contains("--help");
+      assert Arrays.asList(args).contains("--help");
       System.out.println("Retrace " + Version.getVersionString());
       System.out.print(USAGE_MESSAGE);
       return;
@@ -274,7 +284,7 @@
               printStream.println(line);
             }
           } catch (UnsupportedEncodingException e) {
-            retraceDiagnosticsHandler.error(new StringDiagnostic(e.getMessage()));
+            diagnosticsHandler.error(new StringDiagnostic(e.getMessage()));
           }
         });
     run(builder.build());
@@ -302,21 +312,20 @@
   }
 
   private interface MainAction {
-    void run() throws RetraceAbortException;
+    void run() throws RetraceFailedException;
   }
 
   private static void withMainProgramHandler(MainAction action) {
     try {
       action.run();
-    } catch (RetraceAbortException e) {
+    } catch (RetraceFailedException | RetraceAbortException e) {
       // Detail of the errors were already reported
-      System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
+      System.err.println("Retrace failed");
       System.exit(STATUS_ERROR);
-    } catch (RuntimeException e) {
+      throw null;
+    } catch (Throwable t) {
       System.err.println("Retrace failed with an internal error.");
-      System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
-      Throwable cause = e.getCause() == null ? e : e.getCause();
-      cause.printStackTrace();
+      t.printStackTrace();
       System.exit(STATUS_ERROR);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFailedException.java b/src/main/java/com/android/tools/r8/retrace/RetraceFailedException.java
new file mode 100644
index 0000000..94b61ee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFailedException.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+/**
+ * Exception thrown when retrace failed to complete because of errors previously reported through
+ * {@link com.android.tools.r8.DiagnosticsHandler}.
+ */
+@Keep
+public class RetraceFailedException extends Exception {
+
+  public RetraceFailedException() {
+    super("Retrace failed to complete");
+  }
+
+  public RetraceFailedException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 32ca59a..61aa232 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -76,6 +77,15 @@
 
   private static CompilationFailedException failCompilation(
       Reporter reporter, Throwable topMostException) {
+    return failWithFakeEntry(
+        reporter, topMostException, CompilationFailedException::new, AbortException.class);
+  }
+
+  public static <T extends Exception, A extends Exception> T failWithFakeEntry(
+      DiagnosticsHandler diagnosticsHandler,
+      Throwable topMostException,
+      BiFunction<String, Throwable, T> newException,
+      Class<A> abortException) {
     // Find inner-most cause of the failure and compute origin, position and reported for the path.
     boolean hasBeenReported = false;
     Origin origin = Origin.unknown();
@@ -83,7 +93,7 @@
     List<Throwable> suppressed = new ArrayList<>();
     Throwable innerMostCause = topMostException;
     while (true) {
-      hasBeenReported |= innerMostCause instanceof AbortException;
+      hasBeenReported |= abortException.isAssignableFrom(innerMostCause.getClass());
       Origin nextOrigin = getOrigin(innerMostCause);
       if (nextOrigin != Origin.unknown()) {
         origin = nextOrigin;
@@ -105,7 +115,7 @@
 
     // If no abort is seen, the exception is not reported, so report it now.
     if (!hasBeenReported) {
-      reporter.error(new ExceptionDiagnostic(innerMostCause, origin, position));
+      diagnosticsHandler.error(new ExceptionDiagnostic(innerMostCause, origin, position));
     }
 
     // Build the top-level compiler exception and version stack.
@@ -117,8 +127,7 @@
       message.append(", origin: ").append(origin);
     }
     // Create the final exception object.
-    CompilationFailedException rethrow =
-        new CompilationFailedException(message.toString(), innerMostCause);
+    T rethrow = newException.apply(message.toString(), innerMostCause);
     // Replace its stack by the cause stack and insert version info at the top.
     String filename = "Version_" + Version.LABEL + ".java";
     StackTraceElement versionElement =