diff options
author | 2022-12-20 16:25:58 +0000 | |
---|---|---|
committer | 2023-01-17 10:15:09 +0000 | |
commit | d80bbba155fd18a38075cf6f626bee382c2b492c (patch) | |
tree | 90a0282dca2c05f3b3484d7e21de5d4b6213e5b7 /test/141-class-unload/src/Main.java | |
parent | 9e3761d6b98e090cff2e30e0f5e9714f434dd0f2 (diff) |
Keep classes alive for stack traces with copied methods.
Test: Additional test in 141-class-unload.
Test: testrunner.py --host --optimizing
Bug: 263254495
Change-Id: Iea925ab96fb7c8d1b825c0b100e689ab722d3159
Diffstat (limited to 'test/141-class-unload/src/Main.java')
-rw-r--r-- | test/141-class-unload/src/Main.java | 110 |
1 files changed, 94 insertions, 16 deletions
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index 8031dbada8..716f05543c 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -20,6 +20,8 @@ import java.io.FileReader; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.function.Consumer; public class Main { static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar"; @@ -49,6 +51,14 @@ public class Main { testOatFilesUnloaded(getPid()); // Test that objects keep class loader live for sticky GC. testStickyUnload(constructor); + // Test that copied methods recorded in a stack trace prevents unloading. + testCopiedMethodInStackTrace(constructor); + // Test that code preventing unloading holder classes of copied methods recorded in + // a stack trace does not crash when processing a copied method in the boot class path. + testCopiedBcpMethodInStackTrace(); + // Test that code preventing unloading holder classes of copied methods recorded in + // a stack trace does not crash when processing a copied method in an app image. + testCopiedAppImageMethodInStackTrace(); } catch (Exception e) { e.printStackTrace(System.out); } @@ -103,13 +113,12 @@ public class Main { System.out.println(klass2.get()); } - private static void testUnloadLoader(Constructor<?> constructor) - throws Exception { - WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); - // No strong references to class loader, should get unloaded. - doUnloading(); - // If the weak reference is cleared, then it was unloaded. - System.out.println(loader.get()); + private static void testUnloadLoader(Constructor<?> constructor) throws Exception { + WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true); + // No strong references to class loader, should get unloaded. + doUnloading(); + // If the weak reference is cleared, then it was unloaded. + System.out.println(loader.get()); } private static void testStackTrace(Constructor<?> constructor) throws Exception { @@ -138,20 +147,20 @@ public class Main { } static class Pair { - public Pair(Object o, ClassLoader l) { - object = o; - classLoader = new WeakReference<ClassLoader>(l); - } + public Pair(Object o, ClassLoader l) { + object = o; + classLoader = new WeakReference<ClassLoader>(l); + } - public Object object; - public WeakReference<ClassLoader> classLoader; + public Object object; + public WeakReference<ClassLoader> classLoader; } // Make the method not inline-able to prevent the compiler optimizing away the allocation. private static Pair $noinline$testNoUnloadInstanceHelper(Constructor<?> constructor) throws Exception { ClassLoader loader = (ClassLoader) constructor.newInstance( - DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); Object o = testNoUnloadHelper(loader); return new Pair(o, loader); } @@ -165,7 +174,7 @@ public class Main { private static Class<?> setUpUnloadClass(Constructor<?> constructor) throws Exception { ClassLoader loader = (ClassLoader) constructor.newInstance( - DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); Class<?> intHolder = loader.loadClass("IntHolder"); Method getValue = intHolder.getDeclaredMethod("getValue"); Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE); @@ -202,6 +211,75 @@ public class Main { System.out.println("Too small " + (s.length() < 1000)); } + private static void assertStackTraceContains(Throwable t, String className, String methodName) { + boolean found = false; + for (StackTraceElement e : t.getStackTrace()) { + if (className.equals(e.getClassName()) && methodName.equals(e.getMethodName())) { + found = true; + break; + } + } + if (!found) { + throw new Error("Did not find " + className + "." + methodName); + } + } + + private static void testCopiedMethodInStackTrace(Constructor<?> constructor) throws Exception { + Throwable t = $noinline$createStackTraceWithCopiedMethod(constructor); + doUnloading(); + assertStackTraceContains(t, "Iface", "invokeRun"); + } + + private static Throwable $noinline$createStackTraceWithCopiedMethod(Constructor<?> constructor) + throws Exception { + ClassLoader loader = (ClassLoader) constructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, Main.class.getClassLoader()); + Iface impl = (Iface) loader.loadClass("Impl").newInstance(); + Runnable throwingRunnable = new Runnable() { + public void run() { + throw new Error(); + } + }; + try { + impl.invokeRun(throwingRunnable); + System.out.println("UNREACHABLE"); + return null; + } catch (Error expected) { + return expected; + } + } + + private static void testCopiedBcpMethodInStackTrace() { + Consumer<Object> consumer = new Consumer<Object>() { + public void accept(Object o) { + throw new Error(); + } + }; + Error err = null; + try { + Arrays.asList(new Object[] { new Object() }).iterator().forEachRemaining(consumer); + } catch (Error expected) { + err = expected; + } + assertStackTraceContains(err, "Main", "testCopiedBcpMethodInStackTrace"); + } + + private static void testCopiedAppImageMethodInStackTrace() throws Exception { + Iface limpl = (Iface) Class.forName("Impl2").newInstance(); + Runnable throwingRunnable = new Runnable() { + public void run() { + throw new Error(); + } + }; + Error err = null; + try { + limpl.invokeRun(throwingRunnable); + } catch (Error expected) { + err = expected; + } + assertStackTraceContains(err, "Main", "testCopiedAppImageMethodInStackTrace"); + } + private static WeakReference<Class> setUpUnloadClassWeak(Constructor<?> constructor) throws Exception { return new WeakReference<Class>(setUpUnloadClass(constructor)); @@ -242,7 +320,7 @@ public class Main { } private static int getPid() throws Exception { - return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); + return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); } public static native void stopJit(); |