Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2014 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.reflect.InvocationHandler;
     18 import java.lang.reflect.Method;
     19 import java.lang.reflect.Proxy;
     20 import java.nio.ByteBuffer;
     21 import java.util.ArrayList;
     22 
     23 public class Main {
     24     public static void main(String[] args) throws Exception {
     25         String name = System.getProperty("java.vm.name");
     26         if (!"Dalvik".equals(name)) {
     27             System.out.println("This test is not supported on " + name);
     28             return;
     29         }
     30         testRecentAllocationTracking();
     31     }
     32 
     33     private static ArrayList<Object> staticHolder = new ArrayList<>(100000);
     34 
     35     private static void testRecentAllocationTracking() throws Exception {
     36         System.out.println("Confirm empty");
     37         Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
     38         System.out.println("empty=" + empty);
     39 
     40         System.out.println("Confirm enable");
     41         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     42         DdmVmInternal.enableRecentAllocations(true);
     43         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     44 
     45         System.out.println("Capture some allocations (note just this causes allocations)");
     46         Allocations before = new Allocations(DdmVmInternal.getRecentAllocations());
     47         System.out.println("before > 0=" + (before.numberOfEntries > 0));
     48 
     49         System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
     50         final int overflowAllocations = 64 * 1024;  // Won't fit in unsigned 16-bit value.
     51         for (int i = 0; i < overflowAllocations; i++) {
     52             allocate(i, 0);
     53         }
     54         Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
     55         System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
     56         System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
     57         System.out.println("after.numberOfEntries=" + after.numberOfEntries);
     58 
     59         staticHolder.clear();  // Free the allocated objects.
     60 
     61         System.out.println("Disable and confirm back to empty");
     62         DdmVmInternal.enableRecentAllocations(false);
     63         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     64         Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
     65         System.out.println("reset=" + reset);
     66 
     67         System.out.println("Confirm we can disable twice in a row");
     68         DdmVmInternal.enableRecentAllocations(false);
     69         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     70         DdmVmInternal.enableRecentAllocations(false);
     71         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     72 
     73         System.out.println("Confirm we can reenable twice in a row without losing allocations");
     74         DdmVmInternal.enableRecentAllocations(true);
     75         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     76         for (int i = 0; i < 16 * 1024; i++) {
     77             staticHolder.add(new String("fnord"));
     78         }
     79         Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
     80         DdmVmInternal.enableRecentAllocations(true);
     81         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
     82         Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
     83         System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
     84 
     85         System.out.println("Goodbye");
     86         DdmVmInternal.enableRecentAllocations(false);
     87         Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
     88         System.out.println("goodbye=" + goodbye);
     89     }
     90 
     91     // Allocate a simple object. Use depth for a reasonably deep stack.
     92     private static final int ALLOCATE1_DEPTH = 50;
     93 
     94     private static Object createProxy() {
     95         try {
     96             InvocationHandler handler = new InvocationHandler() {
     97                 public Object invoke(Object proxy, Method method, Object[] args) {
     98                     // Don't expect to be invoked.
     99                     return null;
    100                 }
    101             };
    102             return Proxy.newProxyInstance(Main.class.getClassLoader(),
    103                     new Class[] { Runnable.class }, handler);
    104         } catch (Exception e) {
    105             // We don't really expect exceptions here.
    106             throw new RuntimeException(e);
    107         }
    108     }
    109 
    110     private static void allocate(int i, int depth) {
    111         if (depth >= ALLOCATE1_DEPTH) {
    112             // Mix proxies, int arrays and Objects to test the different descriptor paths.
    113             switch (i) {
    114                 case 0:
    115                     staticHolder.add(createProxy());
    116                     break;
    117 
    118                 case 1:
    119                     staticHolder.add(new int[0]);
    120                     break;
    121 
    122                 case 2:
    123                     staticHolder.add(new Object[0]);
    124                     break;
    125 
    126                 default:
    127                     staticHolder.add(new Object());
    128                     break;
    129             }
    130         } else {
    131             allocate(i, depth + 1);
    132         }
    133     }
    134 
    135     private static class Allocations {
    136         final int messageHeaderLen;
    137         final int entryHeaderLen;
    138         final int stackFrameLen;
    139         final int numberOfEntries;
    140         final int offsetToStringTableFromStartOfMessage;
    141         final int numberOfClassNameStrings;
    142         final int numberOfMethodNameStrings;
    143         final int numberOfSourceFileNameStrings;
    144 
    145         Allocations(byte[] allocations) {
    146             ByteBuffer b = ByteBuffer.wrap(allocations);
    147             messageHeaderLen = b.get() & 0xff;
    148             if (messageHeaderLen != 15) {
    149                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
    150             }
    151             entryHeaderLen = b.get() & 0xff;
    152             if (entryHeaderLen != 9) {
    153                 throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
    154             }
    155             stackFrameLen = b.get() & 0xff;
    156             if (stackFrameLen != 8) {
    157                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
    158             }
    159             numberOfEntries = b.getShort() & 0xffff;
    160             offsetToStringTableFromStartOfMessage = b.getInt();
    161             numberOfClassNameStrings = b.getShort() & 0xffff;
    162             numberOfMethodNameStrings = b.getShort() & 0xffff;
    163             numberOfSourceFileNameStrings = b.getShort() & 0xffff;
    164         }
    165 
    166         public String toString() {
    167             return ("Allocations[message header len: " + messageHeaderLen +
    168                     " entry header len: " + entryHeaderLen +
    169                     " stack frame len: " + stackFrameLen +
    170                     " number of entries: " + numberOfEntries +
    171                     " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
    172                     " number of class name strings: " + numberOfClassNameStrings +
    173                     " number of method name strings: " + numberOfMethodNameStrings +
    174                     " number of source file name strings: " + numberOfSourceFileNameStrings +
    175                     "]");
    176         }
    177     }
    178 
    179     private static class DdmVmInternal {
    180         private static final Method enableRecentAllocationsMethod;
    181         private static final Method getRecentAllocationStatusMethod;
    182         private static final Method getRecentAllocationsMethod;
    183         static {
    184             try {
    185                 Class<?> c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
    186                 enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
    187                                                                     Boolean.TYPE);
    188                 getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
    189                 getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
    190             } catch (Exception e) {
    191                 throw new RuntimeException(e);
    192             }
    193         }
    194 
    195         public static void enableRecentAllocations(boolean enable) throws Exception {
    196             enableRecentAllocationsMethod.invoke(null, enable);
    197         }
    198         public static boolean getRecentAllocationStatus() throws Exception {
    199             return (boolean) getRecentAllocationStatusMethod.invoke(null);
    200         }
    201         public static byte[] getRecentAllocations() throws Exception {
    202             return (byte[]) getRecentAllocationsMethod.invoke(null);
    203         }
    204     }
    205 }
    206