blob: 22697f87431562fab7af7a507543b6862bcbcdfd [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -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.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}