Allow retracing of stacktraces with classloader and module
For JVM9 and forward, the stack traces can contain classloader and
module names.
Bug: 148917473
Change-Id: I09b6045c015970a5b0bdf43007c111493c71ccc3
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index 8d3e956..89e7c9d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -262,8 +262,10 @@
* <ul>
* <li>at dalvik.system.NativeStart.main(NativeStart.java:99)
* <li>at dalvik.system.NativeStart.main(:99)
- * <li>dalvik.system.NativeStart.main(Foo.java:)
+ * <li>at dalvik.system.NativeStart.main(Foo.java:)
* <li>at dalvik.system.NativeStart.main(Native Method)
+ * <li>at classloader/named_module@version/foo.bar.baz(:20)
+ * <li>at classloader//foo.bar.baz(:20)
* </ul>
*
* <p>Empirical evidence suggests that the "at" string is never localized.
@@ -275,6 +277,8 @@
private final String startingWhitespace;
private final String at;
+ private final String classLoaderName;
+ private final String moduleName;
private final String clazz;
private final String method;
private final String methodAsString;
@@ -285,6 +289,8 @@
private AtLine(
String startingWhitespace,
String at,
+ String classLoaderName,
+ String moduleName,
String clazz,
String method,
String methodAsString,
@@ -293,6 +299,8 @@
boolean isAmbiguous) {
this.startingWhitespace = startingWhitespace;
this.at = at;
+ this.classLoaderName = classLoaderName;
+ this.moduleName = moduleName;
this.clazz = clazz;
this.method = method;
this.methodAsString = methodAsString;
@@ -314,11 +322,13 @@
|| line.charAt(firstNonWhiteSpace + 2) != ' ') {
return null;
}
- int classStartIndex = firstNonWhiteSpaceCharacterFromIndex(line, firstNonWhiteSpace + 2);
- if (classStartIndex >= line.length() || classStartIndex != firstNonWhiteSpace + 3) {
+ int classClassLoaderOrModuleStartIndex =
+ firstNonWhiteSpaceCharacterFromIndex(line, firstNonWhiteSpace + 2);
+ if (classClassLoaderOrModuleStartIndex >= line.length()
+ || classClassLoaderOrModuleStartIndex != firstNonWhiteSpace + 3) {
return null;
}
- int parensStart = firstCharFromIndex(line, classStartIndex, '(');
+ int parensStart = firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '(');
if (parensStart >= line.length()) {
return null;
}
@@ -330,7 +340,7 @@
return null;
}
int methodSeparator = line.lastIndexOf('.', parensStart);
- if (methodSeparator <= classStartIndex) {
+ if (methodSeparator <= classClassLoaderOrModuleStartIndex) {
return null;
}
// Check if we have a filename and position.
@@ -348,11 +358,32 @@
} else {
fileName = line.substring(parensStart + 1, parensEnd);
}
+ String classLoaderName = null;
+ String moduleName = null;
+ int classStartIndex = classClassLoaderOrModuleStartIndex;
+ int classLoaderOrModuleEndIndex =
+ firstCharFromIndex(line, classClassLoaderOrModuleStartIndex, '/');
+ if (classLoaderOrModuleEndIndex < methodSeparator) {
+ int moduleEndIndex = firstCharFromIndex(line, classLoaderOrModuleEndIndex + 1, '/');
+ if (moduleEndIndex < methodSeparator) {
+ // The stack trace contains both a class loader and module
+ classLoaderName =
+ line.substring(classClassLoaderOrModuleStartIndex, classLoaderOrModuleEndIndex);
+ moduleName = line.substring(classLoaderOrModuleEndIndex + 1, moduleEndIndex);
+ classStartIndex = moduleEndIndex + 1;
+ } else {
+ moduleName =
+ line.substring(classClassLoaderOrModuleStartIndex, classLoaderOrModuleEndIndex);
+ classStartIndex = classLoaderOrModuleEndIndex + 1;
+ }
+ }
String className = line.substring(classStartIndex, methodSeparator);
String methodName = line.substring(methodSeparator + 1, parensStart);
return new AtLine(
line.substring(0, firstNonWhiteSpace),
- line.substring(firstNonWhiteSpace, classStartIndex),
+ line.substring(firstNonWhiteSpace, classClassLoaderOrModuleStartIndex),
+ classLoaderName,
+ moduleName,
className,
methodName,
className + "." + methodName,
@@ -368,6 +399,27 @@
@Override
List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
List<StackTraceLine> lines = new ArrayList<>();
+ String retraceClassLoaderName = classLoaderName;
+ if (retraceClassLoaderName != null) {
+ ClassReference classLoaderReference = Reference.classFromTypeName(retraceClassLoaderName);
+ retraceBase
+ .retrace(classLoaderReference)
+ .forEach(
+ classElement -> {
+ retraceClassAndMethods(
+ retraceBase, verbose, lines, classElement.getClassReference().getTypeName());
+ });
+ } else {
+ retraceClassAndMethods(retraceBase, verbose, lines, retraceClassLoaderName);
+ }
+ return lines;
+ }
+
+ private void retraceClassAndMethods(
+ RetraceBase retraceBase,
+ boolean verbose,
+ List<StackTraceLine> lines,
+ String classLoaderName) {
ClassReference classReference = Reference.classFromTypeName(clazz);
retraceBase
.retrace(classReference)
@@ -380,6 +432,8 @@
new AtLine(
startingWhitespace,
at,
+ classLoaderName,
+ moduleName,
methodReference.getHolderClass().getTypeName(),
methodReference.getMethodName(),
methodDescriptionFromMethodReference(methodReference, verbose),
@@ -390,13 +444,20 @@
: linePosition,
methodElement.getRetraceMethodResult().isAmbiguous()));
});
- return lines;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(startingWhitespace);
sb.append(at);
+ if (classLoaderName != null) {
+ sb.append(classLoaderName);
+ sb.append("/");
+ }
+ if (moduleName != null) {
+ sb.append(moduleName);
+ sb.append("/");
+ }
sb.append(methodAsString);
sb.append("(");
sb.append(fileName);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 2f9bf9d..d4b758e 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -128,11 +128,6 @@
if (line.startsWith(AT_PREFIX)) {
line = line.substring(AT_PREFIX.length());
}
- // TODO(b/148917473): Remove hack.
- if (line.startsWith("java.base/")) {
- originalLine = originalLine.replaceFirst("java.base/", "");
- line = line.substring("java.base/".length());
- }
// Expect only one '(', and only one ')' with an optional ':' in between.
int parenBeginIndex = line.indexOf('(');
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 2d7d0fd..809a521 100644
--- a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -193,7 +194,12 @@
private StackTrace.Builder createStackTraceBuilder() {
StackTrace.Builder builder = StackTrace.builder();
if (canUseRequireNonNull()) {
- builder.addWithoutFileNameAndLineNumber(Objects.class, "requireNonNull");
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
+ builder.addWithoutFileNameAndLineNumber("java.base/java.util.Objects", "requireNonNull");
+ } else {
+ builder.addWithoutFileNameAndLineNumber(Objects.class, "requireNonNull");
+ }
}
return builder;
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index cfc4fba..174dd5e 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
+import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfuscatedRangeToSingleLineStackTrace;
@@ -165,6 +166,12 @@
runRetraceTest(new ObfuscatedRangeToSingleLineStackTrace());
}
+ @Test
+ public void testBootLoaderAndNamedModulesStackTrace() {
+ assumeFalse(useRegExpParsing);
+ runRetraceTest(new NamedModuleStackTrace());
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NamedModuleStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NamedModuleStackTrace.java
new file mode 100644
index 0000000..8bd402f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NamedModuleStackTrace.java
@@ -0,0 +1,56 @@
+// 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.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This is testing the string representation of stack trace elements with built-in class loaders and
+ * named/unnamed modules:
+ * https://docs.oracle.com/javase/10/docs/api/java/lang/StackTraceElement.html#toString()
+ */
+public class NamedModuleStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "SomeFakeException: this is a fake exception",
+ "\tat classloader.a.b.a/named_module@9.0/a.a(:101)",
+ "\tat classloader.a.b.a//a.b(App.java:12)",
+ "\tat named_module@2.1/a.c(Lib.java:80)",
+ "\tat named_module/a.d(Lib.java:81)",
+ "\tat a.e(MyClass.java:9)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.joinLines(
+ "com.android.tools.r8.Classloader -> classloader.a.b.a:",
+ "com.android.tools.r8.Main -> a:",
+ " 101:101:void main(java.lang.String[]):1:1 -> a",
+ " 12:12:void foo(java.lang.String[]):2:2 -> b",
+ " 80:80:void bar(java.lang.String[]):3:3 -> c",
+ " 81:81:void baz(java.lang.String[]):4:4 -> d",
+ " 9:9:void qux(java.lang.String[]):5:5 -> e");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "SomeFakeException: this is a fake exception",
+ "\tat com.android.tools.r8.Classloader/named_module@9.0/com.android.tools.r8.Main.main(Main.java:1)",
+ "\tat com.android.tools.r8.Classloader//com.android.tools.r8.Main.foo(Main.java:2)",
+ "\tat named_module@2.1/com.android.tools.r8.Main.bar(Main.java:3)",
+ "\tat named_module/com.android.tools.r8.Main.baz(Main.java:4)",
+ "\tat com.android.tools.r8.Main.qux(Main.java:5)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}