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 && in != NULL)
768 fclose(in);
769 if (out != stdout && out != NULL)
770 fclose(out);
771 return res;
772 }
773