1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.ref.WeakReference;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 
23 public class Main {
main(String[] args)24     public static void main(String[] args) throws Exception {
25         try {
26             // Check if we're running dalvik or RI.
27             Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
28             System.loadLibrary(args[0]);
29         } catch (ClassNotFoundException e) {
30             usingRI = true;
31             // Add expected JNI_OnLoad log line to match expected-stdout.txt.
32             System.out.println("JNI_OnLoad called");
33         }
34 
35         testClearDexCache();
36         testMultiDex();
37         testRacyLoader();
38         testRacyLoader2();
39         testMisbehavingLoader();
40         testRacyMisbehavingLoader();
41         testRacyMisbehavingLoader2();
42     }
43 
testClearDexCache()44     private static void testClearDexCache() throws Exception {
45         DelegatingLoader delegating_loader = createDelegatingLoader();
46         Class<?> helper = delegating_loader.loadClass("Helper1");
47 
48         WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper);
49         changeInner(delegating_loader);
50         clearResolvedTypes(helper);
51         Runtime.getRuntime().gc();
52         WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper);
53         Runtime.getRuntime().gc();
54 
55         Class<?> test1 = weak_test1.get();
56         if (test1 == null) {
57             System.out.println("test1 disappeared");
58         }
59         Class<?> test2 = weak_test2.get();
60         if (test2 == null) {
61             System.out.println("test2 disappeared");
62         }
63         if (test1 != test2) {
64             System.out.println("test1 != test2");
65         }
66 
67         System.out.println("testClearDexCache done");
68     }
69 
testMultiDex()70     private static void testMultiDex() throws Exception {
71         DelegatingLoader delegating_loader = createDelegatingLoader();
72 
73         Class<?> helper1 = delegating_loader.loadClass("Helper1");
74         WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1);
75 
76         changeInner(delegating_loader);
77 
78         Class<?> helper2 = delegating_loader.loadClass("Helper2");
79         WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2);
80 
81         Runtime.getRuntime().gc();
82 
83         Class<?> test1 = weak_test1.get();
84         if (test1 == null) {
85             System.out.println("test1 disappeared");
86         }
87         Class<?> test2 = weak_test2.get();
88         if (test2 == null) {
89             System.out.println("test2 disappeared");
90         }
91         if (test1 != test2) {
92             System.out.println("test1 != test2");
93         }
94 
95         System.out.println("testMultiDex done");
96     }
97 
testMisbehavingLoader()98     private static void testMisbehavingLoader() throws Exception {
99         ClassLoader system_loader = ClassLoader.getSystemClassLoader();
100         DefiningLoader defining_loader = new DefiningLoader(system_loader);
101         MisbehavingLoader misbehaving_loader =
102             new MisbehavingLoader(system_loader, defining_loader);
103         Class<?> helper = misbehaving_loader.loadClass("Helper1");
104 
105         try {
106             WeakReference<Class<?>> weak_test = wrapHelperGet(helper);
107         } catch (InvocationTargetException ite) {
108             String message = ite.getCause().getMessage();
109             if (usingRI && "Test".equals(message)) {
110               // Replace RI message with dalvik message to match expected-stdout.txt.
111               message = "Initiating class loader of type " +
112                   misbehaving_loader.getClass().getName() +
113                   " returned class Helper2 instead of Test.";
114             }
115             System.out.println(ite.getCause().getClass().getName() + ": " + message);
116         }
117         System.out.println("testMisbehavingLoader done");
118     }
119 
testRacyLoader()120     private static void testRacyLoader() throws Exception {
121         final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
122 
123         final Thread[] threads = new Thread[4];
124         final Object[] results = new Object[threads.length];
125 
126         final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
127         final Class<?> helper1 = racy_loader.loadClass("Helper1");
128         skipVerification(helper1);  // Avoid class loading during verification.
129 
130         for (int i = 0; i != threads.length; ++i) {
131             final int my_index = i;
132             Thread t = new Thread() {
133                 public void run() {
134                     try {
135                         Method get = helper1.getDeclaredMethod("get");
136                         results[my_index] = get.invoke(null);
137                     } catch (InvocationTargetException ite) {
138                         results[my_index] = ite.getCause();
139                     } catch (Throwable t) {
140                         results[my_index] = t;
141                     }
142                 }
143             };
144             t.start();
145             threads[i] = t;
146         }
147         for (Thread t : threads) {
148             t.join();
149         }
150         dumpResultStats(results, 1);
151         System.out.println("testRacyLoader done");
152     }
153 
testRacyLoader2()154     private static void testRacyLoader2() throws Exception {
155         final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
156 
157         final Thread[] threads = new Thread[4];
158         final Object[] results = new Object[threads.length];
159 
160         final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
161         final Class<?> helper1 = racy_loader.loadClass("Helper1");
162         skipVerification(helper1);  // Avoid class loading during verification.
163         final Class<?> helper3 = racy_loader.loadClass("Helper3");
164         skipVerification(helper3);  // Avoid class loading during verification.
165 
166         for (int i = 0; i != threads.length; ++i) {
167             final int my_index = i;
168             Thread t = new Thread() {
169                 public void run() {
170                     try {
171                         Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3;
172                         Method get = helper.getDeclaredMethod("get");
173                         results[my_index] = get.invoke(null);
174                     } catch (InvocationTargetException ite) {
175                         results[my_index] = ite.getCause();
176                     } catch (Throwable t) {
177                         results[my_index] = t;
178                     }
179                 }
180             };
181             t.start();
182             threads[i] = t;
183         }
184         for (Thread t : threads) {
185             t.join();
186         }
187         dumpResultStats(results, 2);
188         System.out.println("testRacyLoader2 done");
189     }
190 
testRacyMisbehavingLoader()191     private static void testRacyMisbehavingLoader() throws Exception {
192         final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
193 
194         final Thread[] threads = new Thread[4];
195         final Object[] results = new Object[threads.length];
196 
197         final RacyMisbehavingLoader racy_loader =
198             new RacyMisbehavingLoader(system_loader, threads.length, false);
199         final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
200         skipVerification(helper1);  // Avoid class loading during verification.
201 
202         for (int i = 0; i != threads.length; ++i) {
203             final int my_index = i;
204             Thread t = new Thread() {
205                 public void run() {
206                     try {
207                         Method get = helper1.getDeclaredMethod("get");
208                         results[my_index] = get.invoke(null);
209                     } catch (InvocationTargetException ite) {
210                         results[my_index] = ite.getCause();
211                     } catch (Throwable t) {
212                         results[my_index] = t;
213                     }
214                 }
215             };
216             t.start();
217             threads[i] = t;
218         }
219         for (Thread t : threads) {
220             t.join();
221         }
222         dumpResultStats(results, 1);
223         System.out.println("testRacyMisbehavingLoader done");
224     }
225 
testRacyMisbehavingLoader2()226     private static void testRacyMisbehavingLoader2() throws Exception {
227         final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
228 
229         final Thread[] threads = new Thread[4];
230         final Object[] results = new Object[threads.length];
231 
232         final RacyMisbehavingLoader racy_loader =
233             new RacyMisbehavingLoader(system_loader, threads.length, true);
234         final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
235         skipVerification(helper1);  // Avoid class loading during verification.
236 
237         for (int i = 0; i != threads.length; ++i) {
238             final int my_index = i;
239             Thread t = new Thread() {
240                 public void run() {
241                     try {
242                         Method get = helper1.getDeclaredMethod("get");
243                         results[my_index] = get.invoke(null);
244                     } catch (InvocationTargetException ite) {
245                         results[my_index] = ite.getCause();
246                     } catch (Throwable t) {
247                         results[my_index] = t;
248                     }
249                 }
250             };
251             t.start();
252             threads[i] = t;
253         }
254         for (Thread t : threads) {
255             t.join();
256         }
257         dumpResultStats(results, 1);
258         System.out.println("testRacyMisbehavingLoader2 done");
259     }
260 
dumpResultStats(Object[] results, int expected_unique)261     private static void dumpResultStats(Object[] results, int expected_unique) throws Exception {
262         int throwables = 0;
263         int classes = 0;
264         int unique_classes = 0;
265         for (int i = 0; i != results.length; ++i) {
266             Object r = results[i];
267             if (r instanceof Throwable) {
268                 ++throwables;
269                 System.out.println(((Throwable) r).getMessage());
270             } else if (isClassPair(r)) {
271                 printPair(r);
272                 Object ref = getSecond(r);
273                 ++classes;
274                 ++unique_classes;
275                 for (int j = 0; j != i; ++j) {
276                     Object rj = results[j];
277                     if (isClassPair(results[j]) && getSecond(results[j]) == ref) {
278                         --unique_classes;
279                         break;
280                     }
281                 }
282             }
283         }
284         System.out.println("total: " + results.length);
285         System.out.println("  throwables: " + throwables);
286         System.out.println("  classes: " + classes
287             + " (" + unique_classes + " unique)");
288         if (expected_unique != unique_classes) {
289             System.out.println("MISMATCH with expected_unique: " + expected_unique);
290             ArrayList<Class<?>> list = new ArrayList<Class<?>>();
291             for (int i = 0; i != results.length; ++i) {
292                 Object r = results[i];
293                 if (isClassPair(r)) {
294                     list.add(getSecond(r));
295                 }
296             }
297             nativeDumpClasses(list.toArray());
298         }
299     }
300 
createDelegatingLoader()301     private static DelegatingLoader createDelegatingLoader() {
302         ClassLoader system_loader = ClassLoader.getSystemClassLoader();
303         DefiningLoader defining_loader = new DefiningLoader(system_loader);
304         return new DelegatingLoader(system_loader, defining_loader);
305     }
306 
changeInner(DelegatingLoader delegating_loader)307     private static void changeInner(DelegatingLoader delegating_loader) {
308         ClassLoader system_loader = ClassLoader.getSystemClassLoader();
309         DefiningLoader defining_loader = new DefiningLoader(system_loader);
310         delegating_loader.resetDefiningLoader(defining_loader);
311     }
312 
wrapHelperGet(Class<?> helper)313     private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception {
314         Method get = helper.getDeclaredMethod("get");
315         Object pair = get.invoke(null);
316         printPair(pair);
317         return new WeakReference<Class<?>>(getSecond(pair));
318     }
319 
printPair(Object pair)320     private static void printPair(Object pair) throws Exception {
321         Method print = pair.getClass().getDeclaredMethod("print");
322         print.invoke(pair);
323     }
324 
getSecond(Object pair)325     private static Class<?> getSecond(Object pair) throws Exception {
326         Field second = pair.getClass().getDeclaredField("second");
327         return (Class<?>) second.get(pair);
328     }
329 
isClassPair(Object r)330     private static boolean isClassPair(Object r) {
331         return r != null && r.getClass().getName().equals("ClassPair");
332     }
333 
clearResolvedTypes(Class<?> c)334     public static void clearResolvedTypes(Class<?> c) {
335         if (!usingRI) {
336             nativeClearResolvedTypes(c);
337         }
338     }
339 
340     // Skip verification of a class on ART. Verification can cause classes to be loaded
341     // while holding a lock on the class being verified and holding that lock can interfere
342     // with the intent of the "racy" tests. In these tests we're waiting in the loadClass()
343     // for all the tested threads to synchronize and they cannot reach that point if they
344     // are waiting for the class lock on ClassLinker::InitializeClass(Helper1/Helper3).
skipVerification(Class<?> c)345     public static void skipVerification(Class<?> c) {
346         if (!usingRI) {
347             nativeSkipVerification(c);
348         }
349     }
350 
nativeClearResolvedTypes(Class<?> c)351     public static native void nativeClearResolvedTypes(Class<?> c);
nativeSkipVerification(Class<?> c)352     public static native void nativeSkipVerification(Class<?> c);
nativeDumpClasses(Object[] array)353     public static native void nativeDumpClasses(Object[] array);
354 
355     static boolean usingRI = false;
356 }
357