summaryrefslogtreecommitdiffstats
path: root/dalvik
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2011-03-29 22:55:01 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2011-03-29 22:55:01 -0700
commit645d772dfc09d9f0a384e3e9bb7894df5887de65 (patch)
treeb753c6a09de8eec53dd26836ab1a3ed6613a0971 /dalvik
parentb39fa9149966992e55b8dd6bc75c27d7a6768ddc (diff)
parente9af8901fc4ed7c05d085e2e492f5dcc857f0146 (diff)
downloadlibcore-645d772dfc09d9f0a384e3e9bb7894df5887de65.zip
libcore-645d772dfc09d9f0a384e3e9bb7894df5887de65.tar.gz
libcore-645d772dfc09d9f0a384e3e9bb7894df5887de65.tar.bz2
am e9af8901: SamplingProfilerIntegration and SamplingProfiler improvements (2/3)
* commit 'e9af8901fc4ed7c05d085e2e492f5dcc857f0146': SamplingProfilerIntegration and SamplingProfiler improvements (2/3)
Diffstat (limited to 'dalvik')
-rw-r--r--dalvik/src/main/java/dalvik/system/VMStack.java14
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java17
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java30
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java23
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java24
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java51
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java124
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/HprofWriter.java23
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java43
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java77
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java44
-rw-r--r--dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java8
12 files changed, 392 insertions, 86 deletions
diff --git a/dalvik/src/main/java/dalvik/system/VMStack.java b/dalvik/src/main/java/dalvik/system/VMStack.java
index 6f38f7e..a8430d8 100644
--- a/dalvik/src/main/java/dalvik/system/VMStack.java
+++ b/dalvik/src/main/java/dalvik/system/VMStack.java
@@ -75,4 +75,18 @@ public final class VMStack {
* doesn't have a stack trace (e.g. because it exited)
*/
native public static StackTraceElement[] getThreadStackTrace(Thread t);
+
+ /**
+ * Retrieves a partial stack trace from the specified thread into
+ * the provided array.
+ *
+ * @param t
+ * thread of interest
+ * @param stackTraceElements
+ * preallocated array for use when only the top of stack is
+ * desired. Unused elements will be filled with null values.
+ * @return the number of elements filled
+ */
+ native public static int fillStackTraceElements(Thread t,
+ StackTraceElement[] stackTraceElements);
}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
index 130d032..dbddcc6 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
@@ -25,17 +25,28 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
-public final class AsciiHprofWriter implements HprofWriter {
+/**
+ * AsciiHprofWriter produces hprof compatible text output for use with
+ * third party tools such as PerfAnal.
+ */
+public final class AsciiHprofWriter {
private final HprofData data;
private final PrintWriter out;
- public AsciiHprofWriter(HprofData data, OutputStream outputStream) {
+ /**
+ * Writes the provided data to the specified stream.
+ */
+ public static void write(HprofData data, OutputStream outputStream) throws IOException {
+ new AsciiHprofWriter(data, outputStream).write();
+ }
+
+ private AsciiHprofWriter(HprofData data, OutputStream outputStream) {
this.data = data;
this.out = new PrintWriter(outputStream);
}
- public void write() throws IOException {
+ private void write() throws IOException {
for (HprofData.ThreadEvent e : data.getThreadHistory()) {
out.println(e);
}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
index 26eb290..9720446 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
@@ -16,6 +16,8 @@
package dalvik.system.profiler;
+import java.io.DataInputStream;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -29,6 +31,34 @@ public final class BinaryHprof {
*/
public static final int ID_SIZE = 4;
+ /**
+ * Prefix of valid magic values from the start of a binary hprof file.
+ */
+ static String MAGIC = "JAVA PROFILE ";
+
+ /**
+ * Returns the file's magic value as a String if found, otherwise null.
+ */
+ public static final String readMagic(DataInputStream in) {
+ try {
+ byte[] bytes = new byte[512];
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = in.readByte();
+ if (b == '\0') {
+ String string = new String(bytes, 0, i, "UTF-8");
+ if (string.startsWith(MAGIC)) {
+ return string;
+ }
+ return null;
+ }
+ bytes[i] = b;
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
public static enum Tag {
STRING_IN_UTF8(0x01, -ID_SIZE),
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
index 65e5178..75a17f3 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
@@ -123,23 +123,14 @@ public final class BinaryHprofReader {
}
private void parseVersion() throws IOException {
- byte[] bytes = new byte[512];
- for (int i = 0; i < bytes.length; i++) {
- byte b = in.readByte();
- if (b == '\0') {
- String version = new String(bytes, 0, i, "UTF-8");
- if (TRACE) {
- System.out.println("\tversion=" + version);
- }
- if (!version.startsWith("JAVA PROFILE ")) {
- throw new MalformedHprofException("Unexpected version: " + version);
- }
- this.version = version;
- return;
- }
- bytes[i] = b;
+ String version = BinaryHprof.readMagic(in);
+ if (version == null) {
+ throw new MalformedHprofException("Could not find HPROF version");
+ }
+ if (TRACE) {
+ System.out.println("\tversion=" + version);
}
- throw new MalformedHprofException("Could not find HPROF version");
+ this.version = version;
}
private void parseIdSize() throws IOException {
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
index 808572d..5c29838 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
@@ -23,7 +23,12 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-public final class BinaryHprofWriter implements HprofWriter {
+/**
+ * BinaryHprofWriter produces hprof compatible binary output for use
+ * with third party tools. Such files can be converted to text with
+ * with {@link HprofBinaryToAscii} or read back in with {@link BinaryHprofReader}.
+ */
+public final class BinaryHprofWriter {
private int nextStringId = 1; // id 0 => null
private int nextClassId = 1;
@@ -36,12 +41,19 @@ public final class BinaryHprofWriter implements HprofWriter {
private final HprofData data;
private final DataOutputStream out;
- public BinaryHprofWriter(HprofData data, OutputStream outputStream) {
+ /**
+ * Writes the provided data to the specified stream.
+ */
+ public static void write(HprofData data, OutputStream outputStream) throws IOException {
+ new BinaryHprofWriter(data, outputStream).write();
+ }
+
+ private BinaryHprofWriter(HprofData data, OutputStream outputStream) {
this.data = data;
this.out = new DataOutputStream(outputStream);
}
- public void write() throws IOException {
+ private void write() throws IOException {
try {
writeHeader(data.getStartMillis());
@@ -65,7 +77,7 @@ public final class BinaryHprofWriter implements HprofWriter {
}
private void writeHeader(long dumpTimeInMilliseconds) throws IOException {
- out.writeBytes("JAVA PROFILE 1.0.2");
+ out.writeBytes(BinaryHprof.MAGIC + "1.0.2");
out.writeByte(0); // null terminated string
out.writeInt(BinaryHprof.ID_SIZE);
out.writeLong(dumpTimeInMilliseconds);
@@ -238,8 +250,4 @@ public final class BinaryHprofWriter implements HprofWriter {
return id;
}
-
- public void close() throws IOException {
- out.close();
- }
}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
new file mode 100644
index 0000000..c20cc32
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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 dalvik.system.profiler;
+
+import dalvik.system.VMStack;
+import java.util.Arrays;
+
+class DalvikThreadSampler implements ThreadSampler {
+
+ private int depth;
+
+ /**
+ * Reusable storage for sampling sized for the specified depth.
+ */
+ private StackTraceElement[][] mutableStackTraceElements;
+
+ @Override public void setDepth(int depth) {
+ this.depth = depth;
+ this.mutableStackTraceElements = new StackTraceElement[depth+1][];
+ for (int i = 1; i < mutableStackTraceElements.length; i++) {
+ this.mutableStackTraceElements[i] = new StackTraceElement[i];
+ }
+ }
+
+ @Override public StackTraceElement[] getStackTrace(Thread thread) {
+ int count = VMStack.fillStackTraceElements(thread, mutableStackTraceElements[depth]);
+ if (count == 0) {
+ return null;
+ }
+ if (count < depth) {
+ System.arraycopy(mutableStackTraceElements[depth], 0,
+ mutableStackTraceElements[count], 0,
+ count);
+ }
+ return mutableStackTraceElements[count];
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
index 2fed9d8..46c443d 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
@@ -17,6 +17,9 @@
package dalvik.system.profiler;
import java.io.BufferedInputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -24,18 +27,25 @@ import java.io.InputStream;
/**
* Run on device with:
- * adb shell dalvikvm 'dalvik.system.SamplingProfiler\$HprofBinaryToAscii'
+ * adb shell dalvikvm 'dalvik.system.profiler.HprofBinaryToAscii'
*
* Run on host with:
* java -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
*/
public final class HprofBinaryToAscii {
+ /**
+ * Main entry point for HprofBinaryToAscii command line tool
+ */
public static void main(String[] args) {
System.exit(convert(args) ? 0 : 1);
}
- public static boolean convert(String[] args) {
+ /**
+ * Reads single file from arguments and attempts to read it as
+ * either a binary hprof file or a version with a text header.
+ */
+ private static boolean convert(String[] args) {
if (args.length != 1) {
usage("binary hprof file argument expected");
@@ -47,29 +57,110 @@ public final class HprofBinaryToAscii {
return false;
}
+ if (startsWithMagic(file)) {
+ HprofData hprofData;
+ try {
+ hprofData = readHprof(file);
+ } catch (IOException e) {
+ System.out.println("Problem reading binary hprof data from "
+ + file + ": " + e.getMessage());
+ return false;
+ }
+ return write(hprofData);
+ }
+
HprofData hprofData;
- InputStream inputStream = null;
try {
- inputStream = new BufferedInputStream(new FileInputStream(file));
- BinaryHprofReader reader = new BinaryHprofReader(inputStream);
- reader.setStrict(false);
- reader.read();
- hprofData = reader.getHprofData();
+ hprofData = readSnapshot(file);
} catch (IOException e) {
- System.out.println("Problem reading binary hprof data from "
+ System.out.println("Problem reading snapshot containing binary hprof data from "
+ file + ": " + e.getMessage());
return false;
+ }
+ return write(hprofData);
+ }
+
+ /**
+ * Probe the start of file to see if it starts with a plausible
+ * binary hprof magic value. If so, it is returned. On any other
+ * case including unexpected errors, false is returned.
+ */
+ private static boolean startsWithMagic(File file) {
+ DataInputStream inputStream = null;
+ try {
+ inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
+ return BinaryHprof.readMagic(inputStream) != null;
+ } catch (IOException e) {
+ return false;
} finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException ignored) {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read and return an HprofData from a vanilla binary hprof file.
+ */
+ private static HprofData readHprof(File file) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ return read(inputStream);
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read a file looking for text header terminated by two newlines,
+ * then proceed to read binary hprof data.
+ */
+ private static HprofData readSnapshot(File file) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ int ch;
+ while ((ch = inputStream.read()) != -1) {
+ if (ch == '\n' && inputStream.read() == '\n') {
+ return read(inputStream);
}
}
+ throw new EOFException("Could not find expected header");
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read binary hprof data from the provided input stream and
+ * return the HprofData object.
+ */
+ private static HprofData read(InputStream inputStream) throws IOException {
+ BinaryHprofReader reader = new BinaryHprofReader(inputStream);
+ reader.setStrict(false);
+ reader.read();
+ return reader.getHprofData();
+ }
+
+ /**
+ * From IoUtils.closeQuietly but replicated for open source
+ * version.
+ */
+ private static void closeQuietly(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException ignored) {
+ }
}
+ }
+
+ /**
+ * Write text verion of hprof data to standard output. Returns
+ * false on error.
+ */
+ private static boolean write(HprofData hprofData) {
try {
- HprofWriter writer = new AsciiHprofWriter(hprofData, System.out);
- writer.write();
+ AsciiHprofWriter.write(hprofData, System.out);
} catch (IOException e) {
System.out.println("Problem writing ASCII hprof data: " + e.getMessage());
return false;
@@ -77,6 +168,9 @@ public final class HprofBinaryToAscii {
return true;
}
+ /**
+ * Prints usage error but does not exit.
+ */
private static void usage(String error) {
System.out.print("ERROR: ");
System.out.println(error);
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/HprofWriter.java
deleted file mode 100644
index b1d5a9b..0000000
--- a/dalvik/src/main/java/dalvik/system/profiler/HprofWriter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2010 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 dalvik.system.profiler;
-
-import java.io.IOException;
-
-public interface HprofWriter {
- public void write() throws IOException;
-}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
new file mode 100644
index 0000000..96e0b27
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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 dalvik.system.profiler;
+
+import java.util.Arrays;
+
+/**
+ * ThreadSampler implementation that only uses Thread.getStackTrace()
+ * and therefore is portable.
+ */
+class PortableThreadSampler implements ThreadSampler {
+
+ private int depth;
+
+ @Override public void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ @Override public StackTraceElement[] getStackTrace(Thread thread) {
+ StackTraceElement[] stackFrames = thread.getStackTrace();
+ if (stackFrames.length == 0) {
+ return null;
+ }
+ if (stackFrames.length > depth) {
+ stackFrames = Arrays.copyOfRange(stackFrames, 0, depth);
+ }
+ return stackFrames;
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
index 13421ec..0266103 100644
--- a/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
+++ b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
@@ -51,8 +51,7 @@ import java.util.TimerTask;
* // another period of measurement
* profiler.stop();
* profiler.shutdown();
- * HprofWriter writer = new AsciiHprofWriter(profiler.getHprofData(), System.out);
- * writer.write();
+ * AsciiHprofWriter.write(profiler.getHprofData(), System.out);
* }</pre>
*/
public final class SamplingProfiler {
@@ -78,7 +77,7 @@ public final class SamplingProfiler {
* everytime profiling stops because once a {@code TimerTask} is
* canceled it cannot be reused.
*/
- private TimerTask sampler;
+ private Sampler sampler;
/**
* The maximum number of {@code StackTraceElements} to retain in
@@ -115,13 +114,22 @@ public final class SamplingProfiler {
private final Map<Thread, Integer> threadIds = new HashMap<Thread, Integer>();
/**
- * Mutable StackTrace that is used for probing stackTraces Map
- * without allocating a StackTrace. If addStackTrace needs to
- * be thread safe, this would need to be reconsidered.
+ * Mutable {@code StackTrace} that is used for probing the {@link
+ * #stackTraces stackTraces} map without allocating a {@code
+ * StackTrace}. If {@link #addStackTrace addStackTrace} needs to
+ * be thread safe, have a single mutable instance would need to be
+ * reconsidered.
*/
private final HprofData.StackTrace mutableStackTrace = new HprofData.StackTrace();
/**
+ * The {@code ThreadSampler} is used to produce a {@code
+ * StackTraceElement} array for a given thread. The array is
+ * expected to be {@link #depth depth} or less in length.
+ */
+ private final ThreadSampler threadSampler;
+
+ /**
* Create a sampling profiler that collects stacks with the
* specified depth from the threads specified by the specified
* thread collector.
@@ -135,14 +143,36 @@ public final class SamplingProfiler {
* depth, simply passing in a value for Integer.MAX_VALUE is not
* advised because of the significant memory need to retain such
* stacks and runtime overhead to compare stacks.
+ *
+ * @param threadSet The thread set specifies which threads to
+ * sample. In a general purpose program, all threads typically
+ * should be sample with a ThreadSet such as provied by {@link
+ * #newThreadGroupTheadSet newThreadGroupTheadSet}. For a
+ * benchmark a fixed set such as provied by {@link
+ * #newArrayThreadSet newArrayThreadSet} can reduce the overhead
+ * of profiling.
*/
public SamplingProfiler(int depth, ThreadSet threadSet) {
this.depth = depth;
this.threadSet = threadSet;
+ this.threadSampler = findDefaultThreadSampler();
+ threadSampler.setDepth(depth);
hprofData.setFlags(BinaryHprof.ControlSettings.CPU_SAMPLING.bitmask);
hprofData.setDepth(depth);
}
+ private static ThreadSampler findDefaultThreadSampler() {
+ if ("Dalvik Core Library".equals(System.getProperty("java.specification.name"))) {
+ String className = "dalvik.system.profiler.DalvikThreadSampler";
+ try {
+ return (ThreadSampler) Class.forName(className).newInstance();
+ } catch (Exception e) {
+ System.out.println("Problem creating " + className + ": " + e);
+ }
+ }
+ return new PortableThreadSampler();
+ }
+
/**
* A ThreadSet specifies the set of threads to sample.
*/
@@ -263,7 +293,15 @@ public final class SamplingProfiler {
if (sampler == null) {
return;
}
- sampler.cancel();
+ synchronized(sampler) {
+ sampler.stop = true;
+ while (!sampler.stopped) {
+ try {
+ sampler.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
sampler = null;
}
@@ -305,9 +343,21 @@ public final class SamplingProfiler {
*/
private class Sampler extends TimerTask {
+ private boolean stop;
+ private boolean stopped;
+
private Thread timerThread;
public void run() {
+ synchronized(this) {
+ if (stop) {
+ cancel();
+ stopped = true;
+ notifyAll();
+ return;
+ }
+ }
+
if (timerThread == null) {
timerThread = Thread.currentThread();
}
@@ -329,15 +379,10 @@ public final class SamplingProfiler {
continue;
}
- // TODO replace with a VMStack.getThreadStackTrace
- // variant to avoid allocating unneeded elements
- StackTraceElement[] stackFrames = thread.getStackTrace();
- if (stackFrames.length == 0) {
+ StackTraceElement[] stackFrames = threadSampler.getStackTrace(thread);
+ if (stackFrames == null) {
continue;
}
- if (stackFrames.length > depth) {
- stackFrames = Arrays.copyOfRange(stackFrames, 0, depth);
- }
recordStackTrace(thread, stackFrames);
}
}
@@ -357,8 +402,10 @@ public final class SamplingProfiler {
int[] countCell = stackTraces.get(mutableStackTrace);
if (countCell == null) {
countCell = new int[1];
+ // cloned because the ThreadSampler may reuse the array
+ StackTraceElement[] stackFramesCopy = stackFrames.clone();
HprofData.StackTrace stackTrace
- = new HprofData.StackTrace(nextStackTraceId++, threadId, stackFrames);
+ = new HprofData.StackTrace(nextStackTraceId++, threadId, stackFramesCopy);
hprofData.addStackTrace(stackTrace, countCell);
}
countCell[0]++;
diff --git a/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
new file mode 100644
index 0000000..104b16d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 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 dalvik.system.profiler;
+
+import java.io.IOException;
+
+/**
+ * The {@code ThreadSampler} interfaces allows a profiler to choose
+ * between portable and VM specific implementations of thread
+ * sampling.
+ */
+public interface ThreadSampler {
+
+ /**
+ * Used to specify the maximum stack depth to collect.
+ */
+ public void setDepth(int depth);
+
+ /**
+ * Return a stack trace for the current thread limited by the
+ * maximum depth specified by {@link #setDepth setDepth}. May
+ * return null if no sample is availble for the thread, which may
+ * happen in cases such as thread termination. The resulting array
+ * should be copied before the next call to {@code getStackTrace}
+ * if the caller wants to use the results, since the {@code
+ * ThreadSampler} may reuse the array. Note that the elements
+ * themselves are immutable and do not need to be copied.
+ */
+ public StackTraceElement[] getStackTrace(Thread thread);
+}
diff --git a/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java b/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
index 6a43aad..d022cd0 100644
--- a/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
+++ b/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
@@ -23,7 +23,6 @@ import dalvik.system.profiler.HprofData.Sample;
import dalvik.system.profiler.HprofData.StackTrace;
import dalvik.system.profiler.HprofData.ThreadEvent;
import dalvik.system.profiler.HprofData;
-import dalvik.system.profiler.HprofWriter;
import dalvik.system.profiler.SamplingProfiler.ThreadSet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -261,9 +260,7 @@ public class SamplingProfilerTest extends TestCase {
*/
private void test_HprofData_ascii(HprofData hprofData) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- HprofWriter writer = new AsciiHprofWriter(hprofData, out);
- writer.write();
- out.close();
+ AsciiHprofWriter.write(hprofData, out);
assertFalse(out.toByteArray().length == 0);
}
@@ -274,8 +271,7 @@ public class SamplingProfilerTest extends TestCase {
private void test_HprofData_binary(HprofData hprofData, boolean strict) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- HprofWriter writer = new BinaryHprofWriter(hprofData, out);
- writer.write();
+ BinaryHprofWriter.write(hprofData, out);
out.close();
byte[] bytes = out.toByteArray();