aboutsummaryrefslogtreecommitdiffstats
path: root/traceview
diff options
context:
space:
mode:
Diffstat (limited to 'traceview')
-rw-r--r--traceview/src/com/android/traceview/Call.java148
-rw-r--r--traceview/src/com/android/traceview/DmTraceReader.java578
-rw-r--r--traceview/src/com/android/traceview/MethodData.java149
-rw-r--r--traceview/src/com/android/traceview/ProfileData.java21
-rw-r--r--traceview/src/com/android/traceview/ProfileProvider.java189
-rw-r--r--traceview/src/com/android/traceview/ProfileSelf.java9
-rw-r--r--traceview/src/com/android/traceview/ProfileView.java10
-rw-r--r--traceview/src/com/android/traceview/ThreadData.java228
-rw-r--r--traceview/src/com/android/traceview/TimeBase.java71
-rw-r--r--traceview/src/com/android/traceview/TimeLineView.java182
-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.java22
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;
+ }
}