Proc.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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 17import java.util.Set; 18import java.util.HashSet; 19import java.util.Arrays; 20import java.util.List; 21import java.util.ArrayList; 22import java.util.LinkedList; 23import java.util.Map; 24import java.util.HashMap; 25import java.util.Collections; 26import java.util.TreeSet; 27import java.io.Serializable; 28 29/** 30 * A Dalvik process. 31 */ 32class Proc implements Serializable { 33 34 private static final long serialVersionUID = 0; 35 36 /** 37 * Default percentage of time to cut off of app class loading times. 38 */ 39 static final int PERCENTAGE_TO_PRELOAD = 75; 40 41 /** 42 * Maximum number of classes to preload for a given process. 43 */ 44 static final int MAX_TO_PRELOAD = 100; 45 46 /** Parent process. */ 47 final Proc parent; 48 49 /** Process ID. */ 50 final int id; 51 52 /** 53 * Name of this process. We may not have the correct name at first, i.e. 54 * some classes could have been loaded before the process name was set. 55 */ 56 String name; 57 58 /** Child processes. */ 59 final List<Proc> children = new ArrayList<Proc>(); 60 61 /** Maps thread ID to operation stack. */ 62 transient final Map<Integer, LinkedList<Operation>> stacks 63 = new HashMap<Integer, LinkedList<Operation>>(); 64 65 /** Number of operations. */ 66 int operationCount; 67 68 /** Sequential list of operations that happened in this process. */ 69 final List<Operation> operations = new ArrayList<Operation>(); 70 71 /** List of past process names. */ 72 final List<String> nameHistory = new ArrayList<String>(); 73 74 /** Constructs a new process. */ 75 Proc(Proc parent, int id) { 76 this.parent = parent; 77 this.id = id; 78 } 79 80 /** Sets name of this process. */ 81 void setName(String name) { 82 if (!name.equals(this.name)) { 83 if (this.name != null) { 84 nameHistory.add(this.name); 85 } 86 this.name = name; 87 } 88 } 89 90 /** 91 * Returns the percentage of time we should cut by preloading for this 92 * app. 93 */ 94 int percentageToPreload() { 95 return PERCENTAGE_TO_PRELOAD; 96 } 97 98 /** 99 * Returns a list of classes which should be preloaded. 100 * 101 * @param takeAllClasses forces all classes to be taken (irrespective of ranking) 102 */ 103 List<LoadedClass> highestRankedClasses(boolean takeAllClasses) { 104 if (!isApplication()) { 105 return Collections.emptyList(); 106 } 107 108 // Sort by rank. 109 Operation[] ranked = new Operation[operations.size()]; 110 ranked = operations.toArray(ranked); 111 Arrays.sort(ranked, new ClassRank()); 112 113 // The percentage of time to save by preloading. 114 int timeToSave = totalTimeMicros() * percentageToPreload() / 100; 115 int timeSaved = 0; 116 117 boolean service = Policy.isService(this.name); 118 119 List<LoadedClass> highest = new ArrayList<LoadedClass>(); 120 for (Operation operation : ranked) { 121 122 // These are actual ranking decisions, which can be overridden 123 if (!takeAllClasses) { 124 if (highest.size() >= MAX_TO_PRELOAD) { 125 System.out.println(name + " got " 126 + (timeSaved * 100 / timeToSave) + "% through"); 127 break; 128 } 129 130 if (timeSaved >= timeToSave) { 131 break; 132 } 133 } 134 135 // The remaining rules apply even to wired-down processes 136 if (!Policy.isPreloadableClass(operation.loadedClass.name)) { 137 continue; 138 } 139 140 if (!operation.loadedClass.systemClass) { 141 continue; 142 } 143 144 // Only load java.* class for services. 145 if (!service || operation.loadedClass.name.startsWith("java.")) { 146 highest.add(operation.loadedClass); 147 } 148 149 // For services, still count the time even if it's not in java.* 150 timeSaved += operation.medianExclusiveTimeMicros(); 151 } 152 153 return highest; 154 } 155 156 /** 157 * Total time spent class loading and initializing. 158 */ 159 int totalTimeMicros() { 160 int totalTime = 0; 161 for (Operation operation : operations) { 162 totalTime += operation.medianExclusiveTimeMicros(); 163 } 164 return totalTime; 165 } 166 167 /** 168 * Returns true if this process is an app. 169 * 170 * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote. 171 */ 172 public boolean isApplication() { 173 return Policy.isFromZygote(name); 174 } 175 176 /** 177 * Starts an operation. 178 * 179 * @param threadId thread the operation started in 180 * @param loadedClass class operation happened to 181 * @param time the operation started 182 */ 183 void startOperation(int threadId, LoadedClass loadedClass, long time, 184 Operation.Type type) { 185 Operation o = new Operation( 186 this, loadedClass, time, operationCount++, type); 187 operations.add(o); 188 189 LinkedList<Operation> stack = stacks.get(threadId); 190 if (stack == null) { 191 stack = new LinkedList<Operation>(); 192 stacks.put(threadId, stack); 193 } 194 195 if (!stack.isEmpty()) { 196 stack.getLast().subops.add(o); 197 } 198 199 stack.add(o); 200 } 201 202 /** 203 * Ends an operation. 204 * 205 * @param threadId thread the operation ended in 206 * @param loadedClass class operation happened to 207 * @param time the operation ended 208 */ 209 Operation endOperation(int threadId, String className, 210 LoadedClass loadedClass, long time) { 211 LinkedList<Operation> stack = stacks.get(threadId); 212 213 if (stack == null || stack.isEmpty()) { 214 didNotStart(className); 215 return null; 216 } 217 218 Operation o = stack.getLast(); 219 if (loadedClass != o.loadedClass) { 220 didNotStart(className); 221 return null; 222 } 223 224 stack.removeLast(); 225 226 o.endTimeNanos = time; 227 return o; 228 } 229 230 /** 231 * Prints an error indicating that we saw the end of an operation but not 232 * the start. A bug in the logging framework which results in dropped logs 233 * causes this. 234 */ 235 private static void didNotStart(String name) { 236 System.err.println("Warning: An operation ended on " + name 237 + " but it never started!"); 238 } 239 240 /** 241 * Prints this process tree to stdout. 242 */ 243 void print() { 244 print(""); 245 } 246 247 /** 248 * Prints a child proc to standard out. 249 */ 250 private void print(String prefix) { 251 System.out.println(prefix + "id=" + id + ", name=" + name); 252 for (Proc child : children) { 253 child.print(prefix + " "); 254 } 255 } 256 257 @Override 258 public String toString() { 259 return this.name; 260 } 261} 262