diff options
author | Brian Carlstrom <bdc@google.com> | 2011-03-28 00:10:59 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2011-03-29 22:34:07 -0700 |
commit | e9af8901fc4ed7c05d085e2e492f5dcc857f0146 (patch) | |
tree | 1010aae7a7bb353eee6eca194d2e983a5791dc2d /dalvik | |
parent | cfa84a2aac159bb8a1763298882df7aa98f7fc6f (diff) | |
download | libcore-e9af8901fc4ed7c05d085e2e492f5dcc857f0146.zip libcore-e9af8901fc4ed7c05d085e2e492f5dcc857f0146.tar.gz libcore-e9af8901fc4ed7c05d085e2e492f5dcc857f0146.tar.bz2 |
SamplingProfilerIntegration and SamplingProfiler improvements (2/3)
Summary:
frameworks/base
- fix profiling to collect data beyond the first snapshot
- avoid many small files, accumulate data over process lifetime
libcore:
- add support for VM specific sampling, trying to cut down overhead
- added support for converting snapshot files to text format
- fixed race in profiler when stopping
dalvik
- added VMStack.setThreadStackTrace interface for filling a stack
trace into an existing StackTraceElement[]
Details:
frameworks/base
Changed snapshots from text to binary hprof format (bumping version to 3)
Changed from one file per snapshot to one file per process lifetime.
Restart profiling after snapshot.
core/java/com/android/internal/os/SamplingProfilerIntegration.java
Add quick test in maybeSnapshot to avoid doing work when the
SamplingProfilerIntegration is disabled. Make maybeSnapshot
private. Remove unneeded memory allocation in handleLowMemory.
core/java/android/app/ActivityThread.java
libcore
Added ThreadSampler interface. This allows VM specific thread
sampling optimizations. The portable version continues to use
Thread.getStackTrace().
dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
Add VMStack.setThreadStackTrace and use in new DalvikThreadSampler
to avoid allocating a full stack trace when only a limited depth
is desired.
dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
dalvik/src/main/java/dalvik/system/VMStack.java
Refactored BinaryHprof.readMagic out of BinaryHprofReader so it
can be used by HprofBinaryToAscii converter to probing file
types. Added magic number constant to be shared between readMagic
and BinaryHprofWriter.
dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
Removed unneeded HprofWriter interface. Changed to simpler static
interface to write HprofData to binary and text formats.
dalvik/src/main/java/dalvik/system/profiler/HprofWriter.java
dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
Added support for reading snapshot files created by
SamplingProfilerIntegration by stripping the text header to allow
easier conversion to the text format.
dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
Fixed race between Sampler and
SamplingProfiler.stop. SamplingProfiler.stop previously simply
called the Sampler's TimerTask.cancel method, but this does not
wait for a currently running Sampler to finish. The TimerTask
documentation says the only reliable way to do this is to have the
run() cancel itself, so that is what is now done, with new code to
ensure that SamplingProfiler.stop does not return until the
Sampler has been terminated.
dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
dalvik
Refactored VMStack_getThreadStackTrace to create helper getTraceBuf
used to implement new VMStack_setThreadStackTrace. The new version
interface fills an existing StackTraceElement[], avoid allocating
unnecessary StackTraceElements.
vm/native/dalvik_system_VMStack.c
Refactor dvmGetStackTraceRaw to create dvmSetStackTraceRaw which
fills in an existing, potentially smaller, StackTraceElement[].
vm/Exception.c
vm/Exception.h
Change stack depths to be unsigned to avoid signed/unsigned comparison warnings.
vm/Ddm.c
vm/Exception.c
vm/Exception.h
Change-Id: Id65df6e72b7c727a0c8af15dbf6943297c591541
Diffstat (limited to 'dalvik')
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 32d8bcf..16a04e6 100644 --- a/dalvik/src/main/java/dalvik/system/VMStack.java +++ b/dalvik/src/main/java/dalvik/system/VMStack.java @@ -79,4 +79,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(); |