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