| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| |
| public class Main { |
| public static void main(String[] args) throws Exception { |
| String name = System.getProperty("java.vm.name"); |
| if (!"Dalvik".equals(name)) { |
| System.out.println("This test is not supported on " + name); |
| return; |
| } |
| testRecentAllocationTracking(); |
| } |
| |
| private static ArrayList<Object> staticHolder = new ArrayList<>(100000); |
| |
| private static void testRecentAllocationTracking() throws Exception { |
| System.out.println("Confirm empty"); |
| Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("empty=" + empty); |
| |
| System.out.println("Confirm enable"); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| DdmVmInternal.enableRecentAllocations(true); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| |
| System.out.println("Capture some allocations (note just this causes allocations)"); |
| Allocations before = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("before > 0=" + (before.numberOfEntries > 0)); |
| |
| System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248"); |
| final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value. |
| for (int i = 0; i < overflowAllocations; i++) { |
| allocate(i, 0); |
| } |
| Allocations after = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations)); |
| System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries)); |
| System.out.println("after.numberOfEntries=" + after.numberOfEntries); |
| |
| staticHolder.clear(); // Free the allocated objects. |
| |
| System.out.println("Disable and confirm back to empty"); |
| DdmVmInternal.enableRecentAllocations(false); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("reset=" + reset); |
| |
| System.out.println("Confirm we can disable twice in a row"); |
| DdmVmInternal.enableRecentAllocations(false); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| DdmVmInternal.enableRecentAllocations(false); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| |
| System.out.println("Confirm we can reenable twice in a row without losing allocations"); |
| DdmVmInternal.enableRecentAllocations(true); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| for (int i = 0; i < 16 * 1024; i++) { |
| staticHolder.add(new String("fnord")); |
| } |
| Allocations first = new Allocations(DdmVmInternal.getRecentAllocations()); |
| DdmVmInternal.enableRecentAllocations(true); |
| System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); |
| Allocations second = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries)); |
| |
| System.out.println("Goodbye"); |
| DdmVmInternal.enableRecentAllocations(false); |
| Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations()); |
| System.out.println("goodbye=" + goodbye); |
| } |
| |
| // Allocate a simple object. Use depth for a reasonably deep stack. |
| private static final int ALLOCATE1_DEPTH = 50; |
| |
| private static Object createProxy() { |
| try { |
| InvocationHandler handler = new InvocationHandler() { |
| public Object invoke(Object proxy, Method method, Object[] args) { |
| // Don't expect to be invoked. |
| return null; |
| } |
| }; |
| return Proxy.newProxyInstance(Main.class.getClassLoader(), |
| new Class[] { Runnable.class }, handler); |
| } catch (Exception e) { |
| // We don't really expect exceptions here. |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private static void allocate(int i, int depth) { |
| if (depth >= ALLOCATE1_DEPTH) { |
| // Mix proxies, int arrays and Objects to test the different descriptor paths. |
| switch (i) { |
| case 0: |
| staticHolder.add(createProxy()); |
| break; |
| |
| case 1: |
| staticHolder.add(new int[0]); |
| break; |
| |
| case 2: |
| staticHolder.add(new Object[0]); |
| break; |
| |
| default: |
| staticHolder.add(new Object()); |
| break; |
| } |
| } else { |
| allocate(i, depth + 1); |
| } |
| } |
| |
| private static class Allocations { |
| final int messageHeaderLen; |
| final int entryHeaderLen; |
| final int stackFrameLen; |
| final int numberOfEntries; |
| final int offsetToStringTableFromStartOfMessage; |
| final int numberOfClassNameStrings; |
| final int numberOfMethodNameStrings; |
| final int numberOfSourceFileNameStrings; |
| |
| Allocations(byte[] allocations) { |
| ByteBuffer b = ByteBuffer.wrap(allocations); |
| messageHeaderLen = b.get() & 0xff; |
| if (messageHeaderLen != 15) { |
| throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen); |
| } |
| entryHeaderLen = b.get() & 0xff; |
| if (entryHeaderLen != 9) { |
| throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen); |
| } |
| stackFrameLen = b.get() & 0xff; |
| if (stackFrameLen != 8) { |
| throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen); |
| } |
| numberOfEntries = b.getShort() & 0xffff; |
| offsetToStringTableFromStartOfMessage = b.getInt(); |
| numberOfClassNameStrings = b.getShort() & 0xffff; |
| numberOfMethodNameStrings = b.getShort() & 0xffff; |
| numberOfSourceFileNameStrings = b.getShort() & 0xffff; |
| } |
| |
| public String toString() { |
| return ("Allocations[message header len: " + messageHeaderLen + |
| " entry header len: " + entryHeaderLen + |
| " stack frame len: " + stackFrameLen + |
| " number of entries: " + numberOfEntries + |
| " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage + |
| " number of class name strings: " + numberOfClassNameStrings + |
| " number of method name strings: " + numberOfMethodNameStrings + |
| " number of source file name strings: " + numberOfSourceFileNameStrings + |
| "]"); |
| } |
| } |
| |
| private static class DdmVmInternal { |
| private static final Method enableRecentAllocationsMethod; |
| private static final Method getRecentAllocationStatusMethod; |
| private static final Method getRecentAllocationsMethod; |
| static { |
| try { |
| Class<?> c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); |
| enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations", |
| Boolean.TYPE); |
| getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus"); |
| getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations"); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| public static void enableRecentAllocations(boolean enable) throws Exception { |
| enableRecentAllocationsMethod.invoke(null, enable); |
| } |
| public static boolean getRecentAllocationStatus() throws Exception { |
| return (boolean) getRecentAllocationStatusMethod.invoke(null); |
| } |
| public static byte[] getRecentAllocations() throws Exception { |
| return (byte[]) getRecentAllocationsMethod.invoke(null); |
| } |
| } |
| } |