auto import from //depot/cupcake/@135843

This commit is contained in:
The Android Open Source Project
2009-03-03 19:29:09 -08:00
parent d4aee0c0ca
commit 52d4c30ca5
2386 changed files with 299112 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_JAVA_RESOURCE_DIRS := resources
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAR_MANIFEST := ../etc/manifest.txt
LOCAL_JAVA_LIBRARIES := \
androidprefs \
sdkstats \
swt \
org.eclipse.jface_3.2.0.I20060605-1400 \
org.eclipse.equinox.common_3.2.0.v20060603 \
org.eclipse.core.commands_3.2.0.I20060605-1400
LOCAL_MODULE := traceview
include $(BUILD_HOST_JAVA_LIBRARY)

View File

@@ -0,0 +1,141 @@
/*
* 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 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;
/** global end time */
long mGlobalEndTime;
private String mName;
/**
* 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;
}
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;
mMethodData = methodData;
mThreadStartTime = time;
mFlags = methodAction & METHOD_ACTION_MASK;
mName = methodData.getProfileName();
}
public void updateName() {
mName = mMethodData.getProfileName();
}
public double addWeight(int x, int y, double weight) {
return mMethodData.addWeight(x, y, weight);
}
public void clearWeight() {
mMethodData.clearWeight();
}
public long getStartTime() {
return mGlobalStartTime;
}
public long getEndTime() {
return mGlobalEndTime;
}
public Color getColor() {
return mMethodData.getColor();
}
public void addExclusiveTime(long elapsed) {
mMethodData.addElapsedExclusive(elapsed);
if ((mFlags & IS_RECURSIVE) == 0) {
mMethodData.addTopExclusive(elapsed);
}
}
public void addInclusiveTime(long elapsed, Call parent) {
boolean isRecursive = (mFlags & IS_RECURSIVE) != 0;
mMethodData.addElapsedInclusive(elapsed, isRecursive, parent);
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
int getThreadId() {
return mThreadId;
}
public MethodData getMethodData() {
return mMethodData;
}
int getMethodAction() {
return mFlags & METHOD_ACTION_MASK;
}
public void dump() {
System.out.printf("%s [%d, %d]\n", mName, mGlobalStartTime, mGlobalEndTime);
}
public void setRecursive(boolean isRecursive) {
if (isRecursive) {
mFlags |= IS_RECURSIVE;
} else {
mFlags &= ~IS_RECURSIVE;
}
}
public boolean isRecursive() {
return (mFlags & IS_RECURSIVE) != 0;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.HashMap;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
public class ColorController {
private static final int[] systemColors = { SWT.COLOR_BLUE, SWT.COLOR_RED,
SWT.COLOR_GREEN, SWT.COLOR_CYAN, SWT.COLOR_MAGENTA, SWT.COLOR_DARK_BLUE,
SWT.COLOR_DARK_RED, SWT.COLOR_DARK_GREEN, SWT.COLOR_DARK_YELLOW,
SWT.COLOR_DARK_CYAN, SWT.COLOR_DARK_MAGENTA, SWT.COLOR_BLACK };
private static RGB[] rgbColors = { new RGB(90, 90, 255), // blue
new RGB(0, 240, 0), // green
new RGB(255, 0, 0), // red
new RGB(0, 255, 255), // cyan
new RGB(255, 80, 255), // magenta
new RGB(200, 200, 0), // yellow
new RGB(40, 0, 200), // dark blue
new RGB(150, 255, 150), // light green
new RGB(150, 0, 0), // dark red
new RGB(30, 150, 150), // dark cyan
new RGB(200, 200, 255), // light blue
new RGB(0, 120, 0), // dark green
new RGB(255, 150, 150), // light red
new RGB(140, 80, 140), // dark magenta
new RGB(150, 100, 50), // brown
new RGB(70, 70, 70), // dark grey
};
private static HashMap<Integer, Color> colorCache = new HashMap<Integer, Color>();
private static HashMap<Integer, Image> imageCache = new HashMap<Integer, Image>();
public ColorController() {
}
public static Color requestColor(Display display, RGB rgb) {
return requestColor(display, rgb.red, rgb.green, rgb.blue);
}
public static Image requestColorSquare(Display display, RGB rgb) {
return requestColorSquare(display, rgb.red, rgb.green, rgb.blue);
}
public static Color requestColor(Display display, int red, int green, int blue) {
int key = (red << 16) | (green << 8) | blue;
Color color = colorCache.get(key);
if (color == null) {
color = new Color(display, red, green, blue);
colorCache.put(key, color);
}
return color;
}
public static Image requestColorSquare(Display display, int red, int green, int blue) {
int key = (red << 16) | (green << 8) | blue;
Image image = imageCache.get(key);
if (image == null) {
image = new Image(display, 8, 14);
GC gc = new GC(image);
Color color = requestColor(display, red, green, blue);
gc.setBackground(color);
gc.fillRectangle(image.getBounds());
gc.dispose();
imageCache.put(key, image);
}
return image;
}
public static void assignMethodColors(Display display, MethodData[] methods) {
int nextColorIndex = 0;
for (MethodData md : methods) {
RGB rgb = rgbColors[nextColorIndex];
if (++nextColorIndex == rgbColors.length)
nextColorIndex = 0;
Color color = requestColor(display, rgb);
Image image = requestColorSquare(display, rgb);
md.setColor(color);
md.setImage(image);
// Compute and set a faded color
int fadedRed = 150 + rgb.red / 4;
int fadedGreen = 150 + rgb.green / 4;
int fadedBlue = 150 + rgb.blue / 4;
RGB faded = new RGB(fadedRed, fadedGreen, fadedBlue);
color = requestColor(display, faded);
image = requestColorSquare(display, faded);
md.setFadedColor(color);
md.setFadedImage(image);
}
}
}

View File

@@ -0,0 +1,602 @@
/*
* 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.FileReader;
import java.io.IOException;
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 int mVersionNumber = 0;
private boolean mDebug = false;
private static final int TRACE_MAGIC = 0x574f4c53;
private boolean mRegression;
private ProfileProvider mProfileProvider;
private String mTraceFileName;
private MethodData mTopLevel;
private ArrayList<Call> mCallList;
private ArrayList<Call> mSwitchList;
private HashMap<Integer, MethodData> mMethodMap;
private HashMap<Integer, ThreadData> mThreadMap;
private ThreadData[] mSortedThreads;
private MethodData[] mSortedMethods;
private long mGlobalEndTime;
private MethodData mContextSwitch;
private int mOffsetToData;
private byte[] mBytes = new byte[8];
// 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$
DmTraceReader(String traceFileName, boolean regression) {
mTraceFileName = traceFileName;
mRegression = regression;
mMethodMap = new HashMap<Integer, MethodData>();
mThreadMap = new HashMap<Integer, ThreadData>();
// 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);
generateTrees();
// dumpTrees();
}
void generateTrees() {
try {
long offset = parseKeys();
parseData(offset);
analyzeData();
} catch (IOException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
@Override
public ProfileProvider getProfileProvider() {
if (mProfileProvider == null)
mProfileProvider = new ProfileProvider(this);
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 {
FileInputStream dataFile = new FileInputStream(filename);
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);
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
System.exit(1);
} catch (IOException ex) {
System.err.println(ex.getMessage());
System.exit(1);
}
return buffer;
}
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();
// read offset
mOffsetToData = buffer.getShort() - 16;
// read startWhen
buffer.getLong();
// Skip over "mOffsetToData" bytes
for (int ii = 0; ii < mOffsetToData; ii++) {
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);
}
prevThreadData = threadData;
}
// Read the next call
call = readCall(buffer, call);
if (call == null) {
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);
}
}
}
void parseDataPass2(MappedByteBuffer buffer) {
mCallList = new ArrayList<Call>();
// 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;
// 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 the thread's global start time has not been set yet,
// then set it.
if (threadData.getGlobalStartTime() == -1)
threadData.setGlobalStartTime(globalTime);
prevThreadData = threadData;
}
globalTime += elapsed;
call.mGlobalStartTime = globalTime;
threadData.handleCall(call, globalTime);
mCallList.add(call);
// Read the next call
call = readCall(buffer, null);
if (call == null) {
break;
}
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);
}
mGlobalEndTime = globalTime;
if (mRegression) {
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 {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(mTraceFileName));
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
}
long offset = 0;
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")) {
return offset;
}
}
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:
break;
}
}
}
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() {
// 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())
return 1;
if (td2.getCpuTime() < td1.getCpuTime())
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())
return 1;
if (md2.getElapsedInclusive() < md1.getElapsedInclusive())
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 (md.getElapsedInclusive() == 0)
break;
nonZero += 1;
}
// Copy the methods with non-zero time
mSortedMethods = new MethodData[nonZero];
int ii = 0;
for (MethodData md : methods) {
if (md.getElapsedInclusive() == 0)
break;
md.setRank(ii);
mSortedMethods[ii++] = md;
}
// Let each method analyze its profile data
for (MethodData md : mSortedMethods) {
md.analyzeData();
}
// 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) {
Call call = new Call(threadData.getId(), mTopLevel,
threadData.getGlobalStartTime(), 0);
call.mGlobalStartTime = threadData.getGlobalStartTime();
call.mGlobalEndTime = threadData.getGlobalEndTime();
record = new TimeLineView.Record(threadData, call);
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);
timeRecs.add(record);
}
if (mRegression) {
dumpTimeRecs(timeRecs);
System.exit(0);
}
return timeRecs;
}
private void dumpCallTimes() {
String action;
System.out.format("id thread global start,end 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());
// }
}
}
private void dumpMethodStats() {
System.out.format("\nExclusive Inclusive Calls Method\n");
for (MethodData md : mSortedMethods) {
System.out.format("%9d %9d %9s %s\n",
md.getElapsedExclusive(), md.getElapsedInclusive(),
md.getCalls(), md.getProfileName());
}
}
private void dumpTimeRecs(ArrayList<TimeLineView.Record> timeRecs) {
System.out.format("\nid thread global start,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,
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 getEndTime() {
return mGlobalEndTime;
}
}

View File

@@ -0,0 +1,192 @@
/*
* 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 com.android.sdkstats.SdkStatsService;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.HashMap;
public class MainWindow extends ApplicationWindow {
private final static String PING_NAME = "Traceview";
private final static String PING_VERSION = "1.0";
private TraceReader mReader;
private String mTraceName;
// A global cache of string names.
public static HashMap<String, String> sStringCache = new HashMap<String, String>();
public MainWindow(String traceName, TraceReader reader) {
super(null);
mReader = reader;
mTraceName = traceName;
}
public void run() {
setBlockOnOpen(true);
open();
Display.getCurrent().dispose();
}
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("Traceview: " + mTraceName);
shell.setBounds(100, 10, 1282, 900);
}
@Override
protected Control createContents(Composite parent) {
ColorController.assignMethodColors(parent.getDisplay(), mReader.getMethods());
SelectionController selectionController = new SelectionController();
GridLayout gridLayout = new GridLayout(1, false);
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
gridLayout.horizontalSpacing = 0;
gridLayout.verticalSpacing = 0;
parent.setLayout(gridLayout);
Display display = parent.getDisplay();
Color darkGray = display.getSystemColor(SWT.COLOR_DARK_GRAY);
// Create a sash form to separate the timeline view (on top)
// and the profile view (on bottom)
SashForm sashForm1 = new SashForm(parent, SWT.VERTICAL);
sashForm1.setBackground(darkGray);
sashForm1.SASH_WIDTH = 3;
GridData data = new GridData(GridData.FILL_BOTH);
sashForm1.setLayoutData(data);
// Create the timeline view
new TimeLineView(sashForm1, mReader, selectionController);
// Create the profile view
new ProfileView(sashForm1, mReader, selectionController);
return sashForm1;
}
/**
* Convert the old two-file format into the current concatenated one.
*
* @param base Base path of the two files, i.e. base.key and base.data
* @return Path to a temporary file that will be deleted on exit.
* @throws IOException
*/
private static String makeTempTraceFile(String base) throws IOException {
// Make a temporary file that will go away on exit and prepare to
// write into it.
File temp = File.createTempFile(base, ".trace");
temp.deleteOnExit();
FileChannel dstChannel = new FileOutputStream(temp).getChannel();
// First copy the contents of the key file into our temp file.
FileChannel srcChannel = new FileInputStream(base + ".key").getChannel();
long size = dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
srcChannel.close();
// Then concatenate the data file.
srcChannel = new FileInputStream(base + ".data").getChannel();
dstChannel.transferFrom(srcChannel, size, srcChannel.size());
// Clean up.
srcChannel.close();
dstChannel.close();
// Return the path of the temp file.
return temp.getPath();
}
public static void main(String[] args) {
TraceReader reader = null;
boolean regression = false;
// ping the usage server
SdkStatsService.ping(PING_NAME, PING_VERSION);
// Process command line arguments
int argc = 0;
int len = args.length;
while (argc < len) {
String arg = args[argc];
if (arg.charAt(0) != '-') {
break;
}
if (arg.equals("-r")) {
regression = true;
} else {
break;
}
argc++;
}
if (argc != len - 1) {
System.out.printf("Usage: java %s [-r] trace%n", MainWindow.class.getName());
System.out.printf(" -r regression only%n");
return;
}
String traceName = args[len - 1];
File file = new File(traceName);
if (file.exists() && file.isDirectory()) {
System.out.printf("Qemu trace files not supported yet.\n");
System.exit(1);
// reader = new QtraceReader(traceName);
} else {
// If the filename as given doesn't exist...
if (!file.exists()) {
// Try appending .trace.
if (new File(traceName + ".trace").exists()) {
traceName = traceName + ".trace";
// Next, see if it is the old two-file trace.
} else if (new File(traceName + ".data").exists()
&& new File(traceName + ".key").exists()) {
try {
traceName = makeTempTraceFile(traceName);
} catch (IOException e) {
System.err.printf("cannot convert old trace file '%s'\n", traceName);
System.exit(1);
}
// Otherwise, give up.
} else {
System.err.printf("trace file '%s' not found\n", traceName);
System.exit(1);
}
}
reader = new DmTraceReader(traceName, regression);
}
reader.getTraceUnits().setTimeScale(TraceUnits.TimeScale.MilliSeconds);
new MainWindow(traceName, reader).run();
}
}

View File

@@ -0,0 +1,458 @@
/*
* 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 org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
public class MethodData {
private int mId;
private int mRank = -1;
private String mClassName;
private String mMethodName;
private String mSignature;
private String mName;
private String mProfileName;
private String mPathname;
private int mLineNumber;
private long mElapsedExclusive;
private long mElapsedInclusive;
private long mTopExclusive;
private int[] mNumCalls = new int[2]; // index 0=normal, 1=recursive
private Color mColor;
private Color mFadedColor;
private Image mImage;
private Image mFadedImage;
private HashMap<Integer, ProfileData> mParents;
private HashMap<Integer, ProfileData> mChildren;
// The parents of this method when this method was in a recursive call
private HashMap<Integer, ProfileData> mRecursiveParents;
// The children of this method when this method was in a recursive call
private HashMap<Integer, ProfileData> mRecursiveChildren;
private ProfileNode[] mProfileNodes;
private int mX;
private int mY;
private double mWeight;
public MethodData(int id, String className) {
mId = id;
mClassName = className;
mMethodName = null;
mSignature = null;
mPathname = null;
mLineNumber = -1;
computeName();
computeProfileName();
}
public MethodData(int id, String className, String methodName,
String signature, String pathname, int lineNumber) {
mId = id;
mClassName = className;
mMethodName = methodName;
mSignature = signature;
mPathname = pathname;
mLineNumber = lineNumber;
computeName();
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;
else {
mX = x;
mY = y;
mWeight = weight;
}
return mWeight;
}
public void clearWeight() {
mWeight = 0;
}
public int getRank() {
return mRank;
}
public void setRank(int rank) {
mRank = rank;
computeProfileName();
}
public void addElapsedExclusive(long time) {
mElapsedExclusive += time;
}
public void addElapsedInclusive(long time, boolean isRecursive, Call parent) {
if (isRecursive == false) {
mElapsedInclusive += time;
mNumCalls[0] += 1;
} else {
mNumCalls[1] += 1;
}
if (parent == null)
return;
// Find the child method in the parent
MethodData parentMethod = parent.mMethodData;
if (parent.isRecursive()) {
parentMethod.mRecursiveChildren = updateInclusive(time,
parentMethod, this, false,
parentMethod.mRecursiveChildren);
} else {
parentMethod.mChildren = updateInclusive(time,
parentMethod, this, false, parentMethod.mChildren);
}
// Find the parent method in the child
if (isRecursive) {
mRecursiveParents = updateInclusive(time, this, parentMethod, true,
mRecursiveParents);
} else {
mParents = updateInclusive(time, this, parentMethod, true,
mParents);
}
}
private HashMap<Integer, ProfileData> updateInclusive(long time,
MethodData contextMethod, MethodData elementMethod,
boolean elementIsParent, HashMap<Integer, ProfileData> map) {
if (map == null) {
map = new HashMap<Integer, ProfileData>(4);
} else {
ProfileData profileData = map.get(elementMethod.mId);
if (profileData != null) {
profileData.addElapsedInclusive(time);
return map;
}
}
ProfileData elementData = new ProfileData(contextMethod,
elementMethod, elementIsParent);
elementData.setElapsedInclusive(time);
elementData.setNumCalls(1);
map.put(elementMethod.mId, elementData);
return map;
}
public void analyzeData() {
// 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);
// Add "self" time to the top of the sorted children
sortedChildren = addSelf(sortedChildren);
// Create the ProfileNode objects that we need
ArrayList<ProfileNode> nodes = new ArrayList<ProfileNode>();
ProfileNode profileNode;
if (mParents != null) {
profileNode = new ProfileNode("Parents", this, sortedParents,
true, false);
nodes.add(profileNode);
}
if (mChildren != null) {
profileNode = new ProfileNode("Children", this, sortedChildren,
false, false);
nodes.add(profileNode);
}
if (mRecursiveParents!= null) {
profileNode = new ProfileNode("Parents while recursive", this,
sortedRecursiveParents, true, true);
nodes.add(profileNode);
}
if (mRecursiveChildren != null) {
profileNode = new ProfileNode("Children while recursive", this,
sortedRecursiveChildren, false, true);
nodes.add(profileNode);
}
mProfileNodes = nodes.toArray(new ProfileNode[nodes.size()]);
}
// Create and return a ProfileData[] array that is a sorted copy
// of the given HashMap values.
private ProfileData[] sortProfileData(HashMap<Integer, ProfileData> map) {
if (map == null)
return null;
// Convert the hash values to an array of ProfileData
Collection<ProfileData> values = map.values();
ProfileData[] sorted = values.toArray(new ProfileData[values.size()]);
// Sort the array by elapsed inclusive time
Arrays.sort(sorted, mByElapsedInclusive);
return sorted;
}
private ProfileData[] addSelf(ProfileData[] children) {
ProfileData[] pdata;
if (children == null) {
pdata = new ProfileData[1];
} else {
pdata = new ProfileData[children.length + 1];
System.arraycopy(children, 0, pdata, 1, children.length);
}
pdata[0] = new ProfileSelf(this);
return pdata;
}
public void addTopExclusive(long time) {
mTopExclusive += time;
}
public long getTopExclusive() {
return mTopExclusive;
}
public int getId() {
return mId;
}
private void computeName() {
if (mMethodName == null) {
mName = mClassName;
return;
}
StringBuilder sb = new StringBuilder();
sb.append(mClassName);
sb.append("."); //$NON-NLS-1$
sb.append(mMethodName);
sb.append(" "); //$NON-NLS-1$
sb.append(mSignature);
mName = sb.toString();
}
public String getName() {
return mName;
}
public String getClassName() {
return mClassName;
}
public String getMethodName() {
return mMethodName;
}
public String getProfileName() {
return mProfileName;
}
public void computeProfileName() {
if (mRank == -1) {
mProfileName = mName;
return;
}
StringBuilder sb = new StringBuilder();
sb.append(mRank);
sb.append(" "); //$NON-NLS-1$
sb.append(getName());
mProfileName = sb.toString();
}
public String getCalls() {
return String.format("%d+%d", mNumCalls[0], mNumCalls[1]);
}
public int getTotalCalls() {
return mNumCalls[0] + mNumCalls[1];
}
public Color getColor() {
return mColor;
}
public void setColor(Color color) {
mColor = color;
}
public void setImage(Image image) {
mImage = image;
}
public Image getImage() {
return mImage;
}
@Override
public String toString() {
return getName();
}
public long getElapsedExclusive() {
return mElapsedExclusive;
}
public long getElapsedInclusive() {
return mElapsedInclusive;
}
public void setFadedColor(Color fadedColor) {
mFadedColor = fadedColor;
}
public Color getFadedColor() {
return mFadedColor;
}
public void setFadedImage(Image fadedImage) {
mFadedImage = fadedImage;
}
public Image getFadedImage() {
return mFadedImage;
}
public void setPathname(String pathname) {
mPathname = pathname;
}
public String getPathname() {
return mPathname;
}
public void setLineNumber(int lineNumber) {
mLineNumber = lineNumber;
}
public int getLineNumber() {
return mLineNumber;
}
public ProfileNode[] getProfileNodes() {
return mProfileNodes;
}
public static class Sorter implements Comparator<MethodData> {
public int compare(MethodData md1, MethodData md2) {
if (mColumn == Column.BY_NAME) {
int result = md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
if (mColumn == Column.BY_INCLUSIVE) {
if (md2.getElapsedInclusive() > md1.getElapsedInclusive())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedInclusive() < md1.getElapsedInclusive())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_EXCLUSIVE) {
if (md2.getElapsedExclusive() > md1.getElapsedExclusive())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedExclusive() < md1.getElapsedExclusive())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_CALLS) {
int result = md1.getTotalCalls() - md2.getTotalCalls();
if (result == 0)
return md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
if (mColumn == Column.BY_TIME_PER_CALL) {
double time1 = md1.getElapsedInclusive();
time1 = time1 / md1.getTotalCalls();
double time2 = md2.getElapsedInclusive();
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;
}
return 0;
}
public void setColumn(Column column) {
// If the sort column specified is the same as last time,
// then reverse the sort order.
if (mColumn == column) {
// Reverse the sort order
if (mDirection == Direction.INCREASING)
mDirection = Direction.DECREASING;
else
mDirection = Direction.INCREASING;
} else {
// Sort names into increasing order, data into decreasing order.
if (column == Column.BY_NAME)
mDirection = Direction.INCREASING;
else
mDirection = Direction.DECREASING;
}
mColumn = column;
}
public Column getColumn() {
return mColumn;
}
public void setDirection(Direction direction) {
mDirection = direction;
}
public Direction getDirection() {
return mDirection;
}
public static enum Column {
BY_NAME, BY_EXCLUSIVE, BY_INCLUSIVE, BY_CALLS, BY_TIME_PER_CALL
};
public static enum Direction {
INCREASING, DECREASING
};
private Column mColumn;
private Direction mDirection;
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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;
public class ProfileData {
protected MethodData mElement;
/** mContext is either the parent or child of mElement */
protected MethodData mContext;
protected boolean mElementIsParent;
protected long mElapsedInclusive;
protected int mNumCalls;
public ProfileData() {
}
public ProfileData(MethodData context, MethodData element,
boolean elementIsParent) {
mContext = context;
mElement = element;
mElementIsParent = elementIsParent;
}
public String getProfileName() {
return mElement.getProfileName();
}
public MethodData getMethodData() {
return mElement;
}
public void addElapsedInclusive(long elapsedInclusive) {
mElapsedInclusive += elapsedInclusive;
mNumCalls += 1;
}
public void setElapsedInclusive(long elapsedInclusive) {
mElapsedInclusive = elapsedInclusive;
}
public long getElapsedInclusive() {
return mElapsedInclusive;
}
public void setNumCalls(int numCalls) {
mNumCalls = numCalls;
}
public String getNumCalls() {
int totalCalls;
if (mElementIsParent)
totalCalls = mContext.getTotalCalls();
else
totalCalls = mElement.getTotalCalls();
return String.format("%d/%d", mNumCalls, totalCalls);
}
public boolean isParent() {
return mElementIsParent;
}
public MethodData getContext() {
return mContext;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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;
public class ProfileNode {
private String mLabel;
private MethodData mMethodData;
private ProfileData[] mChildren;
private boolean mIsParent;
private boolean mIsRecursive;
public ProfileNode(String label, MethodData methodData,
ProfileData[] children, boolean isParent, boolean isRecursive) {
mLabel = label;
mMethodData = methodData;
mChildren = children;
mIsParent = isParent;
mIsRecursive = isRecursive;
}
public String getLabel() {
return mLabel;
}
public ProfileData[] getChildren() {
return mChildren;
}
public boolean isParent() {
return mIsParent;
}
public boolean isRecursive() {
return mIsRecursive;
}
}

View File

@@ -0,0 +1,361 @@
/*
* 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.InputStream;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
class ProfileProvider implements ITreeContentProvider {
private MethodData[] mRoots;
private SelectionAdapter mListener;
private TreeViewer mTreeViewer;
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 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 Pattern mUppercase;
private int mPrevMatchIndex = -1;
public ProfileProvider(TraceReader reader) {
mRoots = reader.getMethods();
mReader = reader;
mTotalTime = reader.getEndTime();
Display display = Display.getCurrent();
InputStream in = getClass().getClassLoader().getResourceAsStream(
"icons/sort_up.png");
mSortUp = new Image(display, in);
in = getClass().getClassLoader().getResourceAsStream(
"icons/sort_down.png");
mSortDown = new Image(display, in);
mUppercase = Pattern.compile("[A-Z]");
}
private MethodData doMatchName(String name, int startIndex) {
// Check if the given "name" has any uppercase letters
boolean hasUpper = mUppercase.matcher(name).matches();
for (int ii = startIndex; ii < mRoots.length; ++ii) {
MethodData md = mRoots[ii];
String fullName = md.getName();
// If there were no upper case letters in the given name,
// then ignore case when matching.
if (!hasUpper)
fullName = fullName.toLowerCase();
if (fullName.indexOf(name) != -1) {
mPrevMatchIndex = ii;
return md;
}
}
mPrevMatchIndex = -1;
return null;
}
public MethodData findMatchingName(String name) {
return doMatchName(name, 0);
}
public MethodData findNextMatchingName(String name) {
return doMatchName(name, mPrevMatchIndex + 1);
}
public MethodData findMatchingTreeItem(TreeItem item) {
if (item == null)
return null;
String text = item.getText();
if (Character.isDigit(text.charAt(0)) == false)
return null;
int spaceIndex = text.indexOf(' ');
String numstr = text.substring(0, spaceIndex);
int rank = Integer.valueOf(numstr);
for (MethodData md : mRoots) {
if (md.getRank() == rank)
return md;
}
return null;
}
public void setTreeViewer(TreeViewer treeViewer) {
mTreeViewer = treeViewer;
}
public String[] getColumnNames() {
return mColumnNames;
}
public int[] getColumnWidths() {
return mColumnWidths;
}
public int[] getColumnAlignments() {
return mColumnAlignments;
}
public Object[] getChildren(Object element) {
if (element instanceof MethodData) {
MethodData md = (MethodData) element;
return md.getProfileNodes();
}
if (element instanceof ProfileNode) {
ProfileNode pn = (ProfileNode) element;
return pn.getChildren();
}
return new Object[0];
}
public Object getParent(Object element) {
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof MethodData)
return true;
if (element instanceof ProfileNode)
return true;
return false;
}
public Object[] getElements(Object element) {
return mRoots;
}
public void dispose() {
}
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
}
public Object getRoot() {
return "root";
}
public SelectionAdapter getColumnListener() {
if (mListener == null)
mListener = new ColumnListener();
return mListener;
}
public LabelProvider getLabelProvider() {
return new ProfileLabelProvider();
}
class ProfileLabelProvider extends LabelProvider implements
ITableLabelProvider, IColorProvider {
Color colorRed;
Color colorParentsBack;
Color colorChildrenBack;
TraceUnits traceUnits;
public ProfileLabelProvider() {
Display display = Display.getCurrent();
colorRed = display.getSystemColor(SWT.COLOR_RED);
colorParentsBack = new Color(display, 230, 230, 255); // blue
colorChildrenBack = new Color(display, 255, 255, 210); // yellow
traceUnits = mReader.getTraceUnits();
}
public String getColumnText(Object element, int col) {
if (element instanceof MethodData) {
MethodData md = (MethodData) element;
if (col == COL_NAME)
return md.getProfileName();
if (col == COL_EXCLUSIVE) {
double val = md.getElapsedExclusive();
val = traceUnits.getScaledValue(val);
return String.format("%.3f", val);
}
if (col == COL_EXCLUSIVE_PER) {
double val = md.getElapsedExclusive();
double per = val * 100.0 / mTotalTime;
return String.format("%.1f%%", per);
}
if (col == COL_INCLUSIVE) {
double val = md.getElapsedInclusive();
val = traceUnits.getScaledValue(val);
return String.format("%.3f", val);
}
if (col == COL_INCLUSIVE_PER) {
double val = md.getElapsedInclusive();
double per = val * 100.0 / mTotalTime;
return String.format("%.1f%%", per);
}
if (col == COL_CALLS)
return md.getCalls();
if (col == COL_TIME_PER_CALL) {
int numCalls = md.getTotalCalls();
double val = md.getElapsedInclusive();
val = val / numCalls;
val = traceUnits.getScaledValue(val);
return String.format("%.3f", val);
}
} else if (element instanceof ProfileSelf) {
ProfileSelf ps = (ProfileSelf) element;
if (col == COL_NAME)
return ps.getProfileName();
if (col == COL_INCLUSIVE) {
double val = ps.getElapsedInclusive();
val = traceUnits.getScaledValue(val);
return String.format("%.3f", val);
}
if (col == COL_INCLUSIVE_PER) {
double total;
double val = ps.getElapsedInclusive();
MethodData context = ps.getContext();
total = context.getElapsedInclusive();
double per = val * 100.0 / total;
return String.format("%.1f%%", per);
}
return "";
} else if (element instanceof ProfileData) {
ProfileData pd = (ProfileData) element;
if (col == COL_NAME)
return pd.getProfileName();
if (col == COL_INCLUSIVE) {
double val = pd.getElapsedInclusive();
val = traceUnits.getScaledValue(val);
return String.format("%.3f", val);
}
if (col == COL_INCLUSIVE_PER) {
double total;
double val = pd.getElapsedInclusive();
MethodData context = pd.getContext();
total = context.getElapsedInclusive();
double per = val * 100.0 / total;
return String.format("%.1f%%", per);
}
if (col == COL_CALLS)
return pd.getNumCalls();
return "";
} else if (element instanceof ProfileNode) {
ProfileNode pn = (ProfileNode) element;
if (col == COL_NAME)
return pn.getLabel();
return "";
}
return "col" + col;
}
public Image getColumnImage(Object element, int col) {
if (col != COL_NAME)
return null;
if (element instanceof MethodData) {
MethodData md = (MethodData) element;
return md.getImage();
}
if (element instanceof ProfileData) {
ProfileData pd = (ProfileData) element;
MethodData md = pd.getMethodData();
return md.getImage();
}
return null;
}
public Color getForeground(Object element) {
return null;
}
public Color getBackground(Object element) {
if (element instanceof ProfileData) {
ProfileData pd = (ProfileData) element;
if (pd.isParent())
return colorParentsBack;
return colorChildrenBack;
}
if (element instanceof ProfileNode) {
ProfileNode pn = (ProfileNode) element;
if (pn.isParent())
return colorParentsBack;
return colorChildrenBack;
}
return null;
}
}
class ColumnListener extends SelectionAdapter {
MethodData.Sorter sorter = new MethodData.Sorter();
@Override
public void widgetSelected(SelectionEvent event) {
TreeColumn column = (TreeColumn) event.widget;
String name = column.getText();
Tree tree = column.getParent();
tree.setRedraw(false);
TreeColumn[] columns = tree.getColumns();
for (TreeColumn col : columns) {
col.setImage(null);
}
if (name == mColumnNames[COL_NAME]) {
// 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);
Arrays.sort(mRoots, sorter);
} else if (name == mColumnNames[COL_EXCLUSIVE_PER]) {
sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE);
Arrays.sort(mRoots, sorter);
} else if (name == mColumnNames[COL_INCLUSIVE]) {
sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE);
Arrays.sort(mRoots, sorter);
} else if (name == mColumnNames[COL_INCLUSIVE_PER]) {
sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE);
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);
Arrays.sort(mRoots, sorter);
}
MethodData.Sorter.Direction direction = sorter.getDirection();
if (direction == MethodData.Sorter.Direction.INCREASING)
column.setImage(mSortDown);
else
column.setImage(mSortUp);
tree.setRedraw(true);
mTreeViewer.refresh();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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;
public class ProfileSelf extends ProfileData {
public ProfileSelf(MethodData methodData) {
mElement = methodData;
mContext = methodData;
}
@Override
public String getProfileName() {
return "self";
}
@Override
public long getElapsedInclusive() {
return mElement.getTopExclusive();
}
}

View File

@@ -0,0 +1,308 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
public class ProfileView extends Composite implements Observer {
private TreeViewer mTreeViewer;
private Text mSearchBox;
private SelectionController mSelectionController;
private ProfileProvider mProfileProvider;
private Color mColorNoMatch;
private Color mColorMatch;
private MethodData mCurrentHighlightedMethod;
public ProfileView(Composite parent, TraceReader reader,
SelectionController selectController) {
super(parent, SWT.NONE);
setLayout(new GridLayout(1, false));
this.mSelectionController = selectController;
mSelectionController.addObserver(this);
// Add a tree viewer at the top
mTreeViewer = new TreeViewer(this, SWT.MULTI | SWT.NONE);
mTreeViewer.setUseHashlookup(true);
mProfileProvider = reader.getProfileProvider();
mProfileProvider.setTreeViewer(mTreeViewer);
SelectionAdapter listener = mProfileProvider.getColumnListener();
final Tree tree = mTreeViewer.getTree();
tree.setHeaderVisible(true);
tree.setLayoutData(new GridData(GridData.FILL_BOTH));
// Get the column names from the ProfileProvider
String[] columnNames = mProfileProvider.getColumnNames();
int[] columnWidths = mProfileProvider.getColumnWidths();
int[] columnAlignments = mProfileProvider.getColumnAlignments();
for (int ii = 0; ii < columnWidths.length; ++ii) {
TreeColumn column = new TreeColumn(tree, SWT.LEFT);
column.setText(columnNames[ii]);
column.setWidth(columnWidths[ii]);
column.setMoveable(true);
column.addSelectionListener(listener);
column.setAlignment(columnAlignments[ii]);
}
// Add a listener to the tree so that we can make the row
// height smaller.
tree.addListener(SWT.MeasureItem, new Listener() {
public void handleEvent(Event event) {
int fontHeight = event.gc.getFontMetrics().getHeight();
event.height = fontHeight;
}
});
mTreeViewer.setContentProvider(mProfileProvider);
mTreeViewer.setLabelProvider(mProfileProvider.getLabelProvider());
mTreeViewer.setInput(mProfileProvider.getRoot());
// Create another composite to hold the label and text box
Composite composite = new Composite(this, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
// Add a label for the search box
Label label = new Label(composite, SWT.NONE);
label.setText("Find:");
// Add a text box for searching for method names
mSearchBox = new Text(composite, SWT.BORDER);
mSearchBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Display display = getDisplay();
mColorNoMatch = new Color(display, 255, 200, 200);
mColorMatch = mSearchBox.getBackground();
mSearchBox.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent ev) {
String query = mSearchBox.getText();
if (query.length() == 0)
return;
findName(query);
}
});
// Add a key listener to the text box so that we can clear
// the text box if the user presses <ESC>.
mSearchBox.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
if (event.keyCode == SWT.ESC) {
mSearchBox.setText("");
} else if (event.keyCode == SWT.CR) {
String query = mSearchBox.getText();
if (query.length() == 0)
return;
findNextName(query);
}
}
});
// Also add a key listener to the tree viewer so that the
// user can just start typing anywhere in the tree view.
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
if (event.keyCode == SWT.ESC) {
mSearchBox.setText("");
} else if (event.keyCode == SWT.BS) {
// Erase the last character from the search box
String text = mSearchBox.getText();
int len = text.length();
String chopped;
if (len <= 1)
chopped = "";
else
chopped = text.substring(0, len - 1);
mSearchBox.setText(chopped);
} else if (event.keyCode == SWT.CR) {
String query = mSearchBox.getText();
if (query.length() == 0)
return;
findNextName(query);
} else {
// Append the given character to the search box
String str = String.valueOf(event.character);
mSearchBox.append(str);
}
event.doit = false;
}
});
// Add a selection listener to the tree so that the user can click
// on a method that is a child or parent and jump to that method.
mTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent ev) {
ISelection sel = ev.getSelection();
if (sel.isEmpty())
return;
if (sel instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection) sel;
Object element = selection.getFirstElement();
if (element == null)
return;
if (element instanceof MethodData) {
MethodData md = (MethodData) element;
highlightMethod(md, true);
}
if (element instanceof ProfileData) {
MethodData md = ((ProfileData) element)
.getMethodData();
highlightMethod(md, true);
}
}
}
});
// Add a tree listener so that we can expand the parents and children
// of a method when a method is expanded.
mTreeViewer.addTreeListener(new ITreeViewerListener() {
public void treeExpanded(TreeExpansionEvent event) {
Object element = event.getElement();
if (element instanceof MethodData) {
MethodData md = (MethodData) element;
expandNode(md);
}
}
public void treeCollapsed(TreeExpansionEvent event) {
}
});
tree.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
Point point = new Point(event.x, event.y);
TreeItem treeItem = tree.getItem(point);
MethodData md = mProfileProvider.findMatchingTreeItem(treeItem);
if (md == null)
return;
ArrayList<Selection> selections = new ArrayList<Selection>();
selections.add(Selection.highlight("MethodData", md));
mSelectionController.change(selections, "ProfileView");
}
});
}
private void findName(String query) {
MethodData md = mProfileProvider.findMatchingName(query);
selectMethod(md);
}
private void findNextName(String query) {
MethodData md = mProfileProvider.findNextMatchingName(query);
selectMethod(md);
}
private void selectMethod(MethodData md) {
if (md == null) {
mSearchBox.setBackground(mColorNoMatch);
return;
}
mSearchBox.setBackground(mColorMatch);
highlightMethod(md, false);
}
public void update(Observable objservable, Object arg) {
// Ignore updates from myself
if (arg == "ProfileView")
return;
// System.out.printf("profileview update from %s\n", arg);
ArrayList<Selection> selections;
selections = mSelectionController.getSelections();
for (Selection selection : selections) {
Selection.Action action = selection.getAction();
if (action != Selection.Action.Highlight)
continue;
String name = selection.getName();
if (name == "MethodData") {
MethodData md = (MethodData) selection.getValue();
highlightMethod(md, true);
return;
}
if (name == "Call") {
Call call = (Call) selection.getValue();
MethodData md = call.mMethodData;
highlightMethod(md, true);
return;
}
}
}
private void highlightMethod(MethodData md, boolean clearSearch) {
if (md == null)
return;
// Avoid an infinite recursion
if (md == mCurrentHighlightedMethod)
return;
if (clearSearch) {
mSearchBox.setText("");
mSearchBox.setBackground(mColorMatch);
}
mCurrentHighlightedMethod = md;
mTreeViewer.collapseAll();
// Expand this node and its children
expandNode(md);
StructuredSelection sel = new StructuredSelection(md);
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]);
}
private void expandNode(MethodData md) {
ProfileNode[] nodes = md.getProfileNodes();
mTreeViewer.setExpandedState(md, true);
// Also expand the "Parents" and "Children" nodes.
for (ProfileNode node : nodes) {
if (node.isRecursive() == false)
mTreeViewer.setExpandedState(node, true);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.HashMap;
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;
}
@Override
public ProfileProvider getProfileProvider() {
return null;
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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;
public class Selection {
private Action mAction;
private String mName;
private Object mValue;
public Selection(Action action, String name, Object value) {
mAction = action;
mName = name;
mValue = value;
}
public static Selection highlight(String name, Object value) {
return new Selection(Action.Highlight, name, value);
}
public static Selection include(String name, Object value) {
return new Selection(Action.Include, name, value);
}
public static Selection exclude(String name, Object value) {
return new Selection(Action.Exclude, name, value);
}
public void setName(String name) {
mName = name;
}
public String getName() {
return mName;
}
public void setValue(Object value) {
mValue = value;
}
public Object getValue() {
return mValue;
}
public void setAction(Action action) {
mAction = action;
}
public Action getAction() {
return mAction;
}
public static enum Action {
Highlight, Include, Exclude, Aggregate
};
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.Observable;
public class SelectionController extends Observable {
private ArrayList<Selection> mSelections;
public void change(ArrayList<Selection> selections, Object arg) {
this.mSelections = selections;
setChanged();
notifyObservers(arg);
}
public ArrayList<Selection> getSelections() {
return mSelections;
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.HashMap;
class ThreadData implements TimeLineView.Row {
private int mId;
private String mName;
private long mGlobalStartTime = -1;
private long mGlobalEndTime = -1;
private long mLastEventTime;
private long mCpuTime;
private Call mRoot;
private Call mCurrent;
private Call mLastContextSwitch;
private ArrayList<Call> mStack = new ArrayList<Call>();
// This is a hash of all the methods that are currently on the stack.
private HashMap<MethodData, Integer> mStackMethods = new HashMap<MethodData, Integer>();
// True if no calls have ever been added to this thread
private boolean mIsEmpty;
ThreadData(int id, String name, MethodData topLevel) {
mId = id;
mName = String.format("[%d] %s", id, name);
mRoot = new Call(mName, topLevel);
mCurrent = mRoot;
mIsEmpty = true;
}
public boolean isEmpty() {
return mIsEmpty;
}
public String getName() {
return mName;
}
public Call getCalltreeRoot() {
return mRoot;
}
void handleCall(Call call, long globalTime) {
mIsEmpty = false;
long currentTime = call.mThreadStartTime;
if (currentTime < mLastEventTime) {
System.err
.printf(
"ThreadData: '%1$s' call time (%2$d) is less than previous time (%3$d) for thread '%4$s'\n",
call.getName(), currentTime, mLastEventTime, mName);
System.exit(1);
}
long elapsed = currentTime - mLastEventTime;
mCpuTime += elapsed;
if (call.getMethodAction() == 0) {
// This is a method entry.
enter(call, elapsed);
} else {
// This is a method exit.
exit(call, elapsed, globalTime);
}
mLastEventTime = currentTime;
mGlobalEndTime = globalTime;
}
private void enter(Call c, long elapsed) {
Call caller = mCurrent;
push(c);
// Check the stack for a matching method to determine if this call
// is recursive.
MethodData md = c.mMethodData;
Integer num = mStackMethods.get(md);
if (num == null) {
num = 0;
} else if (num > 0) {
c.setRecursive(true);
}
num += 1;
mStackMethods.put(md, num);
mCurrent = c;
// Add the elapsed time to the caller's exclusive time
caller.addExclusiveTime(elapsed);
}
private void exit(Call c, long elapsed, long globalTime) {
mCurrent.mGlobalEndTime = globalTime;
Call top = pop();
if (top == null) {
return;
}
if (mCurrent.mMethodData != c.mMethodData) {
String error = "Method exit (" + c.getName()
+ ") does not match current method (" + mCurrent.getName()
+ ")";
throw new RuntimeException(error);
} else {
long duration = c.mThreadStartTime - mCurrent.mThreadStartTime;
Call caller = top();
mCurrent.addExclusiveTime(elapsed);
mCurrent.addInclusiveTime(duration, caller);
if (caller == null) {
caller = mRoot;
}
mCurrent = caller;
}
}
public void push(Call c) {
mStack.add(c);
}
public Call pop() {
ArrayList<Call> stack = mStack;
if (stack.size() == 0)
return null;
Call top = stack.get(stack.size() - 1);
stack.remove(stack.size() - 1);
// Decrement the count on the method in the hash table and remove
// the entry when it goes to zero.
MethodData md = top.mMethodData;
Integer num = mStackMethods.get(md);
if (num != null) {
num -= 1;
if (num <= 0) {
mStackMethods.remove(md);
} else {
mStackMethods.put(md, num);
}
}
return top;
}
public Call top() {
ArrayList<Call> stack = mStack;
if (stack.size() == 0)
return null;
return stack.get(stack.size() - 1);
}
public long endTrace() {
// If we have calls on the stack when the trace ends, then clean up
// the stack and compute the inclusive time of the methods by pretending
// that we are exiting from their methods now.
while (mCurrent != mRoot) {
long duration = mLastEventTime - mCurrent.mThreadStartTime;
pop();
Call caller = top();
mCurrent.addInclusiveTime(duration, caller);
mCurrent.mGlobalEndTime = mGlobalEndTime;
if (caller == null) {
caller = mRoot;
}
mCurrent = caller;
}
return mLastEventTime;
}
@Override
public String toString() {
return mName;
}
public int getId() {
return mId;
}
public void setCpuTime(long cpuTime) {
mCpuTime = cpuTime;
}
public long getCpuTime() {
return mCpuTime;
}
public void setGlobalStartTime(long globalStartTime) {
mGlobalStartTime = globalStartTime;
}
public long getGlobalStartTime() {
return mGlobalStartTime;
}
public void setLastEventTime(long lastEventTime) {
mLastEventTime = lastEventTime;
}
public long getLastEventTime() {
return mLastEventTime;
}
public void setGlobalEndTime(long globalEndTime) {
mGlobalEndTime = globalEndTime;
}
public long getGlobalEndTime() {
return mGlobalEndTime;
}
public void setLastContextSwitch(Call lastContextSwitch) {
mLastContextSwitch = lastContextSwitch;
}
public Call getLastContextSwitch() {
return mLastContextSwitch;
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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;
class TickScaler {
private double mMinVal; // required input
private double mMaxVal; // required input
private double mRangeVal;
private int mNumPixels; // required input
private int mPixelsPerTick; // required input
private double mPixelsPerRange;
private double mTickIncrement;
private double mMinMajorTick;
TickScaler(double minVal, double maxVal, int numPixels, int pixelsPerTick) {
mMinVal = minVal;
mMaxVal = maxVal;
mNumPixels = numPixels;
mPixelsPerTick = pixelsPerTick;
}
public void setMinVal(double minVal) {
mMinVal = minVal;
}
public double getMinVal() {
return mMinVal;
}
public void setMaxVal(double maxVal) {
mMaxVal = maxVal;
}
public double getMaxVal() {
return mMaxVal;
}
public void setNumPixels(int numPixels) {
mNumPixels = numPixels;
}
public int getNumPixels() {
return mNumPixels;
}
public void setPixelsPerTick(int pixelsPerTick) {
mPixelsPerTick = pixelsPerTick;
}
public int getPixelsPerTick() {
return mPixelsPerTick;
}
public void setPixelsPerRange(double pixelsPerRange) {
mPixelsPerRange = pixelsPerRange;
}
public double getPixelsPerRange() {
return mPixelsPerRange;
}
public void setTickIncrement(double tickIncrement) {
mTickIncrement = tickIncrement;
}
public double getTickIncrement() {
return mTickIncrement;
}
public void setMinMajorTick(double minMajorTick) {
mMinMajorTick = minMajorTick;
}
public double getMinMajorTick() {
return mMinMajorTick;
}
// Convert a time value to a 0-based pixel value
public int valueToPixel(double value) {
return (int) Math.ceil(mPixelsPerRange * (value - mMinVal) - 0.5);
}
// Convert a time value to a 0-based fractional pixel
public double valueToPixelFraction(double value) {
return mPixelsPerRange * (value - mMinVal);
}
// Convert a 0-based pixel value to a time value
public double pixelToValue(int pixel) {
return mMinVal + (pixel / mPixelsPerRange);
}
public void computeTicks(boolean useGivenEndPoints) {
int numTicks = mNumPixels / mPixelsPerTick;
mRangeVal = mMaxVal - mMinVal;
mTickIncrement = mRangeVal / numTicks;
double dlogTickIncrement = Math.log10(mTickIncrement);
int logTickIncrement = (int) Math.floor(dlogTickIncrement);
double scale = Math.pow(10, logTickIncrement);
double scaledTickIncr = mTickIncrement / scale;
if (scaledTickIncr > 5.0)
scaledTickIncr = 10;
else if (scaledTickIncr > 2)
scaledTickIncr = 5;
else if (scaledTickIncr > 1)
scaledTickIncr = 2;
else
scaledTickIncr = 1;
mTickIncrement = scaledTickIncr * scale;
if (!useGivenEndPoints) {
// Round up the max val to the next minor tick
double minorTickIncrement = mTickIncrement / 5;
double dval = mMaxVal / minorTickIncrement;
int ival = (int) dval;
if (ival != dval)
mMaxVal = (ival + 1) * minorTickIncrement;
// Round down the min val to a multiple of tickIncrement
ival = (int) (mMinVal / mTickIncrement);
mMinVal = ival * mTickIncrement;
mMinMajorTick = mMinVal;
} else {
int ival = (int) (mMinVal / mTickIncrement);
mMinMajorTick = ival * mTickIncrement;
if (mMinMajorTick < mMinVal)
mMinMajorTick = mMinMajorTick + mTickIncrement;
}
mRangeVal = mMaxVal - mMinVal;
mPixelsPerRange = (double) mNumPixels / mRangeVal;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import java.util.ArrayList;
import java.util.HashMap;
public abstract class TraceReader {
private TraceUnits mTraceUnits;
public TraceUnits getTraceUnits() {
if (mTraceUnits == null)
mTraceUnits = new TraceUnits();
return mTraceUnits;
}
public ArrayList<TimeLineView.Record> getThreadTimeRecords() {
return null;
}
public HashMap<Integer, String> getThreadLabels() {
return null;
}
public MethodData[] getMethods() {
return null;
}
public ThreadData[] getThreads() {
return null;
}
public long getEndTime() {
return 0;
}
public ProfileProvider getProfileProvider() {
return null;
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.text.DecimalFormat;
// This should be a singleton.
public class TraceUnits {
private TimeScale mTimeScale = TimeScale.MicroSeconds;
private double mScale = 1.0;
DecimalFormat mFormatter = new DecimalFormat();
public double getScaledValue(long value) {
return value * mScale;
}
public double getScaledValue(double value) {
return value * mScale;
}
public String valueOf(long value) {
return valueOf((double) value);
}
public String valueOf(double value) {
String pattern;
double scaled = value * mScale;
if ((int) scaled == scaled)
pattern = "###,###";
else
pattern = "###,###.###";
mFormatter.applyPattern(pattern);
return mFormatter.format(scaled);
}
public String labelledString(double value) {
String units = label();
String num = valueOf(value);
return String.format("%s: %s", units, num);
}
public String labelledString(long value) {
return labelledString((double) value);
}
public String label() {
if (mScale == 1.0)
return "usec";
if (mScale == 0.001)
return "msec";
if (mScale == 0.000001)
return "sec";
return null;
}
public void setTimeScale(TimeScale val) {
mTimeScale = val;
switch (val) {
case Seconds:
mScale = 0.000001;
break;
case MilliSeconds:
mScale = 0.001;
break;
case MicroSeconds:
mScale = 1.0;
break;
}
}
public TimeScale getTimeScale() {
return mTimeScale;
}
public enum TimeScale {
Seconds, MilliSeconds, MicroSeconds
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B