Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2009 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.io.File;
     18 import java.lang.ref.WeakReference;
     19 import java.lang.reflect.Constructor;
     20 import java.lang.reflect.Method;
     21 import java.lang.reflect.InvocationTargetException;
     22 
     23 public class Main {
     24     private static final int TEST_LENGTH = 100;
     25 
     26     private static boolean makeArray(int i) {
     27         return i % 10 == 0;
     28     }
     29 
     30     private static void fillArray(Object global[], Object local[], int i) {
     31         // Very stupid linking.
     32         local[0] = global;
     33         for (int j = 1; j < local.length; j++) {
     34             local[j] = global[j];
     35         }
     36     }
     37 
     38     private static Object allocInDifferentLoader() throws Exception {
     39         final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar";
     40         Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
     41         if (pathClassLoader == null) {
     42             throw new AssertionError("Couldn't find path class loader class");
     43         }
     44         Constructor<?> constructor =
     45             pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
     46         ClassLoader loader = (ClassLoader)constructor.newInstance(
     47                 DEX_FILE, ClassLoader.getSystemClassLoader());
     48         Class<?> allocator = loader.loadClass("Allocator");
     49         return allocator.getDeclaredMethod("allocObject", null).invoke(null);
     50     }
     51 
     52     private static void createDumpAndConv() throws RuntimeException {
     53         File dumpFile = null;
     54         File convFile = null;
     55 
     56         try {
     57             // Now dump the heap.
     58             dumpFile = createDump();
     59 
     60             // Run hprof-conv on it.
     61             convFile = getConvFile();
     62 
     63             File hprof_conv = getHprofConf();
     64             try {
     65                 ProcessBuilder pb = new ProcessBuilder(
     66                         hprof_conv.getAbsoluteFile().toString(),
     67                         dumpFile.getAbsoluteFile().toString(),
     68                         convFile.getAbsoluteFile().toString());
     69                 pb.redirectErrorStream(true);
     70                 Process process = pb.start();
     71                 int ret = process.waitFor();
     72                 if (ret != 0) {
     73                     throw new RuntimeException("Exited abnormally with " + ret);
     74                 }
     75             } catch (Exception exc) {
     76                 throw new RuntimeException(exc);
     77             }
     78         } finally {
     79             // Delete the files.
     80             if (dumpFile != null) {
     81                 dumpFile.delete();
     82             }
     83             if (convFile != null) {
     84                 convFile.delete();
     85             }
     86         }
     87     }
     88 
     89     public static void main(String[] args) throws Exception {
     90         testBasicDump();
     91         testAllocationTrackingAndClassUnloading();
     92         testGcAndDump();
     93     }
     94 
     95     private static void testBasicDump() throws Exception {
     96         // Create some data.
     97         Object data[] = new Object[TEST_LENGTH];
     98         for (int i = 0; i < data.length; i++) {
     99             if (makeArray(i)) {
    100                 data[i] = new Object[TEST_LENGTH];
    101             } else {
    102                 data[i] = String.valueOf(i);
    103             }
    104         }
    105         for (int i = 0; i < data.length; i++) {
    106             if (makeArray(i)) {
    107                 Object data2[] = (Object[]) data[i];
    108                 fillArray(data, data2, i);
    109             }
    110         }
    111         System.out.println("Generated data.");
    112         createDumpAndConv();
    113     }
    114 
    115     private static void testAllocationTrackingAndClassUnloading() throws Exception {
    116         Class<?> klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
    117         if (klass == null) {
    118             throw new AssertionError("Couldn't find path class loader class");
    119         }
    120         Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations",
    121                 Boolean.TYPE);
    122         if (enableMethod == null) {
    123             throw new AssertionError("Couldn't find path class loader class");
    124         }
    125         enableMethod.invoke(null, true);
    126         Object o = allocInDifferentLoader();
    127         // Run GC to cause class unloading.
    128         Runtime.getRuntime().gc();
    129         createDumpAndConv();
    130         // TODO: Somehow check contents of hprof file.
    131         enableMethod.invoke(null, false);
    132     }
    133 
    134     private static void testGcAndDump() throws Exception {
    135         Allocator allocator = new Allocator();
    136         Dumper dumper = new Dumper(allocator);
    137         allocator.start();
    138         dumper.start();
    139         try {
    140             allocator.join();
    141             dumper.join();
    142         } catch (InterruptedException e) {
    143             System.out.println("join interrupted");
    144         }
    145     }
    146 
    147     private static class Allocator extends Thread {
    148         private static int ARRAY_SIZE = 1024;
    149         public volatile boolean running = true;
    150         public void run() {
    151             Object[] array = new Object[ARRAY_SIZE];
    152             int i = 0;
    153             while (running) {
    154                 array[i] = new byte[1024];
    155                 if (i % ARRAY_SIZE == 0) {
    156                     Main.sleep(100L);
    157                 }
    158                 i = (i + 1) % ARRAY_SIZE;
    159             }
    160         }
    161     }
    162 
    163     private static class Dumper extends Thread {
    164         Dumper(Allocator allocator) {
    165             this.allocator = allocator;
    166         }
    167         Allocator allocator;
    168         public void run() {
    169             for (int i = 0; i < 5; ++i) {
    170                 Main.sleep(1000L);
    171                 createDumpAndConv();
    172             }
    173             allocator.running = false;
    174         }
    175     }
    176 
    177     public static void sleep(long ms) {
    178         try {
    179             Thread.sleep(ms);
    180         } catch (InterruptedException e) {
    181             System.out.println("sleep interrupted");
    182         }
    183     }
    184 
    185     private static File getHprofConf() {
    186         // Use the java.library.path. It points to the lib directory.
    187         File libDir = new File(System.getProperty("java.library.path").split(":")[0]);
    188         return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv");
    189     }
    190 
    191     private static File createDump() {
    192         java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod();
    193         if (dumpHprofDataMethod != null) {
    194             File f = getDumpFile();
    195             try {
    196                 dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString());
    197                 return f;
    198             } catch (Exception exc) {
    199                 exc.printStackTrace(System.out);
    200             }
    201         } else {
    202             System.out.println("Could not find dump method!");
    203         }
    204         return null;
    205     }
    206 
    207     /**
    208      * Finds VMDebug.dumpHprofData() through reflection.  In the reference
    209      * implementation this will not be available.
    210      *
    211      * @return the reflection object, or null if the method can't be found
    212      */
    213     private static Method getDumpHprofDataMethod() {
    214         ClassLoader myLoader = Main.class.getClassLoader();
    215         Class<?> vmdClass;
    216         try {
    217             vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
    218         } catch (ClassNotFoundException cnfe) {
    219             return null;
    220         }
    221 
    222         Method meth;
    223         try {
    224             meth = vmdClass.getMethod("dumpHprofData", String.class);
    225         } catch (NoSuchMethodException nsme) {
    226             System.out.println("Found VMDebug but not dumpHprofData method");
    227             return null;
    228         }
    229 
    230         return meth;
    231     }
    232 
    233     private static File getDumpFile() {
    234         try {
    235             return File.createTempFile("test-130-hprof", "dump");
    236         } catch (Exception exc) {
    237             return null;
    238         }
    239     }
    240 
    241     private static File getConvFile() {
    242         try {
    243             return File.createTempFile("test-130-hprof", "conv");
    244         } catch (Exception exc) {
    245             return null;
    246         }
    247     }
    248 }
    249