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