• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.io.Serializable;
18 import java.io.IOException;
19 import java.io.BufferedReader;
20 import java.io.InputStreamReader;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.util.List;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 
27 /**
28  * Memory usage information.
29  */
30 class MemoryUsage implements Serializable {
31 
32     private static final long serialVersionUID = 0;
33 
34     static final MemoryUsage NOT_AVAILABLE = new MemoryUsage();
35 
36     static int errorCount = 0;
37 
38     // These values are in 1kB increments (not 4kB like you'd expect).
39     final int nativeSharedPages;
40     final int javaSharedPages;
41     final int otherSharedPages;
42     final int nativePrivatePages;
43     final int javaPrivatePages;
44     final int otherPrivatePages;
45 
46     final int allocCount;
47     final int allocSize;
48     final int freedCount;
49     final int freedSize;
50     final long nativeHeapSize;
51 
MemoryUsage(String line)52     public MemoryUsage(String line) {
53         String[] parsed = line.split(",");
54 
55         nativeSharedPages = Integer.parseInt(parsed[1]);
56         javaSharedPages = Integer.parseInt(parsed[2]);
57         otherSharedPages = Integer.parseInt(parsed[3]);
58         nativePrivatePages = Integer.parseInt(parsed[4]);
59         javaPrivatePages = Integer.parseInt(parsed[5]);
60         otherPrivatePages = Integer.parseInt(parsed[6]);
61         allocCount = Integer.parseInt(parsed[7]);
62         allocSize = Integer.parseInt(parsed[8]);
63         freedCount = Integer.parseInt(parsed[9]);
64         freedSize = Integer.parseInt(parsed[10]);
65         nativeHeapSize = Long.parseLong(parsed[11]);
66     }
67 
MemoryUsage()68     MemoryUsage() {
69         nativeSharedPages = -1;
70         javaSharedPages = -1;
71         otherSharedPages = -1;
72         nativePrivatePages = -1;
73         javaPrivatePages = -1;
74         otherPrivatePages = -1;
75 
76         allocCount = -1;
77         allocSize = -1;
78         freedCount = -1;
79         freedSize = -1;
80         nativeHeapSize = -1;
81     }
82 
MemoryUsage(int nativeSharedPages, int javaSharedPages, int otherSharedPages, int nativePrivatePages, int javaPrivatePages, int otherPrivatePages, int allocCount, int allocSize, int freedCount, int freedSize, long nativeHeapSize)83     MemoryUsage(int nativeSharedPages,
84             int javaSharedPages,
85             int otherSharedPages,
86             int nativePrivatePages,
87             int javaPrivatePages,
88             int otherPrivatePages,
89             int allocCount,
90             int allocSize,
91             int freedCount,
92             int freedSize,
93             long nativeHeapSize) {
94         this.nativeSharedPages = nativeSharedPages;
95         this.javaSharedPages = javaSharedPages;
96         this.otherSharedPages = otherSharedPages;
97         this.nativePrivatePages = nativePrivatePages;
98         this.javaPrivatePages = javaPrivatePages;
99         this.otherPrivatePages = otherPrivatePages;
100         this.allocCount = allocCount;
101         this.allocSize = allocSize;
102         this.freedCount = freedCount;
103         this.freedSize = freedSize;
104         this.nativeHeapSize = nativeHeapSize;
105     }
106 
subtract(MemoryUsage baseline)107     MemoryUsage subtract(MemoryUsage baseline) {
108         return new MemoryUsage(
109                 nativeSharedPages - baseline.nativeSharedPages,
110                 javaSharedPages - baseline.javaSharedPages,
111                 otherSharedPages - baseline.otherSharedPages,
112                 nativePrivatePages - baseline.nativePrivatePages,
113                 javaPrivatePages - baseline.javaPrivatePages,
114                 otherPrivatePages - baseline.otherPrivatePages,
115                 allocCount - baseline.allocCount,
116                 allocSize - baseline.allocSize,
117                 freedCount - baseline.freedCount,
118                 freedSize - baseline.freedSize,
119                 nativeHeapSize - baseline.nativeHeapSize);
120     }
121 
javaHeapSize()122     int javaHeapSize() {
123         return allocSize - freedSize;
124     }
125 
totalHeap()126     int totalHeap() {
127         return javaHeapSize() + (int) nativeHeapSize;
128     }
129 
javaPagesInK()130     int javaPagesInK() {
131         return javaSharedPages + javaPrivatePages;
132     }
133 
nativePagesInK()134     int nativePagesInK() {
135         return nativeSharedPages + nativePrivatePages;
136     }
otherPagesInK()137     int otherPagesInK() {
138         return otherSharedPages + otherPrivatePages;
139     }
140 
totalPages()141     int totalPages() {
142         return javaSharedPages + javaPrivatePages + nativeSharedPages +
143                 nativePrivatePages + otherSharedPages + otherPrivatePages;
144     }
145 
146     /**
147      * Was this information available?
148      */
isAvailable()149     boolean isAvailable() {
150         return nativeSharedPages != -1;
151     }
152 
153     /**
154      * Measures baseline memory usage.
155      */
baseline()156     static MemoryUsage baseline() {
157         return forClass(null);
158     }
159 
160     private static final String CLASS_PATH = "-Xbootclasspath"
161             + ":/system/framework/core.jar"
162             + ":/system/framework/ext.jar"
163             + ":/system/framework/framework.jar"
164             + ":/system/framework/framework-tests.jar"
165             + ":/system/framework/services.jar"
166             + ":/system/framework/loadclass.jar";
167 
168     private static final String[] GET_DIRTY_PAGES = {
169         "adb", "shell", "dalvikvm", CLASS_PATH, "LoadClass" };
170 
171     /**
172      * Measures memory usage for the given class.
173      */
forClass(String className)174     static MemoryUsage forClass(String className) {
175         MeasureWithTimeout measurer = new MeasureWithTimeout(className);
176 
177         new Thread(measurer).start();
178 
179         synchronized (measurer) {
180             if (measurer.memoryUsage == null) {
181                 // Wait up to 10s.
182                 try {
183                     measurer.wait(30000);
184                 } catch (InterruptedException e) {
185                     System.err.println("Interrupted waiting for measurement.");
186                     e.printStackTrace();
187                     return NOT_AVAILABLE;
188                 }
189 
190                 // If it's still null.
191                 if (measurer.memoryUsage == null) {
192                     System.err.println("Timed out while measuring "
193                             + className + ".");
194                     return NOT_AVAILABLE;
195                 }
196             }
197 
198             System.err.println("Got memory usage for " + className + ".");
199             return measurer.memoryUsage;
200         }
201     }
202 
203     static class MeasureWithTimeout implements Runnable {
204 
205         final String className;
206         MemoryUsage memoryUsage = null;
207 
MeasureWithTimeout(String className)208         MeasureWithTimeout(String className) {
209             this.className = className;
210         }
211 
run()212         public void run() {
213             MemoryUsage measured = measure();
214 
215             synchronized (this) {
216                 memoryUsage = measured;
217                 notifyAll();
218             }
219         }
220 
measure()221         private MemoryUsage measure() {
222             String[] commands = GET_DIRTY_PAGES;
223             if (className != null) {
224                 List<String> commandList = new ArrayList<String>(
225                         GET_DIRTY_PAGES.length + 1);
226                 commandList.addAll(Arrays.asList(commands));
227                 commandList.add(className);
228                 commands = commandList.toArray(new String[commandList.size()]);
229             }
230 
231             try {
232                 final Process process = Runtime.getRuntime().exec(commands);
233 
234                 final InputStream err = process.getErrorStream();
235 
236                 // Send error output to stderr.
237                 Thread errThread = new Thread() {
238                     @Override
239                     public void run() {
240                         copy(err, System.err);
241                     }
242                 };
243                 errThread.setDaemon(true);
244                 errThread.start();
245 
246                 BufferedReader in = new BufferedReader(
247                         new InputStreamReader(process.getInputStream()));
248                 String line = in.readLine();
249                 if (line == null || !line.startsWith("DECAFBAD,")) {
250                     System.err.println("Got bad response for " + className
251                             + ": " + line + "; command was " + Arrays.toString(commands));
252                     errorCount += 1;
253                     return NOT_AVAILABLE;
254                 }
255 
256                 in.close();
257                 err.close();
258                 process.destroy();
259 
260                 return new MemoryUsage(line);
261             } catch (IOException e) {
262                 System.err.println("Error getting stats for "
263                         + className + ".");
264                 e.printStackTrace();
265                 return NOT_AVAILABLE;
266             }
267         }
268 
269     }
270 
271     /**
272      * Copies from one stream to another.
273      */
copy(InputStream in, OutputStream out)274     private static void copy(InputStream in, OutputStream out) {
275         byte[] buffer = new byte[1024];
276         int read;
277         try {
278             while ((read = in.read(buffer)) > -1) {
279                 out.write(buffer, 0, read);
280             }
281         } catch (IOException e) {
282             e.printStackTrace();
283         }
284     }
285 
286     /** Measures memory usage information and stores it in the model. */
main(String[] args)287     public static void main(String[] args) throws IOException,
288             ClassNotFoundException {
289         Root root = Root.fromFile(args[0]);
290         root.baseline = baseline();
291         for (LoadedClass loadedClass : root.loadedClasses.values()) {
292             if (loadedClass.systemClass) {
293                 loadedClass.measureMemoryUsage();
294             }
295         }
296         root.toFile(args[0]);
297     }
298 }
299