blob: d8f95f4371a5adab4e2d611571b2e4f47a30b032 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -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
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
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 totalHeap() {
127 return javaHeapSize() + (int) nativeHeapSize;
128 }
129
130 int javaPagesInK() {
131 return javaSharedPages + javaPrivatePages;
132 }
133
134 int nativePagesInK() {
135 return nativeSharedPages + nativePrivatePages;
136 }
137 int otherPagesInK() {
138 return otherSharedPages + otherPrivatePages;
139 }
140
141 int totalPages() {
142 return javaSharedPages + javaPrivatePages + nativeSharedPages +
143 nativePrivatePages + otherSharedPages + otherPrivatePages;
144 }
145
146 /**
147 * Was this information available?
148 */
149 boolean isAvailable() {
150 return nativeSharedPages != -1;
151 }
152
153 /**
154 * Measures baseline memory usage.
155 */
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 */
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
208 MeasureWithTimeout(String className) {
209 this.className = className;
210 }
211
212 public void run() {
213 MemoryUsage measured = measure();
214
215 synchronized (this) {
216 memoryUsage = measured;
217 notifyAll();
218 }
219 }
220
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 */
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. */
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}