Home | History | Annotate | Download | only in hprof-conv
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Strip Android-specific records out of hprof data, back-converting from
     19  * 1.0.3 to 1.0.2.  This removes some useful information, but allows
     20  * Android hprof data to be handled by widely-available tools (like "jhat").
     21  */
     22 #include <stdio.h>
     23 #include <string.h>
     24 #include <stdlib.h>
     25 #include <stdint.h>
     26 #include <errno.h>
     27 #include <assert.h>
     28 #include <unistd.h>
     29 
     30 //#define VERBOSE_DEBUG
     31 #ifdef VERBOSE_DEBUG
     32 # define DBUG(...) fprintf(stderr, __VA_ARGS__)
     33 #else
     34 # define DBUG(...)
     35 #endif
     36 
     37 #ifndef FALSE
     38 # define FALSE 0
     39 # define TRUE (!FALSE)
     40 #endif
     41 
     42 typedef enum HprofBasicType {
     43     HPROF_BASIC_OBJECT = 2,
     44     HPROF_BASIC_BOOLEAN = 4,
     45     HPROF_BASIC_CHAR = 5,
     46     HPROF_BASIC_FLOAT = 6,
     47     HPROF_BASIC_DOUBLE = 7,
     48     HPROF_BASIC_BYTE = 8,
     49     HPROF_BASIC_SHORT = 9,
     50     HPROF_BASIC_INT = 10,
     51     HPROF_BASIC_LONG = 11,
     52 } HprofBasicType;
     53 
     54 typedef enum HprofTag {
     55     /* tags we must handle specially */
     56     HPROF_TAG_HEAP_DUMP                 = 0x0c,
     57     HPROF_TAG_HEAP_DUMP_SEGMENT         = 0x1c,
     58 } HprofTag;
     59 
     60 typedef enum HprofHeapTag {
     61     /* 1.0.2 tags */
     62     HPROF_ROOT_UNKNOWN                  = 0xff,
     63     HPROF_ROOT_JNI_GLOBAL               = 0x01,
     64     HPROF_ROOT_JNI_LOCAL                = 0x02,
     65     HPROF_ROOT_JAVA_FRAME               = 0x03,
     66     HPROF_ROOT_NATIVE_STACK             = 0x04,
     67     HPROF_ROOT_STICKY_CLASS             = 0x05,
     68     HPROF_ROOT_THREAD_BLOCK             = 0x06,
     69     HPROF_ROOT_MONITOR_USED             = 0x07,
     70     HPROF_ROOT_THREAD_OBJECT            = 0x08,
     71     HPROF_CLASS_DUMP                    = 0x20,
     72     HPROF_INSTANCE_DUMP                 = 0x21,
     73     HPROF_OBJECT_ARRAY_DUMP             = 0x22,
     74     HPROF_PRIMITIVE_ARRAY_DUMP          = 0x23,
     75 
     76     /* Android 1.0.3 tags */
     77     HPROF_HEAP_DUMP_INFO                = 0xfe,
     78     HPROF_ROOT_INTERNED_STRING          = 0x89,
     79     HPROF_ROOT_FINALIZING               = 0x8a,
     80     HPROF_ROOT_DEBUGGER                 = 0x8b,
     81     HPROF_ROOT_REFERENCE_CLEANUP        = 0x8c,
     82     HPROF_ROOT_VM_INTERNAL              = 0x8d,
     83     HPROF_ROOT_JNI_MONITOR              = 0x8e,
     84     HPROF_UNREACHABLE                   = 0x90,  /* deprecated */
     85     HPROF_PRIMITIVE_ARRAY_NODATA_DUMP   = 0xc3,
     86 } HprofHeapTag;
     87 
     88 typedef enum HprofHeapId {
     89     HPROF_HEAP_DEFAULT = 0,
     90     HPROF_HEAP_ZYGOTE = 'Z',
     91     HPROF_HEAP_APP = 'A',
     92     HPROF_HEAP_IMAGE = 'I',
     93 } HprofHeapId;
     94 
     95 #define kIdentSize  4
     96 #define kRecHdrLen  9
     97 
     98 #define kFlagAppOnly 1
     99 
    100 /*
    101  * ===========================================================================
    102  *      Expanding buffer
    103  * ===========================================================================
    104  */
    105 
    106 /* simple struct */
    107 typedef struct {
    108     unsigned char* storage;
    109     size_t curLen;
    110     size_t maxLen;
    111 } ExpandBuf;
    112 
    113 /*
    114  * Create an ExpandBuf.
    115  */
    116 static ExpandBuf* ebAlloc(void)
    117 {
    118     static const int kInitialSize = 64;
    119 
    120     ExpandBuf* newBuf = (ExpandBuf*) malloc(sizeof(ExpandBuf));
    121     if (newBuf == NULL)
    122         return NULL;
    123     newBuf->storage = (unsigned char*) malloc(kInitialSize);
    124     newBuf->curLen = 0;
    125     newBuf->maxLen = kInitialSize;
    126 
    127     return newBuf;
    128 }
    129 
    130 /*
    131  * Release the storage associated with an ExpandBuf.
    132  */
    133 static void ebFree(ExpandBuf* pBuf)
    134 {
    135     if (pBuf != NULL) {
    136         free(pBuf->storage);
    137         free(pBuf);
    138     }
    139 }
    140 
    141 /*
    142  * Return a pointer to the data buffer.
    143  *
    144  * The pointer may change as data is added to the buffer, so this value
    145  * should not be cached.
    146  */
    147 static inline unsigned char* ebGetBuffer(ExpandBuf* pBuf)
    148 {
    149     return pBuf->storage;
    150 }
    151 
    152 /*
    153  * Get the amount of data currently in the buffer.
    154  */
    155 static inline size_t ebGetLength(ExpandBuf* pBuf)
    156 {
    157     return pBuf->curLen;
    158 }
    159 
    160 /*
    161  * Empty the buffer.
    162  */
    163 static void ebClear(ExpandBuf* pBuf)
    164 {
    165     pBuf->curLen = 0;
    166 }
    167 
    168 /*
    169  * Ensure that the buffer can hold at least "size" additional bytes.
    170  */
    171 static int ebEnsureCapacity(ExpandBuf* pBuf, int size)
    172 {
    173     assert(size > 0);
    174 
    175     if (pBuf->curLen + size > pBuf->maxLen) {
    176         int newSize = pBuf->curLen + size + 128;    /* oversize slightly */
    177         unsigned char* newStorage = realloc(pBuf->storage, newSize);
    178         if (newStorage == NULL) {
    179             fprintf(stderr, "ERROR: realloc failed on size=%d\n", newSize);
    180             return -1;
    181         }
    182 
    183         pBuf->storage = newStorage;
    184         pBuf->maxLen = newSize;
    185     }
    186 
    187     assert(pBuf->curLen + size <= pBuf->maxLen);
    188     return 0;
    189 }
    190 
    191 /*
    192  * Add data to the buffer after ensuring it can hold it.
    193  */
    194 static int ebAddData(ExpandBuf* pBuf, const void* data, size_t count)
    195 {
    196     ebEnsureCapacity(pBuf, count);
    197     memcpy(pBuf->storage + pBuf->curLen, data, count);
    198     pBuf->curLen += count;
    199     return 0;
    200 }
    201 
    202 /*
    203  * Read a NULL-terminated string from the input.
    204  */
    205 static int ebReadString(ExpandBuf* pBuf, FILE* in)
    206 {
    207     int ic;
    208 
    209     do {
    210         ebEnsureCapacity(pBuf, 1);
    211 
    212         ic = getc(in);
    213         if (feof(in) || ferror(in)) {
    214             fprintf(stderr, "ERROR: failed reading input\n");
    215             return -1;
    216         }
    217 
    218         pBuf->storage[pBuf->curLen++] = (unsigned char) ic;
    219     } while (ic != 0);
    220 
    221     return 0;
    222 }
    223 
    224 /*
    225  * Read some data, adding it to the expanding buffer.
    226  *
    227  * This will ensure that the buffer has enough space to hold the new data
    228  * (plus the previous contents).
    229  */
    230 static int ebReadData(ExpandBuf* pBuf, FILE* in, size_t count, int eofExpected)
    231 {
    232     size_t actual;
    233 
    234     assert(count > 0);
    235 
    236     ebEnsureCapacity(pBuf, count);
    237     actual = fread(pBuf->storage + pBuf->curLen, 1, count, in);
    238     if (actual != count) {
    239         if (eofExpected && feof(in) && !ferror(in)) {
    240             /* return without reporting an error */
    241         } else {
    242             fprintf(stderr, "ERROR: read %d of %d bytes\n", actual, count);
    243             return -1;
    244         }
    245     }
    246 
    247     pBuf->curLen += count;
    248     assert(pBuf->curLen <= pBuf->maxLen);
    249 
    250     return 0;
    251 }
    252 
    253 /*
    254  * Write the data from the buffer.  Resets the data count to zero.
    255  */
    256 static int ebWriteData(ExpandBuf* pBuf, FILE* out)
    257 {
    258     size_t actual;
    259 
    260     assert(pBuf->curLen > 0);
    261     assert(pBuf->curLen <= pBuf->maxLen);
    262 
    263     actual = fwrite(pBuf->storage, 1, pBuf->curLen, out);
    264     if (actual != pBuf->curLen) {
    265         fprintf(stderr, "ERROR: write %d of %d bytes\n", actual, pBuf->curLen);
    266         return -1;
    267     }
    268 
    269     pBuf->curLen = 0;
    270 
    271     return 0;
    272 }
    273 
    274 
    275 /*
    276  * ===========================================================================
    277  *      Hprof stuff
    278  * ===========================================================================
    279  */
    280 
    281 /*
    282  * Get a 2-byte value, in big-endian order, from memory.
    283  */
    284 static uint16_t get2BE(const unsigned char* buf)
    285 {
    286     uint16_t val;
    287 
    288     val = (buf[0] << 8) | buf[1];
    289     return val;
    290 }
    291 
    292 /*
    293  * Get a 4-byte value, in big-endian order, from memory.
    294  */
    295 static uint32_t get4BE(const unsigned char* buf)
    296 {
    297     uint32_t val;
    298 
    299     val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
    300     return val;
    301 }
    302 
    303 /*
    304  * Set a 4-byte value, in big-endian order.
    305  */
    306 static void set4BE(unsigned char* buf, uint32_t val)
    307 {
    308     buf[0] = val >> 24;
    309     buf[1] = val >> 16;
    310     buf[2] = val >> 8;
    311     buf[3] = val;
    312 }
    313 
    314 /*
    315  * Get the size, in bytes, of one of the "basic types".
    316  */
    317 static int computeBasicLen(HprofBasicType basicType)
    318 {
    319     static const int sizes[] = { -1, -1, 4, -1, 1, 2, 4, 8, 1, 2, 4, 8  };
    320     static const size_t maxSize = sizeof(sizes) / sizeof(sizes[0]);
    321 
    322     assert(basicType >= 0);
    323     if (basicType >= maxSize)
    324         return -1;
    325     return sizes[basicType];
    326 }
    327 
    328 /*
    329  * Compute the length of a HPROF_CLASS_DUMP block.
    330  */
    331 static int computeClassDumpLen(const unsigned char* origBuf, int len)
    332 {
    333     const unsigned char* buf = origBuf;
    334     int blockLen = 0;
    335     int i, count;
    336 
    337     blockLen += kIdentSize * 7 + 8;
    338     buf += blockLen;
    339     len -= blockLen;
    340 
    341     if (len < 0)
    342         return -1;
    343 
    344     count = get2BE(buf);
    345     buf += 2;
    346     len -= 2;
    347     DBUG("CDL: 1st count is %d\n", count);
    348     for (i = 0; i < count; i++) {
    349         HprofBasicType basicType;
    350         int basicLen;
    351 
    352         basicType = buf[2];
    353         basicLen = computeBasicLen(basicType);
    354         if (basicLen < 0) {
    355             DBUG("ERROR: invalid basicType %d\n", basicType);
    356             return -1;
    357         }
    358 
    359         buf += 2 + 1 + basicLen;
    360         len -= 2 + 1 + basicLen;
    361         if (len < 0)
    362             return -1;
    363     }
    364 
    365     count = get2BE(buf);
    366     buf += 2;
    367     len -= 2;
    368     DBUG("CDL: 2nd count is %d\n", count);
    369     for (i = 0; i < count; i++) {
    370         HprofBasicType basicType;
    371         int basicLen;
    372 
    373         basicType = buf[kIdentSize];
    374         basicLen = computeBasicLen(basicType);
    375         if (basicLen < 0) {
    376             fprintf(stderr, "ERROR: invalid basicType %d\n", basicType);
    377             return -1;
    378         }
    379 
    380         buf += kIdentSize + 1 + basicLen;
    381         len -= kIdentSize + 1 + basicLen;
    382         if (len < 0)
    383             return -1;
    384     }
    385 
    386     count = get2BE(buf);
    387     buf += 2;
    388     len -= 2;
    389     DBUG("CDL: 3rd count is %d\n", count);
    390     for (i = 0; i < count; i++) {
    391         buf += kIdentSize + 1;
    392         len -= kIdentSize + 1;
    393         if (len < 0)
    394             return -1;
    395     }
    396 
    397     DBUG("Total class dump len: %d\n", buf - origBuf);
    398     return buf - origBuf;
    399 }
    400 
    401 /*
    402  * Compute the length of a HPROF_INSTANCE_DUMP block.
    403  */
    404 static int computeInstanceDumpLen(const unsigned char* origBuf, int len)
    405 {
    406     int extraCount = get4BE(origBuf + kIdentSize * 2 + 4);
    407     return kIdentSize * 2 + 8 + extraCount;
    408 }
    409 
    410 /*
    411  * Compute the length of a HPROF_OBJECT_ARRAY_DUMP block.
    412  */
    413 static int computeObjectArrayDumpLen(const unsigned char* origBuf, int len)
    414 {
    415     int arrayCount = get4BE(origBuf + kIdentSize + 4);
    416     return kIdentSize * 2 + 8 + arrayCount * kIdentSize;
    417 }
    418 
    419 /*
    420  * Compute the length of a HPROF_PRIMITIVE_ARRAY_DUMP block.
    421  */
    422 static int computePrimitiveArrayDumpLen(const unsigned char* origBuf, int len)
    423 {
    424     int arrayCount = get4BE(origBuf + kIdentSize + 4);
    425     HprofBasicType basicType = origBuf[kIdentSize + 8];
    426     int basicLen = computeBasicLen(basicType);
    427 
    428     return kIdentSize + 9 + arrayCount * basicLen;
    429 }
    430 
    431 /*
    432  * Crunch through a heap dump record, writing the original or converted
    433  * data to "out".
    434  */
    435 static int processHeapDump(ExpandBuf* pBuf, FILE* out, int flags)
    436 {
    437     ExpandBuf* pOutBuf = ebAlloc();
    438     unsigned char* origBuf = ebGetBuffer(pBuf);
    439     unsigned char* buf = origBuf;
    440     int len = ebGetLength(pBuf);
    441     int result = -1;
    442     int heapType = HPROF_HEAP_DEFAULT;
    443     int heapIgnore = FALSE;
    444 
    445     pBuf = NULL;        /* we just use the raw pointer from here forward */
    446 
    447     /* copy the original header to the output buffer */
    448     if (ebAddData(pOutBuf, buf, kRecHdrLen) != 0)
    449         goto bail;
    450 
    451     buf += kRecHdrLen;      /* skip past record header */
    452     len -= kRecHdrLen;
    453 
    454     while (len > 0) {
    455         unsigned char subType = buf[0];
    456         int justCopy = TRUE;
    457         int subLen;
    458 
    459         DBUG("--- 0x%02x  ", subType);
    460         switch (subType) {
    461         /* 1.0.2 types */
    462         case HPROF_ROOT_UNKNOWN:
    463             subLen = kIdentSize;
    464             break;
    465         case HPROF_ROOT_JNI_GLOBAL:
    466             subLen = kIdentSize * 2;
    467             break;
    468         case HPROF_ROOT_JNI_LOCAL:
    469             subLen = kIdentSize + 8;
    470             break;
    471         case HPROF_ROOT_JAVA_FRAME:
    472             subLen = kIdentSize + 8;
    473             break;
    474         case HPROF_ROOT_NATIVE_STACK:
    475             subLen = kIdentSize + 4;
    476             break;
    477         case HPROF_ROOT_STICKY_CLASS:
    478             subLen = kIdentSize;
    479             break;
    480         case HPROF_ROOT_THREAD_BLOCK:
    481             subLen = kIdentSize + 4;
    482             break;
    483         case HPROF_ROOT_MONITOR_USED:
    484             subLen = kIdentSize;
    485             break;
    486         case HPROF_ROOT_THREAD_OBJECT:
    487             subLen = kIdentSize + 8;
    488             break;
    489         case HPROF_CLASS_DUMP:
    490             subLen = computeClassDumpLen(buf+1, len-1);
    491             break;
    492         case HPROF_INSTANCE_DUMP:
    493             subLen = computeInstanceDumpLen(buf+1, len-1);
    494             if (heapIgnore) {
    495                 justCopy = FALSE;
    496             }
    497             break;
    498         case HPROF_OBJECT_ARRAY_DUMP:
    499             subLen = computeObjectArrayDumpLen(buf+1, len-1);
    500             if (heapIgnore) {
    501                 justCopy = FALSE;
    502             }
    503             break;
    504         case HPROF_PRIMITIVE_ARRAY_DUMP:
    505             subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
    506             if (heapIgnore) {
    507                 justCopy = FALSE;
    508             }
    509             break;
    510         /* these were added for Android in 1.0.3 */
    511         case HPROF_HEAP_DUMP_INFO:
    512             heapType = get4BE(buf+1);
    513             if ((flags & kFlagAppOnly) != 0
    514                     && (heapType == HPROF_HEAP_ZYGOTE || heapType == HPROF_HEAP_IMAGE)) {
    515                 heapIgnore = TRUE;
    516             } else {
    517                 heapIgnore = FALSE;
    518             }
    519             justCopy = FALSE;
    520             subLen = kIdentSize + 4;
    521             // no 1.0.2 equivalent for this
    522             break;
    523         case HPROF_ROOT_INTERNED_STRING:
    524             buf[0] = HPROF_ROOT_UNKNOWN;
    525             subLen = kIdentSize;
    526             break;
    527         case HPROF_ROOT_FINALIZING:
    528             buf[0] = HPROF_ROOT_UNKNOWN;
    529             subLen = kIdentSize;
    530             break;
    531         case HPROF_ROOT_DEBUGGER:
    532             buf[0] = HPROF_ROOT_UNKNOWN;
    533             subLen = kIdentSize;
    534             break;
    535         case HPROF_ROOT_REFERENCE_CLEANUP:
    536             buf[0] = HPROF_ROOT_UNKNOWN;
    537             subLen = kIdentSize;
    538             break;
    539         case HPROF_ROOT_VM_INTERNAL:
    540             buf[0] = HPROF_ROOT_UNKNOWN;
    541             subLen = kIdentSize;
    542             break;
    543         case HPROF_ROOT_JNI_MONITOR:
    544             /* keep the ident, drop the next 8 bytes */
    545             buf[0] = HPROF_ROOT_UNKNOWN;
    546             justCopy = FALSE;
    547             ebAddData(pOutBuf, buf, 1 + kIdentSize);
    548             subLen = kIdentSize + 8;
    549             break;
    550         case HPROF_UNREACHABLE:
    551             buf[0] = HPROF_ROOT_UNKNOWN;
    552             subLen = kIdentSize;
    553             break;
    554         case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
    555             buf[0] = HPROF_PRIMITIVE_ARRAY_DUMP;
    556             buf[5] = buf[6] = buf[7] = buf[8] = 0;  /* set array len to 0 */
    557             subLen = kIdentSize + 9;
    558             break;
    559 
    560         /* shouldn't get here */
    561         default:
    562             fprintf(stderr, "ERROR: unexpected subtype 0x%02x at offset %d\n",
    563                 subType, buf - origBuf);
    564             goto bail;
    565         }
    566 
    567         if (justCopy) {
    568             /* copy source data */
    569             DBUG("(%d)\n", 1 + subLen);
    570             ebAddData(pOutBuf, buf, 1 + subLen);
    571         } else {
    572             /* other data has been written, or the sub-record omitted */
    573             DBUG("(adv %d)\n", 1 + subLen);
    574         }
    575 
    576         /* advance to next entry */
    577         buf += 1 + subLen;
    578         len -= 1 + subLen;
    579     }
    580 
    581     /*
    582      * Update the record length.
    583      */
    584     set4BE(ebGetBuffer(pOutBuf) + 5, ebGetLength(pOutBuf) - kRecHdrLen);
    585 
    586     if (ebWriteData(pOutBuf, out) != 0)
    587         goto bail;
    588 
    589     result = 0;
    590 
    591 bail:
    592     ebFree(pOutBuf);
    593     return result;
    594 }
    595 
    596 /*
    597  * Filter an hprof data file.
    598  */
    599 static int filterData(FILE* in, FILE* out, int flags)
    600 {
    601     const char *magicString;
    602     ExpandBuf* pBuf;
    603     int result = -1;
    604 
    605     pBuf = ebAlloc();
    606     if (pBuf == NULL)
    607         goto bail;
    608 
    609     /*
    610      * Start with the header.
    611      */
    612     if (ebReadString(pBuf, in) != 0)
    613         goto bail;
    614 
    615     magicString = (const char*)ebGetBuffer(pBuf);
    616     if (strcmp(magicString, "JAVA PROFILE 1.0.3") != 0) {
    617         if (strcmp(magicString, "JAVA PROFILE 1.0.2") == 0) {
    618             fprintf(stderr, "ERROR: HPROF file already in 1.0.2 format.\n");
    619         } else {
    620             fprintf(stderr, "ERROR: expecting HPROF file format 1.0.3\n");
    621         }
    622         goto bail;
    623     }
    624 
    625     /* downgrade to 1.0.2 */
    626     (ebGetBuffer(pBuf))[17] = '2';
    627     if (ebWriteData(pBuf, out) != 0)
    628         goto bail;
    629 
    630     /*
    631      * Copy:
    632      * (4b) identifier size, always 4
    633      * (8b) file creation date
    634      */
    635     if (ebReadData(pBuf, in, 12, FALSE) != 0)
    636         goto bail;
    637     if (ebWriteData(pBuf, out) != 0)
    638         goto bail;
    639 
    640     /*
    641      * Read records until we hit EOF.  Each record begins with:
    642      * (1b) type
    643      * (4b) timestamp
    644      * (4b) length of data that follows
    645      */
    646     while (1) {
    647         assert(ebGetLength(pBuf) == 0);
    648 
    649         /* read type char */
    650         if (ebReadData(pBuf, in, 1, TRUE) != 0)
    651             goto bail;
    652         if (feof(in))
    653             break;
    654 
    655         /* read the rest of the header */
    656         if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0)
    657             goto bail;
    658 
    659         unsigned char* buf = ebGetBuffer(pBuf);
    660         unsigned char type;
    661         unsigned int timestamp, length;
    662 
    663         type = buf[0];
    664         timestamp = get4BE(buf + 1);
    665         length = get4BE(buf + 5);
    666         buf = NULL;     /* ptr invalid after next read op */
    667 
    668         /* read the record data */
    669         if (length != 0) {
    670             if (ebReadData(pBuf, in, length, FALSE) != 0)
    671                 goto bail;
    672         }
    673 
    674         if (type == HPROF_TAG_HEAP_DUMP
    675                 || type == HPROF_TAG_HEAP_DUMP_SEGMENT) {
    676             DBUG("Processing heap dump 0x%02x (%d bytes)\n",
    677                 type, length);
    678             if (processHeapDump(pBuf, out, flags) != 0)
    679                 goto bail;
    680             ebClear(pBuf);
    681         } else {
    682             /* keep */
    683             DBUG("Keeping 0x%02x (%d bytes)\n", type, length);
    684             if (ebWriteData(pBuf, out) != 0)
    685                 goto bail;
    686         }
    687     }
    688 
    689     result = 0;
    690 
    691 bail:
    692     ebFree(pBuf);
    693     return result;
    694 }
    695 
    696 static FILE* fopen_or_default(const char* path, const char* mode, FILE* def) {
    697     if (!strcmp(path, "-")) {
    698         return def;
    699     } else {
    700         return fopen(path, mode);
    701     }
    702 }
    703 
    704 int main(int argc, char** argv)
    705 {
    706     FILE* in = NULL;
    707     FILE* out = NULL;
    708     int flags = 0;
    709     int res = 1;
    710 
    711     int opt;
    712     while ((opt = getopt(argc, argv, "z")) != -1) {
    713         switch (opt) {
    714             case 'z':
    715                 flags |= kFlagAppOnly;
    716                 break;
    717             case '?':
    718             default:
    719                 goto usage;
    720         }
    721     }
    722 
    723     int i;
    724     for (i = optind; i < argc; i++) {
    725         char* arg = argv[i];
    726         if (!in) {
    727             in = fopen_or_default(arg, "rb", stdin);
    728         } else if (!out) {
    729             out = fopen_or_default(arg, "wb", stdout);
    730         } else {
    731             goto usage;
    732         }
    733     }
    734 
    735     if (in == NULL || out == NULL) {
    736         goto usage;
    737     }
    738 
    739     res = filterData(in, out, flags);
    740     goto finish;
    741 
    742 usage:
    743     fprintf(stderr, "Usage: hprof-conf [-z] infile outfile\n");
    744     fprintf(stderr, "\n");
    745     fprintf(stderr, "  -z: exclude non-app heaps, such as Zygote\n");
    746     fprintf(stderr, "\n");
    747     fprintf(stderr, "Specify '-' for either or both files to use stdin/stdout.\n");
    748     fprintf(stderr, "\n");
    749 
    750     fprintf(stderr,
    751         "Copyright (C) 2009 The Android Open Source Project\n\n"
    752         "This software is built from source code licensed under the "
    753         "Apache License,\n"
    754         "Version 2.0 (the \"License\"). You may obtain a copy of the "
    755         "License at\n\n"
    756         "     http://www.apache.org/licenses/LICENSE-2.0\n\n"
    757         "See the associated NOTICE file for this software for further "
    758         "details.\n");
    759     res = 2;
    760 
    761 finish:
    762     if (in != stdin)
    763         fclose(in);
    764     if (out != stdout)
    765         fclose(out);
    766     return res;
    767 }
    768