diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-02-10 15:43:57 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-02-10 15:43:57 -0800 |
| commit | 5d709784bbf5001012d7f25172927d46f6c1abe1 (patch) | |
| tree | a49c3dafdeed5037e5ad85aba23e5666b1faf57d /tools | |
| parent | bcd637a94f10b49d18b87a74a015f9d3453ed77a (diff) | |
| download | android_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-x | tools/get-hprof | 19 | ||||
| -rw-r--r-- | tools/hprof-conv/Android.mk | 21 | ||||
| -rw-r--r-- | tools/hprof-conv/HprofConv.c | 719 |
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); +} + |
