blob: bd61e36f2333912aec3c656423ba204b97b68042 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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>
Jeff Sharkey3db80b52014-11-20 17:48:59 -080028#include <unistd.h>
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080029
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
42typedef 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
54typedef enum HprofTag {
55 /* tags we must handle specially */
56 HPROF_TAG_HEAP_DUMP = 0x0c,
57 HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1c,
58} HprofTag;
59
60typedef 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,
Carl Shapiro98740d62010-02-24 19:47:40 -080084 HPROF_UNREACHABLE = 0x90, /* deprecated */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085 HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
86} HprofHeapTag;
87
Jeff Sharkey3db80b52014-11-20 17:48:59 -080088typedef 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080095#define kIdentSize 4
96#define kRecHdrLen 9
97
Jeff Sharkey3db80b52014-11-20 17:48:59 -080098#define kFlagAppOnly 1
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080099
100/*
101 * ===========================================================================
102 * Expanding buffer
103 * ===========================================================================
104 */
105
106/* simple struct */
107typedef struct {
108 unsigned char* storage;
109 size_t curLen;
110 size_t maxLen;
111} ExpandBuf;
112
113/*
114 * Create an ExpandBuf.
115 */
116static 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 */
133static 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 */
147static 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 */
155static inline size_t ebGetLength(ExpandBuf* pBuf)
156{
157 return pBuf->curLen;
158}
159
160/*
161 * Empty the buffer.
162 */
163static 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 */
171static 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 */
194static 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 */
205static 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 */
230static 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 %d of %d 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 */
256static 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 %d of %d 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 */
284static 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 */
295static 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 */
306static 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 */
317static 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 */
331static 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 */
404static 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 */
413static 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 */
422static 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 */
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800435static int processHeapDump(ExpandBuf* pBuf, FILE* out, int flags)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800436{
437 ExpandBuf* pOutBuf = ebAlloc();
438 unsigned char* origBuf = ebGetBuffer(pBuf);
439 unsigned char* buf = origBuf;
440 int len = ebGetLength(pBuf);
441 int result = -1;
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800442 int heapType = HPROF_HEAP_DEFAULT;
443 int heapIgnore = FALSE;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800444
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);
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800494 if (heapIgnore) {
495 justCopy = FALSE;
496 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800497 break;
498 case HPROF_OBJECT_ARRAY_DUMP:
499 subLen = computeObjectArrayDumpLen(buf+1, len-1);
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800500 if (heapIgnore) {
501 justCopy = FALSE;
502 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800503 break;
504 case HPROF_PRIMITIVE_ARRAY_DUMP:
505 subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800506 if (heapIgnore) {
507 justCopy = FALSE;
508 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800509 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800510 /* these were added for Android in 1.0.3 */
511 case HPROF_HEAP_DUMP_INFO:
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800512 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800519 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 %d\n",
563 subType, 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
591bail:
592 ebFree(pOutBuf);
593 return result;
594}
595
596/*
597 * Filter an hprof data file.
598 */
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800599static int filterData(FILE* in, FILE* out, int flags)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800600{
Siva Velusamy4e120552011-11-03 13:34:29 -0700601 const char *magicString;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800602 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
Siva Velusamy4e120552011-11-03 13:34:29 -0700615 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800622 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
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800674 if (type == HPROF_TAG_HEAP_DUMP
675 || type == HPROF_TAG_HEAP_DUMP_SEGMENT) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800676 DBUG("Processing heap dump 0x%02x (%d bytes)\n",
677 type, length);
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800678 if (processHeapDump(pBuf, out, flags) != 0)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679 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
691bail:
692 ebFree(pBuf);
693 return result;
694}
695
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800696static 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800704int main(int argc, char** argv)
705{
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800706 FILE* in = NULL;
707 FILE* out = NULL;
708 int flags = 0;
709 int res = 1;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800710
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800711 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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800720 }
721 }
722
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800723 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800734
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800735 if (in == NULL || out == NULL) {
736 goto usage;
737 }
738
739 res = filterData(in, out, flags);
740 goto finish;
741
742usage:
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
761finish:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800762 if (in != stdin)
763 fclose(in);
764 if (out != stdout)
765 fclose(out);
Jeff Sharkey3db80b52014-11-20 17:48:59 -0800766 return res;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800767}