Home | History | Annotate | Download | only in src
      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 {
     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.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 
     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 
     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 
     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.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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    320     private static void printPair(Object pair) throws Exception {
    321         Method print = pair.getClass().getDeclaredMethod("print");
    322         print.invoke(pair);
    323     }
    324 
    325     private static Class<?> getSecond(Object pair) throws Exception {
    326         Field second = pair.getClass().getDeclaredField("second");
    327         return (Class<?>) second.get(pair);
    328     }
    329 
    330     private static boolean isClassPair(Object r) {
    331         return r != null && r.getClass().getName().equals("ClassPair");
    332     }
    333 
    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).
    345     public static void skipVerification(Class<?> c) {
    346         if (!usingRI) {
    347             nativeSkipVerification(c);
    348         }
    349     }
    350 
    351     public static native void nativeClearResolvedTypes(Class<?> c);
    352     public static native void nativeSkipVerification(Class<?> c);
    353     public static native void nativeDumpClasses(Object[] array);
    354 
    355     static boolean usingRI = false;
    356 }
    357