2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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;
28 * Memory usage information.
30 class MemoryUsage implements Serializable {
32 private static final long serialVersionUID = 0;
34 static final MemoryUsage NOT_AVAILABLE = new MemoryUsage();
36 static int errorCount = 0;
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;
50 final long nativeHeapSize;
52 public MemoryUsage(String line) {
53 String[] parsed = line.split(",");
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]);
69 nativeSharedPages = -1;
71 otherSharedPages = -1;
72 nativePrivatePages = -1;
73 javaPrivatePages = -1;
74 otherPrivatePages = -1;
83 MemoryUsage(int nativeSharedPages,
86 int nativePrivatePages,
88 int otherPrivatePages,
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;
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);
123 return allocSize - freedSize;
127 return javaHeapSize() + (int) nativeHeapSize;
131 return javaSharedPages + javaPrivatePages;
134 int nativePagesInK() {
135 return nativeSharedPages + nativePrivatePages;
137 int otherPagesInK() {
138 return otherSharedPages + otherPrivatePages;
142 return javaSharedPages + javaPrivatePages + nativeSharedPages +
143 nativePrivatePages + otherSharedPages + otherPrivatePages;
147 * Was this information available?
149 boolean isAvailable() {
150 return nativeSharedPages != -1;
154 * Measures baseline memory usage.
156 static MemoryUsage baseline() {
157 return forClass(null);
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";
168 private static final String[] GET_DIRTY_PAGES = {
169 "adb", "shell", "dalvikvm", CLASS_PATH, "LoadClass" };
172 * Measures memory usage for the given class.
174 static MemoryUsage forClass(String className) {
175 MeasureWithTimeout measurer = new MeasureWithTimeout(className);
177 new Thread(measurer).start();
179 synchronized (measurer) {
180 if (measurer.memoryUsage == null) {
183 measurer.wait(30000);
184 } catch (InterruptedException e) {
185 System.err.println("Interrupted waiting for measurement.");
187 return NOT_AVAILABLE;
190 // If it's still null.
191 if (measurer.memoryUsage == null) {
192 System.err.println("Timed out while measuring "
194 return NOT_AVAILABLE;
198 System.err.println("Got memory usage for " + className + ".");
199 return measurer.memoryUsage;
203 static class MeasureWithTimeout implements Runnable {
205 final String className;
206 MemoryUsage memoryUsage = null;
208 MeasureWithTimeout(String className) {
209 this.className = className;
213 MemoryUsage measured = measure();
215 synchronized (this) {
216 memoryUsage = measured;
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()]);
232 final Process process = Runtime.getRuntime().exec(commands);
234 final InputStream err = process.getErrorStream();
236 // Send error output to stderr.
237 Thread errThread = new Thread() {
240 copy(err, System.err);
243 errThread.setDaemon(true);
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));
253 return NOT_AVAILABLE;
260 return new MemoryUsage(line);
261 } catch (IOException e) {
262 System.err.println("Error getting stats for "
265 return NOT_AVAILABLE;
272 * Copies from one stream to another.
274 private static void copy(InputStream in, OutputStream out) {
275 byte[] buffer = new byte[1024];
278 while ((read = in.read(buffer)) > -1) {
279 out.write(buffer, 0, read);
281 } catch (IOException e) {
286 /** Measures memory usage information and stores it in the model. */
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();
296 root.toFile(args[0]);