diff options
Diffstat (limited to 'traceview')
-rw-r--r-- | traceview/src/com/android/traceview/Call.java | 148 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/DmTraceReader.java | 578 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/MethodData.java | 149 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/ProfileData.java | 21 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/ProfileProvider.java | 189 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/ProfileSelf.java | 9 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/ProfileView.java | 10 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/ThreadData.java | 228 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/TimeBase.java | 71 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/TimeLineView.java | 182 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/TraceAction.java (renamed from traceview/src/com/android/traceview/QtraceReader.java) | 34 | ||||
-rw-r--r-- | traceview/src/com/android/traceview/TraceReader.java | 22 |
12 files changed, 1030 insertions, 611 deletions
diff --git a/traceview/src/com/android/traceview/Call.java b/traceview/src/com/android/traceview/Call.java index 40ac244..53f16cb 100644 --- a/traceview/src/com/android/traceview/Call.java +++ b/traceview/src/com/android/traceview/Call.java @@ -19,52 +19,30 @@ package com.android.traceview; import org.eclipse.swt.graphics.Color; class Call implements TimeLineView.Block { - - // Values for bits within the mFlags field. - private static final int METHOD_ACTION_MASK = 0x3; - private static final int IS_RECURSIVE = 0x10; - - private int mThreadId; - private int mFlags; - MethodData mMethodData; - - /** 0-based thread-local start time */ - long mThreadStartTime; - - /** global start time */ - long mGlobalStartTime; + final private ThreadData mThreadData; + final private MethodData mMethodData; + final Call mCaller; // the caller, or null if this is the root - /** global end time */ - long mGlobalEndTime; - private String mName; + private boolean mIsRecursive; - /** - * This constructor is used for the root of a Call tree. The name is - * the name of the corresponding thread. - */ - Call(String name, MethodData methodData) { - mName = name; - mMethodData = methodData; - } + long mGlobalStartTime; + long mGlobalEndTime; - Call() { - } - - Call(int threadId, MethodData methodData, long time, int methodAction) { - mThreadId = threadId; - mMethodData = methodData; - mThreadStartTime = time; - mFlags = methodAction & METHOD_ACTION_MASK; - mName = methodData.getProfileName(); - } - - public void set(int threadId, MethodData methodData, long time, int methodAction) { - mThreadId = threadId; + long mThreadStartTime; + long mThreadEndTime; + + long mInclusiveRealTime; // real time spent in this call including its children + long mExclusiveRealTime; // real time spent in this call including its children + + long mInclusiveCpuTime; // cpu time spent in this call including its children + long mExclusiveCpuTime; // cpu time spent in this call excluding its children + + Call(ThreadData threadData, MethodData methodData, Call caller) { + mThreadData = threadData; mMethodData = methodData; - mThreadStartTime = time; - mFlags = methodAction & METHOD_ACTION_MASK; mName = methodData.getProfileName(); + mCaller = caller; } public void updateName() { @@ -87,20 +65,24 @@ class Call implements TimeLineView.Block { return mGlobalEndTime; } - public Color getColor() { - return mMethodData.getColor(); + public long getExclusiveCpuTime() { + return mExclusiveCpuTime; } - public void addExclusiveTime(long elapsed) { - mMethodData.addElapsedExclusive(elapsed); - if ((mFlags & IS_RECURSIVE) == 0) { - mMethodData.addTopExclusive(elapsed); - } + public long getInclusiveCpuTime() { + return mInclusiveCpuTime; + } + + public long getExclusiveRealTime() { + return mExclusiveRealTime; } - public void addInclusiveTime(long elapsed, Call parent) { - boolean isRecursive = (mFlags & IS_RECURSIVE) != 0; - mMethodData.addElapsedInclusive(elapsed, isRecursive, parent); + public long getInclusiveRealTime() { + return mInclusiveRealTime; + } + + public Color getColor() { + return mMethodData.getColor(); } public String getName() { @@ -111,31 +93,71 @@ class Call implements TimeLineView.Block { mName = name; } - int getThreadId() { - return mThreadId; + public ThreadData getThreadData() { + return mThreadData; + } + + public int getThreadId() { + return mThreadData.getId(); } public MethodData getMethodData() { return mMethodData; } - int getMethodAction() { - return mFlags & METHOD_ACTION_MASK; + public boolean isContextSwitch() { + return mMethodData.getId() < 0; } - public void dump() { - System.out.printf("%s [%d, %d]\n", mName, mGlobalStartTime, mGlobalEndTime); + public boolean isIgnoredBlock() { + // Ignore the top-level call or context switches within the top-level call. + return mCaller == null || isContextSwitch() && mCaller.mCaller == null; } - public void setRecursive(boolean isRecursive) { - if (isRecursive) { - mFlags |= IS_RECURSIVE; - } else { - mFlags &= ~IS_RECURSIVE; - } + public TimeLineView.Block getParentBlock() { + return mCaller; } public boolean isRecursive() { - return (mFlags & IS_RECURSIVE) != 0; + return mIsRecursive; + } + + void setRecursive(boolean isRecursive) { + mIsRecursive = isRecursive; + } + + void addCpuTime(long elapsedCpuTime) { + mExclusiveCpuTime += elapsedCpuTime; + mInclusiveCpuTime += elapsedCpuTime; + } + + /** + * Record time spent in the method call. + */ + void finish() { + if (mCaller != null) { + mCaller.mInclusiveCpuTime += mInclusiveCpuTime; + mCaller.mInclusiveRealTime += mInclusiveRealTime; + } + + mMethodData.addElapsedExclusive(mExclusiveCpuTime, mExclusiveRealTime); + if (!mIsRecursive) { + mMethodData.addTopExclusive(mExclusiveCpuTime, mExclusiveRealTime); + } + mMethodData.addElapsedInclusive(mInclusiveCpuTime, mInclusiveRealTime, + mIsRecursive, mCaller); + } + + public static final class TraceAction { + public static final int ACTION_ENTER = 0; + public static final int ACTION_EXIT = 1; + + public final int mAction; + public final Call mCall; + + public TraceAction(int action, Call call) { + mAction = action; + mCall = call; + } } } diff --git a/traceview/src/com/android/traceview/DmTraceReader.java b/traceview/src/com/android/traceview/DmTraceReader.java index dcf17a2..fbcd13e 100644 --- a/traceview/src/com/android/traceview/DmTraceReader.java +++ b/traceview/src/com/android/traceview/DmTraceReader.java @@ -35,25 +35,37 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class DmTraceReader extends TraceReader { - - private int mVersionNumber = 0; - private boolean mDebug = false; private static final int TRACE_MAGIC = 0x574f4c53; + + private static final int METHOD_TRACE_ENTER = 0x00; // method entry + private static final int METHOD_TRACE_EXIT = 0x01; // method exit + private static final int METHOD_TRACE_UNROLL = 0x02; // method exited by exception unrolling + + // When in dual clock mode, we report that a context switch has occurred + // when skew between the real time and thread cpu clocks is more than this + // many microseconds. + private static final long MIN_CONTEXT_SWITCH_TIME_USEC = 100; + + private enum ClockSource { + THREAD_CPU, WALL, DUAL, + }; + + private int mVersionNumber; private boolean mRegression; private ProfileProvider mProfileProvider; private String mTraceFileName; private MethodData mTopLevel; private ArrayList<Call> mCallList; - private ArrayList<Call> mSwitchList; private HashMap<String, String> mPropertiesMap; private HashMap<Integer, MethodData> mMethodMap; private HashMap<Integer, ThreadData> mThreadMap; private ThreadData[] mSortedThreads; private MethodData[] mSortedMethods; - private long mGlobalEndTime; + private long mTotalCpuTime; + private long mTotalRealTime; private MethodData mContextSwitch; - private int mOffsetToData; - private byte[] mBytes = new byte[8]; + private int mRecordSize; + private ClockSource mClockSource; // A regex for matching the thread "id name" lines in the .key file private static final Pattern mIdNamePattern = Pattern.compile("(\\d+)\t(.*)"); //$NON-NLS-1$ @@ -64,14 +76,15 @@ public class DmTraceReader extends TraceReader { mPropertiesMap = new HashMap<String, String>(); mMethodMap = new HashMap<Integer, MethodData>(); mThreadMap = new HashMap<Integer, ThreadData>(); + mCallList = new ArrayList<Call>(); // Create a single top-level MethodData object to hold the profile data // for time spent in the unknown caller. mTopLevel = new MethodData(0, "(toplevel)"); mContextSwitch = new MethodData(-1, "(context switch)"); mMethodMap.put(0, mTopLevel); + mMethodMap.put(-1, mContextSwitch); generateTrees(); - // dumpTrees(); } void generateTrees() { @@ -92,38 +105,6 @@ public class DmTraceReader extends TraceReader { return mProfileProvider; } - Call readCall(MappedByteBuffer buffer, Call call) { - int threadId; - int methodId; - long time; - - try { - if (mVersionNumber == 1) - threadId = buffer.get(); - else - threadId = buffer.getShort(); - methodId = buffer.getInt(); - time = buffer.getInt(); - } catch (BufferUnderflowException ex) { - return null; - } - - int methodAction = methodId & 0x03; - methodId = methodId & ~0x03; - MethodData methodData = mMethodMap.get(methodId); - if (methodData == null) { - String name = String.format("(0x%1$x)", methodId); //$NON-NLS-1$ - methodData = new MethodData(methodId, name); - } - - if (call != null) { - call.set(threadId, methodData, time, methodAction); - } else { - call = new Call(threadId, methodData, time, methodAction); - } - return call; - } - private MappedByteBuffer mapFile(String filename, long offset) { MappedByteBuffer buffer = null; try { @@ -151,165 +132,279 @@ public class DmTraceReader extends TraceReader { magic, TRACE_MAGIC); throw new RuntimeException(); } + // read version int version = buffer.getShort(); - + if (version != mVersionNumber) { + System.err.printf( + "Error: version number mismatch; got %d in data header but %d in options\n", + version, mVersionNumber); + throw new RuntimeException(); + } + if (version < 1 || version > 3) { + System.err.printf( + "Error: unsupported trace version number %d. " + + "Please use a newer version of TraceView to read this file.", version); + throw new RuntimeException(); + } + // read offset - mOffsetToData = buffer.getShort() - 16; - + int offsetToData = buffer.getShort() - 16; + // read startWhen buffer.getLong(); - - // Skip over "mOffsetToData" bytes - for (int ii = 0; ii < mOffsetToData; ii++) { + + // read record size + if (version == 1) { + mRecordSize = 9; + } else if (version == 2) { + mRecordSize = 10; + } else { + mRecordSize = buffer.getShort(); + offsetToData -= 2; + } + + // Skip over offsetToData bytes + while (offsetToData-- > 0) { buffer.get(); } - - // Save this position so that we can re-read the data later - buffer.mark(); } private void parseData(long offset) { MappedByteBuffer buffer = mapFile(mTraceFileName, offset); readDataFileHeader(buffer); - parseDataPass1(buffer); - - buffer.reset(); - parseDataPass2(buffer); - } - - private void parseDataPass1(MappedByteBuffer buffer) { - mSwitchList = new ArrayList<Call>(); - - // Read the first call so that we can set "prevThreadData" - Call call = new Call(); - call = readCall(buffer, call); - if (call == null) - return; - long callTime = call.mThreadStartTime; - long prevCallTime = 0; - ThreadData threadData = mThreadMap.get(call.getThreadId()); - if (threadData == null) { - String name = String.format("[%1$d]", call.getThreadId()); //$NON-NLS-1$ - threadData = new ThreadData(call.getThreadId(), name, mTopLevel); - mThreadMap.put(call.getThreadId(), threadData); - } - ThreadData prevThreadData = threadData; - while (true) { - // If a context switch occurred, then insert a placeholder "call" - // record so that we can do something reasonable with the global - // timestamps. - if (prevThreadData != threadData) { - Call switchEnter = new Call(prevThreadData.getId(), - mContextSwitch, prevCallTime, 0); - prevThreadData.setLastContextSwitch(switchEnter); - mSwitchList.add(switchEnter); - Call contextSwitch = threadData.getLastContextSwitch(); - if (contextSwitch != null) { - long prevStartTime = contextSwitch.mThreadStartTime; - long elapsed = callTime - prevStartTime; - long beforeSwitch = elapsed / 2; - long afterSwitch = elapsed - beforeSwitch; - long exitTime = callTime - afterSwitch; - contextSwitch.mThreadStartTime = prevStartTime + beforeSwitch; - Call switchExit = new Call(threadData.getId(), - mContextSwitch, exitTime, 1); - - mSwitchList.add(switchExit); + + ArrayList<TraceAction> trace = null; + if (mClockSource == ClockSource.THREAD_CPU) { + trace = new ArrayList<TraceAction>(); + } + + final boolean haveThreadClock = mClockSource != ClockSource.WALL; + final boolean haveGlobalClock = mClockSource != ClockSource.THREAD_CPU; + + // Parse all call records to obtain elapsed time information. + ThreadData prevThreadData = null; + for (;;) { + int threadId; + int methodId; + long threadTime, globalTime; + try { + int recordSize = mRecordSize; + + if (mVersionNumber == 1) { + threadId = buffer.get(); + recordSize -= 1; + } else { + threadId = buffer.getShort(); + recordSize -= 2; } - prevThreadData = threadData; - } - // Read the next call - call = readCall(buffer, call); - if (call == null) { + methodId = buffer.getInt(); + recordSize -= 4; + + switch (mClockSource) { + case WALL: + threadTime = 0; + globalTime = buffer.getInt(); + recordSize -= 4; + break; + case DUAL: + threadTime = buffer.getInt(); + globalTime = buffer.getInt(); + recordSize -= 8; + break; + default: + case THREAD_CPU: + threadTime = buffer.getInt(); + globalTime = 0; + recordSize -= 4; + break; + } + + while (recordSize-- > 0) { + buffer.get(); + } + } catch (BufferUnderflowException ex) { break; } - prevCallTime = callTime; - callTime = call.mThreadStartTime; - threadData = mThreadMap.get(call.getThreadId()); - if (threadData == null) { - String name = String.format("[%d]", call.getThreadId()); - threadData = new ThreadData(call.getThreadId(), name, mTopLevel); - mThreadMap.put(call.getThreadId(), threadData); + int methodAction = methodId & 0x03; + methodId = methodId & ~0x03; + MethodData methodData = mMethodMap.get(methodId); + if (methodData == null) { + String name = String.format("(0x%1$x)", methodId); //$NON-NLS-1$ + methodData = new MethodData(methodId, name); + mMethodMap.put(methodId, methodData); } - } - } - void parseDataPass2(MappedByteBuffer buffer) { - mCallList = new ArrayList<Call>(); + ThreadData threadData = mThreadMap.get(threadId); + if (threadData == null) { + String name = String.format("[%1$d]", threadId); //$NON-NLS-1$ + threadData = new ThreadData(threadId, name, mTopLevel); + mThreadMap.put(threadId, threadData); + } - // Read the first call so that we can set "prevThreadData" - Call call = readCall(buffer, null); - long callTime = call.mThreadStartTime; - long prevCallTime = callTime; - ThreadData threadData = mThreadMap.get(call.getThreadId()); - ThreadData prevThreadData = threadData; - threadData.setGlobalStartTime(0); - - int nthContextSwitch = 0; + long elapsedGlobalTime = 0; + if (haveGlobalClock) { + if (!threadData.mHaveGlobalTime) { + threadData.mGlobalStartTime = globalTime; + threadData.mHaveGlobalTime = true; + } else { + elapsedGlobalTime = globalTime - threadData.mGlobalEndTime; + } + threadData.mGlobalEndTime = globalTime; + } - // Assign a global timestamp to each event. - long globalTime = 0; - while (true) { - long elapsed = callTime - prevCallTime; - if (threadData != prevThreadData) { - // Get the next context switch. This one is entered - // by the previous thread. - Call contextSwitch = mSwitchList.get(nthContextSwitch++); - mCallList.add(contextSwitch); - elapsed = contextSwitch.mThreadStartTime - prevCallTime; - globalTime += elapsed; - elapsed = 0; - contextSwitch.mGlobalStartTime = globalTime; - prevThreadData.handleCall(contextSwitch, globalTime); - - if (!threadData.isEmpty()) { - // This context switch is exited by the current thread. - contextSwitch = mSwitchList.get(nthContextSwitch++); - mCallList.add(contextSwitch); - contextSwitch.mGlobalStartTime = globalTime; - elapsed = callTime - contextSwitch.mThreadStartTime; - threadData.handleCall(contextSwitch, globalTime); + if (haveThreadClock) { + long elapsedThreadTime = 0; + if (!threadData.mHaveThreadTime) { + threadData.mThreadStartTime = threadTime; + threadData.mThreadCurrentTime = threadTime; + threadData.mHaveThreadTime = true; + } else { + elapsedThreadTime = threadTime - threadData.mThreadEndTime; + } + threadData.mThreadEndTime = threadTime; + + if (!haveGlobalClock) { + // Detect context switches whenever execution appears to switch from one + // thread to another. This assumption is only valid on uniprocessor + // systems (which is why we now have a dual clock mode). + // We represent context switches in the trace by pushing a call record + // with MethodData mContextSwitch onto the stack of the previous + // thread. We arbitrarily set the start and end time of the context + // switch such that the context switch occurs in the middle of the thread + // time and itself accounts for zero thread time. + if (prevThreadData != null && prevThreadData != threadData) { + // Begin context switch from previous thread. + Call switchCall = prevThreadData.enter(mContextSwitch, trace); + switchCall.mThreadStartTime = prevThreadData.mThreadEndTime; + mCallList.add(switchCall); + + // Return from context switch to current thread. + Call top = threadData.top(); + if (top.getMethodData() == mContextSwitch) { + threadData.exit(mContextSwitch, trace); + long beforeSwitch = elapsedThreadTime / 2; + top.mThreadStartTime += beforeSwitch; + top.mThreadEndTime = top.mThreadStartTime; + } + } + prevThreadData = threadData; + } else { + // If we have a global clock, then we can detect context switches (or blocking + // calls or cpu suspensions or clock anomalies) by comparing global time to + // thread time for successive calls that occur on the same thread. + // As above, we represent the context switch using a special method call. + long sleepTime = elapsedGlobalTime - elapsedThreadTime; + if (sleepTime > MIN_CONTEXT_SWITCH_TIME_USEC) { + Call switchCall = threadData.enter(mContextSwitch, trace); + long beforeSwitch = elapsedThreadTime / 2; + long afterSwitch = elapsedThreadTime - beforeSwitch; + switchCall.mGlobalStartTime = globalTime - elapsedGlobalTime + beforeSwitch; + switchCall.mGlobalEndTime = globalTime - afterSwitch; + switchCall.mThreadStartTime = threadTime - afterSwitch; + switchCall.mThreadEndTime = switchCall.mThreadStartTime; + threadData.exit(mContextSwitch, trace); + mCallList.add(switchCall); + } } - // If the thread's global start time has not been set yet, - // then set it. - if (threadData.getGlobalStartTime() == -1) - threadData.setGlobalStartTime(globalTime); - prevThreadData = threadData; + // Add thread CPU time. + Call top = threadData.top(); + top.addCpuTime(elapsedThreadTime); } - globalTime += elapsed; - call.mGlobalStartTime = globalTime; - - threadData.handleCall(call, globalTime); - mCallList.add(call); - - // Read the next call - call = readCall(buffer, null); - if (call == null) { - break; + switch (methodAction) { + case METHOD_TRACE_ENTER: { + Call call = threadData.enter(methodData, trace); + if (haveGlobalClock) { + call.mGlobalStartTime = globalTime; + } + if (haveThreadClock) { + call.mThreadStartTime = threadTime; + } + mCallList.add(call); + break; + } + case METHOD_TRACE_EXIT: + case METHOD_TRACE_UNROLL: { + Call call = threadData.exit(methodData, trace); + if (call != null) { + if (haveGlobalClock) { + call.mGlobalEndTime = globalTime; + } + if (haveThreadClock) { + call.mThreadEndTime = threadTime; + } + } + break; + } + default: + throw new RuntimeException("Unrecognized method action: " + methodAction); } - prevCallTime = callTime; - callTime = call.mThreadStartTime; - threadData = mThreadMap.get(call.getThreadId()); } - // Allow each thread to do any cleanup of the call stack. - // Also add the elapsed time for each thread to the toplevel - // method's inclusive time. - for (int id : mThreadMap.keySet()) { - threadData = mThreadMap.get(id); - long endTime = threadData.endTrace(); - if (endTime > 0) - mTopLevel.addElapsedInclusive(endTime, false, null); + // Exit any pending open-ended calls. + for (ThreadData threadData : mThreadMap.values()) { + threadData.endTrace(trace); + } + + // Recreate the global timeline from thread times, if needed. + if (!haveGlobalClock) { + long globalTime = 0; + prevThreadData = null; + for (TraceAction traceAction : trace) { + Call call = traceAction.mCall; + ThreadData threadData = call.getThreadData(); + + if (traceAction.mAction == TraceAction.ACTION_ENTER) { + long threadTime = call.mThreadStartTime; + globalTime += call.mThreadStartTime - threadData.mThreadCurrentTime; + call.mGlobalStartTime = globalTime; + if (!threadData.mHaveGlobalTime) { + threadData.mHaveGlobalTime = true; + threadData.mGlobalStartTime = globalTime; + } + threadData.mThreadCurrentTime = threadTime; + } else if (traceAction.mAction == TraceAction.ACTION_EXIT) { + long threadTime = call.mThreadEndTime; + globalTime += call.mThreadEndTime - threadData.mThreadCurrentTime; + call.mGlobalEndTime = globalTime; + threadData.mGlobalEndTime = globalTime; + threadData.mThreadCurrentTime = threadTime; + } // else, ignore ACTION_INCOMPLETE calls, nothing to do + prevThreadData = threadData; + } + } + + // Finish updating all calls and calculate the total time spent. + for (int i = mCallList.size() - 1; i >= 0; i--) { + Call call = mCallList.get(i); + + // Calculate exclusive real-time by subtracting inclusive real time + // accumulated by children from the total span. + long realTime = call.mGlobalEndTime - call.mGlobalStartTime; + call.mExclusiveRealTime = Math.max(realTime - call.mInclusiveRealTime, 0); + call.mInclusiveRealTime = realTime; + + call.finish(); + } + mTotalCpuTime = 0; + mTotalRealTime = 0; + for (ThreadData threadData : mThreadMap.values()) { + Call rootCall = threadData.getRootCall(); + threadData.updateRootCallTimeBounds(); + rootCall.finish(); + mTotalCpuTime += rootCall.mInclusiveCpuTime; + mTotalRealTime += rootCall.mInclusiveRealTime; } - mGlobalEndTime = globalTime; - if (mRegression) { + System.out.format("totalCpuTime %dus\n", mTotalCpuTime); + System.out.format("totalRealTime %dus\n", mTotalRealTime); + + dumpThreadTimes(); dumpCallTimes(); } } @@ -353,7 +448,7 @@ public class DmTraceReader extends TraceReader { continue; } if (line.equals("*end")) { - return offset; + break; } } switch (mode) { @@ -372,6 +467,11 @@ public class DmTraceReader extends TraceReader { break; } } + + if (mClockSource == null) { + mClockSource = ClockSource.THREAD_CPU; + } + return offset; } void parseOption(String line) { @@ -380,6 +480,16 @@ public class DmTraceReader extends TraceReader { String key = tokens[0]; String value = tokens[1]; mPropertiesMap.put(key, value); + + if (key.equals("clock")) { + if (value.equals("thread-cpu")) { + mClockSource = ClockSource.THREAD_CPU; + } else if (value.equals("wall")) { + mClockSource = ClockSource.WALL; + } else if (value.equals("dual")) { + mClockSource = ClockSource.DUAL; + } + } } } @@ -435,38 +545,30 @@ public class DmTraceReader extends TraceReader { } private void analyzeData() { + final TimeBase timeBase = getPreferredTimeBase(); + // Sort the threads into decreasing cpu time Collection<ThreadData> tv = mThreadMap.values(); mSortedThreads = tv.toArray(new ThreadData[tv.size()]); Arrays.sort(mSortedThreads, new Comparator<ThreadData>() { public int compare(ThreadData td1, ThreadData td2) { - if (td2.getCpuTime() > td1.getCpuTime()) + if (timeBase.getTime(td2) > timeBase.getTime(td1)) return 1; - if (td2.getCpuTime() < td1.getCpuTime()) + if (timeBase.getTime(td2) < timeBase.getTime(td1)) return -1; return td2.getName().compareTo(td1.getName()); } }); - // Analyze the call tree so that we can label the "worst" children. - // Also set all the root pointers in each node in the call tree. - long sum = 0; - for (ThreadData t : mSortedThreads) { - if (t.isEmpty() == false) { - Call root = t.getCalltreeRoot(); - root.mGlobalStartTime = t.getGlobalStartTime(); - } - } - // Sort the methods into decreasing inclusive time Collection<MethodData> mv = mMethodMap.values(); MethodData[] methods; methods = mv.toArray(new MethodData[mv.size()]); Arrays.sort(methods, new Comparator<MethodData>() { public int compare(MethodData md1, MethodData md2) { - if (md2.getElapsedInclusive() > md1.getElapsedInclusive()) + if (timeBase.getElapsedInclusiveTime(md2) > timeBase.getElapsedInclusiveTime(md1)) return 1; - if (md2.getElapsedInclusive() < md1.getElapsedInclusive()) + if (timeBase.getElapsedInclusiveTime(md2) < timeBase.getElapsedInclusiveTime(md1)) return -1; return md1.getName().compareTo(md2.getName()); } @@ -475,7 +577,7 @@ public class DmTraceReader extends TraceReader { // Count the number of methods with non-zero inclusive time int nonZero = 0; for (MethodData md : methods) { - if (md.getElapsedInclusive() == 0) + if (timeBase.getElapsedInclusiveTime(md) == 0) break; nonZero += 1; } @@ -484,7 +586,7 @@ public class DmTraceReader extends TraceReader { mSortedMethods = new MethodData[nonZero]; int ii = 0; for (MethodData md : methods) { - if (md.getElapsedInclusive() == 0) + if (timeBase.getElapsedInclusiveTime(md) == 0) break; md.setRank(ii); mSortedMethods[ii++] = md; @@ -492,7 +594,7 @@ public class DmTraceReader extends TraceReader { // Let each method analyze its profile data for (MethodData md : mSortedMethods) { - md.analyzeData(); + md.analyzeData(timeBase); } // Update all the calls to include the method rank in @@ -522,67 +624,65 @@ public class DmTraceReader extends TraceReader { // entire execution of the thread. for (ThreadData threadData : mSortedThreads) { if (!threadData.isEmpty() && threadData.getId() != 0) { - Call call = new Call(threadData.getId(), mTopLevel, - threadData.getGlobalStartTime(), 0); - call.mGlobalStartTime = threadData.getGlobalStartTime(); - call.mGlobalEndTime = threadData.getGlobalEndTime(); - record = new TimeLineView.Record(threadData, call); + record = new TimeLineView.Record(threadData, threadData.getRootCall()); timeRecs.add(record); } } for (Call call : mCallList) { - if (call.getMethodAction() != 0 || call.getThreadId() == 0) - continue; - ThreadData threadData = mThreadMap.get(call.getThreadId()); - record = new TimeLineView.Record(threadData, call); + record = new TimeLineView.Record(call.getThreadData(), call); timeRecs.add(record); } - + if (mRegression) { dumpTimeRecs(timeRecs); System.exit(0); } return timeRecs; } - + + private void dumpThreadTimes() { + System.out.print("\nThread Times\n"); + System.out.print("id t-start t-end g-start g-end name\n"); + for (ThreadData threadData : mThreadMap.values()) { + System.out.format("%2d %8d %8d %8d %8d %s\n", + threadData.getId(), + threadData.mThreadStartTime, threadData.mThreadEndTime, + threadData.mGlobalStartTime, threadData.mGlobalEndTime, + threadData.getName()); + } + } + private void dumpCallTimes() { - String action; - - System.out.format("id thread global start,end method\n"); + System.out.print("\nCall Times\n"); + System.out.print("id t-start t-end g-start g-end excl. incl. method\n"); for (Call call : mCallList) { - if (call.getMethodAction() == 0) { - action = "+"; - } else { - action = " "; - } - long callTime = call.mThreadStartTime; - System.out.format("%2d %6d %8d %8d %s %s\n", - call.getThreadId(), callTime, call.mGlobalStartTime, - call.mGlobalEndTime, action, call.getMethodData().getName()); -// if (call.getMethodAction() == 0 && call.getGlobalEndTime() < call.getGlobalStartTime()) { -// System.out.printf("endtime %d < startTime %d\n", -// call.getGlobalEndTime(), call.getGlobalStartTime()); -// } + System.out.format("%2d %8d %8d %8d %8d %8d %8d %s\n", + call.getThreadId(), call.mThreadStartTime, call.mThreadEndTime, + call.mGlobalStartTime, call.mGlobalEndTime, + call.mExclusiveCpuTime, call.mInclusiveCpuTime, + call.getMethodData().getName()); } } private void dumpMethodStats() { - System.out.format("\nExclusive Inclusive Calls Method\n"); + System.out.print("\nMethod Stats\n"); + System.out.print("Excl Cpu Incl Cpu Excl Real Incl Real Calls Method\n"); for (MethodData md : mSortedMethods) { System.out.format("%9d %9d %9s %s\n", - md.getElapsedExclusive(), md.getElapsedInclusive(), + md.getElapsedExclusiveCpuTime(), md.getElapsedInclusiveCpuTime(), + md.getElapsedExclusiveRealTime(), md.getElapsedInclusiveRealTime(), md.getCalls(), md.getProfileName()); } } private void dumpTimeRecs(ArrayList<TimeLineView.Record> timeRecs) { - System.out.format("\nid thread global start,end method\n"); + System.out.print("\nTime Records\n"); + System.out.print("id t-start t-end g-start g-end method\n"); for (TimeLineView.Record record : timeRecs) { Call call = (Call) record.block; - long callTime = call.mThreadStartTime; - System.out.format("%2d %6d %8d %8d %s\n", - call.getThreadId(), callTime, + System.out.format("%2d %8d %8d %8d %8d %s\n", + call.getThreadId(), call.mThreadStartTime, call.mThreadEndTime, call.mGlobalStartTime, call.mGlobalEndTime, call.getMethodData().getName()); } @@ -608,12 +708,48 @@ public class DmTraceReader extends TraceReader { } @Override - public long getEndTime() { - return mGlobalEndTime; + public long getTotalCpuTime() { + return mTotalCpuTime; + } + + @Override + public long getTotalRealTime() { + return mTotalRealTime; + } + + @Override + public boolean haveCpuTime() { + return mClockSource != ClockSource.WALL; + } + + @Override + public boolean haveRealTime() { + return mClockSource != ClockSource.THREAD_CPU; } @Override public HashMap<String, String> getProperties() { return mPropertiesMap; } + + @Override + public TimeBase getPreferredTimeBase() { + if (mClockSource == ClockSource.WALL) { + return TimeBase.REAL_TIME; + } + return TimeBase.CPU_TIME; + } + + @Override + public String getClockSource() { + switch (mClockSource) { + case THREAD_CPU: + return "cpu time"; + case WALL: + return "real time"; + case DUAL: + return "real time, dual clock"; + } + return null; + } } diff --git a/traceview/src/com/android/traceview/MethodData.java b/traceview/src/com/android/traceview/MethodData.java index d54b72b..47f6df4 100644 --- a/traceview/src/com/android/traceview/MethodData.java +++ b/traceview/src/com/android/traceview/MethodData.java @@ -36,9 +36,12 @@ public class MethodData { private String mProfileName; private String mPathname; private int mLineNumber; - private long mElapsedExclusive; - private long mElapsedInclusive; - private long mTopExclusive; + private long mElapsedExclusiveCpuTime; + private long mElapsedInclusiveCpuTime; + private long mTopExclusiveCpuTime; + private long mElapsedExclusiveRealTime; + private long mElapsedInclusiveRealTime; + private long mTopExclusiveRealTime; private int[] mNumCalls = new int[2]; // index 0=normal, 1=recursive private Color mColor; private Color mFadedColor; @@ -81,16 +84,6 @@ public class MethodData { computeProfileName(); } - private Comparator<ProfileData> mByElapsedInclusive = new Comparator<ProfileData>() { - public int compare(ProfileData pd1, ProfileData pd2) { - if (pd2.getElapsedInclusive() > pd1.getElapsedInclusive()) - return 1; - if (pd2.getElapsedInclusive() < pd1.getElapsedInclusive()) - return -1; - return 0; - } - }; - public double addWeight(int x, int y, double weight) { if (mX == x && mY == y) mWeight += weight; @@ -115,13 +108,16 @@ public class MethodData { computeProfileName(); } - public void addElapsedExclusive(long time) { - mElapsedExclusive += time; + public void addElapsedExclusive(long cpuTime, long realTime) { + mElapsedExclusiveCpuTime += cpuTime; + mElapsedExclusiveRealTime += realTime; } - public void addElapsedInclusive(long time, boolean isRecursive, Call parent) { + public void addElapsedInclusive(long cpuTime, long realTime, + boolean isRecursive, Call parent) { if (isRecursive == false) { - mElapsedInclusive += time; + mElapsedInclusiveCpuTime += cpuTime; + mElapsedInclusiveRealTime += realTime; mNumCalls[0] += 1; } else { mNumCalls[1] += 1; @@ -131,27 +127,27 @@ public class MethodData { return; // Find the child method in the parent - MethodData parentMethod = parent.mMethodData; + MethodData parentMethod = parent.getMethodData(); if (parent.isRecursive()) { - parentMethod.mRecursiveChildren = updateInclusive(time, + parentMethod.mRecursiveChildren = updateInclusive(cpuTime, realTime, parentMethod, this, false, parentMethod.mRecursiveChildren); } else { - parentMethod.mChildren = updateInclusive(time, + parentMethod.mChildren = updateInclusive(cpuTime, realTime, parentMethod, this, false, parentMethod.mChildren); } // Find the parent method in the child if (isRecursive) { - mRecursiveParents = updateInclusive(time, this, parentMethod, true, + mRecursiveParents = updateInclusive(cpuTime, realTime, this, parentMethod, true, mRecursiveParents); } else { - mParents = updateInclusive(time, this, parentMethod, true, + mParents = updateInclusive(cpuTime, realTime, this, parentMethod, true, mParents); } } - private HashMap<Integer, ProfileData> updateInclusive(long time, + private HashMap<Integer, ProfileData> updateInclusive(long cpuTime, long realTime, MethodData contextMethod, MethodData elementMethod, boolean elementIsParent, HashMap<Integer, ProfileData> map) { if (map == null) { @@ -159,30 +155,30 @@ public class MethodData { } else { ProfileData profileData = map.get(elementMethod.mId); if (profileData != null) { - profileData.addElapsedInclusive(time); + profileData.addElapsedInclusive(cpuTime, realTime); return map; } } ProfileData elementData = new ProfileData(contextMethod, elementMethod, elementIsParent); - elementData.setElapsedInclusive(time); + elementData.setElapsedInclusive(cpuTime, realTime); elementData.setNumCalls(1); map.put(elementMethod.mId, elementData); return map; } - public void analyzeData() { + public void analyzeData(TimeBase timeBase) { // Sort the parents and children into decreasing inclusive time ProfileData[] sortedParents; ProfileData[] sortedChildren; ProfileData[] sortedRecursiveParents; ProfileData[] sortedRecursiveChildren; - sortedParents = sortProfileData(mParents); - sortedChildren = sortProfileData(mChildren); - sortedRecursiveParents = sortProfileData(mRecursiveParents); - sortedRecursiveChildren = sortProfileData(mRecursiveChildren); + sortedParents = sortProfileData(mParents, timeBase); + sortedChildren = sortProfileData(mChildren, timeBase); + sortedRecursiveParents = sortProfileData(mRecursiveParents, timeBase); + sortedRecursiveChildren = sortProfileData(mRecursiveChildren, timeBase); // Add "self" time to the top of the sorted children sortedChildren = addSelf(sortedChildren); @@ -215,7 +211,8 @@ public class MethodData { // Create and return a ProfileData[] array that is a sorted copy // of the given HashMap values. - private ProfileData[] sortProfileData(HashMap<Integer, ProfileData> map) { + private ProfileData[] sortProfileData(HashMap<Integer, ProfileData> map, + final TimeBase timeBase) { if (map == null) return null; @@ -224,7 +221,15 @@ public class MethodData { ProfileData[] sorted = values.toArray(new ProfileData[values.size()]); // Sort the array by elapsed inclusive time - Arrays.sort(sorted, mByElapsedInclusive); + Arrays.sort(sorted, new Comparator<ProfileData>() { + public int compare(ProfileData pd1, ProfileData pd2) { + if (timeBase.getElapsedInclusiveTime(pd2) > timeBase.getElapsedInclusiveTime(pd1)) + return 1; + if (timeBase.getElapsedInclusiveTime(pd2) < timeBase.getElapsedInclusiveTime(pd1)) + return -1; + return 0; + } + }); return sorted; } @@ -240,12 +245,17 @@ public class MethodData { return pdata; } - public void addTopExclusive(long time) { - mTopExclusive += time; + public void addTopExclusive(long cpuTime, long realTime) { + mTopExclusiveCpuTime += cpuTime; + mTopExclusiveRealTime += realTime; } - public long getTopExclusive() { - return mTopExclusive; + public long getTopExclusiveCpuTime() { + return mTopExclusiveCpuTime; + } + + public long getTopExclusiveRealTime() { + return mTopExclusiveRealTime; } public int getId() { @@ -329,12 +339,20 @@ public class MethodData { return getName(); } - public long getElapsedExclusive() { - return mElapsedExclusive; + public long getElapsedExclusiveCpuTime() { + return mElapsedExclusiveCpuTime; + } + + public long getElapsedExclusiveRealTime() { + return mElapsedExclusiveRealTime; } - public long getElapsedInclusive() { - return mElapsedInclusive; + public long getElapsedInclusiveCpuTime() { + return mElapsedInclusiveCpuTime; + } + + public long getElapsedInclusiveRealTime() { + return mElapsedInclusiveRealTime; } public void setFadedColor(Color fadedColor) { @@ -379,17 +397,31 @@ public class MethodData { int result = md1.getName().compareTo(md2.getName()); return (mDirection == Direction.INCREASING) ? result : -result; } - if (mColumn == Column.BY_INCLUSIVE) { - if (md2.getElapsedInclusive() > md1.getElapsedInclusive()) + if (mColumn == Column.BY_INCLUSIVE_CPU_TIME) { + if (md2.getElapsedInclusiveCpuTime() > md1.getElapsedInclusiveCpuTime()) + return (mDirection == Direction.INCREASING) ? -1 : 1; + if (md2.getElapsedInclusiveCpuTime() < md1.getElapsedInclusiveCpuTime()) + return (mDirection == Direction.INCREASING) ? 1 : -1; + return md1.getName().compareTo(md2.getName()); + } + if (mColumn == Column.BY_EXCLUSIVE_CPU_TIME) { + if (md2.getElapsedExclusiveCpuTime() > md1.getElapsedExclusiveCpuTime()) + return (mDirection == Direction.INCREASING) ? -1 : 1; + if (md2.getElapsedExclusiveCpuTime() < md1.getElapsedExclusiveCpuTime()) + return (mDirection == Direction.INCREASING) ? 1 : -1; + return md1.getName().compareTo(md2.getName()); + } + if (mColumn == Column.BY_INCLUSIVE_REAL_TIME) { + if (md2.getElapsedInclusiveRealTime() > md1.getElapsedInclusiveRealTime()) return (mDirection == Direction.INCREASING) ? -1 : 1; - if (md2.getElapsedInclusive() < md1.getElapsedInclusive()) + if (md2.getElapsedInclusiveRealTime() < md1.getElapsedInclusiveRealTime()) return (mDirection == Direction.INCREASING) ? 1 : -1; return md1.getName().compareTo(md2.getName()); } - if (mColumn == Column.BY_EXCLUSIVE) { - if (md2.getElapsedExclusive() > md1.getElapsedExclusive()) + if (mColumn == Column.BY_EXCLUSIVE_REAL_TIME) { + if (md2.getElapsedExclusiveRealTime() > md1.getElapsedExclusiveRealTime()) return (mDirection == Direction.INCREASING) ? -1 : 1; - if (md2.getElapsedExclusive() < md1.getElapsedExclusive()) + if (md2.getElapsedExclusiveRealTime() < md1.getElapsedExclusiveRealTime()) return (mDirection == Direction.INCREASING) ? 1 : -1; return md1.getName().compareTo(md2.getName()); } @@ -399,10 +431,25 @@ public class MethodData { return md1.getName().compareTo(md2.getName()); return (mDirection == Direction.INCREASING) ? result : -result; } - if (mColumn == Column.BY_TIME_PER_CALL) { - double time1 = md1.getElapsedInclusive(); + if (mColumn == Column.BY_CPU_TIME_PER_CALL) { + double time1 = md1.getElapsedInclusiveCpuTime(); + time1 = time1 / md1.getTotalCalls(); + double time2 = md2.getElapsedInclusiveCpuTime(); + time2 = time2 / md2.getTotalCalls(); + double diff = time1 - time2; + int result = 0; + if (diff < 0) + result = -1; + else if (diff > 0) + result = 1; + if (result == 0) + return md1.getName().compareTo(md2.getName()); + return (mDirection == Direction.INCREASING) ? result : -result; + } + if (mColumn == Column.BY_REAL_TIME_PER_CALL) { + double time1 = md1.getElapsedInclusiveRealTime(); time1 = time1 / md1.getTotalCalls(); - double time2 = md2.getElapsedInclusive(); + double time2 = md2.getElapsedInclusiveRealTime(); time2 = time2 / md2.getTotalCalls(); double diff = time1 - time2; int result = 0; @@ -449,7 +496,9 @@ public class MethodData { } public static enum Column { - BY_NAME, BY_EXCLUSIVE, BY_INCLUSIVE, BY_CALLS, BY_TIME_PER_CALL + BY_NAME, BY_EXCLUSIVE_CPU_TIME, BY_EXCLUSIVE_REAL_TIME, + BY_INCLUSIVE_CPU_TIME, BY_INCLUSIVE_REAL_TIME, BY_CALLS, + BY_REAL_TIME_PER_CALL, BY_CPU_TIME_PER_CALL, }; public static enum Direction { diff --git a/traceview/src/com/android/traceview/ProfileData.java b/traceview/src/com/android/traceview/ProfileData.java index f0c1d61..e3c47fb 100644 --- a/traceview/src/com/android/traceview/ProfileData.java +++ b/traceview/src/com/android/traceview/ProfileData.java @@ -24,7 +24,8 @@ public class ProfileData { /** mContext is either the parent or child of mElement */ protected MethodData mContext; protected boolean mElementIsParent; - protected long mElapsedInclusive; + protected long mElapsedInclusiveCpuTime; + protected long mElapsedInclusiveRealTime; protected int mNumCalls; public ProfileData() { @@ -45,17 +46,23 @@ public class ProfileData { return mElement; } - public void addElapsedInclusive(long elapsedInclusive) { - mElapsedInclusive += elapsedInclusive; + public void addElapsedInclusive(long cpuTime, long realTime) { + mElapsedInclusiveCpuTime += cpuTime; + mElapsedInclusiveRealTime += realTime; mNumCalls += 1; } - public void setElapsedInclusive(long elapsedInclusive) { - mElapsedInclusive = elapsedInclusive; + public void setElapsedInclusive(long cpuTime, long realTime) { + mElapsedInclusiveCpuTime = cpuTime; + mElapsedInclusiveRealTime = realTime; } - public long getElapsedInclusive() { - return mElapsedInclusive; + public long getElapsedInclusiveCpuTime() { + return mElapsedInclusiveCpuTime; + } + + public long getElapsedInclusiveRealTime() { + return mElapsedInclusiveRealTime; } public void setNumCalls(int numCalls) { diff --git a/traceview/src/com/android/traceview/ProfileProvider.java b/traceview/src/com/android/traceview/ProfileProvider.java index fe5c832..63a1da6 100644 --- a/traceview/src/com/android/traceview/ProfileProvider.java +++ b/traceview/src/com/android/traceview/ProfileProvider.java @@ -44,26 +44,40 @@ class ProfileProvider implements ITreeContentProvider { private TraceReader mReader; private Image mSortUp; private Image mSortDown; - private String mColumnNames[] = { "Name", "Incl %", "Inclusive", "Excl %", - "Exclusive", "Calls+Recur\nCalls/Total", "Time/Call" }; - private int mColumnWidths[] = { 370, 70, 70, 70, 70, 90, 70 }; - private int mColumnAlignments[] = { SWT.LEFT, SWT.RIGHT, SWT.RIGHT, - SWT.RIGHT, SWT.RIGHT, SWT.CENTER, SWT.RIGHT }; + private String mColumnNames[] = { "Name", + "Incl Cpu Time %", "Incl Cpu Time", "Excl Cpu Time %", "Excl Cpu Time", + "Incl Real Time %", "Incl Real Time", "Excl Real Time %", "Excl Real Time", + "Calls+Recur\nCalls/Total", "Cpu Time/Call", "Real Time/Call" }; + private int mColumnWidths[] = { 370, + 100, 100, 100, 100, + 100, 100, 100, 100, + 100, 100, 100 }; + private int mColumnAlignments[] = { SWT.LEFT, + SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, + SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, + SWT.CENTER, SWT.RIGHT, SWT.RIGHT }; private static final int COL_NAME = 0; - private static final int COL_INCLUSIVE_PER = 1; - private static final int COL_INCLUSIVE = 2; - private static final int COL_EXCLUSIVE_PER = 3; - private static final int COL_EXCLUSIVE = 4; - private static final int COL_CALLS = 5; - private static final int COL_TIME_PER_CALL = 6; - private long mTotalTime; + private static final int COL_INCLUSIVE_CPU_TIME_PER = 1; + private static final int COL_INCLUSIVE_CPU_TIME = 2; + private static final int COL_EXCLUSIVE_CPU_TIME_PER = 3; + private static final int COL_EXCLUSIVE_CPU_TIME = 4; + private static final int COL_INCLUSIVE_REAL_TIME_PER = 5; + private static final int COL_INCLUSIVE_REAL_TIME = 6; + private static final int COL_EXCLUSIVE_REAL_TIME_PER = 7; + private static final int COL_EXCLUSIVE_REAL_TIME = 8; + private static final int COL_CALLS = 9; + private static final int COL_CPU_TIME_PER_CALL = 10; + private static final int COL_REAL_TIME_PER_CALL = 11; + private long mTotalCpuTime; + private long mTotalRealTime; private Pattern mUppercase; private int mPrevMatchIndex = -1; public ProfileProvider(TraceReader reader) { mRoots = reader.getMethods(); mReader = reader; - mTotalTime = reader.getEndTime(); + mTotalCpuTime = reader.getTotalCpuTime(); + mTotalRealTime = reader.getTotalRealTime(); Display display = Display.getCurrent(); InputStream in = getClass().getClassLoader().getResourceAsStream( "icons/sort_up.png"); @@ -126,7 +140,22 @@ class ProfileProvider implements ITreeContentProvider { } public int[] getColumnWidths() { - return mColumnWidths; + int[] widths = Arrays.copyOf(mColumnWidths, mColumnWidths.length); + if (!mReader.haveCpuTime()) { + widths[COL_EXCLUSIVE_CPU_TIME] = 0; + widths[COL_EXCLUSIVE_CPU_TIME_PER] = 0; + widths[COL_INCLUSIVE_CPU_TIME] = 0; + widths[COL_INCLUSIVE_CPU_TIME_PER] = 0; + widths[COL_CPU_TIME_PER_CALL] = 0; + } + if (!mReader.haveRealTime()) { + widths[COL_EXCLUSIVE_REAL_TIME] = 0; + widths[COL_EXCLUSIVE_REAL_TIME_PER] = 0; + widths[COL_INCLUSIVE_REAL_TIME] = 0; + widths[COL_INCLUSIVE_REAL_TIME_PER] = 0; + widths[COL_REAL_TIME_PER_CALL] = 0; + } + return widths; } public int[] getColumnAlignments() { @@ -201,31 +230,58 @@ class ProfileProvider implements ITreeContentProvider { MethodData md = (MethodData) element; if (col == COL_NAME) return md.getProfileName(); - if (col == COL_EXCLUSIVE) { - double val = md.getElapsedExclusive(); + if (col == COL_EXCLUSIVE_CPU_TIME) { + double val = md.getElapsedExclusiveCpuTime(); + val = traceUnits.getScaledValue(val); + return String.format("%.3f", val); + } + if (col == COL_EXCLUSIVE_CPU_TIME_PER) { + double val = md.getElapsedExclusiveCpuTime(); + double per = val * 100.0 / mTotalCpuTime; + return String.format("%.1f%%", per); + } + if (col == COL_INCLUSIVE_CPU_TIME) { + double val = md.getElapsedInclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } - if (col == COL_EXCLUSIVE_PER) { - double val = md.getElapsedExclusive(); - double per = val * 100.0 / mTotalTime; + if (col == COL_INCLUSIVE_CPU_TIME_PER) { + double val = md.getElapsedInclusiveCpuTime(); + double per = val * 100.0 / mTotalCpuTime; return String.format("%.1f%%", per); } - if (col == COL_INCLUSIVE) { - double val = md.getElapsedInclusive(); + if (col == COL_EXCLUSIVE_REAL_TIME) { + double val = md.getElapsedExclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } - if (col == COL_INCLUSIVE_PER) { - double val = md.getElapsedInclusive(); - double per = val * 100.0 / mTotalTime; + if (col == COL_EXCLUSIVE_REAL_TIME_PER) { + double val = md.getElapsedExclusiveRealTime(); + double per = val * 100.0 / mTotalRealTime; + return String.format("%.1f%%", per); + } + if (col == COL_INCLUSIVE_REAL_TIME) { + double val = md.getElapsedInclusiveRealTime(); + val = traceUnits.getScaledValue(val); + return String.format("%.3f", val); + } + if (col == COL_INCLUSIVE_REAL_TIME_PER) { + double val = md.getElapsedInclusiveRealTime(); + double per = val * 100.0 / mTotalRealTime; return String.format("%.1f%%", per); } if (col == COL_CALLS) return md.getCalls(); - if (col == COL_TIME_PER_CALL) { + if (col == COL_CPU_TIME_PER_CALL) { int numCalls = md.getTotalCalls(); - double val = md.getElapsedInclusive(); + double val = md.getElapsedInclusiveCpuTime(); + val = val / numCalls; + val = traceUnits.getScaledValue(val); + return String.format("%.3f", val); + } + if (col == COL_REAL_TIME_PER_CALL) { + int numCalls = md.getTotalCalls(); + double val = md.getElapsedInclusiveRealTime(); val = val / numCalls; val = traceUnits.getScaledValue(val); return String.format("%.3f", val); @@ -234,16 +290,29 @@ class ProfileProvider implements ITreeContentProvider { ProfileSelf ps = (ProfileSelf) element; if (col == COL_NAME) return ps.getProfileName(); - if (col == COL_INCLUSIVE) { - double val = ps.getElapsedInclusive(); + if (col == COL_INCLUSIVE_CPU_TIME) { + double val = ps.getElapsedInclusiveCpuTime(); + val = traceUnits.getScaledValue(val); + return String.format("%.3f", val); + } + if (col == COL_INCLUSIVE_CPU_TIME_PER) { + double total; + double val = ps.getElapsedInclusiveCpuTime(); + MethodData context = ps.getContext(); + total = context.getElapsedInclusiveCpuTime(); + double per = val * 100.0 / total; + return String.format("%.1f%%", per); + } + if (col == COL_INCLUSIVE_REAL_TIME) { + double val = ps.getElapsedInclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } - if (col == COL_INCLUSIVE_PER) { + if (col == COL_INCLUSIVE_REAL_TIME_PER) { double total; - double val = ps.getElapsedInclusive(); + double val = ps.getElapsedInclusiveRealTime(); MethodData context = ps.getContext(); - total = context.getElapsedInclusive(); + total = context.getElapsedInclusiveRealTime(); double per = val * 100.0 / total; return String.format("%.1f%%", per); } @@ -252,16 +321,29 @@ class ProfileProvider implements ITreeContentProvider { ProfileData pd = (ProfileData) element; if (col == COL_NAME) return pd.getProfileName(); - if (col == COL_INCLUSIVE) { - double val = pd.getElapsedInclusive(); + if (col == COL_INCLUSIVE_CPU_TIME) { + double val = pd.getElapsedInclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } - if (col == COL_INCLUSIVE_PER) { + if (col == COL_INCLUSIVE_CPU_TIME_PER) { double total; - double val = pd.getElapsedInclusive(); + double val = pd.getElapsedInclusiveCpuTime(); MethodData context = pd.getContext(); - total = context.getElapsedInclusive(); + total = context.getElapsedInclusiveCpuTime(); + double per = val * 100.0 / total; + return String.format("%.1f%%", per); + } + if (col == COL_INCLUSIVE_REAL_TIME) { + double val = pd.getElapsedInclusiveRealTime(); + val = traceUnits.getScaledValue(val); + return String.format("%.3f", val); + } + if (col == COL_INCLUSIVE_REAL_TIME_PER) { + double total; + double val = pd.getElapsedInclusiveRealTime(); + MethodData context = pd.getContext(); + total = context.getElapsedInclusiveRealTime(); double per = val * 100.0 / total; return String.format("%.1f%%", per); } @@ -330,23 +412,38 @@ class ProfileProvider implements ITreeContentProvider { // Sort names alphabetically sorter.setColumn(MethodData.Sorter.Column.BY_NAME); Arrays.sort(mRoots, sorter); - } else if (name == mColumnNames[COL_EXCLUSIVE]) { - sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE); + } else if (name == mColumnNames[COL_EXCLUSIVE_CPU_TIME]) { + sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_CPU_TIME); + Arrays.sort(mRoots, sorter); + } else if (name == mColumnNames[COL_EXCLUSIVE_CPU_TIME_PER]) { + sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); - } else if (name == mColumnNames[COL_EXCLUSIVE_PER]) { - sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE); + } else if (name == mColumnNames[COL_INCLUSIVE_CPU_TIME]) { + sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); - } else if (name == mColumnNames[COL_INCLUSIVE]) { - sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE); + } else if (name == mColumnNames[COL_INCLUSIVE_CPU_TIME_PER]) { + sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); - } else if (name == mColumnNames[COL_INCLUSIVE_PER]) { - sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE); + } else if (name == mColumnNames[COL_EXCLUSIVE_REAL_TIME]) { + sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_REAL_TIME); + Arrays.sort(mRoots, sorter); + } else if (name == mColumnNames[COL_EXCLUSIVE_REAL_TIME_PER]) { + sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_REAL_TIME); + Arrays.sort(mRoots, sorter); + } else if (name == mColumnNames[COL_INCLUSIVE_REAL_TIME]) { + sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_REAL_TIME); + Arrays.sort(mRoots, sorter); + } else if (name == mColumnNames[COL_INCLUSIVE_REAL_TIME_PER]) { + sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_REAL_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_CALLS]) { sorter.setColumn(MethodData.Sorter.Column.BY_CALLS); Arrays.sort(mRoots, sorter); - } else if (name == mColumnNames[COL_TIME_PER_CALL]) { - sorter.setColumn(MethodData.Sorter.Column.BY_TIME_PER_CALL); + } else if (name == mColumnNames[COL_CPU_TIME_PER_CALL]) { + sorter.setColumn(MethodData.Sorter.Column.BY_CPU_TIME_PER_CALL); + Arrays.sort(mRoots, sorter); + } else if (name == mColumnNames[COL_REAL_TIME_PER_CALL]) { + sorter.setColumn(MethodData.Sorter.Column.BY_REAL_TIME_PER_CALL); Arrays.sort(mRoots, sorter); } MethodData.Sorter.Direction direction = sorter.getDirection(); diff --git a/traceview/src/com/android/traceview/ProfileSelf.java b/traceview/src/com/android/traceview/ProfileSelf.java index 3a4f3d9..45543b2 100644 --- a/traceview/src/com/android/traceview/ProfileSelf.java +++ b/traceview/src/com/android/traceview/ProfileSelf.java @@ -28,7 +28,12 @@ public class ProfileSelf extends ProfileData { } @Override - public long getElapsedInclusive() { - return mElement.getTopExclusive(); + public long getElapsedInclusiveCpuTime() { + return mElement.getTopExclusiveCpuTime(); + } + + @Override + public long getElapsedInclusiveRealTime() { + return mElement.getTopExclusiveRealTime(); } } diff --git a/traceview/src/com/android/traceview/ProfileView.java b/traceview/src/com/android/traceview/ProfileView.java index 506efca..8889b13 100644 --- a/traceview/src/com/android/traceview/ProfileView.java +++ b/traceview/src/com/android/traceview/ProfileView.java @@ -279,7 +279,7 @@ public class ProfileView extends Composite implements Observer { } if (name == "Call") { Call call = (Call) selection.getValue(); - MethodData md = call.mMethodData; + MethodData md = call.getMethodData(); highlightMethod(md, true); return; } @@ -304,9 +304,11 @@ public class ProfileView extends Composite implements Observer { mTreeViewer.setSelection(sel, true); Tree tree = mTreeViewer.getTree(); TreeItem[] items = tree.getSelection(); - tree.setTopItem(items[0]); - // workaround a Mac bug by adding showItem(). - tree.showItem(items[0]); + if (items.length != 0) { + tree.setTopItem(items[0]); + // workaround a Mac bug by adding showItem(). + tree.showItem(items[0]); + } } private void expandNode(MethodData md) { diff --git a/traceview/src/com/android/traceview/ThreadData.java b/traceview/src/com/android/traceview/ThreadData.java index 54ea891..f71e9c2 100644 --- a/traceview/src/com/android/traceview/ThreadData.java +++ b/traceview/src/com/android/traceview/ThreadData.java @@ -23,158 +23,130 @@ 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 boolean mIsEmpty; + + private Call mRootCall; 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; + + boolean mHaveGlobalTime; + long mGlobalStartTime; + long mGlobalEndTime; + + boolean mHaveThreadTime; + long mThreadStartTime; + long mThreadEndTime; + + long mThreadCurrentTime; // only used while parsing thread-cpu clock 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; + mRootCall = new Call(this, topLevel, null); + mRootCall.setName(mName); + mStack.add(mRootCall); } public String getName() { return mName; } - public Call getCalltreeRoot() { - return mRoot; + public Call getRootCall() { + return mRootCall; + } + + /** + * Returns true if no calls have ever been recorded for this thread. + */ + public boolean isEmpty() { + return mIsEmpty; } - 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); + Call enter(MethodData method, ArrayList<TraceAction> trace) { + if (mIsEmpty) { + mIsEmpty = false; + if (trace != null) { + trace.add(new TraceAction(TraceAction.ACTION_ENTER, mRootCall)); + } } - 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); + + Call caller = top(); + Call call = new Call(this, method, caller); + mStack.add(call); + + if (trace != null) { + trace.add(new TraceAction(TraceAction.ACTION_ENTER, call)); } - 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); + Integer num = mStackMethods.get(method); if (num == null) { num = 0; } else if (num > 0) { - c.setRecursive(true); + call.setRecursive(true); } - num += 1; - mStackMethods.put(md, num); - mCurrent = c; + mStackMethods.put(method, num + 1); - // Add the elapsed time to the caller's exclusive time - caller.addExclusiveTime(elapsed); + return call; } - private void exit(Call c, long elapsed, long globalTime) { - mCurrent.mGlobalEndTime = globalTime; - Call top = pop(); - if (top == null) { - return; + Call exit(MethodData method, ArrayList<TraceAction> trace) { + Call call = top(); + if (call.mCaller == null) { + return null; } - if (mCurrent.mMethodData != c.mMethodData) { - String error = "Method exit (" + c.getName() - + ") does not match current method (" + mCurrent.getName() + if (call.getMethodData() != method) { + String error = "Method exit (" + method.getName() + + ") does not match current method (" + call.getMethodData().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); - } + mStack.remove(mStack.size() - 1); - 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 (trace != null) { + trace.add(new TraceAction(TraceAction.ACTION_EXIT, call)); + } + + Integer num = mStackMethods.get(method); if (num != null) { - num -= 1; - if (num <= 0) { - mStackMethods.remove(md); + if (num == 1) { + mStackMethods.remove(method); } else { - mStackMethods.put(md, num); + mStackMethods.put(method, num - 1); } } - return top; + + return call; } - public Call top() { - ArrayList<Call> stack = mStack; - if (stack.size() == 0) - return null; - return stack.get(stack.size() - 1); + Call top() { + return mStack.get(mStack.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; + void endTrace(ArrayList<TraceAction> trace) { + for (int i = mStack.size() - 1; i >= 1; i--) { + Call call = mStack.get(i); + call.mGlobalEndTime = mGlobalEndTime; + call.mThreadEndTime = mThreadEndTime; + if (trace != null) { + trace.add(new TraceAction(TraceAction.ACTION_INCOMPLETE, call)); } - mCurrent = caller; } - return mLastEventTime; + mStack.clear(); + mStackMethods.clear(); + } + + void updateRootCallTimeBounds() { + if (!mIsEmpty) { + mRootCall.mGlobalStartTime = mGlobalStartTime; + mRootCall.mGlobalEndTime = mGlobalEndTime; + mRootCall.mThreadStartTime = mThreadStartTime; + mRootCall.mThreadEndTime = mThreadEndTime; + } } @Override @@ -186,43 +158,11 @@ class ThreadData implements TimeLineView.Row { 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; + return mRootCall.mInclusiveCpuTime; } - public Call getLastContextSwitch() { - return mLastContextSwitch; + public long getRealTime() { + return mRootCall.mInclusiveRealTime; } } diff --git a/traceview/src/com/android/traceview/TimeBase.java b/traceview/src/com/android/traceview/TimeBase.java new file mode 100644 index 0000000..b6b23cb --- /dev/null +++ b/traceview/src/com/android/traceview/TimeBase.java @@ -0,0 +1,71 @@ +/* + * 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; + +interface TimeBase { + public static final TimeBase CPU_TIME = new CpuTimeBase(); + public static final TimeBase REAL_TIME = new RealTimeBase(); + + public long getTime(ThreadData threadData); + public long getElapsedInclusiveTime(MethodData methodData); + public long getElapsedExclusiveTime(MethodData methodData); + public long getElapsedInclusiveTime(ProfileData profileData); + + public static final class CpuTimeBase implements TimeBase { + @Override + public long getTime(ThreadData threadData) { + return threadData.getCpuTime(); + } + + @Override + public long getElapsedInclusiveTime(MethodData methodData) { + return methodData.getElapsedInclusiveCpuTime(); + } + + @Override + public long getElapsedExclusiveTime(MethodData methodData) { + return methodData.getElapsedExclusiveCpuTime(); + } + + @Override + public long getElapsedInclusiveTime(ProfileData profileData) { + return profileData.getElapsedInclusiveCpuTime(); + } + } + + public static final class RealTimeBase implements TimeBase { + @Override + public long getTime(ThreadData threadData) { + return threadData.getRealTime(); + } + + @Override + public long getElapsedInclusiveTime(MethodData methodData) { + return methodData.getElapsedInclusiveRealTime(); + } + + @Override + public long getElapsedExclusiveTime(MethodData methodData) { + return methodData.getElapsedExclusiveRealTime(); + } + + @Override + public long getElapsedInclusiveTime(ProfileData profileData) { + return profileData.getElapsedInclusiveRealTime(); + } + } +} diff --git a/traceview/src/com/android/traceview/TimeLineView.java b/traceview/src/com/android/traceview/TimeLineView.java index c9eb8e7..b04a411 100644 --- a/traceview/src/com/android/traceview/TimeLineView.java +++ b/traceview/src/com/android/traceview/TimeLineView.java @@ -54,10 +54,8 @@ import java.util.Observer; public class TimeLineView extends Composite implements Observer { private HashMap<String, RowData> mRowByName; - private double mTotalElapsed; private RowData[] mRows; private Segment[] mSegments; - private ArrayList<Segment> mSegmentList = new ArrayList<Segment>(); private HashMap<Integer, String> mThreadLabels; private Timescale mTimescale; private Surface mSurface; @@ -89,19 +87,21 @@ public class TimeLineView extends Composite implements Observer { private static final int rowYSpace = rowHeight + rowYMargin; private static final int majorTickLength = 8; private static final int minorTickLength = 4; - private static final int timeLineOffsetY = 38; + private static final int timeLineOffsetY = 58; private static final int tickToFontSpacing = 2; /** start of first row */ - private static final int topMargin = 70; + private static final int topMargin = 90; private int mMouseRow = -1; private int mNumRows; private int mStartRow; private int mEndRow; private TraceUnits mUnits; + private String mClockSource; + private boolean mHaveCpuTime; + private boolean mHaveRealTime; private int mSmallFontWidth; private int mSmallFontHeight; - private int mMediumFontWidth; private SelectionController mSelectionController; private MethodData mHighlightMethodData; private Call mHighlightCall; @@ -118,6 +118,13 @@ public class TimeLineView extends Composite implements Observer { public Color getColor(); public double addWeight(int x, int y, double weight); public void clearWeight(); + public long getExclusiveCpuTime(); + public long getInclusiveCpuTime(); + public long getExclusiveRealTime(); + public long getInclusiveRealTime(); + public boolean isContextSwitch(); + public boolean isIgnoredBlock(); + public Block getParentBlock(); } public static interface Row { @@ -142,6 +149,9 @@ public class TimeLineView extends Composite implements Observer { this.mSelectionController = selectionController; selectionController.addObserver(this); mUnits = reader.getTraceUnits(); + mClockSource = reader.getClockSource(); + mHaveCpuTime = reader.haveCpuTime(); + mHaveRealTime = reader.haveRealTime(); mThreadLabels = reader.getThreadLabels(); Display display = getDisplay(); @@ -169,11 +179,6 @@ public class TimeLineView extends Composite implements Observer { mSmallFontWidth = gc.getFontMetrics().getAverageCharWidth(); mSmallFontHeight = gc.getFontMetrics().getHeight(); - if (mSetFonts) { - gc.setFont(mFontRegistry.get("medium")); //$NON-NLS-1$ - } - mMediumFontWidth = gc.getFontMetrics().getAverageCharWidth(); - image.dispose(); gc.dispose(); @@ -403,6 +408,8 @@ public class TimeLineView extends Composite implements Observer { } }); + ArrayList<Segment> segmentList = new ArrayList<Segment>(); + // The records are sorted into increasing start time, // so the minimum start time is the start time of the first record. double minVal = 0; @@ -415,6 +422,10 @@ public class TimeLineView extends Composite implements Observer { for (Record rec : records) { Row row = rec.row; Block block = rec.block; + if (block.isIgnoredBlock()) { + continue; + } + String rowName = row.getName(); RowData rd = mRowByName.get(rowName); if (rd == null) { @@ -426,7 +437,6 @@ public class TimeLineView extends Composite implements Observer { if (blockEndTime > rd.mEndTime) { long start = Math.max(blockStartTime, rd.mEndTime); rd.mElapsed += blockEndTime - start; - mTotalElapsed += blockEndTime - start; rd.mEndTime = blockEndTime; } if (blockEndTime > maxVal) @@ -447,7 +457,7 @@ public class TimeLineView extends Composite implements Observer { if (topStartTime < blockStartTime) { Segment segment = new Segment(rd, top, topStartTime, blockStartTime); - mSegmentList.add(segment); + segmentList.add(segment); } // If this block starts where the previous (top) block ends, @@ -457,7 +467,7 @@ public class TimeLineView extends Composite implements Observer { rd.push(block); } else { // We may have to pop several frames here. - popFrames(rd, top, blockStartTime); + popFrames(rd, top, blockStartTime, segmentList); rd.push(block); } } @@ -465,7 +475,7 @@ public class TimeLineView extends Composite implements Observer { // Clean up the stack of each row for (RowData rd : mRowByName.values()) { Block top = rd.top(); - popFrames(rd, top, Integer.MAX_VALUE); + popFrames(rd, top, Integer.MAX_VALUE, segmentList); } mSurface.setRange(minVal, maxVal); @@ -495,7 +505,7 @@ public class TimeLineView extends Composite implements Observer { // Sort the blocks into increasing rows, and within rows into // increasing start values. - mSegments = mSegmentList.toArray(new Segment[mSegmentList.size()]); + mSegments = segmentList.toArray(new Segment[segmentList.size()]); Arrays.sort(mSegments, new Comparator<Segment>() { public int compare(Segment bd1, Segment bd2) { RowData rd1 = bd1.mRowData; @@ -524,13 +534,14 @@ public class TimeLineView extends Composite implements Observer { } } - private void popFrames(RowData rd, Block top, long startTime) { + private static void popFrames(RowData rd, Block top, long startTime, + ArrayList<Segment> segmentList) { long topEndTime = top.getEndTime(); long lastEndTime = top.getStartTime(); while (topEndTime <= startTime) { if (topEndTime > lastEndTime) { Segment segment = new Segment(rd, top, lastEndTime, topEndTime); - mSegmentList.add(segment); + segmentList.add(segment); lastEndTime = topEndTime; } rd.pop(); @@ -543,7 +554,7 @@ public class TimeLineView extends Composite implements Observer { // If we get here, then topEndTime > startTime if (lastEndTime < startTime) { Segment bd = new Segment(rd, top, lastEndTime, startTime); - mSegmentList.add(bd); + segmentList.add(bd); } } @@ -648,7 +659,9 @@ public class TimeLineView extends Composite implements Observer { private Cursor mZoomCursor; private String mMethodName = null; private Color mMethodColor = null; + private String mDetails; private int mMethodStartY; + private int mDetailsStartY; private int mMarkStartX; private int mMarkEndX; @@ -662,6 +675,7 @@ public class TimeLineView extends Composite implements Observer { mZoomCursor = new Cursor(display, SWT.CURSOR_SIZEWE); setCursor(mZoomCursor); mMethodStartY = mSmallFontHeight + 1; + mDetailsStartY = mMethodStartY + mSmallFontHeight + 1; addPaintListener(new PaintListener() { public void paintControl(PaintEvent pe) { draw(pe.display, pe.gc); @@ -680,7 +694,7 @@ public class TimeLineView extends Composite implements Observer { public void setMarkEnd(int x) { mMarkEndX = x; } - + public void setMethodName(String name) { mMethodName = name; } @@ -688,7 +702,11 @@ public class TimeLineView extends Composite implements Observer { public void setMethodColor(Color color) { mMethodColor = color; } - + + public void setDetails(String details) { + mDetails = details; + } + private void mouseMove(MouseEvent me) { me.y = -1; mSurface.mouseMove(me); @@ -734,7 +752,10 @@ public class TimeLineView extends Composite implements Observer { // Draw the method name and color, if needed drawMethod(display, gcImage); - + + // Draw the details, if needed + drawDetails(display, gcImage); + // Draw the off-screen buffer to the screen gc.drawImage(image, 0, 0); @@ -771,7 +792,11 @@ public class TimeLineView extends Composite implements Observer { // Display the maximum data value double maxVal = mScaleInfo.getMaxVal(); info = mUnits.labelledString(maxVal); - info = String.format(" max %s ", info); //$NON-NLS-1$ + if (mClockSource != null) { + info = String.format(" max %s (%s)", info, mClockSource); //$NON-NLS-1$ + } else { + info = String.format(" max %s ", info); //$NON-NLS-1$ + } Point extent = gc.stringExtent(info); Point dim = getSize(); int x1 = dim.x - RightMargin - extent.x; @@ -791,7 +816,17 @@ public class TimeLineView extends Composite implements Observer { x1 += width + METHOD_BLOCK_MARGIN; gc.drawString(mMethodName, x1, y1, true); } - + + private void drawDetails(Display display, GC gc) { + if (mDetails == null) { + return; + } + + int x1 = LeftMargin + 2 * mSmallFontWidth + METHOD_BLOCK_MARGIN; + int y1 = mDetailsStartY; + gc.drawString(mDetails, x1, y1, true); + } + private void drawTicks(Display display, GC gc) { Point dim = getSize(); int y2 = majorTickLength + timeLineOffsetY; @@ -1049,6 +1084,7 @@ public class TimeLineView extends Composite implements Observer { String blockName = null; Color blockColor = null; + String blockDetails = null; if (mDebug) { double pixelsPerRange = mScaleInfo.getPixelsPerRange(); @@ -1073,8 +1109,30 @@ public class TimeLineView extends Composite implements Observer { if (mMouseRow == strip.mRowData.mRank) { if (mMouse.x >= strip.mX && mMouse.x < strip.mX + strip.mWidth) { - blockName = strip.mSegment.mBlock.getName(); + Block block = strip.mSegment.mBlock; + blockName = block.getName(); blockColor = strip.mColor; + if (mHaveCpuTime) { + if (mHaveRealTime) { + blockDetails = String.format( + "excl cpu %s, incl cpu %s, " + + "excl real %s, incl real %s", + mUnits.labelledString(block.getExclusiveCpuTime()), + mUnits.labelledString(block.getInclusiveCpuTime()), + mUnits.labelledString(block.getExclusiveRealTime()), + mUnits.labelledString(block.getInclusiveRealTime())); + } else { + blockDetails = String.format( + "excl cpu %s, incl cpu %s", + mUnits.labelledString(block.getExclusiveCpuTime()), + mUnits.labelledString(block.getInclusiveCpuTime())); + } + } else { + blockDetails = String.format( + "excl real %s, incl real %s", + mUnits.labelledString(block.getExclusiveRealTime()), + mUnits.labelledString(block.getInclusiveRealTime())); + } } if (mMouseSelect.x >= strip.mX && mMouseSelect.x < strip.mX + strip.mWidth) { @@ -1125,6 +1183,7 @@ public class TimeLineView extends Composite implements Observer { if (blockName != null) { mTimescale.setMethodName(blockName); mTimescale.setMethodColor(blockColor); + mTimescale.setDetails(blockDetails); mShowHighlightName = false; } else if (mShowHighlightName) { // Draw the highlighted method name @@ -1136,10 +1195,12 @@ public class TimeLineView extends Composite implements Observer { if (md != null) { mTimescale.setMethodName(md.getProfileName()); mTimescale.setMethodColor(md.getColor()); + mTimescale.setDetails(null); } } else { mTimescale.setMethodName(null); mTimescale.setMethodColor(null); + mTimescale.setDetails(null); } mTimescale.redraw(); @@ -1152,7 +1213,7 @@ public class TimeLineView extends Composite implements Observer { } private void drawHighlights(GC gc, Point dim) { - int height = highlightHeight; + int height = mHighlightHeight; if (height <= 0) return; for (Range range : mHighlightExclusive) { @@ -1278,13 +1339,15 @@ public class TimeLineView extends Composite implements Observer { long callEnd = -1; RowData callRowData = null; int prevMethodStart = -1; + int prevMethodEnd = -1; int prevCallStart = -1; + int prevCallEnd = -1; if (mHighlightCall != null) { int callPixelStart = -1; int callPixelEnd = -1; - callStart = mHighlightCall.mGlobalStartTime; - callEnd = mHighlightCall.mGlobalEndTime; - callMethod = mHighlightCall.mMethodData; + callStart = mHighlightCall.getStartTime(); + callEnd = mHighlightCall.getEndTime(); + callMethod = mHighlightCall.getMethodData(); if (callStart >= minVal) callPixelStart = mScaleInfo.valueToPixel(callStart); if (callEnd <= maxVal) @@ -1306,7 +1369,11 @@ public class TimeLineView extends Composite implements Observer { continue; if (segment.mStartTime >= maxVal) continue; + Block block = segment.mBlock; + + // Skip over blocks that were not assigned a color, including the + // top level block and others that have zero inclusive time. Color color = block.getColor(); if (color == null) continue; @@ -1318,6 +1385,7 @@ public class TimeLineView extends Composite implements Observer { int pixelStart = mScaleInfo.valueToPixel(recordStart); int pixelEnd = mScaleInfo.valueToPixel(recordEnd); int width = pixelEnd - pixelStart; + boolean isContextSwitch = segment.mIsContextSwitch; RowData rd = segment.mRowData; MethodData md = block.getMethodData(); @@ -1342,24 +1410,25 @@ public class TimeLineView extends Composite implements Observer { if (mHighlightMethodData != null) { if (mHighlightMethodData == md) { - if (prevMethodStart != pixelStart) { + if (prevMethodStart != pixelStart || prevMethodEnd != pixelEnd) { prevMethodStart = pixelStart; + prevMethodEnd = pixelEnd; int rangeWidth = width; if (rangeWidth == 0) rangeWidth = 1; mHighlightExclusive.add(new Range(pixelStart + LeftMargin, rangeWidth, y1, color)); - Call call = (Call) block; - callStart = call.mGlobalStartTime; + callStart = block.getStartTime(); int callPixelStart = -1; if (callStart >= minVal) callPixelStart = mScaleInfo.valueToPixel(callStart); - if (prevCallStart != callPixelStart) { + int callPixelEnd = -1; + callEnd = block.getEndTime(); + if (callEnd <= maxVal) + callPixelEnd = mScaleInfo.valueToPixel(callEnd); + if (prevCallStart != callPixelStart || prevCallEnd != callPixelEnd) { prevCallStart = callPixelStart; - int callPixelEnd = -1; - callEnd = call.mGlobalEndTime; - if (callEnd <= maxVal) - callPixelEnd = mScaleInfo.valueToPixel(callEnd); + prevCallEnd = callPixelEnd; mHighlightInclusive.add(new Range( callPixelStart + LeftMargin, callPixelEnd + LeftMargin, y1, color)); @@ -1372,8 +1441,9 @@ public class TimeLineView extends Composite implements Observer { if (segment.mStartTime >= callStart && segment.mEndTime <= callEnd && callMethod == md && callRowData == rd) { - if (prevMethodStart != pixelStart) { + if (prevMethodStart != pixelStart || prevMethodEnd != pixelEnd) { prevMethodStart = pixelStart; + prevMethodEnd = pixelEnd; int rangeWidth = width; if (rangeWidth == 0) rangeWidth = 1; @@ -1418,7 +1488,7 @@ public class TimeLineView extends Composite implements Observer { // how much of the region [N - 0.5, N + 0.5] is covered // by the segment. double weight = computeWeight(recordStart, recordEnd, - pixelStart); + isContextSwitch, pixelStart); weight = block.addWeight(pixelStart, rd.mRank, weight); if (weight > pix.mMaxWeight) { pix.setFields(pixelStart, weight, segment, color, @@ -1426,13 +1496,15 @@ public class TimeLineView extends Composite implements Observer { } } else { int x1 = pixelStart + LeftMargin; - Strip strip = new Strip(x1, y1, width, rowHeight, rd, - segment, color); + Strip strip = new Strip( + x1, isContextSwitch ? y1 + rowHeight - 1 : y1, + width, isContextSwitch ? 1 : rowHeight, + rd, segment, color); mStripList.add(strip); } } else { double weight = computeWeight(recordStart, recordEnd, - pixelStart); + isContextSwitch, pixelStart); weight = block.addWeight(pixelStart, rd.mRank, weight); if (weight > pix.mMaxWeight) { pix.setFields(pixelStart, weight, segment, color, rd); @@ -1444,7 +1516,7 @@ public class TimeLineView extends Composite implements Observer { // Compute the weight for the next pixel pixelStart += 1; weight = computeWeight(recordStart, recordEnd, - pixelStart); + isContextSwitch, pixelStart); weight = block.addWeight(pixelStart, rd.mRank, weight); pix.setFields(pixelStart, weight, segment, color, rd); } else if (width > 1) { @@ -1455,8 +1527,10 @@ public class TimeLineView extends Composite implements Observer { pixelStart += 1; width -= 1; int x1 = pixelStart + LeftMargin; - Strip strip = new Strip(x1, y1, width, rowHeight, rd, - segment, color); + Strip strip = new Strip( + x1, isContextSwitch ? y1 + rowHeight - 1 : y1, + width, isContextSwitch ? 1 : rowHeight, + rd,segment, color); mStripList.add(strip); } } @@ -1483,7 +1557,11 @@ public class TimeLineView extends Composite implements Observer { } } - private double computeWeight(double start, double end, int pixel) { + private double computeWeight(double start, double end, + boolean isContextSwitch, int pixel) { + if (isContextSwitch) { + return 0; + } double pixelStartFraction = mScaleInfo.valueToPixelFraction(start); double pixelEndFraction = mScaleInfo.valueToPixelFraction(end); double leftEndPoint = Math.max(pixelStartFraction, pixel - 0.5); @@ -1811,7 +1889,7 @@ public class TimeLineView extends Composite implements Observer { } else { mFadeColors = true; mShowHighlightName = true; - highlightHeight = highlightHeights[mHighlightStep]; + mHighlightHeight = highlightHeights[mHighlightStep]; getDisplay().timerExec(HIGHLIGHT_TIMER_INTERVAL, mHighlightAnimator); } redraw(); @@ -1820,7 +1898,7 @@ public class TimeLineView extends Composite implements Observer { private void clearHighlights() { // System.out.printf("clearHighlights()\n"); mShowHighlightName = false; - highlightHeight = 0; + mHighlightHeight = 0; mHighlightMethodData = null; mHighlightCall = null; mFadeColors = false; @@ -1900,7 +1978,7 @@ public class TimeLineView extends Composite implements Observer { private static final int ZOOM_TIMER_INTERVAL = 10; private static final int HIGHLIGHT_TIMER_INTERVAL = 50; private static final int ZOOM_STEPS = 8; // must be even - private int highlightHeight = 4; + private int mHighlightHeight = 4; private final int[] highlightHeights = { 0, 2, 4, 5, 6, 5, 4, 2, 4, 5, 6 }; private final int HIGHLIGHT_STEPS = highlightHeights.length; @@ -1989,7 +2067,12 @@ public class TimeLineView extends Composite implements Observer { private static class Segment { Segment(RowData rowData, Block block, long startTime, long endTime) { mRowData = rowData; - mBlock = block; + if (block.isContextSwitch()) { + mBlock = block.getParentBlock(); + mIsContextSwitch = true; + } else { + mBlock = block; + } mStartTime = startTime; mEndTime = endTime; } @@ -1998,6 +2081,7 @@ public class TimeLineView extends Composite implements Observer { private Block mBlock; private long mStartTime; private long mEndTime; + private boolean mIsContextSwitch; } private static class Strip { diff --git a/traceview/src/com/android/traceview/QtraceReader.java b/traceview/src/com/android/traceview/TraceAction.java index c4db4a2..6717300 100644 --- a/traceview/src/com/android/traceview/QtraceReader.java +++ b/traceview/src/com/android/traceview/TraceAction.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2011 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. @@ -16,30 +16,16 @@ package com.android.traceview; -import java.util.ArrayList; -import java.util.HashMap; +final class TraceAction { + public static final int ACTION_ENTER = 0; + public static final int ACTION_EXIT = 1; + public static final int ACTION_INCOMPLETE = 2; -public class QtraceReader extends TraceReader { - QtraceReader(String traceName) { - } - - @Override - public MethodData[] getMethods() { - return null; - } - - @Override - public HashMap<Integer, String> getThreadLabels() { - return null; - } - - @Override - public ArrayList<TimeLineView.Record> getThreadTimeRecords() { - return null; - } + public final int mAction; + public final Call mCall; - @Override - public ProfileProvider getProfileProvider() { - return null; + public TraceAction(int action, Call call) { + mAction = action; + mCall = call; } } diff --git a/traceview/src/com/android/traceview/TraceReader.java b/traceview/src/com/android/traceview/TraceReader.java index e936629..fa76d27 100644 --- a/traceview/src/com/android/traceview/TraceReader.java +++ b/traceview/src/com/android/traceview/TraceReader.java @@ -45,10 +45,22 @@ public abstract class TraceReader { return null; } - public long getEndTime() { + public long getTotalCpuTime() { return 0; } + public long getTotalRealTime() { + return 0; + } + + public boolean haveCpuTime() { + return false; + } + + public boolean haveRealTime() { + return false; + } + public HashMap<String, String> getProperties() { return null; } @@ -56,4 +68,12 @@ public abstract class TraceReader { public ProfileProvider getProfileProvider() { return null; } + + public TimeBase getPreferredTimeBase() { + return TimeBase.CPU_TIME; + } + + public String getClockSource() { + return null; + } } |