• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 
17 package com.android.traceview;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 
22 class ThreadData implements TimeLineView.Row {
23 
24     private int mId;
25     private String mName;
26     private long mGlobalStartTime = -1;
27     private long mGlobalEndTime = -1;
28     private long mLastEventTime;
29     private long mCpuTime;
30     private Call mRoot;
31     private Call mCurrent;
32     private Call mLastContextSwitch;
33     private ArrayList<Call> mStack = new ArrayList<Call>();
34 
35     // This is a hash of all the methods that are currently on the stack.
36     private HashMap<MethodData, Integer> mStackMethods = new HashMap<MethodData, Integer>();
37 
38     // True if no calls have ever been added to this thread
39     private boolean mIsEmpty;
40 
ThreadData(int id, String name, MethodData topLevel)41     ThreadData(int id, String name, MethodData topLevel) {
42         mId = id;
43         mName = String.format("[%d] %s", id, name);
44         mRoot = new Call(mName, topLevel);
45         mCurrent = mRoot;
46         mIsEmpty = true;
47     }
48 
isEmpty()49     public boolean isEmpty() {
50         return mIsEmpty;
51     }
52 
getName()53     public String getName() {
54         return mName;
55     }
56 
getCalltreeRoot()57     public Call getCalltreeRoot() {
58         return mRoot;
59     }
60 
handleCall(Call call, long globalTime)61     void handleCall(Call call, long globalTime) {
62         mIsEmpty = false;
63         long currentTime = call.mThreadStartTime;
64         if (currentTime < mLastEventTime) {
65             System.err
66             .printf(
67                     "ThreadData: '%1$s' call time (%2$d) is less than previous time (%3$d) for thread '%4$s'\n",
68                     call.getName(), currentTime, mLastEventTime, mName);
69             System.exit(1);
70         }
71         long elapsed = currentTime - mLastEventTime;
72         mCpuTime += elapsed;
73         if (call.getMethodAction() == 0) {
74             // This is a method entry.
75             enter(call, elapsed);
76         } else {
77             // This is a method exit.
78             exit(call, elapsed, globalTime);
79         }
80         mLastEventTime = currentTime;
81         mGlobalEndTime = globalTime;
82     }
83 
enter(Call c, long elapsed)84     private void enter(Call c, long elapsed) {
85         Call caller = mCurrent;
86         push(c);
87 
88         // Check the stack for a matching method to determine if this call
89         // is recursive.
90         MethodData md = c.mMethodData;
91         Integer num = mStackMethods.get(md);
92         if (num == null) {
93             num = 0;
94         } else if (num > 0) {
95             c.setRecursive(true);
96         }
97         num += 1;
98         mStackMethods.put(md, num);
99         mCurrent = c;
100 
101         // Add the elapsed time to the caller's exclusive time
102         caller.addExclusiveTime(elapsed);
103     }
104 
exit(Call c, long elapsed, long globalTime)105     private void exit(Call c, long elapsed, long globalTime) {
106         mCurrent.mGlobalEndTime = globalTime;
107         Call top = pop();
108         if (top == null) {
109             return;
110         }
111 
112         if (mCurrent.mMethodData != c.mMethodData) {
113             String error = "Method exit (" + c.getName()
114                     + ") does not match current method (" + mCurrent.getName()
115                     + ")";
116             throw new RuntimeException(error);
117         } else {
118             long duration = c.mThreadStartTime - mCurrent.mThreadStartTime;
119             Call caller = top();
120             mCurrent.addExclusiveTime(elapsed);
121             mCurrent.addInclusiveTime(duration, caller);
122             if (caller == null) {
123                 caller = mRoot;
124             }
125             mCurrent = caller;
126         }
127     }
128 
push(Call c)129     public void push(Call c) {
130         mStack.add(c);
131     }
132 
pop()133     public Call pop() {
134         ArrayList<Call> stack = mStack;
135         if (stack.size() == 0)
136             return null;
137         Call top = stack.get(stack.size() - 1);
138         stack.remove(stack.size() - 1);
139 
140         // Decrement the count on the method in the hash table and remove
141         // the entry when it goes to zero.
142         MethodData md = top.mMethodData;
143         Integer num = mStackMethods.get(md);
144         if (num != null) {
145             num -= 1;
146             if (num <= 0) {
147                 mStackMethods.remove(md);
148             } else {
149                 mStackMethods.put(md, num);
150             }
151         }
152         return top;
153     }
154 
top()155     public Call top() {
156         ArrayList<Call> stack = mStack;
157         if (stack.size() == 0)
158             return null;
159         return stack.get(stack.size() - 1);
160     }
161 
endTrace()162     public long endTrace() {
163         // If we have calls on the stack when the trace ends, then clean up
164         // the stack and compute the inclusive time of the methods by pretending
165         // that we are exiting from their methods now.
166         while (mCurrent != mRoot) {
167             long duration = mLastEventTime - mCurrent.mThreadStartTime;
168             pop();
169             Call caller = top();
170             mCurrent.addInclusiveTime(duration, caller);
171             mCurrent.mGlobalEndTime = mGlobalEndTime;
172             if (caller == null) {
173                 caller = mRoot;
174             }
175             mCurrent = caller;
176         }
177         return mLastEventTime;
178     }
179 
180     @Override
toString()181     public String toString() {
182         return mName;
183     }
184 
getId()185     public int getId() {
186         return mId;
187     }
188 
setCpuTime(long cpuTime)189     public void setCpuTime(long cpuTime) {
190         mCpuTime = cpuTime;
191     }
192 
getCpuTime()193     public long getCpuTime() {
194         return mCpuTime;
195     }
196 
setGlobalStartTime(long globalStartTime)197     public void setGlobalStartTime(long globalStartTime) {
198         mGlobalStartTime = globalStartTime;
199     }
200 
getGlobalStartTime()201     public long getGlobalStartTime() {
202         return mGlobalStartTime;
203     }
204 
setLastEventTime(long lastEventTime)205     public void setLastEventTime(long lastEventTime) {
206         mLastEventTime = lastEventTime;
207     }
208 
getLastEventTime()209     public long getLastEventTime() {
210         return mLastEventTime;
211     }
212 
setGlobalEndTime(long globalEndTime)213     public void setGlobalEndTime(long globalEndTime) {
214         mGlobalEndTime = globalEndTime;
215     }
216 
getGlobalEndTime()217     public long getGlobalEndTime() {
218         return mGlobalEndTime;
219     }
220 
setLastContextSwitch(Call lastContextSwitch)221     public void setLastContextSwitch(Call lastContextSwitch) {
222         mLastContextSwitch = lastContextSwitch;
223     }
224 
getLastContextSwitch()225     public Call getLastContextSwitch() {
226         return mLastContextSwitch;
227     }
228 }
229