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.Method;
18 import java.nio.ByteBuffer;
19 
20 public class Main {
main(String[] args)21     public static void main(String[] args) throws Exception {
22         String name = System.getProperty("java.vm.name");
23         if (!"Dalvik".equals(name)) {
24             System.out.println("This test is not supported on " + name);
25             return;
26         }
27         testRecentAllocationTracking();
28     }
29 
testRecentAllocationTracking()30     private static void testRecentAllocationTracking() throws Exception {
31         System.out.println("Confirm empty");
32         Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
33         System.out.println("empty=" + empty);
34 
35         System.out.println("Confirm enable");
36         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
37         DdmVmInternal.enableRecentAllocations(true);
38         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
39 
40         System.out.println("Capture some allocations (note just this causes allocations)");
41         Allocations before = new Allocations(DdmVmInternal.getRecentAllocations());
42         System.out.println("before > 0=" + (before.numberOfEntries > 0));
43 
44         System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
45         final int overflowAllocations = 64 * 1024;  // Won't fit in unsigned 16-bit value.
46         for (int i = 0; i < overflowAllocations; i++) {
47             new Object() {
48                 // Add a finalizer so that the allocation won't be eliminated.
49                 public void finalize() {
50                     System.out.print("");
51                 }
52             };
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         System.out.println("Disable and confirm back to empty");
60         DdmVmInternal.enableRecentAllocations(false);
61         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
62         Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
63         System.out.println("reset=" + reset);
64 
65         System.out.println("Confirm we can disable twice in a row");
66         DdmVmInternal.enableRecentAllocations(false);
67         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
68         DdmVmInternal.enableRecentAllocations(false);
69         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
70 
71         System.out.println("Confirm we can reenable twice in a row without losing allocations");
72         DdmVmInternal.enableRecentAllocations(true);
73         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
74         for (int i = 0; i < 16 * 1024; i++) {
75             new String("fnord");
76         }
77         Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
78         DdmVmInternal.enableRecentAllocations(true);
79         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
80         Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
81         System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
82 
83         System.out.println("Goodbye");
84         DdmVmInternal.enableRecentAllocations(false);
85         Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
86         System.out.println("goodbye=" + goodbye);
87     }
88 
89     private static class Allocations {
90         final int messageHeaderLen;
91         final int entryHeaderLen;
92         final int stackFrameLen;
93         final int numberOfEntries;
94         final int offsetToStringTableFromStartOfMessage;
95         final int numberOfClassNameStrings;
96         final int numberOfMethodNameStrings;
97         final int numberOfSourceFileNameStrings;
98 
Allocations(byte[] allocations)99         Allocations(byte[] allocations) {
100             ByteBuffer b = ByteBuffer.wrap(allocations);
101             messageHeaderLen = b.get() & 0xff;
102             if (messageHeaderLen != 15) {
103                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
104             }
105             entryHeaderLen = b.get() & 0xff;
106             if (entryHeaderLen != 9) {
107                 throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
108             }
109             stackFrameLen = b.get() & 0xff;
110             if (stackFrameLen != 8) {
111                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
112             }
113             numberOfEntries = b.getShort() & 0xffff;
114             offsetToStringTableFromStartOfMessage = b.getInt();
115             numberOfClassNameStrings = b.getShort() & 0xffff;
116             numberOfMethodNameStrings = b.getShort() & 0xffff;
117             numberOfSourceFileNameStrings = b.getShort() & 0xffff;
118         }
119 
toString()120         public String toString() {
121             return ("Allocations[message header len: " + messageHeaderLen +
122                     " entry header len: " + entryHeaderLen +
123                     " stack frame len: " + stackFrameLen +
124                     " number of entries: " + numberOfEntries +
125                     " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
126                     " number of class name strings: " + numberOfClassNameStrings +
127                     " number of method name strings: " + numberOfMethodNameStrings +
128                     " number of source file name strings: " + numberOfSourceFileNameStrings +
129                     "]");
130         }
131     }
132 
133     private static class DdmVmInternal {
134         private static final Method enableRecentAllocationsMethod;
135         private static final Method getRecentAllocationStatusMethod;
136         private static final Method getRecentAllocationsMethod;
137         static {
138             try {
139                 Class<?> c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
140                 enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
141                                                                     Boolean.TYPE);
142                 getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
143                 getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
144             } catch (Exception e) {
145                 throw new RuntimeException(e);
146             }
147         }
148 
enableRecentAllocations(boolean enable)149         public static void enableRecentAllocations(boolean enable) throws Exception {
150             enableRecentAllocationsMethod.invoke(null, enable);
151         }
getRecentAllocationStatus()152         public static boolean getRecentAllocationStatus() throws Exception {
153             return (boolean) getRecentAllocationStatusMethod.invoke(null);
154         }
getRecentAllocations()155         public static byte[] getRecentAllocations() throws Exception {
156             return (byte[]) getRecentAllocationsMethod.invoke(null);
157         }
158     }
159 }
160