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  */
ebAlloc(void)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  */
ebFree(ExpandBuf * pBuf)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  */
ebGetBuffer(ExpandBuf * pBuf)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  */
ebGetLength(ExpandBuf * pBuf)155 static inline size_t ebGetLength(ExpandBuf* pBuf)
156 {
157     return pBuf->curLen;
158 }
159 
160 /*
161  * Empty the buffer.
162  */
ebClear(ExpandBuf * pBuf)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  */
ebEnsureCapacity(ExpandBuf * pBuf,int size)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  */
ebAddData(ExpandBuf * pBuf,const void * data,size_t count)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  */
ebReadString(ExpandBuf * pBuf,FILE * in)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  */
ebReadData(ExpandBuf * pBuf,FILE * in,size_t count,int eofExpected)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 %zu of %zu 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  */
ebWriteData(ExpandBuf * pBuf,FILE * out)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 %zu of %zu 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  */
get2BE(const unsigned char * buf)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  */
get4BE(const unsigned char * buf)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  */
set4BE(unsigned char * buf,uint32_t val)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  */
computeBasicLen(HprofBasicType basicType)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  */
computeClassDumpLen(const unsigned char * origBuf,int len)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  */
computeInstanceDumpLen(const unsigned char * origBuf,int len)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  */
computeObjectArrayDumpLen(const unsigned char * origBuf,int len)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  */
computePrimitiveArrayDumpLen(const unsigned char * origBuf,int len)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  */
processHeapDump(ExpandBuf * pBuf,FILE * out,int flags)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 %zu\n",
563                 subType, (size_t) (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  */
filterData(FILE * in,FILE * out,int flags)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 
fopen_or_default(const char * path,const char * mode,FILE * def)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 
main(int argc,char ** argv)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