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         }
49         Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
50         System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
51         System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
52         System.out.println("after.numberOfEntries=" + after.numberOfEntries);
53 
54         System.out.println("Disable and confirm back to empty");
55         DdmVmInternal.enableRecentAllocations(false);
56         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
57         Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
58         System.out.println("reset=" + reset);
59 
60         System.out.println("Confirm we can disable twice in a row");
61         DdmVmInternal.enableRecentAllocations(false);
62         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
63         DdmVmInternal.enableRecentAllocations(false);
64         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
65 
66         System.out.println("Confirm we can reenable twice in a row without losing allocations");
67         DdmVmInternal.enableRecentAllocations(true);
68         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
69         for (int i = 0; i < 16 * 1024; i++) {
70             new String("fnord");
71         }
72         Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
73         DdmVmInternal.enableRecentAllocations(true);
74         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
75         Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
76         System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
77 
78         System.out.println("Goodbye");
79         DdmVmInternal.enableRecentAllocations(false);
80         Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
81         System.out.println("goodbye=" + goodbye);
82     }
83 
84     private static class Allocations {
85         final int messageHeaderLen;
86         final int entryHeaderLen;
87         final int stackFrameLen;
88         final int numberOfEntries;
89         final int offsetToStringTableFromStartOfMessage;
90         final int numberOfClassNameStrings;
91         final int numberOfMethodNameStrings;
92         final int numberOfSourceFileNameStrings;
93 
Allocations(byte[] allocations)94         Allocations(byte[] allocations) {
95             ByteBuffer b = ByteBuffer.wrap(allocations);
96             messageHeaderLen = b.get() & 0xff;
97             if (messageHeaderLen != 15) {
98                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
99             }
100             entryHeaderLen = b.get() & 0xff;
101             if (entryHeaderLen != 9) {
102                 throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
103             }
104             stackFrameLen = b.get() & 0xff;
105             if (stackFrameLen != 8) {
106                 throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
107             }
108             numberOfEntries = b.getShort() & 0xffff;
109             offsetToStringTableFromStartOfMessage = b.getInt();
110             numberOfClassNameStrings = b.getShort() & 0xffff;
111             numberOfMethodNameStrings = b.getShort() & 0xffff;
112             numberOfSourceFileNameStrings = b.getShort() & 0xffff;
113         }
114 
toString()115         public String toString() {
116             return ("Allocations[message header len: " + messageHeaderLen +
117                     " entry header len: " + entryHeaderLen +
118                     " stack frame len: " + stackFrameLen +
119                     " number of entries: " + numberOfEntries +
120                     " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
121                     " number of class name strings: " + numberOfClassNameStrings +
122                     " number of method name strings: " + numberOfMethodNameStrings +
123                     " number of source file name strings: " + numberOfSourceFileNameStrings +
124                     "]");
125         }
126     }
127 
128     private static class DdmVmInternal {
129         private static final Method enableRecentAllocationsMethod;
130         private static final Method getRecentAllocationStatusMethod;
131         private static final Method getRecentAllocationsMethod;
132         static {
133             try {
134                 Class c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
135                 enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
136                                                                     Boolean.TYPE);
137                 getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
138                 getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
139             } catch (Exception e) {
140                 throw new RuntimeException(e);
141             }
142         }
143 
enableRecentAllocations(boolean enable)144         public static void enableRecentAllocations(boolean enable) throws Exception {
145             enableRecentAllocationsMethod.invoke(null, enable);
146         }
getRecentAllocationStatus()147         public static boolean getRecentAllocationStatus() throws Exception {
148             return (boolean) getRecentAllocationStatusMethod.invoke(null);
149         }
getRecentAllocations()150         public static byte[] getRecentAllocations() throws Exception {
151             return (byte[]) getRecentAllocationsMethod.invoke(null);
152         }
153     }
154 }
155