blob: e5dfb2ac4c0b7382265beb8d3dbc9bdd09d85cdc [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17import java.io.Serializable;
18import java.io.IOException;
19import java.io.BufferedReader;
20import java.io.InputStreamReader;
21import java.io.InputStream;
22import java.io.OutputStream;
23import java.util.List;
24import java.util.ArrayList;
25import java.util.Arrays;
26
27/**
28 * Memory usage information.
29 */
30class 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 static final int MAXIMUM_ERRORS = 10; // give up after this many fails
38
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
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
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
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
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
122 int javaHeapSize() {
123 return allocSize - freedSize;
124 }
125
126 int javaPagesInK() {
127 return (javaSharedPages + javaPrivatePages) * 4;
128 }
129
130 int nativePagesInK() {
131 return (nativeSharedPages + nativePrivatePages) * 4;
132 }
133 int otherPagesInK() {
134 return (otherSharedPages + otherPrivatePages) * 4;
135 }
136
137 /**
138 * Was this information available?
139 */
140 boolean isAvailable() {
141 return nativeSharedPages != -1;
142 }
143
144 /**
145 * Measures baseline memory usage.
146 */
147 static MemoryUsage baseline() {
148 return forClass(null);
149 }
150
151 private static final String CLASS_PATH = "-Xbootclasspath"
152 + ":/system/framework/core.jar"
153 + ":/system/framework/ext.jar"
154 + ":/system/framework/framework.jar"
155 + ":/system/framework/framework-tests.jar"
156 + ":/system/framework/services.jar"
157 + ":/system/framework/loadclass.jar";
158
159 private static final String[] GET_DIRTY_PAGES = {
160 "adb", "-e", "shell", "dalvikvm", CLASS_PATH, "LoadClass" };
161
162 /**
163 * Measures memory usage for the given class.
164 */
165 static MemoryUsage forClass(String className) {
166
167 // This is a coarse approximation for determining that no device is connected,
168 // or that the communication protocol has changed, but we'll keep going and stop whining.
169 if (errorCount >= MAXIMUM_ERRORS) {
170 return NOT_AVAILABLE;
171 }
172
173 MeasureWithTimeout measurer = new MeasureWithTimeout(className);
174
175 new Thread(measurer).start();
176
177 synchronized (measurer) {
178 if (measurer.memoryUsage == null) {
179 // Wait up to 10s.
180 try {
181 measurer.wait(30000);
182 } catch (InterruptedException e) {
183 System.err.println("Interrupted waiting for measurement.");
184 e.printStackTrace();
185 return NOT_AVAILABLE;
186 }
187
188 // If it's still null.
189 if (measurer.memoryUsage == null) {
190 System.err.println("Timed out while measuring "
191 + className + ".");
192 return NOT_AVAILABLE;
193 }
194 }
195
196 System.err.println("Got memory usage for " + className + ".");
197 return measurer.memoryUsage;
198 }
199 }
200
201 static class MeasureWithTimeout implements Runnable {
202
203 final String className;
204 MemoryUsage memoryUsage = null;
205
206 MeasureWithTimeout(String className) {
207 this.className = className;
208 }
209
210 public void run() {
211 MemoryUsage measured = measure();
212
213 synchronized (this) {
214 memoryUsage = measured;
215 notifyAll();
216 }
217 }
218
219 private MemoryUsage measure() {
220 String[] commands = GET_DIRTY_PAGES;
221 if (className != null) {
222 List<String> commandList = new ArrayList<String>(
223 GET_DIRTY_PAGES.length + 1);
224 commandList.addAll(Arrays.asList(commands));
225 commandList.add(className);
226 commands = commandList.toArray(new String[commandList.size()]);
227 }
228
229 try {
230 final Process process = Runtime.getRuntime().exec(commands);
231
232 final InputStream err = process.getErrorStream();
233
234 // Send error output to stderr.
235 Thread errThread = new Thread() {
236 @Override
237 public void run() {
238 copy(err, System.err);
239 }
240 };
241 errThread.setDaemon(true);
242 errThread.start();
243
244 BufferedReader in = new BufferedReader(
245 new InputStreamReader(process.getInputStream()));
246 String line = in.readLine();
247 if (line == null || !line.startsWith("DECAFBAD,")) {
248 System.err.println("Got bad response for " + className
249 + ": " + line);
250 errorCount += 1;
251 return NOT_AVAILABLE;
252 }
253
254 in.close();
255 err.close();
256 process.destroy();
257
258 return new MemoryUsage(line);
259 } catch (IOException e) {
260 System.err.println("Error getting stats for "
261 + className + ".");
262 e.printStackTrace();
263 return NOT_AVAILABLE;
264 }
265 }
266
267 }
268
269 /**
270 * Copies from one stream to another.
271 */
272 private static void copy(InputStream in, OutputStream out) {
273 byte[] buffer = new byte[1024];
274 int read;
275 try {
276 while ((read = in.read(buffer)) > -1) {
277 out.write(buffer, 0, read);
278 }
279 } catch (IOException e) {
280 e.printStackTrace();
281 }
282 }
283}