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