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