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