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