Home | History | Annotate | Download | only in preload
      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 
     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 }
    299