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