| /* |
| * 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; |
| } |
| } |