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