/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.util.List; import java.util.ArrayList; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; import java.io.Serializable; /** * A Dalvik process. */ class Proc implements Serializable { private static final long serialVersionUID = 0; /** Parent process. */ final Proc parent; /** Process ID. */ final int id; /** * Name of this process. We may not have the correct name at first, i.e. * some classes could have been loaded before the process name was set. */ String name; /** Child processes. */ final List children = new ArrayList(); /** Maps thread ID to operation stack. */ transient final Map> stacks = new HashMap>(); /** Number of operations. */ int operationCount; /** Sequential list of operations that happened in this process. */ final List operations = new ArrayList(); /** List of past process names. */ final List nameHistory = new ArrayList(); /** Constructs a new process. */ Proc(Proc parent, int id) { this.parent = parent; this.id = id; } /** Sets name of this process. */ void setName(String name) { if (!name.equals(this.name)) { if (this.name != null) { nameHistory.add(this.name); } this.name = name; } } /** * Returns true if this process comes from the zygote. */ public boolean fromZygote() { return parent != null && parent.name.equals("zygote") && !name.equals("com.android.development"); } /** * Starts an operation. * * @param threadId thread the operation started in * @param loadedClass class operation happened to * @param time the operation started */ void startOperation(int threadId, LoadedClass loadedClass, long time, Operation.Type type) { Operation o = new Operation( this, loadedClass, time, operationCount++, type); operations.add(o); LinkedList stack = stacks.get(threadId); if (stack == null) { stack = new LinkedList(); stacks.put(threadId, stack); } if (!stack.isEmpty()) { stack.getLast().subops.add(o); } stack.add(o); } /** * Ends an operation. * * @param threadId thread the operation ended in * @param loadedClass class operation happened to * @param time the operation ended */ Operation endOperation(int threadId, String className, LoadedClass loadedClass, long time) { LinkedList stack = stacks.get(threadId); if (stack == null || stack.isEmpty()) { didNotStart(className); return null; } Operation o = stack.getLast(); if (loadedClass != o.loadedClass) { didNotStart(className); return null; } stack.removeLast(); o.endTimeNanos = time; return o; } /** * Prints an error indicating that we saw the end of an operation but not * the start. A bug in the logging framework which results in dropped logs * causes this. */ private static void didNotStart(String name) { System.err.println("Warning: An operation ended on " + name + " but it never started!"); } /** * Prints this process tree to stdout. */ void print() { print(""); } /** * Prints a child proc to standard out. */ private void print(String prefix) { System.out.println(prefix + "id=" + id + ", name=" + name); for (Proc child : children) { child.print(prefix + " "); } } @Override public String toString() { return this.name; } }