summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:43:57 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:43:57 -0800
commit5d709784bbf5001012d7f25172927d46f6c1abe1 (patch)
treea49c3dafdeed5037e5ad85aba23e5666b1faf57d /tools
parentbcd637a94f10b49d18b87a74a015f9d3453ed77a (diff)
downloadandroid_dalvik-5d709784bbf5001012d7f25172927d46f6c1abe1.tar.gz
android_dalvik-5d709784bbf5001012d7f25172927d46f6c1abe1.tar.bz2
android_dalvik-5d709784bbf5001012d7f25172927d46f6c1abe1.zip
auto import from //branches/cupcake/...@130745
Diffstat (limited to 'tools')
-rwxr-xr-xtools/get-hprof19
-rw-r--r--tools/hprof-conv/Android.mk21
-rw-r--r--tools/hprof-conv/HprofConv.c719
3 files changed, 745 insertions, 14 deletions
diff --git a/tools/get-hprof b/tools/get-hprof
index f87a13c02..d56d7ada3 100755
--- a/tools/get-hprof
+++ b/tools/get-hprof
@@ -14,14 +14,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Grab a pair of hprof files using adb. If an argument is specified, grab
+# Grab an hprof file using adb. If an argument is specified, grab
# the so-named file. If no argument is specified, grab the last such file
# as found by using "ls".
FILE_BASE="$1"
if [ "x$FILE_BASE" = "x" ]; then
# Note: substr() is to get rid of the final carriage return.
- FILE_BASE=`adb shell ls '/data/misc/heap-dump*.hprof' | tail -1 | \
+ FILE_BASE=`adb shell ls -l '/data/misc/heap-dump*.hprof' | tail -1 | \
awk '{ printf("%s", substr($7, 1, length($7) - 1)); }'`
if [ "x$FILE_BASE" = "x" ]; then
echo "No file base defined."
@@ -29,22 +29,13 @@ if [ "x$FILE_BASE" = "x" ]; then
fi
fi
-FILE_BASE=/data/misc/`echo $FILE_BASE | sed -e 's,^/data/misc/,,;s,-head$,,'`
-
-HEAD_FILE=/tmp/hprof-head
-TAIL_FILE=/tmp/hprof-tail
+FILE_BASE=/data/misc/${FILE_BASE}
OUT_FILE=heap-dump.hprof
-adb pull "${FILE_BASE}-head" "$HEAD_FILE"
-if [ "$?" -ne 0 ]; then
- echo "Failed pulling ${FILE_BASE}-head."
- exit 1
-fi
-adb pull "$FILE_BASE" "$TAIL_FILE"
+adb pull "$FILE_BASE" "$OUT_FILE"
if [ $? -ne 0 ]; then
echo "Failed pulling $FILE_BASE."
exit 1
fi
-cat "$HEAD_FILE" "$TAIL_FILE" > "$OUT_FILE"
-rm "$HEAD_FILE" "$TAIL_FILE"
echo "hat $OUT_FILE"
+exit 0
diff --git a/tools/hprof-conv/Android.mk b/tools/hprof-conv/Android.mk
new file mode 100644
index 000000000..fbac5fada
--- /dev/null
+++ b/tools/hprof-conv/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2009 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := HprofConv.c
+LOCAL_MODULE := hprof-conv
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/hprof-conv/HprofConv.c b/tools/hprof-conv/HprofConv.c
new file mode 100644
index 000000000..10f3d2cf3
--- /dev/null
+++ b/tools/hprof-conv/HprofConv.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Strip Android-specific records out of hprof data, back-converting from
+ * 1.0.3 to 1.0.2. This removes some useful information, but allows
+ * Android hprof data to be handled by widely-available tools (like "jhat").
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+//#define VERBOSE_DEBUG
+#ifdef VERBOSE_DEBUG
+# define DBUG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DBUG(...)
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+typedef enum HprofBasicType {
+ HPROF_BASIC_OBJECT = 2,
+ HPROF_BASIC_BOOLEAN = 4,
+ HPROF_BASIC_CHAR = 5,
+ HPROF_BASIC_FLOAT = 6,
+ HPROF_BASIC_DOUBLE = 7,
+ HPROF_BASIC_BYTE = 8,
+ HPROF_BASIC_SHORT = 9,
+ HPROF_BASIC_INT = 10,
+ HPROF_BASIC_LONG = 11,
+} HprofBasicType;
+
+typedef enum HprofTag {
+ /* tags we must handle specially */
+ HPROF_TAG_HEAP_DUMP = 0x0c,
+ HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1c,
+} HprofTag;
+
+typedef enum HprofHeapTag {
+ /* 1.0.2 tags */
+ HPROF_ROOT_UNKNOWN = 0xff,
+ HPROF_ROOT_JNI_GLOBAL = 0x01,
+ HPROF_ROOT_JNI_LOCAL = 0x02,
+ HPROF_ROOT_JAVA_FRAME = 0x03,
+ HPROF_ROOT_NATIVE_STACK = 0x04,
+ HPROF_ROOT_STICKY_CLASS = 0x05,
+ HPROF_ROOT_THREAD_BLOCK = 0x06,
+ HPROF_ROOT_MONITOR_USED = 0x07,
+ HPROF_ROOT_THREAD_OBJECT = 0x08,
+ HPROF_CLASS_DUMP = 0x20,
+ HPROF_INSTANCE_DUMP = 0x21,
+ HPROF_OBJECT_ARRAY_DUMP = 0x22,
+ HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
+
+ /* Android 1.0.3 tags */
+ HPROF_HEAP_DUMP_INFO = 0xfe,
+ HPROF_ROOT_INTERNED_STRING = 0x89,
+ HPROF_ROOT_FINALIZING = 0x8a,
+ HPROF_ROOT_DEBUGGER = 0x8b,
+ HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+ HPROF_ROOT_VM_INTERNAL = 0x8d,
+ HPROF_ROOT_JNI_MONITOR = 0x8e,
+ HPROF_UNREACHABLE = 0x90,
+ HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+} HprofHeapTag;
+
+#define kIdentSize 4
+#define kRecHdrLen 9
+
+
+/*
+ * ===========================================================================
+ * Expanding buffer
+ * ===========================================================================
+ */
+
+/* simple struct */
+typedef struct {
+ unsigned char* storage;
+ size_t curLen;
+ size_t maxLen;
+} ExpandBuf;
+
+/*
+ * Create an ExpandBuf.
+ */
+static ExpandBuf* ebAlloc(void)
+{
+ static const int kInitialSize = 64;
+
+ ExpandBuf* newBuf = (ExpandBuf*) malloc(sizeof(ExpandBuf));
+ if (newBuf == NULL)
+ return NULL;
+ newBuf->storage = (unsigned char*) malloc(kInitialSize);
+ newBuf->curLen = 0;
+ newBuf->maxLen = kInitialSize;
+
+ return newBuf;
+}
+
+/*
+ * Release the storage associated with an ExpandBuf.
+ */
+static void ebFree(ExpandBuf* pBuf)
+{
+ if (pBuf != NULL) {
+ free(pBuf->storage);
+ free(pBuf);
+ }
+}
+
+/*
+ * Return a pointer to the data buffer.
+ *
+ * The pointer may change as data is added to the buffer, so this value
+ * should not be cached.
+ */
+static inline unsigned char* ebGetBuffer(ExpandBuf* pBuf)
+{
+ return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+static inline size_t ebGetLength(ExpandBuf* pBuf)
+{
+ return pBuf->curLen;
+}
+
+/*
+ * Empty the buffer.
+ */
+static void ebClear(ExpandBuf* pBuf)
+{
+ pBuf->curLen = 0;
+}
+
+/*
+ * Ensure that the buffer can hold at least "size" additional bytes.
+ */
+static int ebEnsureCapacity(ExpandBuf* pBuf, int size)
+{
+ assert(size > 0);
+
+ if (pBuf->curLen + size > pBuf->maxLen) {
+ int newSize = pBuf->curLen + size + 128; /* oversize slightly */
+ unsigned char* newStorage = realloc(pBuf->storage, newSize);
+ if (newStorage == NULL) {
+ fprintf(stderr, "ERROR: realloc failed on size=%d\n", newSize);
+ return -1;
+ }
+
+ pBuf->storage = newStorage;
+ pBuf->maxLen = newSize;
+ }
+
+ assert(pBuf->curLen + size <= pBuf->maxLen);
+ return 0;
+}
+
+/*
+ * Add data to the buffer after ensuring it can hold it.
+ */
+static int ebAddData(ExpandBuf* pBuf, const void* data, size_t count)
+{
+ ebEnsureCapacity(pBuf, count);
+ memcpy(pBuf->storage + pBuf->curLen, data, count);
+ pBuf->curLen += count;
+ return 0;
+}
+
+/*
+ * Read a NULL-terminated string from the input.
+ */
+static int ebReadString(ExpandBuf* pBuf, FILE* in)
+{
+ int ic;
+
+ do {
+ ebEnsureCapacity(pBuf, 1);
+
+ ic = getc(in);
+ if (feof(in) || ferror(in)) {
+ fprintf(stderr, "ERROR: failed reading input\n");
+ return -1;
+ }
+
+ pBuf->storage[pBuf->curLen++] = (unsigned char) ic;
+ } while (ic != 0);
+
+ return 0;
+}
+
+/*
+ * Read some data, adding it to the expanding buffer.
+ *
+ * This will ensure that the buffer has enough space to hold the new data
+ * (plus the previous contents).
+ */
+static int ebReadData(ExpandBuf* pBuf, FILE* in, size_t count, int eofExpected)
+{
+ size_t actual;
+
+ assert(count > 0);
+
+ ebEnsureCapacity(pBuf, count);
+ actual = fread(pBuf->storage + pBuf->curLen, 1, count, in);
+ if (actual != count) {
+ if (eofExpected && feof(in) && !ferror(in)) {
+ /* return without reporting an error */
+ } else {
+ fprintf(stderr, "ERROR: read %d of %d bytes\n", actual, count);
+ return -1;
+ }
+ }
+
+ pBuf->curLen += count;
+ assert(pBuf->curLen <= pBuf->maxLen);
+
+ return 0;
+}
+
+/*
+ * Write the data from the buffer. Resets the data count to zero.
+ */
+static int ebWriteData(ExpandBuf* pBuf, FILE* out)
+{
+ size_t actual;
+
+ assert(pBuf->curLen > 0);
+ assert(pBuf->curLen <= pBuf->maxLen);
+
+ actual = fwrite(pBuf->storage, 1, pBuf->curLen, out);
+ if (actual != pBuf->curLen) {
+ fprintf(stderr, "ERROR: write %d of %d bytes\n", actual, pBuf->curLen);
+ return -1;
+ }
+
+ pBuf->curLen = 0;
+
+ return 0;
+}
+
+
+/*
+ * ===========================================================================
+ * Hprof stuff
+ * ===========================================================================
+ */
+
+/*
+ * Get a 2-byte value, in big-endian order, from memory.
+ */
+static uint16_t get2BE(const unsigned char* buf)
+{
+ uint16_t val;
+
+ val = (buf[0] << 8) | buf[1];
+ return val;
+}
+
+/*
+ * Get a 4-byte value, in big-endian order, from memory.
+ */
+static uint32_t get4BE(const unsigned char* buf)
+{
+ uint32_t val;
+
+ val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return val;
+}
+
+/*
+ * Set a 4-byte value, in big-endian order.
+ */
+static void set4BE(unsigned char* buf, uint32_t val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+}
+
+/*
+ * Get the size, in bytes, of one of the "basic types".
+ */
+static int computeBasicLen(HprofBasicType basicType)
+{
+ static const int sizes[] = { -1, -1, 4, -1, 1, 2, 4, 8, 1, 2, 4, 8 };
+ static const size_t maxSize = sizeof(sizes) / sizeof(sizes[0]);
+
+ assert(basicType >= 0);
+ if (basicType >= maxSize)
+ return -1;
+ return sizes[basicType];
+}
+
+/*
+ * Compute the length of a HPROF_CLASS_DUMP block.
+ */
+static int computeClassDumpLen(const unsigned char* origBuf, int len)
+{
+ const unsigned char* buf = origBuf;
+ int blockLen = 0;
+ int i, count;
+
+ blockLen += kIdentSize * 7 + 8;
+ buf += blockLen;
+ len -= blockLen;
+
+ if (len < 0)
+ return -1;
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 1st count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ HprofBasicType basicType;
+ int basicLen;
+
+ basicType = buf[2];
+ basicLen = computeBasicLen(basicType);
+ if (basicLen < 0) {
+ DBUG("ERROR: invalid basicType %d\n", basicType);
+ return -1;
+ }
+
+ buf += 2 + 1 + basicLen;
+ len -= 2 + 1 + basicLen;
+ if (len < 0)
+ return -1;
+ }
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 2nd count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ HprofBasicType basicType;
+ int basicLen;
+
+ basicType = buf[kIdentSize];
+ basicLen = computeBasicLen(basicType);
+ if (basicLen < 0) {
+ fprintf(stderr, "ERROR: invalid basicType %d\n", basicType);
+ return -1;
+ }
+
+ buf += kIdentSize + 1 + basicLen;
+ len -= kIdentSize + 1 + basicLen;
+ if (len < 0)
+ return -1;
+ }
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 3rd count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ buf += kIdentSize + 1;
+ len -= kIdentSize + 1;
+ if (len < 0)
+ return -1;
+ }
+
+ DBUG("Total class dump len: %d\n", buf - origBuf);
+ return buf - origBuf;
+}
+
+/*
+ * Compute the length of a HPROF_INSTANCE_DUMP block.
+ */
+static int computeInstanceDumpLen(const unsigned char* origBuf, int len)
+{
+ int extraCount = get4BE(origBuf + kIdentSize * 2 + 4);
+ return kIdentSize * 2 + 8 + extraCount;
+}
+
+/*
+ * Compute the length of a HPROF_OBJECT_ARRAY_DUMP block.
+ */
+static int computeObjectArrayDumpLen(const unsigned char* origBuf, int len)
+{
+ int arrayCount = get4BE(origBuf + kIdentSize + 4);
+ return kIdentSize * 2 + 8 + arrayCount * kIdentSize;
+}
+
+/*
+ * Compute the length of a HPROF_PRIMITIVE_ARRAY_DUMP block.
+ */
+static int computePrimitiveArrayDumpLen(const unsigned char* origBuf, int len)
+{
+ int arrayCount = get4BE(origBuf + kIdentSize + 4);
+ HprofBasicType basicType = origBuf[kIdentSize + 8];
+ int basicLen = computeBasicLen(basicType);
+
+ return kIdentSize + 9 + arrayCount * basicLen;
+}
+
+/*
+ * Crunch through a heap dump record, writing the original or converted
+ * data to "out".
+ */
+static int processHeapDump(ExpandBuf* pBuf, FILE* out)
+{
+ ExpandBuf* pOutBuf = ebAlloc();
+ unsigned char* origBuf = ebGetBuffer(pBuf);
+ unsigned char* buf = origBuf;
+ int len = ebGetLength(pBuf);
+ int result = -1;
+
+ pBuf = NULL; /* we just use the raw pointer from here forward */
+
+ /* copy the original header to the output buffer */
+ if (ebAddData(pOutBuf, buf, kRecHdrLen) != 0)
+ goto bail;
+
+ buf += kRecHdrLen; /* skip past record header */
+ len -= kRecHdrLen;
+
+ while (len > 0) {
+ unsigned char subType = buf[0];
+ int justCopy = TRUE;
+ int subLen;
+
+ DBUG("--- 0x%02x ", subType);
+ switch (subType) {
+ /* 1.0.2 types */
+ case HPROF_ROOT_UNKNOWN:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_JNI_GLOBAL:
+ subLen = kIdentSize * 2;
+ break;
+ case HPROF_ROOT_JNI_LOCAL:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_ROOT_JAVA_FRAME:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_ROOT_NATIVE_STACK:
+ subLen = kIdentSize + 4;
+ break;
+ case HPROF_ROOT_STICKY_CLASS:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_THREAD_BLOCK:
+ subLen = kIdentSize + 4;
+ break;
+ case HPROF_ROOT_MONITOR_USED:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_THREAD_OBJECT:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_CLASS_DUMP:
+ subLen = computeClassDumpLen(buf+1, len-1);
+ break;
+ case HPROF_INSTANCE_DUMP:
+ subLen = computeInstanceDumpLen(buf+1, len-1);
+ break;
+ case HPROF_OBJECT_ARRAY_DUMP:
+ subLen = computeObjectArrayDumpLen(buf+1, len-1);
+ break;
+ case HPROF_PRIMITIVE_ARRAY_DUMP:
+ subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
+ break;
+
+ /* these were added for Android in 1.0.3 */
+ case HPROF_HEAP_DUMP_INFO:
+ justCopy = FALSE;
+ subLen = kIdentSize + 4;
+ // no 1.0.2 equivalent for this
+ break;
+ case HPROF_ROOT_INTERNED_STRING:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_FINALIZING:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_DEBUGGER:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_REFERENCE_CLEANUP:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_VM_INTERNAL:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_JNI_MONITOR:
+ /* keep the ident, drop the next 8 bytes */
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ justCopy = FALSE;
+ ebAddData(pOutBuf, buf, 1 + kIdentSize);
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_UNREACHABLE:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
+ buf[0] = HPROF_PRIMITIVE_ARRAY_DUMP;
+ buf[5] = buf[6] = buf[7] = buf[8] = 0; /* set array len to 0 */
+ subLen = kIdentSize + 9;
+ break;
+
+ /* shouldn't get here */
+ default:
+ fprintf(stderr, "ERROR: unexpected subtype 0x%02x at offset %d\n",
+ subType, buf - origBuf);
+ goto bail;
+ }
+
+ if (justCopy) {
+ /* copy source data */
+ DBUG("(%d)\n", 1 + subLen);
+ ebAddData(pOutBuf, buf, 1 + subLen);
+ } else {
+ /* other data has been written, or the sub-record omitted */
+ DBUG("(adv %d)\n", 1 + subLen);
+ }
+
+ /* advance to next entry */
+ buf += 1 + subLen;
+ len -= 1 + subLen;
+ }
+
+ /*
+ * Update the record length.
+ */
+ set4BE(ebGetBuffer(pOutBuf) + 5, ebGetLength(pOutBuf) - kRecHdrLen);
+
+ if (ebWriteData(pOutBuf, out) != 0)
+ goto bail;
+
+ result = 0;
+
+bail:
+ ebFree(pOutBuf);
+ return result;
+}
+
+/*
+ * Filter an hprof data file.
+ */
+static int filterData(FILE* in, FILE* out)
+{
+ ExpandBuf* pBuf;
+ int result = -1;
+
+ pBuf = ebAlloc();
+ if (pBuf == NULL)
+ goto bail;
+
+ /*
+ * Start with the header.
+ */
+ if (ebReadString(pBuf, in) != 0)
+ goto bail;
+
+ if (strcmp((const char*)ebGetBuffer(pBuf), "JAVA PROFILE 1.0.3") != 0) {
+ fprintf(stderr, "ERROR: expecting 1.0.3\n");
+ goto bail;
+ }
+
+ /* downgrade to 1.0.2 */
+ (ebGetBuffer(pBuf))[17] = '2';
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+
+ /*
+ * Copy:
+ * (4b) identifier size, always 4
+ * (8b) file creation date
+ */
+ if (ebReadData(pBuf, in, 12, FALSE) != 0)
+ goto bail;
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+
+ /*
+ * Read records until we hit EOF. Each record begins with:
+ * (1b) type
+ * (4b) timestamp
+ * (4b) length of data that follows
+ */
+ while (1) {
+ assert(ebGetLength(pBuf) == 0);
+
+ /* read type char */
+ if (ebReadData(pBuf, in, 1, TRUE) != 0)
+ goto bail;
+ if (feof(in))
+ break;
+
+ /* read the rest of the header */
+ if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0)
+ goto bail;
+
+ unsigned char* buf = ebGetBuffer(pBuf);
+ unsigned char type;
+ unsigned int timestamp, length;
+
+ type = buf[0];
+ timestamp = get4BE(buf + 1);
+ length = get4BE(buf + 5);
+ buf = NULL; /* ptr invalid after next read op */
+
+ /* read the record data */
+ if (length != 0) {
+ if (ebReadData(pBuf, in, length, FALSE) != 0)
+ goto bail;
+ }
+
+ if (type == HPROF_TAG_HEAP_DUMP ||
+ type == HPROF_TAG_HEAP_DUMP_SEGMENT)
+ {
+ DBUG("Processing heap dump 0x%02x (%d bytes)\n",
+ type, length);
+ if (processHeapDump(pBuf, out) != 0)
+ goto bail;
+ ebClear(pBuf);
+ } else {
+ /* keep */
+ DBUG("Keeping 0x%02x (%d bytes)\n", type, length);
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+ }
+ }
+
+ result = 0;
+
+bail:
+ ebFree(pBuf);
+ return result;
+}
+
+/*
+ * Get args.
+ */
+int main(int argc, char** argv)
+{
+ FILE* in = stdin;
+ FILE* out = stdout;
+ int cc;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: hprof-conf infile outfile\n\n");
+ fprintf(stderr,
+ "Specify '-' for either or both to use stdin/stdout.\n\n");
+
+ fprintf(stderr,
+ "Copyright (C) 2009 The Android Open Source Project\n\n"
+ "This software is built from source code licensed under the "
+ "Apache License,\n"
+ "Version 2.0 (the \"License\"). You may obtain a copy of the "
+ "License at\n\n"
+ " http://www.apache.org/licenses/LICENSE-2.0\n\n"
+ "See the associated NOTICE file for this software for further "
+ "details.\n");
+
+ return 2;
+ }
+
+ if (strcmp(argv[1], "-") != 0) {
+ in = fopen(argv[1], "rb");
+ if (in == NULL) {
+ fprintf(stderr, "ERROR: failed to open input '%s': %s\n",
+ argv[1], strerror(errno));
+ return 1;
+ }
+ }
+ if (strcmp(argv[2], "-") != 0) {
+ out = fopen(argv[2], "wb");
+ if (out == NULL) {
+ fprintf(stderr, "ERROR: failed to open output '%s': %s\n",
+ argv[2], strerror(errno));
+ if (in != stdin)
+ fclose(in);
+ return 1;
+ }
+ }
+
+ cc = filterData(in, out);
+
+ if (in != stdin)
+ fclose(in);
+ if (out != stdout)
+ fclose(out);
+ return (cc != 0);
+}
+