diff options
Diffstat (limited to 'traceview/src/com/android/traceview/DmTraceReader.java')
-rw-r--r-- | traceview/src/com/android/traceview/DmTraceReader.java | 754 |
1 files changed, 0 insertions, 754 deletions
diff --git a/traceview/src/com/android/traceview/DmTraceReader.java b/traceview/src/com/android/traceview/DmTraceReader.java deleted file mode 100644 index 9bd6882..0000000 --- a/traceview/src/com/android/traceview/DmTraceReader.java +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.traceview; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.BufferUnderflowException; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class DmTraceReader extends TraceReader { - 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 HashMap<String, String> mPropertiesMap; - private HashMap<Integer, MethodData> mMethodMap; - private HashMap<Integer, ThreadData> mThreadMap; - private ThreadData[] mSortedThreads; - private MethodData[] mSortedMethods; - private long mTotalCpuTime; - private long mTotalRealTime; - private MethodData mContextSwitch; - 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$ - - public DmTraceReader(String traceFileName, boolean regression) throws IOException { - mTraceFileName = traceFileName; - mRegression = regression; - 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(); - } - - void generateTrees() throws IOException { - long offset = parseKeys(); - parseData(offset); - analyzeData(); - } - - @Override - public ProfileProvider getProfileProvider() { - if (mProfileProvider == null) - mProfileProvider = new ProfileProvider(this); - return mProfileProvider; - } - - private MappedByteBuffer mapFile(String filename, long offset) throws IOException { - MappedByteBuffer buffer = null; - FileInputStream dataFile = new FileInputStream(filename); - try { - File file = new File(filename); - FileChannel fc = dataFile.getChannel(); - buffer = fc.map(FileChannel.MapMode.READ_ONLY, offset, file.length() - offset); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - return buffer; - } finally { - dataFile.close(); // this *also* closes the associated channel, fc - } - } - - private void readDataFileHeader(MappedByteBuffer buffer) { - int magic = buffer.getInt(); - if (magic != TRACE_MAGIC) { - System.err.printf( - "Error: magic number mismatch; got 0x%x, expected 0x%x\n", - 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 - int offsetToData = buffer.getShort() - 16; - - // read startWhen - buffer.getLong(); - - // 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(); - } - } - - private void parseData(long offset) throws IOException { - MappedByteBuffer buffer = mapFile(mTraceFileName, offset); - readDataFileHeader(buffer); - - 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; - } - - 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; - } - - 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); - } - - 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); - } - - long elapsedGlobalTime = 0; - if (haveGlobalClock) { - if (!threadData.mHaveGlobalTime) { - threadData.mGlobalStartTime = globalTime; - threadData.mHaveGlobalTime = true; - } else { - elapsedGlobalTime = globalTime - threadData.mGlobalEndTime; - } - threadData.mGlobalEndTime = 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); - } - } - - // Add thread CPU time. - Call top = threadData.top(); - top.addCpuTime(elapsedThreadTime); - } - - 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); - } - } - - // 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; - } - - if (mRegression) { - System.out.format("totalCpuTime %dus\n", mTotalCpuTime); - System.out.format("totalRealTime %dus\n", mTotalRealTime); - - dumpThreadTimes(); - dumpCallTimes(); - } - } - - static final int PARSE_VERSION = 0; - static final int PARSE_THREADS = 1; - static final int PARSE_METHODS = 2; - static final int PARSE_OPTIONS = 4; - - long parseKeys() throws IOException { - long offset = 0; - BufferedReader in = null; - try { - in = new BufferedReader(new InputStreamReader( - new FileInputStream(mTraceFileName), "US-ASCII")); - - int mode = PARSE_VERSION; - String line = null; - while (true) { - line = in.readLine(); - if (line == null) { - throw new IOException("Key section does not have an *end marker"); - } - - // Calculate how much we have read from the file so far. The - // extra byte is for the line ending not included by readLine(). - offset += line.length() + 1; - if (line.startsWith("*")) { - if (line.equals("*version")) { - mode = PARSE_VERSION; - continue; - } - if (line.equals("*threads")) { - mode = PARSE_THREADS; - continue; - } - if (line.equals("*methods")) { - mode = PARSE_METHODS; - continue; - } - if (line.equals("*end")) { - break; - } - } - switch (mode) { - case PARSE_VERSION: - mVersionNumber = Integer.decode(line); - mode = PARSE_OPTIONS; - break; - case PARSE_THREADS: - parseThread(line); - break; - case PARSE_METHODS: - parseMethod(line); - break; - case PARSE_OPTIONS: - parseOption(line); - break; - } - } - } catch (FileNotFoundException ex) { - System.err.println(ex.getMessage()); - } finally { - if (in != null) { - in.close(); - } - } - - if (mClockSource == null) { - mClockSource = ClockSource.THREAD_CPU; - } - - return offset; - } - - void parseOption(String line) { - String[] tokens = line.split("="); - if (tokens.length == 2) { - 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; - } - } - } - } - - void parseThread(String line) { - String idStr = null; - String name = null; - Matcher matcher = mIdNamePattern.matcher(line); - if (matcher.find()) { - idStr = matcher.group(1); - name = matcher.group(2); - } - if (idStr == null) return; - if (name == null) name = "(unknown)"; - - int id = Integer.decode(idStr); - mThreadMap.put(id, new ThreadData(id, name, mTopLevel)); - } - - void parseMethod(String line) { - String[] tokens = line.split("\t"); - int id = Long.decode(tokens[0]).intValue(); - String className = tokens[1]; - String methodName = null; - String signature = null; - String pathname = null; - int lineNumber = -1; - if (tokens.length == 6) { - methodName = tokens[2]; - signature = tokens[3]; - pathname = tokens[4]; - lineNumber = Integer.decode(tokens[5]); - pathname = constructPathname(className, pathname); - } else if (tokens.length > 2) { - if (tokens[3].startsWith("(")) { - methodName = tokens[2]; - signature = tokens[3]; - } else { - pathname = tokens[2]; - lineNumber = Integer.decode(tokens[3]); - } - } - - mMethodMap.put(id, new MethodData(id, className, methodName, signature, - pathname, lineNumber)); - } - - private String constructPathname(String className, String pathname) { - int index = className.lastIndexOf('/'); - if (index > 0 && index < className.length() - 1 - && pathname.endsWith(".java")) - pathname = className.substring(0, index + 1) + pathname; - return pathname; - } - - 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>() { - @Override - public int compare(ThreadData td1, ThreadData td2) { - if (timeBase.getTime(td2) > timeBase.getTime(td1)) - return 1; - if (timeBase.getTime(td2) < timeBase.getTime(td1)) - return -1; - return td2.getName().compareTo(td1.getName()); - } - }); - - // 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>() { - @Override - public int compare(MethodData md1, MethodData md2) { - if (timeBase.getElapsedInclusiveTime(md2) > timeBase.getElapsedInclusiveTime(md1)) - return 1; - if (timeBase.getElapsedInclusiveTime(md2) < timeBase.getElapsedInclusiveTime(md1)) - return -1; - return md1.getName().compareTo(md2.getName()); - } - }); - - // Count the number of methods with non-zero inclusive time - int nonZero = 0; - for (MethodData md : methods) { - if (timeBase.getElapsedInclusiveTime(md) == 0) - break; - nonZero += 1; - } - - // Copy the methods with non-zero time - mSortedMethods = new MethodData[nonZero]; - int ii = 0; - for (MethodData md : methods) { - if (timeBase.getElapsedInclusiveTime(md) == 0) - break; - md.setRank(ii); - mSortedMethods[ii++] = md; - } - - // Let each method analyze its profile data - for (MethodData md : mSortedMethods) { - md.analyzeData(timeBase); - } - - // Update all the calls to include the method rank in - // their name. - for (Call call : mCallList) { - call.updateName(); - } - - if (mRegression) { - dumpMethodStats(); - } - } - - /* - * This method computes a list of records that describe the the execution - * timeline for each thread. Each record is a pair: (row, block) where: row: - * is the ThreadData object block: is the call (containing the start and end - * times) - */ - @Override - public ArrayList<TimeLineView.Record> getThreadTimeRecords() { - TimeLineView.Record record; - ArrayList<TimeLineView.Record> timeRecs; - timeRecs = new ArrayList<TimeLineView.Record>(); - - // For each thread, push a "toplevel" call that encompasses the - // entire execution of the thread. - for (ThreadData threadData : mSortedThreads) { - if (!threadData.isEmpty() && threadData.getId() != 0) { - record = new TimeLineView.Record(threadData, threadData.getRootCall()); - timeRecs.add(record); - } - } - - for (Call call : mCallList) { - 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() { - 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) { - 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.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 %9d %9d %9s %s\n", - md.getElapsedExclusiveCpuTime(), md.getElapsedInclusiveCpuTime(), - md.getElapsedExclusiveRealTime(), md.getElapsedInclusiveRealTime(), - md.getCalls(), md.getProfileName()); - } - } - - private void dumpTimeRecs(ArrayList<TimeLineView.Record> timeRecs) { - 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; - System.out.format("%2d %8d %8d %8d %8d %s\n", - call.getThreadId(), call.mThreadStartTime, call.mThreadEndTime, - call.mGlobalStartTime, call.mGlobalEndTime, - call.getMethodData().getName()); - } - } - - @Override - public HashMap<Integer, String> getThreadLabels() { - HashMap<Integer, String> labels = new HashMap<Integer, String>(); - for (ThreadData t : mThreadMap.values()) { - labels.put(t.getId(), t.getName()); - } - return labels; - } - - @Override - public MethodData[] getMethods() { - return mSortedMethods; - } - - @Override - public ThreadData[] getThreads() { - return mSortedThreads; - } - - @Override - 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; - } -} |