summaryrefslogtreecommitdiff
path: root/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java')
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java253
1 files changed, 253 insertions, 0 deletions
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
new file mode 100644
index 0000000..5c29838
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
@@ -0,0 +1,253 @@
+/*
+ * 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.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 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;
+ private int nextStackFrameId = 1;
+ private final Map<String, Integer> stringToId = new HashMap<String, Integer>();
+ private final Map<String, Integer> classNameToId = new HashMap<String, Integer>();
+ private final Map<StackTraceElement, Integer> stackFrameToId
+ = new HashMap<StackTraceElement, Integer>();
+
+ private final HprofData data;
+ private final DataOutputStream out;
+
+ /**
+ * 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);
+ }
+
+ private void write() throws IOException {
+ try {
+ writeHeader(data.getStartMillis());
+
+ writeControlSettings(data.getFlags(), data.getDepth());
+
+ for (HprofData.ThreadEvent event : data.getThreadHistory()) {
+ writeThreadEvent(event);
+ }
+
+ Set<HprofData.Sample> samples = data.getSamples();
+ int total = 0;
+ for (HprofData.Sample sample : samples) {
+ total += sample.count;
+ writeStackTrace(sample.stackTrace);
+ }
+ writeCpuSamples(total, samples);
+
+ } finally {
+ out.flush();
+ }
+ }
+
+ private void writeHeader(long dumpTimeInMilliseconds) throws IOException {
+ out.writeBytes(BinaryHprof.MAGIC + "1.0.2");
+ out.writeByte(0); // null terminated string
+ out.writeInt(BinaryHprof.ID_SIZE);
+ out.writeLong(dumpTimeInMilliseconds);
+ }
+
+ private void writeControlSettings(int flags, int depth) throws IOException {
+ if (depth > Short.MAX_VALUE) {
+ throw new IllegalArgumentException("depth too large for binary hprof: "
+ + depth + " > " + Short.MAX_VALUE);
+ }
+ writeRecordHeader(BinaryHprof.Tag.CONTROL_SETTINGS,
+ 0,
+ BinaryHprof.Tag.CONTROL_SETTINGS.maximumSize);
+ out.writeInt(flags);
+ out.writeShort((short) depth);
+ }
+
+ private void writeThreadEvent(HprofData.ThreadEvent e) throws IOException {
+ switch (e.type) {
+ case START:
+ writeStartThread(e);
+ return;
+ case END:
+ writeStopThread(e);
+ return;
+ }
+ throw new IllegalStateException(e.type.toString());
+ }
+
+ private void writeStartThread(HprofData.ThreadEvent e) throws IOException {
+ int threadNameId = writeString(e.threadName);
+ int groupNameId = writeString(e.groupName);
+ int parentGroupNameId = writeString(e.parentGroupName);
+ writeRecordHeader(BinaryHprof.Tag.START_THREAD,
+ 0,
+ BinaryHprof.Tag.START_THREAD.maximumSize);
+ out.writeInt(e.threadId);
+ writeId(e.objectId);
+ out.writeInt(0); // stack trace where thread was started unavailable
+ writeId(threadNameId);
+ writeId(groupNameId);
+ writeId(parentGroupNameId);
+ }
+
+ private void writeStopThread(HprofData.ThreadEvent e) throws IOException {
+ writeRecordHeader(BinaryHprof.Tag.END_THREAD,
+ 0,
+ BinaryHprof.Tag.END_THREAD.maximumSize);
+ out.writeInt(e.threadId);
+ }
+
+ private void writeRecordHeader(BinaryHprof.Tag hprofTag,
+ int timeDeltaInMicroseconds,
+ int recordLength) throws IOException {
+ String error = hprofTag.checkSize(recordLength);
+ if (error != null) {
+ throw new AssertionError(error);
+ }
+ out.writeByte(hprofTag.tag);
+ out.writeInt(timeDeltaInMicroseconds);
+ out.writeInt(recordLength);
+ }
+
+ private void writeId(int id) throws IOException {
+ out.writeInt(id);
+ }
+
+ /**
+ * Ensures that a string has been writen to the out and
+ * returns its ID. The ID of a null string is zero, and
+ * doesn't actually result in any output. In a string has
+ * already been written previously, the earlier ID will be
+ * returned and no output will be written.
+ */
+ private int writeString(String string) throws IOException {
+ if (string == null) {
+ return 0;
+ }
+ Integer identifier = stringToId.get(string);
+ if (identifier != null) {
+ return identifier;
+ }
+
+ int id = nextStringId++;
+ stringToId.put(string, id);
+
+ byte[] bytes = string.getBytes("UTF-8");
+ writeRecordHeader(BinaryHprof.Tag.STRING_IN_UTF8,
+ 0,
+ BinaryHprof.ID_SIZE + bytes.length);
+ out.writeInt(id);
+ out.write(bytes, 0, bytes.length);
+
+ return id;
+ }
+
+ private void writeCpuSamples(int totalSamples, Set<HprofData.Sample> samples)
+ throws IOException {
+ int samplesCount = samples.size();
+ if (samplesCount == 0) {
+ return;
+ }
+ writeRecordHeader(BinaryHprof.Tag.CPU_SAMPLES, 0, 4 + 4 + (samplesCount * (4 + 4)));
+ out.writeInt(totalSamples);
+ out.writeInt(samplesCount);
+ for (HprofData.Sample sample : samples) {
+ out.writeInt(sample.count);
+ out.writeInt(sample.stackTrace.stackTraceId);
+ }
+ }
+
+ private void writeStackTrace(HprofData.StackTrace stackTrace) throws IOException {
+ int frames = stackTrace.stackFrames.length;
+ int[] stackFrameIds = new int[frames];
+ for (int i = 0; i < frames; i++) {
+ stackFrameIds[i] = writeStackFrame(stackTrace.stackFrames[i]);
+ }
+ writeRecordHeader(BinaryHprof.Tag.STACK_TRACE,
+ 0,
+ 4 + 4 + 4 + (frames * BinaryHprof.ID_SIZE));
+ out.writeInt(stackTrace.stackTraceId);
+ out.writeInt(stackTrace.threadId);
+ out.writeInt(frames);
+ for (int stackFrameId : stackFrameIds) {
+ writeId(stackFrameId);
+ }
+ }
+
+ private int writeLoadClass(String className) throws IOException {
+ Integer identifier = classNameToId.get(className);
+ if (identifier != null) {
+ return identifier;
+ }
+ int id = nextClassId++;
+ classNameToId.put(className, id);
+
+ int classNameId = writeString(className);
+ writeRecordHeader(BinaryHprof.Tag.LOAD_CLASS,
+ 0,
+ BinaryHprof.Tag.LOAD_CLASS.maximumSize);
+ out.writeInt(id);
+ writeId(0); // class object ID
+ out.writeInt(0); // stack trace where class was loaded is unavailable
+ writeId(classNameId);
+
+ return id;
+ }
+
+ private int writeStackFrame(StackTraceElement stackFrame) throws IOException {
+ Integer identifier = stackFrameToId.get(stackFrame);
+ if (identifier != null) {
+ return identifier;
+ }
+
+ int id = nextStackFrameId++;
+ stackFrameToId.put(stackFrame, id);
+
+ int classId = writeLoadClass(stackFrame.getClassName());
+ int methodNameId = writeString(stackFrame.getMethodName());
+ int sourceId = writeString(stackFrame.getFileName());
+ writeRecordHeader(BinaryHprof.Tag.STACK_FRAME,
+ 0,
+ BinaryHprof.Tag.STACK_FRAME.maximumSize);
+ writeId(id);
+ writeId(methodNameId);
+ writeId(0); // method signature is unavailable from StackTraceElement
+ writeId(sourceId);
+ out.writeInt(classId);
+ out.writeInt(stackFrame.getLineNumber());
+
+ return id;
+ }
+}