HprofConv.c revision 7365493ad8d360c1dcf9cd8b6eee62747af01cae
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 41typedef 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 53typedef enum HprofTag { 54 /* tags we must handle specially */ 55 HPROF_TAG_HEAP_DUMP = 0x0c, 56 HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1c, 57} HprofTag; 58 59typedef 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 */ 98typedef struct { 99 unsigned char* storage; 100 size_t curLen; 101 size_t maxLen; 102} ExpandBuf; 103 104/* 105 * Create an ExpandBuf. 106 */ 107static 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 */ 124static 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 */ 138static 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 */ 146static inline size_t ebGetLength(ExpandBuf* pBuf) 147{ 148 return pBuf->curLen; 149} 150 151/* 152 * Empty the buffer. 153 */ 154static 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 */ 162static 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 */ 185static 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 */ 196static 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 */ 221static 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 */ 247static 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 */ 275static 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 */ 286static 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 */ 297static 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 */ 308static 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 */ 322static 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 */ 395static 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 */ 404static 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 */ 413static 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 */ 426static 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 565bail: 566 ebFree(pOutBuf); 567 return result; 568} 569 570/* 571 * Filter an hprof data file. 572 */ 573static int filterData(FILE* in, FILE* out) 574{ 575 ExpandBuf* pBuf; 576 int result = -1; 577 578 pBuf = ebAlloc(); 579 if (pBuf == NULL) 580 goto bail; 581 582 /* 583 * Start with the header. 584 */ 585 if (ebReadString(pBuf, in) != 0) 586 goto bail; 587 588 if (strcmp((const char*)ebGetBuffer(pBuf), "JAVA PROFILE 1.0.3") != 0) { 589 fprintf(stderr, "ERROR: expecting 1.0.3\n"); 590 goto bail; 591 } 592 593 /* downgrade to 1.0.2 */ 594 (ebGetBuffer(pBuf))[17] = '2'; 595 if (ebWriteData(pBuf, out) != 0) 596 goto bail; 597 598 /* 599 * Copy: 600 * (4b) identifier size, always 4 601 * (8b) file creation date 602 */ 603 if (ebReadData(pBuf, in, 12, FALSE) != 0) 604 goto bail; 605 if (ebWriteData(pBuf, out) != 0) 606 goto bail; 607 608 /* 609 * Read records until we hit EOF. Each record begins with: 610 * (1b) type 611 * (4b) timestamp 612 * (4b) length of data that follows 613 */ 614 while (1) { 615 assert(ebGetLength(pBuf) == 0); 616 617 /* read type char */ 618 if (ebReadData(pBuf, in, 1, TRUE) != 0) 619 goto bail; 620 if (feof(in)) 621 break; 622 623 /* read the rest of the header */ 624 if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0) 625 goto bail; 626 627 unsigned char* buf = ebGetBuffer(pBuf); 628 unsigned char type; 629 unsigned int timestamp, length; 630 631 type = buf[0]; 632 timestamp = get4BE(buf + 1); 633 length = get4BE(buf + 5); 634 buf = NULL; /* ptr invalid after next read op */ 635 636 /* read the record data */ 637 if (length != 0) { 638 if (ebReadData(pBuf, in, length, FALSE) != 0) 639 goto bail; 640 } 641 642 if (type == HPROF_TAG_HEAP_DUMP || 643 type == HPROF_TAG_HEAP_DUMP_SEGMENT) 644 { 645 DBUG("Processing heap dump 0x%02x (%d bytes)\n", 646 type, length); 647 if (processHeapDump(pBuf, out) != 0) 648 goto bail; 649 ebClear(pBuf); 650 } else { 651 /* keep */ 652 DBUG("Keeping 0x%02x (%d bytes)\n", type, length); 653 if (ebWriteData(pBuf, out) != 0) 654 goto bail; 655 } 656 } 657 658 result = 0; 659 660bail: 661 ebFree(pBuf); 662 return result; 663} 664 665/* 666 * Get args. 667 */ 668int main(int argc, char** argv) 669{ 670 FILE* in = stdin; 671 FILE* out = stdout; 672 int cc; 673 674 if (argc != 3) { 675 fprintf(stderr, "Usage: hprof-conf infile outfile\n\n"); 676 fprintf(stderr, 677 "Specify '-' for either or both to use stdin/stdout.\n\n"); 678 679 fprintf(stderr, 680 "Copyright (C) 2009 The Android Open Source Project\n\n" 681 "This software is built from source code licensed under the " 682 "Apache License,\n" 683 "Version 2.0 (the \"License\"). You may obtain a copy of the " 684 "License at\n\n" 685 " http://www.apache.org/licenses/LICENSE-2.0\n\n" 686 "See the associated NOTICE file for this software for further " 687 "details.\n"); 688 689 return 2; 690 } 691 692 if (strcmp(argv[1], "-") != 0) { 693 in = fopen(argv[1], "rb"); 694 if (in == NULL) { 695 fprintf(stderr, "ERROR: failed to open input '%s': %s\n", 696 argv[1], strerror(errno)); 697 return 1; 698 } 699 } 700 if (strcmp(argv[2], "-") != 0) { 701 out = fopen(argv[2], "wb"); 702 if (out == NULL) { 703 fprintf(stderr, "ERROR: failed to open output '%s': %s\n", 704 argv[2], strerror(errno)); 705 if (in != stdin) 706 fclose(in); 707 return 1; 708 } 709 } 710 711 cc = filterData(in, out); 712 713 if (in != stdin) 714 fclose(in); 715 if (out != stdout) 716 fclose(out); 717 return (cc != 0); 718} 719