blob: 54ea89166450cd6c89c4d4bcb0dd287aed0707c1 [file] [log] [blame]
/*
* Copyright (C) 2006 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.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.HashMap;
class ThreadData implements TimeLineView.Row {
private int mId;
private String mName;
private long mGlobalStartTime = -1;
private long mGlobalEndTime = -1;
private long mLastEventTime;
private long mCpuTime;
private Call mRoot;
private Call mCurrent;
private Call mLastContextSwitch;
private ArrayList<Call> mStack = new ArrayList<Call>();
// This is a hash of all the methods that are currently on the stack.
private HashMap<MethodData, Integer> mStackMethods = new HashMap<MethodData, Integer>();
// True if no calls have ever been added to this thread
private boolean mIsEmpty;
ThreadData(int id, String name, MethodData topLevel) {
mId = id;
mName = String.format("[%d] %s", id, name);
mRoot = new Call(mName, topLevel);
mCurrent = mRoot;
mIsEmpty = true;
}
public boolean isEmpty() {
return mIsEmpty;
}
public String getName() {
return mName;
}
public Call getCalltreeRoot() {
return mRoot;
}
void handleCall(Call call, long globalTime) {
mIsEmpty = false;
long currentTime = call.mThreadStartTime;
if (currentTime < mLastEventTime) {
System.err
.printf(
"ThreadData: '%1$s' call time (%2$d) is less than previous time (%3$d) for thread '%4$s'\n",
call.getName(), currentTime, mLastEventTime, mName);
System.exit(1);
}
long elapsed = currentTime - mLastEventTime;
mCpuTime += elapsed;
if (call.getMethodAction() == 0) {
// This is a method entry.
enter(call, elapsed);
} else {
// This is a method exit.
exit(call, elapsed, globalTime);
}
mLastEventTime = currentTime;
mGlobalEndTime = globalTime;
}
private void enter(Call c, long elapsed) {
Call caller = mCurrent;
push(c);
// Check the stack for a matching method to determine if this call
// is recursive.
MethodData md = c.mMethodData;
Integer num = mStackMethods.get(md);
if (num == null) {
num = 0;
} else if (num > 0) {
c.setRecursive(true);
}
num += 1;
mStackMethods.put(md, num);
mCurrent = c;
// Add the elapsed time to the caller's exclusive time
caller.addExclusiveTime(elapsed);
}
private void exit(Call c, long elapsed, long globalTime) {
mCurrent.mGlobalEndTime = globalTime;
Call top = pop();
if (top == null) {
return;
}
if (mCurrent.mMethodData != c.mMethodData) {
String error = "Method exit (" + c.getName()
+ ") does not match current method (" + mCurrent.getName()
+ ")";
throw new RuntimeException(error);
} else {
long duration = c.mThreadStartTime - mCurrent.mThreadStartTime;
Call caller = top();
mCurrent.addExclusiveTime(elapsed);
mCurrent.addInclusiveTime(duration, caller);
if (caller == null) {
caller = mRoot;
}
mCurrent = caller;
}
}
public void push(Call c) {
mStack.add(c);
}
public Call pop() {
ArrayList<Call> stack = mStack;
if (stack.size() == 0)
return null;
Call top = stack.get(stack.size() - 1);
stack.remove(stack.size() - 1);
// Decrement the count on the method in the hash table and remove
// the entry when it goes to zero.
MethodData md = top.mMethodData;
Integer num = mStackMethods.get(md);
if (num != null) {
num -= 1;
if (num <= 0) {
mStackMethods.remove(md);
} else {
mStackMethods.put(md, num);
}
}
return top;
}
public Call top() {
ArrayList<Call> stack = mStack;
if (stack.size() == 0)
return null;
return stack.get(stack.size() - 1);
}
public long endTrace() {
// If we have calls on the stack when the trace ends, then clean up
// the stack and compute the inclusive time of the methods by pretending
// that we are exiting from their methods now.
while (mCurrent != mRoot) {
long duration = mLastEventTime - mCurrent.mThreadStartTime;
pop();
Call caller = top();
mCurrent.addInclusiveTime(duration, caller);
mCurrent.mGlobalEndTime = mGlobalEndTime;
if (caller == null) {
caller = mRoot;
}
mCurrent = caller;
}
return mLastEventTime;
}
@Override
public String toString() {
return mName;
}
public int getId() {
return mId;
}
public void setCpuTime(long cpuTime) {
mCpuTime = cpuTime;
}
public long getCpuTime() {
return mCpuTime;
}
public void setGlobalStartTime(long globalStartTime) {
mGlobalStartTime = globalStartTime;
}
public long getGlobalStartTime() {
return mGlobalStartTime;
}
public void setLastEventTime(long lastEventTime) {
mLastEventTime = lastEventTime;
}
public long getLastEventTime() {
return mLastEventTime;
}
public void setGlobalEndTime(long globalEndTime) {
mGlobalEndTime = globalEndTime;
}
public long getGlobalEndTime() {
return mGlobalEndTime;
}
public void setLastContextSwitch(Call lastContextSwitch) {
mLastContextSwitch = lastContextSwitch;
}
public Call getLastContextSwitch() {
return mLastContextSwitch;
}
}