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.List;
18import java.util.ArrayList;
19import java.util.LinkedList;
20import java.util.Map;
21import java.util.HashMap;
22import java.io.Serializable;
23
24/**
25 * A Dalvik process.
26 */
27class Proc implements Serializable {
28
29    private static final long serialVersionUID = 0;
30
31    /** Parent process. */
32    final Proc parent;
33
34    /** Process ID. */
35    final int id;
36
37    /**
38     * Name of this process. We may not have the correct name at first, i.e.
39     * some classes could have been loaded before the process name was set.
40     */
41    String name;
42
43    /** Child processes. */
44    final List<Proc> children = new ArrayList<Proc>();
45
46    /** Maps thread ID to operation stack. */
47    transient final Map<Integer, LinkedList<Operation>> stacks
48            = new HashMap<Integer, LinkedList<Operation>>();
49
50    /** Number of operations. */
51    int operationCount;
52
53    /** Sequential list of operations that happened in this process. */
54    final List<Operation> operations = new ArrayList<Operation>();
55
56    /** List of past process names. */
57    final List<String> nameHistory = new ArrayList<String>();
58
59    /** Constructs a new process. */
60    Proc(Proc parent, int id) {
61        this.parent = parent;
62        this.id = id;
63    }
64
65    /** Sets name of this process. */
66    void setName(String name) {
67        if (!name.equals(this.name)) {
68            if (this.name != null) {
69                nameHistory.add(this.name);
70            }
71            this.name = name;
72        }
73    }
74
75    /**
76     * Returns true if this process comes from the zygote.
77     */
78    public boolean fromZygote() {
79        return parent != null && parent.name.equals("zygote")
80                && !name.equals("com.android.development");
81    }
82
83    /**
84     * Starts an operation.
85     *
86     * @param threadId thread the operation started in
87     * @param loadedClass class operation happened to
88     * @param time the operation started
89     */
90    void startOperation(int threadId, LoadedClass loadedClass, long time,
91            Operation.Type type) {
92        Operation o = new Operation(
93                this, loadedClass, time, operationCount++, type);
94        operations.add(o);
95
96        LinkedList<Operation> stack = stacks.get(threadId);
97        if (stack == null) {
98            stack = new LinkedList<Operation>();
99            stacks.put(threadId, stack);
100        }
101
102        if (!stack.isEmpty()) {
103            stack.getLast().subops.add(o);
104        }
105
106        stack.add(o);
107    }
108
109    /**
110     * Ends an operation.
111     *
112     * @param threadId thread the operation ended in
113     * @param loadedClass class operation happened to
114     * @param time the operation ended
115     */
116    Operation endOperation(int threadId, String className,
117            LoadedClass loadedClass, long time) {
118        LinkedList<Operation> stack = stacks.get(threadId);
119
120        if (stack == null || stack.isEmpty()) {
121            didNotStart(className);
122            return null;
123        }
124
125        Operation o = stack.getLast();
126        if (loadedClass != o.loadedClass) {
127            didNotStart(className);
128            return null;
129        }
130
131        stack.removeLast();
132
133        o.endTimeNanos = time;
134        return o;
135    }
136
137    /**
138     * Prints an error indicating that we saw the end of an operation but not
139     * the start. A bug in the logging framework which results in dropped logs
140     * causes this.
141     */
142    private static void didNotStart(String name) {
143        System.err.println("Warning: An operation ended on " + name
144            + " but it never started!");
145    }
146
147    /**
148     * Prints this process tree to stdout.
149     */
150    void print() {
151        print("");
152    }
153
154    /**
155     * Prints a child proc to standard out.
156     */
157    private void print(String prefix) {
158        System.out.println(prefix + "id=" + id + ", name=" + name);
159        for (Proc child : children) {
160            child.print(prefix + "    ");
161        }
162    }
163
164    @Override
165    public String toString() {
166        return this.name;
167    }
168}
169