TraceDump.c revision 9f640af99bebc6e96f6e1e9903557e2c8f567483
1/* //device/tools/dmtracedump/TraceDump.c 2** 3** Copyright 2006, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18/* 19 * Process dmtrace output. 20 * 21 * This is the wrong way to go about it -- C is a clumsy language for 22 * shuffling data around. It'll do for a first pass. 23 */ 24#define NOT_VM 25#include "Profile.h" // from VM header 26 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31#include <inttypes.h> 32#include <time.h> 33#include <errno.h> 34#include <assert.h> 35 36/* Version number in the key file. 37 * Version 1 uses one byte for the thread id. 38 * Version 2 uses two bytes for the thread ids. 39 * Version 3 encodes the record size and adds an optional extra timestamp field. 40 */ 41int versionNumber; 42 43/* arbitrarily limit indentation */ 44#define MAX_STACK_DEPTH 10000 45 46/* thread list in key file is not reliable, so just max out */ 47#define MAX_THREADS 32768 48 49/* Size of temporary buffers for escaping html strings */ 50#define HTML_BUFSIZE 10240 51 52/* Size of methodId->method cache */ 53#define METHOD_CACHE_SIZE 2048 54#define METHOD_CACHE_SIZE_MASK (METHOD_CACHE_SIZE - 1) 55 56/* Some filter constants */ 57#define FILTER_TAG '*' 58#define FILTER_FLAG_THREAD '+' 59#define FILTER_TYPE_CLASS 0 60#define FILTER_TYPE_METHOD 1 61 62#define DEFAULT_ACTIVE_THREADS 8 63 64char *htmlHeader = 65"<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n" 66"<script langugage=\"javascript\">\n" 67"function toggle(item) {\n" 68" obj=document.getElementById(item);\n" 69" visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n" 70" key=document.getElementById(\"x\" + item);\n" 71" if (visible) {\n" 72" obj.style.display=\"none\";\n" 73" key.innerHTML=\"+\";\n" 74" } else {\n" 75" obj.style.display=\"block\";\n" 76" key.innerHTML=\"-\";\n" 77" }\n" 78"}\n" 79"function onMouseOver(obj) {\n" 80" obj.style.background=\"lightblue\";\n" 81"}\n" 82"function onMouseOut(obj) {\n" 83" obj.style.background=\"white\";\n" 84"}\n" 85"</script>\n" 86"<style type=\"text/css\">\n" 87"div { font-family: courier; font-size: 13 }\n" 88"div.parent { margin-left: 15; display: none }\n" 89"div.leaf { margin-left: 10 }\n" 90"div.header { margin-left: 10 }\n" 91"div.link { margin-left: 10; cursor: move }\n" 92"span.parent { padding-right: 10; }\n" 93"span.leaf { padding-right: 10; }\n" 94"a img { border: 0;}\n" 95"table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n" 96"a { text-decoration: none; }\n" 97"a:hover { text-decoration: underline; }\n" 98"table.sortable th, table.sortable td { text-align: left;}" 99"table.sortable tr.odd td { background-color: #ddd; }\n" 100"table.sortable tr.even td { background-color: #fff; }\n" 101"</style>\n" 102"</head><body>\n\n"; 103 104char *htmlFooter = "\n</body>\n</html>\n"; 105char *profileSeparator = 106 "======================================================================"; 107 108const char* tableHeader = 109 "<table class='sortable' id='%s'><tr>\n" 110 "<th>Method</th>\n" 111 "<th>Run 1 (us)</th>\n" 112 "<th>Run 2 (us)</th>\n" 113 "<th>Diff (us)</th>\n" 114 "<th>Diff (%%)</th>\n" 115 "<th>1: # calls</th>\n" 116 "<th>2: # calls</th>\n" 117 "</tr>\n"; 118 119const char* tableHeaderMissing = 120 "<table class='sortable' id='%s'>\n" 121 "<th>Method</th>\n" 122 "<th>Exclusive</th>\n" 123 "<th>Inclusive</th>\n" 124 "<th># calls</th>\n"; 125 126#define GRAPH_LABEL_VISITED 0x0001 127#define GRAPH_NODE_VISITED 0x0002 128 129/* 130 * Values from the header of the data file. 131 */ 132typedef struct DataHeader { 133 unsigned int magic; 134 short version; 135 short offsetToData; 136 long long startWhen; 137 short recordSize; 138} DataHeader; 139 140/* 141 * Entry from the thread list. 142 */ 143typedef struct ThreadEntry { 144 int threadId; 145 const char* threadName; 146 uint64_t elapsedTime; 147} ThreadEntry; 148 149struct MethodEntry; 150typedef struct TimedMethod { 151 struct TimedMethod *next; 152 uint64_t elapsedInclusive; 153 int numCalls; 154 struct MethodEntry *method; 155} TimedMethod; 156 157typedef struct ClassEntry { 158 const char *className; 159 uint64_t elapsedExclusive; 160 int numMethods; 161 struct MethodEntry **methods; /* list of methods in this class */ 162 int numCalls[2]; /* 0=normal, 1=recursive */ 163} ClassEntry; 164 165typedef struct UniqueMethodEntry { 166 uint64_t elapsedExclusive; 167 int numMethods; 168 struct MethodEntry **methods; /* list of methods with same name */ 169 int numCalls[2]; /* 0=normal, 1=recursive */ 170} UniqueMethodEntry; 171 172/* 173 * Entry from the method list. 174 */ 175typedef struct MethodEntry { 176 unsigned int methodId; 177 const char* className; 178 const char* methodName; 179 const char* signature; 180 const char* fileName; 181 int lineNum; 182 uint64_t elapsedExclusive; 183 uint64_t elapsedInclusive; 184 uint64_t topExclusive; /* non-recursive exclusive time */ 185 uint64_t recursiveInclusive; 186 struct TimedMethod *parents[2]; /* 0=normal, 1=recursive */ 187 struct TimedMethod *children[2]; /* 0=normal, 1=recursive */ 188 int numCalls[2]; /* 0=normal, 1=recursive */ 189 int index; /* used after sorting to number methods */ 190 int recursiveEntries; /* number of entries on the stack */ 191 int graphState; /* used when graphing to see if this method has been visited before */ 192} MethodEntry; 193 194/* 195 * The parsed contents of the key file. 196 */ 197typedef struct DataKeys { 198 char* fileData; /* contents of the entire file */ 199 long fileLen; 200 int numThreads; 201 ThreadEntry* threads; 202 int numMethods; 203 MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */ 204 int* methodCache; /* methodId->methodIndex mapping */ 205 // TODO change to map methodId->method itself 206} DataKeys; 207 208#define TOPLEVEL_INDEX 0 209#define UNKNOWN_INDEX 1 210 211typedef struct StackEntry { 212 MethodEntry *method; 213 uint64_t entryTime; 214} StackEntry; 215 216typedef struct CallStack { 217 int top; 218 StackEntry calls[MAX_STACK_DEPTH]; 219 uint64_t lastEventTime; 220 uint64_t threadStartTime; 221 uint64_t* remTimes; 222 // Note: remTimes keeps a sum of 'un-allocated' time for each thread, in case 223 // we need to allocate it to one (or many) filter later. This would happen when 224 // we see a method exit that maches a filter, but whose entry we hadn't seen. 225 // TODO: consider moving remTimes into FilterTimes and change logic appropriately 226} CallStack; 227 228typedef struct DiffEntry { 229 MethodEntry* method1; 230 MethodEntry* method2; 231 int64_t differenceExclusive; 232 int64_t differenceInclusive; 233 double differenceExclusivePercentage; 234 double differenceInclusivePercentage; 235} DiffEntry; 236 237// Global options 238typedef struct Options { 239 const char* traceFileName; 240 const char* diffFileName; 241 const char* graphFileName; 242 const char* filterFileName; 243 int keepDotFile; 244 int dump; 245 int outputHtml; 246 const char* sortableUrl; 247 int threshold; 248} Options; 249 250typedef struct TraceData { 251 int numClasses; 252 ClassEntry *classes; 253 CallStack *stacks[MAX_THREADS]; 254 int depth[MAX_THREADS]; 255 int numUniqueMethods; 256 UniqueMethodEntry *uniqueMethods; 257} TraceData; 258 259typedef struct FilterKey { 260 int type[2]; /* 0=class, 1=method; 2 needed for start and end keys */ 261 uint32_t flags; /* 1st bit = include cross-thread time */ 262 char* keys[2]; /* 2 needed for start and end keys */ 263} FilterKey; 264 265typedef struct FilterTimes { 266 uint64_t totalWaitTime; 267 uint64_t* threadWaitTimes; 268 uint64_t* threadExecutionTimesWhileWaiting; 269 uint64_t* threadExecutionTimes; 270} FilterTimes; 271 272typedef struct Filter { 273 char* filterName; 274 FilterKey* filterKeys; 275 int numKeys; 276 int activeCount; 277 int* activeThreads; 278 int* activationKeys; 279 FilterTimes times; 280} Filter; 281 282int numFilters = 0; // global 283 284static Options gOptions; 285 286/* Escapes characters in the source string that are html special entities. 287 * The escaped string is written to "dest" which must be large enough to 288 * hold the result. A pointer to "dest" is returned. The characters and 289 * their corresponding escape sequences are: 290 * '<' < 291 * '>' > 292 * '&' & 293 */ 294char *htmlEscape(const char *src, char *dest, int len) 295{ 296 char *destStart = dest; 297 298 if (src == NULL) 299 return NULL; 300 301 int nbytes = 0; 302 while (*src) { 303 if (*src == '<') { 304 nbytes += 4; 305 if (nbytes >= len) 306 break; 307 *dest++ = '&'; 308 *dest++ = 'l'; 309 *dest++ = 't'; 310 *dest++ = ';'; 311 } else if (*src == '>') { 312 nbytes += 4; 313 if (nbytes >= len) 314 break; 315 *dest++ = '&'; 316 *dest++ = 'g'; 317 *dest++ = 't'; 318 *dest++ = ';'; 319 } else if (*src == '&') { 320 nbytes += 5; 321 if (nbytes >= len) 322 break; 323 *dest++ = '&'; 324 *dest++ = 'a'; 325 *dest++ = 'm'; 326 *dest++ = 'p'; 327 *dest++ = ';'; 328 } else { 329 nbytes += 1; 330 if (nbytes >= len) 331 break; 332 *dest++ = *src; 333 } 334 src += 1; 335 } 336 if (nbytes >= len) { 337 fprintf(stderr, "htmlEscape(): buffer overflow\n"); 338 exit(1); 339 } 340 *dest = 0; 341 342 return destStart; 343} 344 345/* Initializes a MethodEntry 346 */ 347void initMethodEntry(MethodEntry *method, unsigned int methodId, 348 const char *className, const char *methodName, 349 const char *signature, const char* fileName, 350 const char* lineNumStr) 351{ 352 method->methodId = methodId; 353 method->className = className; 354 method->methodName = methodName; 355 method->signature = signature; 356 method->fileName = fileName; 357 method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1; 358 method->elapsedExclusive = 0; 359 method->elapsedInclusive = 0; 360 method->topExclusive = 0; 361 method->recursiveInclusive = 0; 362 method->parents[0] = NULL; 363 method->parents[1] = NULL; 364 method->children[0] = NULL; 365 method->children[1] = NULL; 366 method->numCalls[0] = 0; 367 method->numCalls[1] = 0; 368 method->index = 0; 369 method->recursiveEntries = 0; 370} 371 372/* 373 * This comparison function is called from qsort() to sort 374 * methods into decreasing order of exclusive elapsed time. 375 */ 376int compareElapsedExclusive(const void *a, const void *b) { 377 uint64_t elapsed1, elapsed2; 378 int result; 379 380 const MethodEntry *methodA = *(const MethodEntry**)a; 381 const MethodEntry *methodB = *(const MethodEntry**)b; 382 elapsed1 = methodA->elapsedExclusive; 383 elapsed2 = methodB->elapsedExclusive; 384 if (elapsed1 < elapsed2) 385 return 1; 386 if (elapsed1 > elapsed2) 387 return -1; 388 389 /* If the elapsed times of two methods are equal, then sort them 390 * into alphabetical order. 391 */ 392 result = strcmp(methodA->className, methodB->className); 393 if (result == 0) { 394 if (methodA->methodName == NULL || methodB->methodName == NULL) { 395 unsigned int idA = methodA->methodId; 396 unsigned int idB = methodB->methodId; 397 if (idA < idB) 398 return -1; 399 if (idA > idB) 400 return 1; 401 return 0; 402 } 403 result = strcmp(methodA->methodName, methodB->methodName); 404 if (result == 0) 405 result = strcmp(methodA->signature, methodB->signature); 406 } 407 return result; 408} 409 410/* 411 * This comparison function is called from qsort() to sort 412 * methods into decreasing order of inclusive elapsed time. 413 */ 414int compareElapsedInclusive(const void *a, const void *b) { 415 const MethodEntry *methodA, *methodB; 416 uint64_t elapsed1, elapsed2; 417 int result; 418 419 methodA = *(MethodEntry const **)a; 420 methodB = *(MethodEntry const **)b; 421 elapsed1 = methodA->elapsedInclusive; 422 elapsed2 = methodB->elapsedInclusive; 423 if (elapsed1 < elapsed2) 424 return 1; 425 if (elapsed1 > elapsed2) 426 return -1; 427 428 /* If the elapsed times of two methods are equal, then sort them 429 * into alphabetical order. 430 */ 431 result = strcmp(methodA->className, methodB->className); 432 if (result == 0) { 433 if (methodA->methodName == NULL || methodB->methodName == NULL) { 434 unsigned int idA = methodA->methodId; 435 unsigned int idB = methodB->methodId; 436 if (idA < idB) 437 return -1; 438 if (idA > idB) 439 return 1; 440 return 0; 441 } 442 result = strcmp(methodA->methodName, methodB->methodName); 443 if (result == 0) 444 result = strcmp(methodA->signature, methodB->signature); 445 } 446 return result; 447} 448 449/* 450 * This comparison function is called from qsort() to sort 451 * threads into decreasing order of elapsed time. 452 */ 453int compareElapsed(const void *a, const void *b) { 454 const ThreadEntry *threadA, *threadB; 455 uint64_t elapsed1, elapsed2; 456 int result = 0; 457 458 threadA = (ThreadEntry const *)a; 459 threadB = (ThreadEntry const *)b; 460 elapsed1 = threadA->elapsedTime; 461 elapsed2 = threadB->elapsedTime; 462 if (elapsed1 < elapsed2) 463 return 1; 464 if (elapsed1 > elapsed2) 465 return -1; 466 467 /* If the elapsed times of two threads are equal, then sort them 468 * by thread id. 469 */ 470 int idA = threadA->threadId; 471 int idB = threadB->threadId; 472 if (idA < idB) 473 result = -1; 474 if (idA > idB) 475 result = 1; 476 477 return result; 478} 479 480/* 481 * This comparison function is called from qsort() to sort 482 * TimedMethods into decreasing order of inclusive elapsed time. 483 */ 484int compareTimedMethod(const void *a, const void *b) { 485 const TimedMethod *timedA, *timedB; 486 uint64_t elapsed1, elapsed2; 487 int result; 488 489 timedA = (TimedMethod const *)a; 490 timedB = (TimedMethod const *)b; 491 elapsed1 = timedA->elapsedInclusive; 492 elapsed2 = timedB->elapsedInclusive; 493 if (elapsed1 < elapsed2) 494 return 1; 495 if (elapsed1 > elapsed2) 496 return -1; 497 498 /* If the elapsed times of two methods are equal, then sort them 499 * into alphabetical order. 500 */ 501 MethodEntry *methodA = timedA->method; 502 MethodEntry *methodB = timedB->method; 503 result = strcmp(methodA->className, methodB->className); 504 if (result == 0) { 505 if (methodA->methodName == NULL || methodB->methodName == NULL) { 506 unsigned int idA = methodA->methodId; 507 unsigned int idB = methodB->methodId; 508 if (idA < idB) 509 return -1; 510 if (idA > idB) 511 return 1; 512 return 0; 513 } 514 result = strcmp(methodA->methodName, methodB->methodName); 515 if (result == 0) 516 result = strcmp(methodA->signature, methodB->signature); 517 } 518 return result; 519} 520 521/* 522 * This comparison function is called from qsort() to sort 523 * MethodEntry pointers into alphabetical order of class names. 524 */ 525int compareClassNames(const void *a, const void *b) { 526 int result; 527 528 const MethodEntry *methodA = *(const MethodEntry**)a; 529 const MethodEntry *methodB = *(const MethodEntry**)b; 530 result = strcmp(methodA->className, methodB->className); 531 if (result == 0) { 532 unsigned int idA = methodA->methodId; 533 unsigned int idB = methodB->methodId; 534 if (idA < idB) 535 return -1; 536 if (idA > idB) 537 return 1; 538 return 0; 539 } 540 return result; 541} 542 543/* 544 * This comparison function is called from qsort() to sort 545 * classes into decreasing order of exclusive elapsed time. 546 */ 547int compareClassExclusive(const void *a, const void *b) { 548 uint64_t elapsed1, elapsed2; 549 int result; 550 551 const ClassEntry *classA = *(const ClassEntry**)a; 552 const ClassEntry *classB = *(const ClassEntry**)b; 553 elapsed1 = classA->elapsedExclusive; 554 elapsed2 = classB->elapsedExclusive; 555 if (elapsed1 < elapsed2) 556 return 1; 557 if (elapsed1 > elapsed2) 558 return -1; 559 560 /* If the elapsed times of two classs are equal, then sort them 561 * into alphabetical order. 562 */ 563 result = strcmp(classA->className, classB->className); 564 if (result == 0) { 565 /* Break ties with the first method id. This is probably not 566 * needed. 567 */ 568 unsigned int idA = classA->methods[0]->methodId; 569 unsigned int idB = classB->methods[0]->methodId; 570 if (idA < idB) 571 return -1; 572 if (idA > idB) 573 return 1; 574 return 0; 575 } 576 return result; 577} 578 579/* 580 * This comparison function is called from qsort() to sort 581 * MethodEntry pointers into alphabetical order by method name, 582 * then by class name. 583 */ 584int compareMethodNames(const void *a, const void *b) { 585 int result; 586 587 const MethodEntry *methodA = *(const MethodEntry**)a; 588 const MethodEntry *methodB = *(const MethodEntry**)b; 589 if (methodA->methodName == NULL || methodB->methodName == NULL) { 590 return compareClassNames(a, b); 591 } 592 result = strcmp(methodA->methodName, methodB->methodName); 593 if (result == 0) { 594 result = strcmp(methodA->className, methodB->className); 595 if (result == 0) { 596 unsigned int idA = methodA->methodId; 597 unsigned int idB = methodB->methodId; 598 if (idA < idB) 599 return -1; 600 if (idA > idB) 601 return 1; 602 return 0; 603 } 604 } 605 return result; 606} 607 608/* 609 * This comparison function is called from qsort() to sort 610 * unique methods into decreasing order of exclusive elapsed time. 611 */ 612int compareUniqueExclusive(const void *a, const void *b) { 613 uint64_t elapsed1, elapsed2; 614 int result; 615 616 const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a; 617 const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b; 618 elapsed1 = uniqueA->elapsedExclusive; 619 elapsed2 = uniqueB->elapsedExclusive; 620 if (elapsed1 < elapsed2) 621 return 1; 622 if (elapsed1 > elapsed2) 623 return -1; 624 625 /* If the elapsed times of two methods are equal, then sort them 626 * into alphabetical order. 627 */ 628 result = strcmp(uniqueA->methods[0]->className, 629 uniqueB->methods[0]->className); 630 if (result == 0) { 631 unsigned int idA = uniqueA->methods[0]->methodId; 632 unsigned int idB = uniqueB->methods[0]->methodId; 633 if (idA < idB) 634 return -1; 635 if (idA > idB) 636 return 1; 637 return 0; 638 } 639 return result; 640} 641 642/* 643 * Free a DataKeys struct. 644 */ 645void freeDataKeys(DataKeys* pKeys) 646{ 647 if (pKeys == NULL) 648 return; 649 650 free(pKeys->fileData); 651 free(pKeys->threads); 652 free(pKeys->methods); 653 free(pKeys->methodCache); 654 free(pKeys); 655} 656 657/* 658 * Find the offset to the next occurrence of the specified character. 659 * 660 * "data" should point somewhere within the current line. "len" is the 661 * number of bytes left in the buffer. 662 * 663 * Returns -1 if we hit the end of the buffer. 664 */ 665int findNextChar(const char* data, int len, char lookFor) 666{ 667 const char* start = data; 668 669 while (len > 0) { 670 if (*data == lookFor) 671 return data - start; 672 673 data++; 674 len--; 675 } 676 677 return -1; 678} 679 680int countLinesToChar(const char* data, int len, const char toFind) 681{ 682 int count = 0; 683 int next; 684 685 while (*data != toFind) { 686 next = findNextChar(data, len, '\n'); 687 if (next < 0) 688 return count; 689 count++; 690 data += next+1; 691 len -= next+1; 692 } 693 694 return count; 695} 696 697/* 698 * Count the number of lines until the next token. 699 * 700 * Returns 0 if none found before EOF. 701 */ 702int countLinesToToken(const char* data, int len) 703{ 704 return countLinesToChar(data, len, TOKEN_CHAR); 705} 706 707/* 708 * Make sure we're at the start of the right section. 709 * 710 * Returns the length of the token line, or -1 if something is wrong. 711 */ 712int checkToken(const char* data, int len, const char* cmpStr) 713{ 714 int cmpLen = strlen(cmpStr); 715 int next; 716 717 if (*data != TOKEN_CHAR) { 718 fprintf(stderr, 719 "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data); 720 return -1; 721 } 722 723 next = findNextChar(data, len, '\n'); 724 if (next < cmpLen+1) 725 return -1; 726 727 if (strncmp(data+1, cmpStr, cmpLen) != 0) { 728 fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1); 729 return -1; 730 } 731 732 return next+1; 733} 734 735/* 736 * Parse the "*version" section. 737 */ 738long parseVersion(DataKeys* pKeys, long offset, int verbose) 739{ 740 char* data; 741 char* dataEnd; 742 int i, count, next; 743 744 if (offset < 0) 745 return -1; 746 747 data = pKeys->fileData + offset; 748 dataEnd = pKeys->fileData + pKeys->fileLen; 749 next = checkToken(data, dataEnd - data, "version"); 750 if (next <= 0) 751 return -1; 752 753 data += next; 754 755 /* 756 * Count the number of items in the "version" section. 757 */ 758 count = countLinesToToken(data, dataEnd - data); 759 if (count <= 0) { 760 fprintf(stderr, 761 "ERROR: failed while reading version (found %d)\n", count); 762 return -1; 763 } 764 765 /* find the end of the line */ 766 next = findNextChar(data, dataEnd - data, '\n'); 767 if (next < 0) 768 return -1; 769 770 data[next] = '\0'; 771 versionNumber = strtoul(data, NULL, 0); 772 if (verbose) 773 printf("VERSION: %d\n", versionNumber); 774 775 data += next+1; 776 777 /* skip over the rest of the stuff, which is "name=value" lines */ 778 for (i = 1; i < count; i++) { 779 next = findNextChar(data, dataEnd - data, '\n'); 780 if (next < 0) 781 return -1; 782 //data[next] = '\0'; 783 //printf("IGNORING: '%s'\n", data); 784 data += next+1; 785 } 786 787 return data - pKeys->fileData; 788} 789 790/* 791 * Parse the "*threads" section. 792 */ 793long parseThreads(DataKeys* pKeys, long offset) 794{ 795 char* data; 796 char* dataEnd; 797 int i, next, tab, count; 798 799 if (offset < 0) 800 return -1; 801 802 data = pKeys->fileData + offset; 803 dataEnd = pKeys->fileData + pKeys->fileLen; 804 next = checkToken(data, dataEnd - data, "threads"); 805 806 data += next; 807 808 /* 809 * Count the number of thread entries (one per line). 810 */ 811 count = countLinesToToken(data, dataEnd - data); 812 if (count <= 0) { 813 fprintf(stderr, 814 "ERROR: failed while reading threads (found %d)\n", count); 815 return -1; 816 } 817 818 //printf("+++ found %d threads\n", count); 819 pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count); 820 if (pKeys->threads == NULL) 821 return -1; 822 823 /* 824 * Extract all entries. 825 */ 826 for (i = 0; i < count; i++) { 827 next = findNextChar(data, dataEnd - data, '\n'); 828 assert(next > 0); 829 data[next] = '\0'; 830 831 tab = findNextChar(data, next, '\t'); 832 data[tab] = '\0'; 833 834 pKeys->threads[i].threadId = atoi(data); 835 pKeys->threads[i].threadName = data + tab +1; 836 837 data += next+1; 838 } 839 840 pKeys->numThreads = count; 841 return data - pKeys->fileData; 842} 843 844/* 845 * Parse the "*methods" section. 846 */ 847long parseMethods(DataKeys* pKeys, long offset) 848{ 849 char* data; 850 char* dataEnd; 851 int i, next, count; 852 853 if (offset < 0) 854 return -1; 855 856 data = pKeys->fileData + offset; 857 dataEnd = pKeys->fileData + pKeys->fileLen; 858 next = checkToken(data, dataEnd - data, "methods"); 859 if (next < 0) 860 return -1; 861 862 data += next; 863 864 /* 865 * Count the number of method entries (one per line). 866 */ 867 count = countLinesToToken(data, dataEnd - data); 868 if (count <= 0) { 869 fprintf(stderr, 870 "ERROR: failed while reading methods (found %d)\n", count); 871 return -1; 872 } 873 874 /* Reserve an extra method at location 0 for the "toplevel" method, 875 * and another extra method for all other "unknown" methods. 876 */ 877 count += 2; 878 pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count); 879 if (pKeys->methods == NULL) 880 return -1; 881 initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], 0, "(toplevel)", 882 NULL, NULL, NULL, NULL); 883 initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], 0, "(unknown)", 884 NULL, NULL, NULL, NULL); 885 886 /* 887 * Extract all entries, starting with index 2. 888 */ 889 for (i = UNKNOWN_INDEX + 1; i < count; i++) { 890 int tab1, tab2, tab3, tab4, tab5; 891 unsigned int id; 892 char* endptr; 893 894 next = findNextChar(data, dataEnd - data, '\n'); 895 assert(next > 0); 896 data[next] = '\0'; 897 898 tab1 = findNextChar(data, next, '\t'); 899 tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t'); 900 tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t'); 901 tab4 = findNextChar(data+(tab1+tab2+tab3+3), 902 next-(tab1+tab2+tab3+3), '\t'); 903 tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4), 904 next-(tab1+tab2+tab3+tab4+4), '\t'); 905 if (tab1 < 0) { 906 fprintf(stderr, "ERROR: missing field on method line: '%s'\n", 907 data); 908 return -1; 909 } 910 assert(data[tab1] == '\t'); 911 data[tab1] = '\0'; 912 913 id = strtoul(data, &endptr, 0); 914 if (*endptr != '\0') { 915 fprintf(stderr, "ERROR: bad method ID '%s'\n", data); 916 return -1; 917 } 918 919 // Allow files that specify just a function name, instead of requiring 920 // "class \t method \t signature" 921 if (tab2 > 0 && tab3 > 0) { 922 tab2 += tab1+1; 923 tab3 += tab2+1; 924 assert(data[tab2] == '\t'); 925 assert(data[tab3] == '\t'); 926 data[tab2] = data[tab3] = '\0'; 927 928 // This is starting to get awkward. Allow filename and line #. 929 if (tab4 > 0 && tab5 > 0) { 930 tab4 += tab3+1; 931 tab5 += tab4+1; 932 933 assert(data[tab4] == '\t'); 934 assert(data[tab5] == '\t'); 935 data[tab4] = data[tab5] = '\0'; 936 937 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, 938 data + tab2 +1, data + tab3 +1, data + tab4 +1, 939 data + tab5 +1); 940 } else { 941 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, 942 data + tab2 +1, data + tab3 +1, NULL, NULL); 943 } 944 } else { 945 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, 946 NULL, NULL, NULL, NULL); 947 } 948 949 data += next+1; 950 } 951 952 pKeys->numMethods = count; 953 return data - pKeys->fileData; 954} 955 956/* 957 * Parse the "*end" section. 958 */ 959long parseEnd(DataKeys* pKeys, long offset) 960{ 961 char* data; 962 char* dataEnd; 963 int next; 964 965 if (offset < 0) 966 return -1; 967 968 data = pKeys->fileData + offset; 969 dataEnd = pKeys->fileData + pKeys->fileLen; 970 next = checkToken(data, dataEnd - data, "end"); 971 if (next < 0) 972 return -1; 973 974 data += next; 975 976 return data - pKeys->fileData; 977} 978 979/* 980 * Sort the thread list entries. 981 */ 982static int compareThreads(const void* thread1, const void* thread2) 983{ 984 return ((const ThreadEntry*) thread1)->threadId - 985 ((const ThreadEntry*) thread2)->threadId; 986} 987 988void sortThreadList(DataKeys* pKeys) 989{ 990 qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]), 991 compareThreads); 992} 993 994/* 995 * Sort the method list entries. 996 */ 997static int compareMethods(const void* meth1, const void* meth2) 998{ 999 unsigned int id1, id2; 1000 1001 id1 = ((const MethodEntry*) meth1)->methodId; 1002 id2 = ((const MethodEntry*) meth2)->methodId; 1003 if (id1 < id2) 1004 return -1; 1005 if (id1 > id2) 1006 return 1; 1007 return 0; 1008} 1009 1010void sortMethodList(DataKeys* pKeys) 1011{ 1012 qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry), 1013 compareMethods); 1014} 1015 1016/* 1017 * Parse the key section, and return a copy of the parsed contents. 1018 */ 1019DataKeys* parseKeys(FILE *fp, int verbose) 1020{ 1021 DataKeys* pKeys = NULL; 1022 long offset; 1023 int i; 1024 1025 pKeys = (DataKeys*) calloc(1, sizeof(DataKeys)); 1026 if (pKeys == NULL) 1027 goto fail; 1028 1029 /* 1030 * We load the entire file into memory. We do this, rather than memory- 1031 * mapping it, because we want to change some whitespace to NULs. 1032 */ 1033 if (fseek(fp, 0L, SEEK_END) != 0) { 1034 perror("fseek"); 1035 goto fail; 1036 } 1037 pKeys->fileLen = ftell(fp); 1038 if (pKeys->fileLen == 0) { 1039 fprintf(stderr, "Key file is empty.\n"); 1040 goto fail; 1041 } 1042 rewind(fp); 1043 1044 pKeys->fileData = (char*) malloc(pKeys->fileLen); 1045 if (pKeys->fileData == NULL) { 1046 fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen); 1047 goto fail; 1048 } 1049 1050 if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen) 1051 { 1052 fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n", 1053 pKeys->fileLen); 1054 goto fail; 1055 } 1056 1057 offset = 0; 1058 1059 offset = parseVersion(pKeys, offset, verbose); 1060 offset = parseThreads(pKeys, offset); 1061 offset = parseMethods(pKeys, offset); 1062 offset = parseEnd(pKeys, offset); 1063 if (offset < 0) 1064 goto fail; 1065 1066 /* Leave fp pointing to the beginning of the data section. */ 1067 fseek(fp, offset, SEEK_SET); 1068 1069 sortThreadList(pKeys); 1070 sortMethodList(pKeys); 1071 1072 /* 1073 * Dump list of threads. 1074 */ 1075 if (verbose) { 1076 printf("Threads (%d):\n", pKeys->numThreads); 1077 for (i = 0; i < pKeys->numThreads; i++) { 1078 printf("%2d %s\n", 1079 pKeys->threads[i].threadId, pKeys->threads[i].threadName); 1080 } 1081 } 1082 1083#if 0 1084 /* 1085 * Dump list of methods. 1086 */ 1087 if (verbose) { 1088 printf("Methods (%d):\n", pKeys->numMethods); 1089 for (i = 0; i < pKeys->numMethods; i++) { 1090 printf("0x%08x %s : %s : %s\n", 1091 pKeys->methods[i].methodId >> 2, pKeys->methods[i].className, 1092 pKeys->methods[i].methodName, pKeys->methods[i].signature); 1093 } 1094 } 1095#endif 1096 1097 return pKeys; 1098 1099fail: 1100 freeDataKeys(pKeys); 1101 return NULL; 1102} 1103 1104 1105/* 1106 * Read values from the binary data file. 1107 */ 1108 1109/* Make the return value "unsigned int" instead of "unsigned short" so that 1110 * we can detect EOF. 1111 */ 1112unsigned int read2LE(FILE* fp) 1113{ 1114 unsigned int val; 1115 1116 val = getc(fp); 1117 val |= getc(fp) << 8; 1118 return val; 1119} 1120unsigned int read4LE(FILE* fp) 1121{ 1122 unsigned int val; 1123 1124 val = getc(fp); 1125 val |= getc(fp) << 8; 1126 val |= getc(fp) << 16; 1127 val |= getc(fp) << 24; 1128 return val; 1129} 1130unsigned long long read8LE(FILE* fp) 1131{ 1132 unsigned long long val; 1133 1134 val = getc(fp); 1135 val |= (unsigned long long) getc(fp) << 8; 1136 val |= (unsigned long long) getc(fp) << 16; 1137 val |= (unsigned long long) getc(fp) << 24; 1138 val |= (unsigned long long) getc(fp) << 32; 1139 val |= (unsigned long long) getc(fp) << 40; 1140 val |= (unsigned long long) getc(fp) << 48; 1141 val |= (unsigned long long) getc(fp) << 56; 1142 return val; 1143} 1144 1145/* 1146 * Parse the header of the data section. 1147 * 1148 * Returns with the file positioned at the start of the record data. 1149 */ 1150int parseDataHeader(FILE *fp, DataHeader* pHeader) 1151{ 1152 int bytesToRead; 1153 1154 pHeader->magic = read4LE(fp); 1155 pHeader->version = read2LE(fp); 1156 pHeader->offsetToData = read2LE(fp); 1157 pHeader->startWhen = read8LE(fp); 1158 bytesToRead = pHeader->offsetToData - 16; 1159 if (pHeader->version == 1) { 1160 pHeader->recordSize = 9; 1161 } else if (pHeader->version == 2) { 1162 pHeader->recordSize = 10; 1163 } else if (pHeader->version == 3) { 1164 pHeader->recordSize = read2LE(fp); 1165 bytesToRead -= 2; 1166 } else { 1167 fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version); 1168 return -1; 1169 } 1170 1171 if (fseek(fp, bytesToRead, SEEK_CUR) != 0) { 1172 return -1; 1173 } 1174 1175 return 0; 1176} 1177 1178/* 1179 * Look up a method by its method ID (using binary search). 1180 * 1181 * Returns NULL if no matching method was found. 1182 */ 1183MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId) 1184{ 1185 int hi, lo, mid; 1186 unsigned int id; 1187 int hashedId; 1188 1189 /* Create cache if it doesn't already exist */ 1190 if (pKeys->methodCache == NULL) { 1191 pKeys->methodCache = (int*) calloc(METHOD_CACHE_SIZE, sizeof(int)); 1192 } 1193 1194 // ids are multiples of 4, so shift 1195 hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK; 1196 if (pKeys->methodCache[hashedId]) /* cache hit */ 1197 if (pKeys->methods[pKeys->methodCache[hashedId]].methodId == methodId) 1198 return &pKeys->methods[pKeys->methodCache[hashedId]]; 1199 1200 lo = 0; 1201 hi = pKeys->numMethods - 1; 1202 1203 while (hi >= lo) { 1204 mid = (hi + lo) / 2; 1205 1206 id = pKeys->methods[mid].methodId; 1207 if (id == methodId) { /* match, put in cache */ 1208 hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK; 1209 pKeys->methodCache[hashedId] = mid; 1210 return &pKeys->methods[mid]; 1211 } else if (id < methodId) /* too low */ 1212 lo = mid + 1; 1213 else /* too high */ 1214 hi = mid - 1; 1215 } 1216 1217 return NULL; 1218} 1219 1220/* 1221 * Reads the next data record, and assigns the data values to threadId, 1222 * methodVal and elapsedTime. On end-of-file, the threadId, methodVal, 1223 * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise 1224 * returns 0. 1225 */ 1226int readDataRecord(FILE *dataFp, DataHeader* dataHeader, 1227 int *threadId, unsigned int *methodVal, uint64_t *elapsedTime) 1228{ 1229 int id; 1230 int bytesToRead; 1231 1232 bytesToRead = dataHeader->recordSize; 1233 if (dataHeader->version == 1) { 1234 id = getc(dataFp); 1235 bytesToRead -= 1; 1236 } else { 1237 id = read2LE(dataFp); 1238 bytesToRead -= 2; 1239 } 1240 if (id == EOF) 1241 return 1; 1242 *threadId = id; 1243 1244 *methodVal = read4LE(dataFp); 1245 *elapsedTime = read4LE(dataFp); 1246 bytesToRead -= 8; 1247 1248 while (bytesToRead-- > 0) { 1249 getc(dataFp); 1250 } 1251 1252 if (feof(dataFp)) { 1253 fprintf(stderr, "WARNING: hit EOF mid-record\n"); 1254 return 1; 1255 } 1256 return 0; 1257} 1258 1259/* 1260 * Read the key file and use it to produce formatted output from the 1261 * data file. 1262 */ 1263void dumpTrace() 1264{ 1265 static const char* actionStr[] = { "ent", "xit", "unr", "???" }; 1266 MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0, 1267 {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 }; 1268 char bogusBuf[80]; 1269 char spaces[MAX_STACK_DEPTH+1]; 1270 FILE* dataFp = NULL; 1271 DataHeader dataHeader; 1272 DataKeys* pKeys = NULL; 1273 int i; 1274 TraceData traceData; 1275 1276 //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName); 1277 1278 memset(spaces, '.', MAX_STACK_DEPTH); 1279 spaces[MAX_STACK_DEPTH] = '\0'; 1280 1281 for (i = 0; i < MAX_THREADS; i++) 1282 traceData.depth[i] = 2; // adjust for return from start function 1283 1284 dataFp = fopen(gOptions.traceFileName, "r"); 1285 if (dataFp == NULL) 1286 goto bail; 1287 1288 if ((pKeys = parseKeys(dataFp, 1)) == NULL) 1289 goto bail; 1290 1291 if (parseDataHeader(dataFp, &dataHeader) < 0) 1292 goto bail; 1293 1294 printf("Trace (threadID action usecs class.method signature):\n"); 1295 1296 while (1) { 1297 MethodEntry* method; 1298 int threadId; 1299 unsigned int methodVal; 1300 uint64_t elapsedTime; 1301 int action, printDepth; 1302 unsigned int methodId, lastEnter = 0; 1303 int mismatch = 0; 1304 char depthNote; 1305 1306 /* 1307 * Extract values from file. 1308 */ 1309 if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime)) 1310 break; 1311 1312 action = METHOD_ACTION(methodVal); 1313 methodId = METHOD_ID(methodVal); 1314 1315 /* 1316 * Generate a line of output. 1317 */ 1318 if (action == METHOD_TRACE_ENTER) { 1319 traceData.depth[threadId]++; 1320 lastEnter = methodId; 1321 } else { 1322 /* quick test for mismatched adjacent enter/exit */ 1323 if (lastEnter != 0 && lastEnter != methodId) 1324 mismatch = 1; 1325 } 1326 1327 printDepth = traceData.depth[threadId]; 1328 depthNote = ' '; 1329 if (printDepth < 0) { 1330 printDepth = 0; 1331 depthNote = '-'; 1332 } else if (printDepth > MAX_STACK_DEPTH) { 1333 printDepth = MAX_STACK_DEPTH; 1334 depthNote = '+'; 1335 } 1336 1337 method = lookupMethod(pKeys, methodId); 1338 if (method == NULL) { 1339 method = &bogusMethod; 1340 sprintf(bogusBuf, "methodId: 0x%x", methodId); 1341 method->signature = bogusBuf; 1342 } 1343 1344 if (method->methodName) { 1345 printf("%2d %s%c %8lld%c%s%s.%s %s\n", threadId, 1346 actionStr[action], mismatch ? '!' : ' ', 1347 elapsedTime, depthNote, 1348 spaces + (MAX_STACK_DEPTH - printDepth), 1349 method->className, method->methodName, method->signature); 1350 } else { 1351 printf("%2d %s%c %8lld%c%s%s\n", threadId, 1352 actionStr[action], mismatch ? '!' : ' ', 1353 elapsedTime, depthNote, 1354 spaces + (MAX_STACK_DEPTH - printDepth), 1355 method->className); 1356 } 1357 1358 if (action != METHOD_TRACE_ENTER) { 1359 traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */ 1360 lastEnter = 0; 1361 } 1362 1363 mismatch = 0; 1364 } 1365 1366bail: 1367 if (dataFp != NULL) 1368 fclose(dataFp); 1369 if (pKeys != NULL) 1370 freeDataKeys(pKeys); 1371} 1372 1373/* This routine adds the given time to the parent and child methods. 1374 * This is called when the child routine exits, after the child has 1375 * been popped from the stack. The elapsedTime parameter is the 1376 * duration of the child routine, including time spent in called routines. 1377 */ 1378void addInclusiveTime(MethodEntry *parent, MethodEntry *child, 1379 uint64_t elapsedTime) 1380{ 1381 TimedMethod *pTimed; 1382 1383#if 0 1384 bool verbose = false; 1385 if (strcmp(child->className, debugClassName) == 0) 1386 verbose = true; 1387#endif 1388 1389 int childIsRecursive = (child->recursiveEntries > 0); 1390 int parentIsRecursive = (parent->recursiveEntries > 1); 1391 1392 if (child->recursiveEntries == 0) { 1393 child->elapsedInclusive += elapsedTime; 1394 } else if (child->recursiveEntries == 1) { 1395 child->recursiveInclusive += elapsedTime; 1396 } 1397 child->numCalls[childIsRecursive] += 1; 1398 1399#if 0 1400 if (verbose) { 1401 fprintf(stderr, 1402 "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n", 1403 child->className, child->recursiveEntries, 1404 elapsedTime, child->elapsedInclusive, 1405 child->recursiveInclusive); 1406 } 1407#endif 1408 1409 /* Find the child method in the parent */ 1410 TimedMethod *children = parent->children[parentIsRecursive]; 1411 for (pTimed = children; pTimed; pTimed = pTimed->next) { 1412 if (pTimed->method == child) { 1413 pTimed->elapsedInclusive += elapsedTime; 1414 pTimed->numCalls += 1; 1415 break; 1416 } 1417 } 1418 if (pTimed == NULL) { 1419 /* Allocate a new TimedMethod */ 1420 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod)); 1421 pTimed->elapsedInclusive = elapsedTime; 1422 pTimed->numCalls = 1; 1423 pTimed->method = child; 1424 1425 /* Add it to the front of the list */ 1426 pTimed->next = children; 1427 parent->children[parentIsRecursive] = pTimed; 1428 } 1429 1430 /* Find the parent method in the child */ 1431 TimedMethod *parents = child->parents[childIsRecursive]; 1432 for (pTimed = parents; pTimed; pTimed = pTimed->next) { 1433 if (pTimed->method == parent) { 1434 pTimed->elapsedInclusive += elapsedTime; 1435 pTimed->numCalls += 1; 1436 break; 1437 } 1438 } 1439 if (pTimed == NULL) { 1440 /* Allocate a new TimedMethod */ 1441 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod)); 1442 pTimed->elapsedInclusive = elapsedTime; 1443 pTimed->numCalls = 1; 1444 pTimed->method = parent; 1445 1446 /* Add it to the front of the list */ 1447 pTimed->next = parents; 1448 child->parents[childIsRecursive] = pTimed; 1449 } 1450 1451#if 0 1452 if (verbose) { 1453 fprintf(stderr, 1454 " %s %d eI: %lld\n", 1455 parent->className, parent->recursiveEntries, 1456 pTimed->elapsedInclusive); 1457 } 1458#endif 1459} 1460 1461/* Sorts a linked list and returns a newly allocated array containing 1462 * the sorted entries. 1463 */ 1464TimedMethod *sortTimedMethodList(TimedMethod *list, int *num) 1465{ 1466 int ii; 1467 TimedMethod *pTimed, *sorted; 1468 1469 /* Count the elements */ 1470 int num_entries = 0; 1471 for (pTimed = list; pTimed; pTimed = pTimed->next) 1472 num_entries += 1; 1473 *num = num_entries; 1474 if (num_entries == 0) 1475 return NULL; 1476 1477 /* Copy all the list elements to a new array and sort them */ 1478 sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries); 1479 for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii) 1480 memcpy(&sorted[ii], pTimed, sizeof(TimedMethod)); 1481 qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod); 1482 1483 /* Fix up the "next" pointers so that they work. */ 1484 for (ii = 0; ii < num_entries - 1; ++ii) 1485 sorted[ii].next = &sorted[ii + 1]; 1486 sorted[num_entries - 1].next = NULL; 1487 1488 return sorted; 1489} 1490 1491/* Define flag values for printInclusiveMethod() */ 1492static const int kIsRecursive = 1; 1493 1494/* This prints the inclusive stats for all the parents or children of a 1495 * method, depending on the list that is passed in. 1496 */ 1497void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls, 1498 int flags) 1499{ 1500 int num; 1501 TimedMethod *pTimed; 1502 char buf[80]; 1503 char *anchor_close; 1504 char *spaces = " "; /* 6 spaces */ 1505 int num_spaces = strlen(spaces); 1506 char *space_ptr = &spaces[num_spaces]; 1507 char *className, *methodName, *signature; 1508 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; 1509 char signatureBuf[HTML_BUFSIZE]; 1510 1511 anchor_close = ""; 1512 if (gOptions.outputHtml) 1513 anchor_close = "</a>"; 1514 1515 TimedMethod *sorted = sortTimedMethodList(list, &num); 1516 double methodTotal = method->elapsedInclusive; 1517 for (pTimed = sorted; pTimed; pTimed = pTimed->next) { 1518 MethodEntry *relative = pTimed->method; 1519 className = (char*)(relative->className); 1520 methodName = (char*)(relative->methodName); 1521 signature = (char*)(relative->signature); 1522 double per = 100.0 * pTimed->elapsedInclusive / methodTotal; 1523 sprintf(buf, "[%d]", relative->index); 1524 if (gOptions.outputHtml) { 1525 int len = strlen(buf); 1526 if (len > num_spaces) 1527 len = num_spaces; 1528 sprintf(buf, "<a href=\"#m%d\">[%d]", 1529 relative->index, relative->index); 1530 space_ptr = &spaces[len]; 1531 className = htmlEscape(className, classBuf, HTML_BUFSIZE); 1532 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); 1533 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); 1534 } 1535 int nCalls = numCalls; 1536 if (nCalls == 0) 1537 nCalls = relative->numCalls[0] + relative->numCalls[1]; 1538 if (relative->methodName) { 1539 if (flags & kIsRecursive) { 1540 // Don't display percentages for recursive functions 1541 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s.%s %s\n", 1542 "", "", "", 1543 space_ptr, buf, anchor_close, 1544 pTimed->numCalls, nCalls, 1545 pTimed->elapsedInclusive, 1546 className, methodName, signature); 1547 } else { 1548 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s.%s %s\n", 1549 "", "", per, 1550 space_ptr, buf, anchor_close, 1551 pTimed->numCalls, nCalls, 1552 pTimed->elapsedInclusive, 1553 className, methodName, signature); 1554 } 1555 } else { 1556 if (flags & kIsRecursive) { 1557 // Don't display percentages for recursive functions 1558 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s\n", 1559 "", "", "", 1560 space_ptr, buf, anchor_close, 1561 pTimed->numCalls, nCalls, 1562 pTimed->elapsedInclusive, 1563 className); 1564 } else { 1565 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s\n", 1566 "", "", per, 1567 space_ptr, buf, anchor_close, 1568 pTimed->numCalls, nCalls, 1569 pTimed->elapsedInclusive, 1570 className); 1571 } 1572 } 1573 } 1574} 1575 1576void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method) 1577{ 1578 int ii; 1579 1580 method->recursiveEntries = 0; 1581 for (ii = 0; ii < top; ++ii) { 1582 if (pStack->calls[ii].method == method) 1583 method->recursiveEntries += 1; 1584 } 1585} 1586 1587void stackDump(CallStack *pStack, int top) 1588{ 1589 int ii; 1590 1591 for (ii = 0; ii < top; ++ii) { 1592 MethodEntry *method = pStack->calls[ii].method; 1593 uint64_t entryTime = pStack->calls[ii].entryTime; 1594 if (method->methodName) { 1595 fprintf(stderr, " %2d: %8llu %s.%s %s\n", ii, entryTime, 1596 method->className, method->methodName, method->signature); 1597 } else { 1598 fprintf(stderr, " %2d: %8llu %s\n", ii, entryTime, method->className); 1599 } 1600 } 1601} 1602 1603void outputTableOfContents() 1604{ 1605 printf("<a name=\"contents\"></a>\n"); 1606 printf("<h2>Table of Contents</h2>\n"); 1607 printf("<ul>\n"); 1608 printf(" <li><a href=\"#exclusive\">Exclusive profile</a></li>\n"); 1609 printf(" <li><a href=\"#inclusive\">Inclusive profile</a></li>\n"); 1610 printf(" <li><a href=\"#thread\">Thread profile</a></li>\n"); 1611 printf(" <li><a href=\"#class\">Class/method profile</a></li>\n"); 1612 printf(" <li><a href=\"#method\">Method/class profile</a></li>\n"); 1613 printf("</ul>\n\n"); 1614} 1615 1616void outputNavigationBar() 1617{ 1618 printf("<a href=\"#contents\">[Top]</a>\n"); 1619 printf("<a href=\"#exclusive\">[Exclusive]</a>\n"); 1620 printf("<a href=\"#inclusive\">[Inclusive]</a>\n"); 1621 printf("<a href=\"#thread\">[Thread]</a>\n"); 1622 printf("<a href=\"#class\">[Class]</a>\n"); 1623 printf("<a href=\"#method\">[Method]</a>\n"); 1624 printf("<br><br>\n"); 1625} 1626 1627void printExclusiveProfile(MethodEntry **pMethods, int numMethods, 1628 uint64_t sumThreadTime) 1629{ 1630 int ii; 1631 MethodEntry* method; 1632 double total, sum, per, sum_per; 1633 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; 1634 char signatureBuf[HTML_BUFSIZE]; 1635 char anchor_buf[80]; 1636 char *anchor_close = ""; 1637 1638 total = sumThreadTime; 1639 anchor_buf[0] = 0; 1640 if (gOptions.outputHtml) { 1641 anchor_close = "</a>"; 1642 printf("<a name=\"exclusive\"></a>\n"); 1643 printf("<hr>\n"); 1644 outputNavigationBar(); 1645 } else { 1646 printf("\n%s\n", profileSeparator); 1647 } 1648 1649 /* First, sort the methods into decreasing order of inclusive 1650 * elapsed time so that we can assign the method indices. 1651 */ 1652 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive); 1653 1654 for (ii = 0; ii < numMethods; ++ii) 1655 pMethods[ii]->index = ii; 1656 1657 /* Sort the methods into decreasing order of exclusive elapsed time. 1658 */ 1659 qsort(pMethods, numMethods, sizeof(MethodEntry*), 1660 compareElapsedExclusive); 1661 1662 printf("Total cycles: %llu\n\n", sumThreadTime); 1663 if (gOptions.outputHtml) { 1664 printf("<br><br>\n"); 1665 } 1666 printf("Exclusive elapsed times for each method, not including time spent in\n"); 1667 printf("children, sorted by exclusive time.\n\n"); 1668 if (gOptions.outputHtml) { 1669 printf("<br><br>\n<pre>\n"); 1670 } 1671 1672 printf(" Usecs self %% sum %% Method\n"); 1673 sum = 0; 1674 1675 for (ii = 0; ii < numMethods; ++ii) { 1676 char *className, *methodName, *signature; 1677 1678 method = pMethods[ii]; 1679 /* Don't show methods with zero cycles */ 1680 if (method->elapsedExclusive == 0) 1681 break; 1682 className = (char*)(method->className); 1683 methodName = (char*)(method->methodName); 1684 signature = (char*)(method->signature); 1685 sum += method->elapsedExclusive; 1686 per = 100.0 * method->elapsedExclusive / total; 1687 sum_per = 100.0 * sum / total; 1688 if (gOptions.outputHtml) { 1689 sprintf(anchor_buf, "<a href=\"#m%d\">", method->index); 1690 className = htmlEscape(className, classBuf, HTML_BUFSIZE); 1691 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); 1692 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); 1693 } 1694 if (method->methodName) { 1695 printf("%9llu %6.2f %6.2f %s[%d]%s %s.%s %s\n", 1696 method->elapsedExclusive, per, sum_per, 1697 anchor_buf, method->index, anchor_close, 1698 className, methodName, signature); 1699 } else { 1700 printf("%9llu %6.2f %6.2f %s[%d]%s %s\n", 1701 method->elapsedExclusive, per, sum_per, 1702 anchor_buf, method->index, anchor_close, 1703 className); 1704 } 1705 } 1706 if (gOptions.outputHtml) { 1707 printf("</pre>\n"); 1708 } 1709} 1710 1711/* check to make sure that the child method meets the threshold of the parent */ 1712int checkThreshold(MethodEntry* parent, MethodEntry* child) 1713{ 1714 double parentTime = parent->elapsedInclusive; 1715 double childTime = child->elapsedInclusive; 1716 int64_t percentage = (childTime / parentTime) * 100.0; 1717 return (percentage < gOptions.threshold) ? 0 : 1; 1718} 1719 1720void createLabels(FILE* file, MethodEntry* method) 1721{ 1722 fprintf(file, "node%d[label = \"[%d] %s.%s (%llu, %llu, %d)\"]\n", 1723 method->index, method->index, method->className, method->methodName, 1724 method->elapsedInclusive / 1000, 1725 method->elapsedExclusive / 1000, 1726 method->numCalls[0]); 1727 1728 method->graphState = GRAPH_LABEL_VISITED; 1729 1730 TimedMethod* child; 1731 for (child = method->children[0] ; child ; child = child->next) { 1732 MethodEntry* childMethod = child->method; 1733 1734 if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) { 1735 createLabels(file, child->method); 1736 } 1737 } 1738} 1739 1740void createLinks(FILE* file, MethodEntry* method) 1741{ 1742 method->graphState |= GRAPH_NODE_VISITED; 1743 1744 TimedMethod* child; 1745 for (child = method->children[0] ; child ; child = child->next) { 1746 MethodEntry* childMethod = child->method; 1747 if (checkThreshold(method, child->method)) { 1748 fprintf(file, "node%d -> node%d\n", method->index, child->method->index); 1749 // only visit children that haven't been visited before 1750 if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) { 1751 createLinks(file, child->method); 1752 } 1753 } 1754 } 1755} 1756 1757void createInclusiveProfileGraphNew(DataKeys* dataKeys) 1758{ 1759 // create a temporary file in /tmp 1760 char path[FILENAME_MAX]; 1761 if (gOptions.keepDotFile) { 1762 snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName); 1763 } else { 1764 snprintf(path, FILENAME_MAX, "/tmp/dot-%d-%d.dot", (int)time(NULL), rand()); 1765 } 1766 1767 FILE* file = fopen(path, "w+"); 1768 1769 fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n"); 1770 1771 createLabels(file, dataKeys->methods); 1772 createLinks(file, dataKeys->methods); 1773 1774 fprintf(file, "}"); 1775 fclose(file); 1776 1777 // now that we have the dot file generate the image 1778 char command[1024]; 1779 snprintf(command, 1024, "dot -Tpng -o '%s' '%s'", gOptions.graphFileName, path); 1780 1781 system(command); 1782 1783 if (! gOptions.keepDotFile) { 1784 remove(path); 1785 } 1786} 1787 1788void printInclusiveProfile(MethodEntry **pMethods, int numMethods, 1789 uint64_t sumThreadTime) 1790{ 1791 int ii; 1792 MethodEntry* method; 1793 double total, sum, per, sum_per; 1794 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; 1795 char signatureBuf[HTML_BUFSIZE]; 1796 char anchor_buf[80]; 1797 1798 total = sumThreadTime; 1799 anchor_buf[0] = 0; 1800 if (gOptions.outputHtml) { 1801 printf("<a name=\"inclusive\"></a>\n"); 1802 printf("<hr>\n"); 1803 outputNavigationBar(); 1804 } else { 1805 printf("\n%s\n", profileSeparator); 1806 } 1807 1808 /* Sort the methods into decreasing order of inclusive elapsed time. */ 1809 qsort(pMethods, numMethods, sizeof(MethodEntry*), 1810 compareElapsedInclusive); 1811 1812 printf("\nInclusive elapsed times for each method and its parents and children,\n"); 1813 printf("sorted by inclusive time.\n\n"); 1814 1815 if (gOptions.outputHtml) { 1816 printf("<br><br>\n<pre>\n"); 1817 } 1818 1819 printf("index %%/total %%/self index calls usecs name\n"); 1820 for (ii = 0; ii < numMethods; ++ii) { 1821 int num; 1822 TimedMethod *pTimed; 1823 double excl_per; 1824 char buf[40]; 1825 char *className, *methodName, *signature; 1826 1827 method = pMethods[ii]; 1828 /* Don't show methods with zero cycles */ 1829 if (method->elapsedInclusive == 0) 1830 break; 1831 1832 className = (char*)(method->className); 1833 methodName = (char*)(method->methodName); 1834 signature = (char*)(method->signature); 1835 1836 if (gOptions.outputHtml) { 1837 printf("<a name=\"m%d\"></a>", method->index); 1838 className = htmlEscape(className, classBuf, HTML_BUFSIZE); 1839 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); 1840 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); 1841 } 1842 printf("----------------------------------------------------\n"); 1843 1844 /* Sort and print the parents */ 1845 int numCalls = method->numCalls[0] + method->numCalls[1]; 1846 printInclusiveMethod(method, method->parents[0], numCalls, 0); 1847 if (method->parents[1]) { 1848 printf(" +++++++++++++++++++++++++\n"); 1849 printInclusiveMethod(method, method->parents[1], numCalls, 1850 kIsRecursive); 1851 } 1852 1853 per = 100.0 * method->elapsedInclusive / total; 1854 sprintf(buf, "[%d]", ii); 1855 if (method->methodName) { 1856 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s.%s %s\n", 1857 buf, 1858 per, "", "", method->numCalls[0], method->numCalls[1], 1859 method->elapsedInclusive, 1860 className, methodName, signature); 1861 } else { 1862 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s\n", 1863 buf, 1864 per, "", "", method->numCalls[0], method->numCalls[1], 1865 method->elapsedInclusive, 1866 className); 1867 } 1868 excl_per = 100.0 * method->topExclusive / method->elapsedInclusive; 1869 printf("%6s %5s %5.1f%% %6s %6s %6s %9llu\n", 1870 "", "", excl_per, "excl", "", "", method->topExclusive); 1871 1872 /* Sort and print the children */ 1873 printInclusiveMethod(method, method->children[0], 0, 0); 1874 if (method->children[1]) { 1875 printf(" +++++++++++++++++++++++++\n"); 1876 printInclusiveMethod(method, method->children[1], 0, 1877 kIsRecursive); 1878 } 1879 } 1880 if (gOptions.outputHtml) { 1881 printf("</pre>\n"); 1882 } 1883} 1884 1885void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime, Filter** filters) 1886{ 1887 int ii, jj; 1888 ThreadEntry thread; 1889 double total, per, sum_per; 1890 uint64_t sum; 1891 char threadBuf[HTML_BUFSIZE]; 1892 char anchor_buf[80]; 1893 int drawTable; 1894 1895 total = sumThreadTime; 1896 anchor_buf[0] = 0; 1897 if (gOptions.outputHtml) { 1898 printf("<a name=\"thread\"></a>\n"); 1899 printf("<hr>\n"); 1900 outputNavigationBar(); 1901 } else { 1902 printf("\n%s\n", profileSeparator); 1903 } 1904 1905 /* Sort the threads into decreasing order of elapsed time. */ 1906 qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed); 1907 1908 printf("\nElapsed times for each thread, sorted by elapsed time.\n"); 1909 printf("Also includes percentage of time spent during the <i>execution</i> of any filters.\n\n"); 1910 1911 if (gOptions.outputHtml) { 1912 printf("<br><br>\n<pre>\n"); 1913 } 1914 1915 printf(" Usecs self %% sum %%"); 1916 for (ii = 0; ii < numFilters; ++ii) { 1917 printf(" %s %%", filters[ii]->filterName); 1918 } 1919 printf(" tid ThreadName\n"); 1920 sum = 0; 1921 1922 for (ii = 0; ii < numThreads; ++ii) { 1923 int threadId; 1924 char *threadName; 1925 uint64_t time; 1926 1927 thread = pThreads[ii]; 1928 1929 threadId = thread.threadId; 1930 threadName = (char*)(thread.threadName); 1931 time = thread.elapsedTime; 1932 1933 sum += time; 1934 per = 100.0 * time / total; 1935 sum_per = 100.0 * sum / total; 1936 1937 if (gOptions.outputHtml) { 1938 threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE); 1939 } 1940 1941 printf("%9llu %6.2f %6.2f", time, per, sum_per); 1942 for (jj = 0; jj < numFilters; jj++) { 1943 printf(" %6.2f", 100.0 * filters[jj]->times.threadExecutionTimes[threadId] / time); 1944 } 1945 printf(" %3d %s\n", threadId, threadName); 1946 } 1947 1948 if (gOptions.outputHtml) 1949 printf("</pre><br />"); 1950 1951 printf("\n\nBreak-down of portion of time spent by each thread while waiting on a filter method.\n"); 1952 1953 for (ii = 0; ii < numFilters; ++ii) { 1954 // Draw a table for each filter that measures wait time 1955 drawTable = 0; 1956 for (jj = 0; jj < filters[ii]->numKeys; jj++) 1957 if (filters[ii]->filterKeys[jj].flags == 1) 1958 drawTable = 1; 1959 1960 if (drawTable) { 1961 1962 if (gOptions.outputHtml) 1963 printf("<br/><br/>\n<pre>\n"); 1964 printf("Filter: %s\n", filters[ii]->filterName); 1965 printf("Total waiting cycles: %llu (%6.2f%% of total)\n", 1966 filters[ii]->times.totalWaitTime, 1967 100.0 * filters[ii]->times.totalWaitTime / sum); 1968 1969 if (filters[ii]->times.totalWaitTime > 0) { 1970 1971 printf("Details: \n\n"); 1972 1973 printf(" Waiting cycles %% of total waiting time execution time while waiting thread name\n"); 1974 1975 for (jj = 0; jj < numThreads; jj++) { 1976 1977 thread = pThreads[jj]; 1978 1979 char *threadName; 1980 threadName = (char*) thread.threadName; 1981 if (gOptions.outputHtml) { 1982 threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE); 1983 } 1984 1985 printf(" %9llu %6.2f %6.2f %s\n", 1986 filters[ii]->times.threadWaitTimes[thread.threadId], 1987 100.0 * filters[ii]->times.threadWaitTimes[thread.threadId] / filters[ii]->times.totalWaitTime, 1988 100.0 * filters[ii]->times.threadExecutionTimesWhileWaiting[thread.threadId] / filters[ii]->times.totalWaitTime, 1989 threadName); 1990 } 1991 } 1992 1993 if (gOptions.outputHtml) 1994 printf("</pre>\n"); 1995 1996 } 1997 } 1998 1999} 2000 2001void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods) 2002{ 2003 int ii; 2004 2005 /* Sort the methods into alphabetical order to find the unique class 2006 * names. 2007 */ 2008 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames); 2009 2010 /* Count the number of unique class names. */ 2011 const char *currentClassName = ""; 2012 const char *firstClassName = NULL; 2013 traceData->numClasses = 0; 2014 for (ii = 0; ii < numMethods; ++ii) { 2015 if (pMethods[ii]->methodName == NULL) { 2016 continue; 2017 } 2018 if (strcmp(pMethods[ii]->className, currentClassName) != 0) { 2019 // Remember the first one 2020 if (firstClassName == NULL) { 2021 firstClassName = pMethods[ii]->className; 2022 } 2023 traceData->numClasses += 1; 2024 currentClassName = pMethods[ii]->className; 2025 } 2026 } 2027 2028 if (traceData->numClasses == 0) { 2029 traceData->classes = NULL; 2030 return; 2031 } 2032 2033 /* Allocate space for all of the unique class names */ 2034 traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses); 2035 2036 /* Initialize the classes array */ 2037 memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses); 2038 ClassEntry *pClass = traceData->classes; 2039 pClass->className = currentClassName = firstClassName; 2040 int prevNumMethods = 0; 2041 for (ii = 0; ii < numMethods; ++ii) { 2042 if (pMethods[ii]->methodName == NULL) { 2043 continue; 2044 } 2045 if (strcmp(pMethods[ii]->className, currentClassName) != 0) { 2046 pClass->numMethods = prevNumMethods; 2047 (++pClass)->className = currentClassName = pMethods[ii]->className; 2048 prevNumMethods = 0; 2049 } 2050 prevNumMethods += 1; 2051 } 2052 pClass->numMethods = prevNumMethods; 2053 2054 /* Create the array of MethodEntry pointers for each class */ 2055 pClass = NULL; 2056 currentClassName = ""; 2057 int nextMethod = 0; 2058 for (ii = 0; ii < numMethods; ++ii) { 2059 if (pMethods[ii]->methodName == NULL) { 2060 continue; 2061 } 2062 if (strcmp(pMethods[ii]->className, currentClassName) != 0) { 2063 currentClassName = pMethods[ii]->className; 2064 if (pClass == NULL) 2065 pClass = traceData->classes; 2066 else 2067 pClass++; 2068 /* Allocate space for the methods array */ 2069 int nbytes = sizeof(MethodEntry*) * pClass->numMethods; 2070 pClass->methods = (MethodEntry**) malloc(nbytes); 2071 nextMethod = 0; 2072 } 2073 pClass->methods[nextMethod++] = pMethods[ii]; 2074 } 2075} 2076 2077/* Prints a number of html non-breaking spaces according so that the length 2078 * of the string "buf" is at least "width" characters wide. If width is 2079 * negative, then trailing spaces are added instead of leading spaces. 2080 */ 2081void printHtmlField(char *buf, int width) 2082{ 2083 int ii; 2084 2085 int leadingSpaces = 1; 2086 if (width < 0) { 2087 width = -width; 2088 leadingSpaces = 0; 2089 } 2090 int len = strlen(buf); 2091 int numSpaces = width - len; 2092 if (numSpaces <= 0) { 2093 printf("%s", buf); 2094 return; 2095 } 2096 if (leadingSpaces == 0) 2097 printf("%s", buf); 2098 for (ii = 0; ii < numSpaces; ++ii) 2099 printf(" "); 2100 if (leadingSpaces == 1) 2101 printf("%s", buf); 2102} 2103 2104void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime) 2105{ 2106 int ii, jj; 2107 MethodEntry* method; 2108 double total, sum, per, sum_per; 2109 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; 2110 char signatureBuf[HTML_BUFSIZE]; 2111 2112 total = sumThreadTime; 2113 if (gOptions.outputHtml) { 2114 printf("<a name=\"class\"></a>\n"); 2115 printf("<hr>\n"); 2116 outputNavigationBar(); 2117 } else { 2118 printf("\n%s\n", profileSeparator); 2119 } 2120 2121 if (traceData->numClasses == 0) { 2122 printf("\nNo classes.\n"); 2123 if (gOptions.outputHtml) { 2124 printf("<br><br>\n"); 2125 } 2126 return; 2127 } 2128 2129 printf("\nExclusive elapsed time for each class, summed over all the methods\n"); 2130 printf("in the class.\n\n"); 2131 if (gOptions.outputHtml) { 2132 printf("<br><br>\n"); 2133 } 2134 2135 /* For each class, sum the exclusive times in all of the methods 2136 * in that class. Also sum the number of method calls. Also 2137 * sort the methods so the most expensive appear at the top. 2138 */ 2139 ClassEntry *pClass = traceData->classes; 2140 for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) { 2141 //printf("%s %d methods\n", pClass->className, pClass->numMethods); 2142 int numMethods = pClass->numMethods; 2143 for (jj = 0; jj < numMethods; ++jj) { 2144 method = pClass->methods[jj]; 2145 pClass->elapsedExclusive += method->elapsedExclusive; 2146 pClass->numCalls[0] += method->numCalls[0]; 2147 pClass->numCalls[1] += method->numCalls[1]; 2148 } 2149 2150 /* Sort the methods into decreasing order of exclusive time */ 2151 qsort(pClass->methods, numMethods, sizeof(MethodEntry*), 2152 compareElapsedExclusive); 2153 } 2154 2155 /* Allocate an array of pointers to the classes for more efficient 2156 * sorting. 2157 */ 2158 ClassEntry **pClasses; 2159 pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses); 2160 for (ii = 0; ii < traceData->numClasses; ++ii) 2161 pClasses[ii] = &traceData->classes[ii]; 2162 2163 /* Sort the classes into decreasing order of exclusive time */ 2164 qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive); 2165 2166 if (gOptions.outputHtml) { 2167 printf("<div class=\"header\"><span class=\"parent\"> </span> "); 2168 printf("Cycles %%/total Cumul.%% Calls+Recur Class</div>\n"); 2169 } else { 2170 printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n"); 2171 } 2172 2173 sum = 0; 2174 for (ii = 0; ii < traceData->numClasses; ++ii) { 2175 char *className, *methodName, *signature; 2176 2177 /* Skip classes with zero cycles */ 2178 pClass = pClasses[ii]; 2179 if (pClass->elapsedExclusive == 0) 2180 break; 2181 2182 per = 100.0 * pClass->elapsedExclusive / total; 2183 sum += pClass->elapsedExclusive; 2184 sum_per = 100.0 * sum / total; 2185 className = (char*)(pClass->className); 2186 if (gOptions.outputHtml) { 2187 char buf[80]; 2188 2189 className = htmlEscape(className, classBuf, HTML_BUFSIZE); 2190 printf("<div class=\"link\" onClick=\"javascript:toggle('d%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xd%d\">+</span>", ii, ii); 2191 sprintf(buf, "%llu", pClass->elapsedExclusive); 2192 printHtmlField(buf, 9); 2193 printf(" "); 2194 sprintf(buf, "%.1f", per); 2195 printHtmlField(buf, 7); 2196 printf(" "); 2197 sprintf(buf, "%.1f", sum_per); 2198 printHtmlField(buf, 7); 2199 printf(" "); 2200 sprintf(buf, "%d", pClass->numCalls[0]); 2201 printHtmlField(buf, 6); 2202 printf("+"); 2203 sprintf(buf, "%d", pClass->numCalls[1]); 2204 printHtmlField(buf, -6); 2205 printf(" "); 2206 printf("%s", className); 2207 printf("</div>\n"); 2208 printf("<div class=\"parent\" id=\"d%d\">\n", ii); 2209 } else { 2210 printf("---------------------------------------------\n"); 2211 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n", 2212 pClass->elapsedExclusive, per, sum_per, 2213 pClass->numCalls[0], pClass->numCalls[1], 2214 className); 2215 } 2216 2217 int numMethods = pClass->numMethods; 2218 double classExclusive = pClass->elapsedExclusive; 2219 double sumMethods = 0; 2220 for (jj = 0; jj < numMethods; ++jj) { 2221 method = pClass->methods[jj]; 2222 methodName = (char*)(method->methodName); 2223 signature = (char*)(method->signature); 2224 per = 100.0 * method->elapsedExclusive / classExclusive; 2225 sumMethods += method->elapsedExclusive; 2226 sum_per = 100.0 * sumMethods / classExclusive; 2227 if (gOptions.outputHtml) { 2228 char buf[80]; 2229 2230 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); 2231 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); 2232 printf("<div class=\"leaf\"><span class=\"leaf\"> </span>"); 2233 sprintf(buf, "%llu", method->elapsedExclusive); 2234 printHtmlField(buf, 9); 2235 printf(" "); 2236 sprintf(buf, "%llu", method->elapsedInclusive); 2237 printHtmlField(buf, 9); 2238 printf(" "); 2239 sprintf(buf, "%.1f", per); 2240 printHtmlField(buf, 7); 2241 printf(" "); 2242 sprintf(buf, "%.1f", sum_per); 2243 printHtmlField(buf, 7); 2244 printf(" "); 2245 sprintf(buf, "%d", method->numCalls[0]); 2246 printHtmlField(buf, 6); 2247 printf("+"); 2248 sprintf(buf, "%d", method->numCalls[1]); 2249 printHtmlField(buf, -6); 2250 printf(" "); 2251 printf("<a href=\"#m%d\">[%d]</a> %s %s", 2252 method->index, method->index, methodName, signature); 2253 printf("</div>\n"); 2254 } else { 2255 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s %s\n", 2256 method->elapsedExclusive, 2257 method->elapsedInclusive, 2258 per, sum_per, 2259 method->numCalls[0], method->numCalls[1], 2260 method->index, methodName, signature); 2261 } 2262 } 2263 if (gOptions.outputHtml) { 2264 printf("</div>\n"); 2265 } 2266 } 2267} 2268 2269void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods) 2270{ 2271 int ii; 2272 2273 /* Sort the methods into alphabetical order of method names 2274 * to find the unique method names. 2275 */ 2276 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames); 2277 2278 /* Count the number of unique method names, ignoring class and 2279 * signature. 2280 */ 2281 const char *currentMethodName = ""; 2282 traceData->numUniqueMethods = 0; 2283 for (ii = 0; ii < numMethods; ++ii) { 2284 if (pMethods[ii]->methodName == NULL) 2285 continue; 2286 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { 2287 traceData->numUniqueMethods += 1; 2288 currentMethodName = pMethods[ii]->methodName; 2289 } 2290 } 2291 if (traceData->numUniqueMethods == 0) 2292 return; 2293 2294 /* Allocate space for pointers to all of the unique methods */ 2295 int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods; 2296 traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes); 2297 2298 /* Initialize the uniqueMethods array */ 2299 memset(traceData->uniqueMethods, 0, nbytes); 2300 UniqueMethodEntry *pUnique = traceData->uniqueMethods; 2301 currentMethodName = NULL; 2302 int prevNumMethods = 0; 2303 for (ii = 0; ii < numMethods; ++ii) { 2304 if (pMethods[ii]->methodName == NULL) 2305 continue; 2306 if (currentMethodName == NULL) 2307 currentMethodName = pMethods[ii]->methodName; 2308 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { 2309 currentMethodName = pMethods[ii]->methodName; 2310 pUnique->numMethods = prevNumMethods; 2311 pUnique++; 2312 prevNumMethods = 0; 2313 } 2314 prevNumMethods += 1; 2315 } 2316 pUnique->numMethods = prevNumMethods; 2317 2318 /* Create the array of MethodEntry pointers for each unique method */ 2319 pUnique = NULL; 2320 currentMethodName = ""; 2321 int nextMethod = 0; 2322 for (ii = 0; ii < numMethods; ++ii) { 2323 if (pMethods[ii]->methodName == NULL) 2324 continue; 2325 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { 2326 currentMethodName = pMethods[ii]->methodName; 2327 if (pUnique == NULL) 2328 pUnique = traceData->uniqueMethods; 2329 else 2330 pUnique++; 2331 /* Allocate space for the methods array */ 2332 int nbytes = sizeof(MethodEntry*) * pUnique->numMethods; 2333 pUnique->methods = (MethodEntry**) malloc(nbytes); 2334 nextMethod = 0; 2335 } 2336 pUnique->methods[nextMethod++] = pMethods[ii]; 2337 } 2338} 2339 2340void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime) 2341{ 2342 int ii, jj; 2343 MethodEntry* method; 2344 double total, sum, per, sum_per; 2345 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; 2346 char signatureBuf[HTML_BUFSIZE]; 2347 2348 if (traceData->numUniqueMethods == 0) 2349 return; 2350 2351 total = sumThreadTime; 2352 if (gOptions.outputHtml) { 2353 printf("<a name=\"method\"></a>\n"); 2354 printf("<hr>\n"); 2355 outputNavigationBar(); 2356 } else { 2357 printf("\n%s\n", profileSeparator); 2358 } 2359 2360 printf("\nExclusive elapsed time for each method, summed over all the classes\n"); 2361 printf("that contain a method with the same name.\n\n"); 2362 if (gOptions.outputHtml) { 2363 printf("<br><br>\n"); 2364 } 2365 2366 /* For each unique method, sum the exclusive times in all of the methods 2367 * with the same name. Also sum the number of method calls. Also 2368 * sort the methods so the most expensive appear at the top. 2369 */ 2370 UniqueMethodEntry *pUnique = traceData->uniqueMethods; 2371 for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) { 2372 int numMethods = pUnique->numMethods; 2373 for (jj = 0; jj < numMethods; ++jj) { 2374 method = pUnique->methods[jj]; 2375 pUnique->elapsedExclusive += method->elapsedExclusive; 2376 pUnique->numCalls[0] += method->numCalls[0]; 2377 pUnique->numCalls[1] += method->numCalls[1]; 2378 } 2379 2380 /* Sort the methods into decreasing order of exclusive time */ 2381 qsort(pUnique->methods, numMethods, sizeof(MethodEntry*), 2382 compareElapsedExclusive); 2383 } 2384 2385 /* Allocate an array of pointers to the methods for more efficient 2386 * sorting. 2387 */ 2388 UniqueMethodEntry **pUniqueMethods; 2389 int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods; 2390 pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes); 2391 for (ii = 0; ii < traceData->numUniqueMethods; ++ii) 2392 pUniqueMethods[ii] = &traceData->uniqueMethods[ii]; 2393 2394 /* Sort the methods into decreasing order of exclusive time */ 2395 qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*), 2396 compareUniqueExclusive); 2397 2398 if (gOptions.outputHtml) { 2399 printf("<div class=\"header\"><span class=\"parent\"> </span> "); 2400 printf("Cycles %%/total Cumul.%% Calls+Recur Method</div>\n"); 2401 } else { 2402 printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n"); 2403 } 2404 2405 sum = 0; 2406 for (ii = 0; ii < traceData->numUniqueMethods; ++ii) { 2407 char *className, *methodName, *signature; 2408 2409 /* Skip methods with zero cycles */ 2410 pUnique = pUniqueMethods[ii]; 2411 if (pUnique->elapsedExclusive == 0) 2412 break; 2413 2414 per = 100.0 * pUnique->elapsedExclusive / total; 2415 sum += pUnique->elapsedExclusive; 2416 sum_per = 100.0 * sum / total; 2417 methodName = (char*)(pUnique->methods[0]->methodName); 2418 if (gOptions.outputHtml) { 2419 char buf[80]; 2420 2421 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); 2422 printf("<div class=\"link\" onClick=\"javascript:toggle('e%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xe%d\">+</span>", ii, ii); 2423 sprintf(buf, "%llu", pUnique->elapsedExclusive); 2424 printHtmlField(buf, 9); 2425 printf(" "); 2426 sprintf(buf, "%.1f", per); 2427 printHtmlField(buf, 7); 2428 printf(" "); 2429 sprintf(buf, "%.1f", sum_per); 2430 printHtmlField(buf, 7); 2431 printf(" "); 2432 sprintf(buf, "%d", pUnique->numCalls[0]); 2433 printHtmlField(buf, 6); 2434 printf("+"); 2435 sprintf(buf, "%d", pUnique->numCalls[1]); 2436 printHtmlField(buf, -6); 2437 printf(" "); 2438 printf("%s", methodName); 2439 printf("</div>\n"); 2440 printf("<div class=\"parent\" id=\"e%d\">\n", ii); 2441 } else { 2442 printf("---------------------------------------------\n"); 2443 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n", 2444 pUnique->elapsedExclusive, per, sum_per, 2445 pUnique->numCalls[0], pUnique->numCalls[1], 2446 methodName); 2447 } 2448 int numMethods = pUnique->numMethods; 2449 double methodExclusive = pUnique->elapsedExclusive; 2450 double sumMethods = 0; 2451 for (jj = 0; jj < numMethods; ++jj) { 2452 method = pUnique->methods[jj]; 2453 className = (char*)(method->className); 2454 signature = (char*)(method->signature); 2455 per = 100.0 * method->elapsedExclusive / methodExclusive; 2456 sumMethods += method->elapsedExclusive; 2457 sum_per = 100.0 * sumMethods / methodExclusive; 2458 if (gOptions.outputHtml) { 2459 char buf[80]; 2460 2461 className = htmlEscape(className, classBuf, HTML_BUFSIZE); 2462 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); 2463 printf("<div class=\"leaf\"><span class=\"leaf\"> </span>"); 2464 sprintf(buf, "%llu", method->elapsedExclusive); 2465 printHtmlField(buf, 9); 2466 printf(" "); 2467 sprintf(buf, "%llu", method->elapsedInclusive); 2468 printHtmlField(buf, 9); 2469 printf(" "); 2470 sprintf(buf, "%.1f", per); 2471 printHtmlField(buf, 7); 2472 printf(" "); 2473 sprintf(buf, "%.1f", sum_per); 2474 printHtmlField(buf, 7); 2475 printf(" "); 2476 sprintf(buf, "%d", method->numCalls[0]); 2477 printHtmlField(buf, 6); 2478 printf("+"); 2479 sprintf(buf, "%d", method->numCalls[1]); 2480 printHtmlField(buf, -6); 2481 printf(" "); 2482 printf("<a href=\"#m%d\">[%d]</a> %s.%s %s", 2483 method->index, method->index, 2484 className, methodName, signature); 2485 printf("</div>\n"); 2486 } else { 2487 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n", 2488 method->elapsedExclusive, 2489 method->elapsedInclusive, 2490 per, sum_per, 2491 method->numCalls[0], method->numCalls[1], 2492 method->index, className, methodName, signature); 2493 } 2494 } 2495 if (gOptions.outputHtml) { 2496 printf("</div>\n"); 2497 } 2498 } 2499} 2500 2501/* 2502 * Determines whether the given FilterKey matches the method. The FilterKey's 2503 * key that is used to match against the method is determined by index. 2504 */ 2505int keyMatchesMethod(FilterKey filterKey, MethodEntry* method, int index) 2506{ 2507 if (filterKey.type[index] == 0) { // Class 2508#if 0 2509 fprintf(stderr, " class is %s; filter key is %s\n", method->className, filterKey.keys[index]); 2510#endif 2511 if (strcmp(method->className, filterKey.keys[index]) == 0) { 2512 return 1; 2513 } 2514 } else { // Method 2515 if (method->methodName != NULL) { 2516 // Get fully-qualified name 2517 // TODO: parse class name and method name an put them in structure to avoid 2518 // allocating memory here 2519 char* str = malloc ((strlen(method->className) + strlen(method->methodName) + 2) * sizeof(char)); 2520 strcpy(str, method->className); 2521 strcat(str, "."); 2522 strcat(str, method->methodName); 2523#if 0 2524 fprintf(stderr, " method is %s; filter key is %s\n", str, filterKey.keys[index]); 2525#endif 2526 if (strcmp(str, filterKey.keys[index]) == 0) { 2527 free(str); 2528 return 1; 2529 } 2530 free(str); 2531 } 2532 } 2533 return 0; 2534} 2535 2536/* 2537 * Adds the appropriate times to the given filter based on the given method. Activates and 2538 * de-activates filters as necessary. 2539 * 2540 * A filter is activated when the given method matches the 'entry' key of one of its FilterKeys. 2541 * It is de-activated when the method matches the 'exit' key of the same FilterKey that activated it 2542 * in the first place. Thus, a filter may be active more than once on the same thread (activated by 2543 * different FilterKeys). A filter may also be active on different threads at the same time. 2544 * 2545 * While the filter is active on thread 1, elapsed time is allocated to different buckets which 2546 * include: thread execution time (i.e., time thread 1 spent executing while filter was active), 2547 * thread waiting time (i.e., time thread 1 waited while other threads executed), and execution 2548 * time while waiting (i.e., time thread x spent executing while thread 1 was waiting). We also 2549 * keep track of the total waiting time for a given filter. 2550 * 2551 * Lastly, we keep track of remaining (un-allocated) time for cases in which we exit a method we 2552 * had not entered before, and that method happens to match the 'exit' key of a FilterKey. 2553 */ 2554int filterMethod(MethodEntry* method, Filter* filter, int entry, int threadId, int numThreads, 2555 uint64_t elapsed, uint64_t remTime) 2556{ 2557 int ii, jj; 2558 int activeCount, addedWaitTimeThreadsCount; 2559 int* activeThreads; 2560 int* activationKeys; 2561 int* addedWaitTimeThreads; 2562 2563 // flags 2564 int addWaitTime = 0; 2565 int deactivation = 0; 2566 int addedExecutionTime = 0; 2567 int addedExecutionTimeWhileWaiting = 0; 2568 int addedWaitTime; 2569 int addedRemTime = 0; 2570 int threadKeyPairActive = 0; 2571 2572 if (filter->times.threadWaitTimes == NULL && filter->times.threadExecutionTimes == NULL && 2573 filter->times.threadExecutionTimesWhileWaiting == NULL) { 2574 filter->times.threadWaitTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t)); 2575 filter->times.threadExecutionTimesWhileWaiting = 2576 (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t)); 2577 filter->times.threadExecutionTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t)); 2578 } 2579 2580 int verbose = 0; 2581 2582 if (verbose) 2583 fprintf(stderr, 2584 "Running %s filter for class %s method %s, thread %d; activeCount: %d time: %llu\n", 2585 filter->filterName, method->className, method->methodName, threadId, 2586 filter->activeCount, elapsed); 2587 2588 // If active on some thread 2589 if (filter->activeCount > 0) { 2590 2591 // Initialize active structures in case there are any de-activations 2592 activeThreads = (int*) calloc(filter->activeCount, sizeof(int)); 2593 activationKeys = (int*) calloc(filter->activeCount, sizeof(int)); 2594 activeCount = 0; 2595 2596 // Initialize structure to help us determine which threads we've already added wait time to 2597 addedWaitTimeThreads = (int*) calloc(filter->activeCount, sizeof(int)); 2598 addedWaitTimeThreadsCount = 0; 2599 2600 // Add times to appropriate sums and de-activate (if necessary) 2601 for (ii = 0; ii < filter->activeCount; ii++) { 2602 2603 if (verbose) { 2604 fprintf(stderr, " Analyzing active thread with id %d, activated by key [%s, %s]\n", 2605 filter->activeThreads[ii], 2606 filter->filterKeys[filter->activationKeys[ii]].keys[0], 2607 filter->filterKeys[filter->activationKeys[ii]].keys[1]); 2608 } 2609 2610 // If active on THIS thread -> add to execution time (only add once!) 2611 if (filter->activeThreads[ii] == threadId && !addedExecutionTime) { 2612 if (verbose) 2613 fprintf(stderr, " Adding execution time to this thead\n"); 2614 filter->times.threadExecutionTimes[threadId] += elapsed; 2615 addedExecutionTime = 1; 2616 } 2617 2618 // If active on ANOTHER thread (or this one too) with CROSS_THREAD_FLAG -> add to 2619 // both thread's waiting time + total 2620 if (filter->filterKeys[filter->activationKeys[ii]].flags == 1) { 2621 2622 // Add time to thread that is waiting (add to each waiting thread at most once!) 2623 addedWaitTime = 0; 2624 for (jj = 0; jj < addedWaitTimeThreadsCount; jj++) { 2625 if (addedWaitTimeThreads[jj] == filter->activeThreads[ii]) 2626 addedWaitTime = 1; 2627 } 2628 if (!addedWaitTime) { 2629 if (verbose) 2630 fprintf(stderr, " Adding wait time to waiting thread\n"); 2631 filter->times.threadWaitTimes[filter->activeThreads[ii]] += elapsed; 2632 addedWaitTimeThreads[addedWaitTimeThreadsCount++] = filter->activeThreads[ii]; 2633 } 2634 2635 // Add execution time to this thread while the other is waiting (only add once!) 2636 // [Flag is needed only because outside for loop might iterate through same 2637 // thread twice?] TODO: verify 2638 if (!addedExecutionTimeWhileWaiting) { 2639 if (verbose) 2640 fprintf(stderr, " Adding exec time to this thread while thread waits\n"); 2641 filter->times.threadExecutionTimesWhileWaiting[threadId] += elapsed; 2642 addedExecutionTimeWhileWaiting = 1; 2643 } 2644 2645 addWaitTime = 1; 2646 } 2647 2648 // If a method exit matches the EXIT method of an ACTIVE key -> de-activate 2649 // the KEY (not the entire filter!!) 2650 if (!entry && keyMatchesMethod(filter->filterKeys[filter->activationKeys[ii]], 2651 method, 1)) { 2652 if (verbose) 2653 fprintf(stderr, " Exit key matched!\n"); 2654 2655 // Deactivate by removing (NOT adding) entries from activeThreads and activationKeys 2656 deactivation = 1; // singal that lists should be replaced 2657 } else { 2658 // No de-activation -> copy old entries into new lists 2659 activeThreads[activeCount] = filter->activeThreads[ii]; 2660 activationKeys[activeCount++] = filter->activationKeys[ii]; 2661 } 2662 } 2663 2664 // If waiting on ANY thread, add wait time to total (but only ONCE!) 2665 if (addWaitTime) { 2666 filter->times.totalWaitTime += elapsed; 2667 } 2668 2669 // If de-activation occurred, replace lists 2670 if (deactivation) { 2671 // TODO: Free memory from old lists 2672 2673 // Set new lists 2674 filter->activeThreads = activeThreads; 2675 filter->activationKeys = activationKeys; 2676 filter->activeCount = activeCount; 2677 } else { 2678 // TODO: Free memory from new lists 2679 } 2680 2681 } // Else, continue (we might be activating the filter on a different thread) 2682 2683 2684 if (entry) { // ENTRY 2685 if (verbose) 2686 fprintf(stderr, " Here at the entry\n"); 2687 // If method matches entry key -> activate thread (do not add time since it's a new entry!) 2688 for (ii = 0; ii < filter->numKeys; ii++) { 2689 if (keyMatchesMethod(filter->filterKeys[ii], method, 0)) { 2690 if (verbose) 2691 fprintf(stderr, " Entry key matched!\n"); 2692 // Activate thread only if thread/key pair is not already active 2693 for (jj = 0; jj < filter->activeCount; jj++) { 2694 if (filter->activeThreads[jj] == threadId && filter->activationKeys[jj] == ii) 2695 threadKeyPairActive = 1; 2696 } 2697 // TODO: WORRY ABOUT MEMORY WHEN ACTIVE_COUNT > DEFAULT_ACTIVE_THREAD (unlikely) 2698 // TODO: what if the same thread is active multiple times by different keys? 2699 // nothing, we just have to make sure we dont double-add, and we dont.. 2700 if (!threadKeyPairActive) { 2701 filter->activeThreads[filter->activeCount] = threadId; 2702 filter->activationKeys[filter->activeCount++] = ii; 2703 } 2704 } 2705 } 2706 } else { // EXIT 2707 // If method matches a terminal key -> add remTime to total (no need to active/de-activate) 2708 for (ii = 0; ii < filter->numKeys; ii++) { 2709 if (!deactivation && keyMatchesMethod(filter->filterKeys[ii], method, 1) && 2710 keyMatchesMethod(filter->filterKeys[ii], method, 0)) { 2711 // Add remTime(s) 2712 // TODO: think about how we should add remTimes.. should we add remTime to threads 2713 // that were waiting or being waited on? for now, keep it simple and just add the 2714 // execution time to the current thread. 2715 filter->times.threadExecutionTimes[threadId] += remTime; 2716 addedRemTime = 1; 2717 } 2718 } 2719 } 2720 2721 return addedExecutionTime | (addedRemTime << 1); 2722} 2723 2724void dumpFilters(Filter** filters) { 2725 int i; 2726 for (i = 0; i < numFilters; i++) { 2727 int j; 2728 fprintf(stderr, "FILTER %s\n", filters[i]->filterName); 2729 for (j = 0; j < filters[i]->numKeys; j++) { 2730 fprintf(stderr, "Keys: %s, type %d", filters[i]->filterKeys[j].keys[0], 2731 filters[i]->filterKeys[j].type[0]); 2732 if (filters[i]->filterKeys[j].keys[1] != NULL) { 2733 fprintf(stderr, " AND %s, type %d", filters[i]->filterKeys[j].keys[1], 2734 filters[i]->filterKeys[j].type[1]); 2735 } 2736 fprintf(stderr, "; flags: %d\n", filters[i]->filterKeys[j].flags); 2737 } 2738 } 2739} 2740 2741/* 2742 * See parseFilters for required data format. 2743 * 'data' must point to the beginning of a filter definition. 2744 */ 2745char* parseFilter(char* data, char* dataEnd, Filter** filters, int num) { 2746 2747 Filter* filter; 2748 int next, count, i; 2749 int tmpOffset, tmpKeyLen; 2750 char* tmpKey; 2751 char* key1; 2752 char* key2; 2753 2754 filter = (Filter*) malloc(sizeof(Filter)); 2755 filter->activeCount = 0; 2756 filter->activeThreads = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int)); 2757 filter->activationKeys = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int)); 2758 2759 next = findNextChar(data + 1, dataEnd - data - 1, '\n'); 2760 if (next < 0) { 2761 // TODO: what should we do here? 2762 // End of file reached... 2763 } 2764 data[next+1] = '\0'; 2765 filter->filterName = data + 1; 2766 data += next + 2; // Careful 2767 2768 /* 2769 * Count the number of keys (one per line). 2770 */ 2771 count = countLinesToChar(data, dataEnd - data, FILTER_TAG); 2772 if (count <= 0) { 2773 fprintf(stderr, 2774 "ERROR: failed while parsing filter %s (found %d keys)\n", 2775 filter->filterName, count); 2776 return NULL; // TODO: Should do something else 2777 // Could return filter with 0 keys instead (probably better to avoid random segfaults) 2778 } 2779 2780 filter->filterKeys = (FilterKey*) malloc(sizeof(FilterKey) * count); 2781 2782 /* 2783 * Extract all entries. 2784 */ 2785 tmpOffset = 0; 2786 for (i = 0; i < count; i++) { 2787 next = findNextChar(data, dataEnd - data, '\n'); 2788 // assert(next > 0); // TODO: revise... (skip if next == 0 ?) 2789 data[next] = '\0'; 2790 tmpKey = data; 2791 2792 if (*data == FILTER_FLAG_THREAD) { 2793 filter->filterKeys[i].flags = 1; 2794 tmpKey++; 2795 } else { 2796 filter->filterKeys[i].flags = 0; 2797 } 2798 2799 tmpOffset = findNextChar(tmpKey, next, ','); 2800 2801 if (tmpOffset < 0) { 2802 // No comma, so only 1 key 2803 key1 = tmpKey; 2804 key2 = tmpKey; 2805 2806 // Get type for key1 2807 filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS; // default 2808 tmpOffset = findNextChar(key1, next, '('); 2809 if (tmpOffset > 0) { 2810 if (findNextChar(key1, next, ')') == tmpOffset + 1) { 2811 filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD; 2812 filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD; 2813 } 2814 key1[tmpOffset] = '\0'; 2815 } 2816 } else { 2817 // Pair of keys 2818 tmpKey[tmpOffset] = '\0'; 2819 key1 = tmpKey; 2820 key2 = tmpKey + tmpOffset + 1; 2821 2822 // Get type for key1 2823 filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS; 2824 tmpKeyLen = tmpOffset; 2825 tmpOffset = findNextChar(key1, tmpKeyLen, '('); 2826 if (tmpOffset > 0) { 2827 if (findNextChar(key1, tmpKeyLen, ')') == tmpOffset + 1) { 2828 filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD; 2829 } 2830 key1[tmpOffset] = '\0'; 2831 } 2832 2833 // Get type for key2 2834 filter->filterKeys[i].type[1] = FILTER_TYPE_CLASS; 2835 tmpOffset = findNextChar(key2, next - tmpKeyLen, '('); 2836 if (tmpOffset > 0) { 2837 if (findNextChar(key2, next - tmpKeyLen, ')') == tmpOffset + 1) { 2838 filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD; 2839 } 2840 key2[tmpOffset] = '\0'; 2841 } 2842 } 2843 2844 filter->filterKeys[i].keys[0] = key1; 2845 filter->filterKeys[i].keys[1] = key2; 2846 data += next+1; 2847 } 2848 2849 filter->numKeys = count; 2850 filters[num] = filter; 2851 2852 return data; 2853} 2854 2855/* 2856 * Parses filters from given file. The file must follow the following format: 2857 * 2858 * *FilterName <- creates a new filter with keys to follow 2859 * A.method() <- key that triggers whenever A.method() enters/exit 2860 * Class <- key that triggers whenever any method from Class enters/exits 2861 * +CrossThread <- same as above, but keeps track of execution times accross threads 2862 * B.m(),C.m() <- key that triggers filter on when B.m() enters and off when C.m() exits 2863 * 2864 * TODO: add concrete example to make things clear 2865 */ 2866Filter** parseFilters(const char* filterFileName) { 2867 2868 Filter** filters = NULL; 2869 FILE* fp = NULL; 2870 long len; 2871 char* data; 2872 char* dataEnd; 2873 char* dataStart; 2874 int i, next, count; 2875 2876 fp = fopen(filterFileName, "r"); 2877 if (fp == NULL) 2878 goto bail; 2879 2880 if (fseek(fp, 0L, SEEK_END) != 0) { 2881 perror("fseek"); 2882 goto bail; 2883 } 2884 2885 len = ftell(fp); 2886 if (len == 0) { 2887 fprintf(stderr, "WARNING: Filter file is empty.\n"); 2888 goto bail; 2889 } 2890 rewind(fp); 2891 2892 data = (char*) malloc(len); 2893 if (data == NULL) { 2894 fprintf(stderr, "ERROR: unable to alloc %ld bytes for filter file\n", len); 2895 goto bail; 2896 } 2897 2898 // Read file into memory 2899 if (fread(data, 1, len, fp) != (size_t) len) { 2900 fprintf(stderr, "ERROR: unable to read %ld bytes from filter file\n", len); 2901 goto bail; 2902 } 2903 2904 dataStart = data; 2905 dataEnd = data + len; 2906 2907 // Figure out how many filters there are 2908 numFilters = 0; 2909 next = -1; 2910 2911 while (1) { 2912 if (*data == FILTER_TAG) 2913 numFilters++; 2914 next = findNextChar(data, len, '\n'); 2915 if (next < 0) 2916 break; 2917 data += next+1; 2918 len -= next+1; 2919 } 2920 2921 if (numFilters == 0) { 2922 fprintf(stderr, "WARNING: no filters found. Continuing without filters\n"); 2923 goto bail; 2924 } 2925 2926 filters = (Filter**) calloc(numFilters, sizeof(Filter *)); 2927 if (filters == NULL) { 2928 fprintf(stderr, "ERROR: unable to alloc memory for filters"); 2929 goto bail; 2930 } 2931 2932 data = dataStart; 2933 for (i = 0; i < numFilters; i++) { 2934 data = parseFilter(data, dataEnd, filters, i); 2935 } 2936 2937 return filters; 2938 2939bail: 2940 if (fp != NULL) 2941 fclose(fp); 2942 2943 return NULL; 2944 2945} 2946 2947 2948/* 2949 * Read the key and data files and return the MethodEntries for those files 2950 */ 2951DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, 2952 uint64_t* threadTime, Filter** filters) 2953{ 2954 DataKeys* dataKeys = NULL; 2955 MethodEntry **pMethods = NULL; 2956 MethodEntry* method; 2957 FILE* dataFp = NULL; 2958 DataHeader dataHeader; 2959 int ii, jj, numThreads; 2960 uint64_t currentTime; 2961 MethodEntry* caller; 2962 2963 dataFp = fopen(traceFileName, "r"); 2964 if (dataFp == NULL) 2965 goto bail; 2966 2967 if ((dataKeys = parseKeys(dataFp, 0)) == NULL) 2968 goto bail; 2969 2970 if (parseDataHeader(dataFp, &dataHeader) < 0) 2971 goto bail; 2972 2973 numThreads = dataKeys->numThreads; 2974 2975#if 0 2976 FILE *dumpStream = fopen("debug", "w"); 2977#endif 2978 while (1) { 2979 int threadId; 2980 unsigned int methodVal; 2981 int action; 2982 unsigned int methodId; 2983 CallStack *pStack; 2984 2985 /* 2986 * Extract values from file. 2987 */ 2988 if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, ¤tTime)) 2989 break; 2990 2991 action = METHOD_ACTION(methodVal); 2992 methodId = METHOD_ID(methodVal); 2993 2994 /* Get the call stack for this thread */ 2995 pStack = traceData->stacks[threadId]; 2996 2997 /* If there is no call stack yet for this thread, then allocate one */ 2998 if (pStack == NULL) { 2999 pStack = malloc(sizeof(CallStack)); 3000 pStack->top = 0; 3001 pStack->lastEventTime = currentTime; 3002 pStack->threadStartTime = currentTime; 3003 pStack->remTimes = (uint64_t*) calloc(numFilters, sizeof(uint64_t)); 3004 traceData->stacks[threadId] = pStack; 3005 } 3006 3007 /* Lookup the current method */ 3008 method = lookupMethod(dataKeys, methodId); 3009 if (method == NULL) 3010 method = &dataKeys->methods[UNKNOWN_INDEX]; 3011 3012#if 0 3013 if (method->methodName) { 3014 fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n", 3015 threadId, currentTime, action, pStack->threadStartTime, 3016 method->recursiveEntries, 3017 pStack->top, method->className, method->methodName, 3018 method->signature); 3019 } else { 3020 printf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n", 3021 threadId, currentTime, action, pStack->threadStartTime, 3022 method->recursiveEntries, 3023 pStack->top, method->className); 3024 } 3025#endif 3026 3027 if (action == METHOD_TRACE_ENTER) { 3028 /* This is a method entry */ 3029 if (pStack->top >= MAX_STACK_DEPTH) { 3030 fprintf(stderr, "Stack overflow (exceeded %d frames)\n", 3031 MAX_STACK_DEPTH); 3032 exit(1); 3033 } 3034 3035 /* Get the caller method */ 3036 if (pStack->top >= 1) 3037 caller = pStack->calls[pStack->top - 1].method; 3038 else 3039 caller = &dataKeys->methods[TOPLEVEL_INDEX]; 3040 countRecursiveEntries(pStack, pStack->top, caller); 3041 caller->elapsedExclusive += currentTime - pStack->lastEventTime; 3042#if 0 3043 if (caller->elapsedExclusive > 10000000) 3044 fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n", 3045 caller->elapsedExclusive, currentTime, 3046 pStack->lastEventTime, 3047 currentTime - pStack->lastEventTime); 3048#endif 3049 if (caller->recursiveEntries <= 1) { 3050 caller->topExclusive += currentTime - pStack->lastEventTime; 3051 } 3052 3053 /* Push the method on the stack for this thread */ 3054 pStack->calls[pStack->top].method = method; 3055 pStack->calls[pStack->top++].entryTime = currentTime; 3056 3057 // For each filter 3058 int result = 0; 3059 for (ii = 0; ii < numFilters; ii++) { 3060 result = filterMethod(method, filters[ii], 1, threadId, numThreads, 3061 currentTime - pStack->lastEventTime, pStack->remTimes[ii]); 3062 3063 // TODO: make remTimes work properly 3064 // Consider moving remTimes handling together with the rest 3065 // of time handling and clean up the return codes 3066 /* 3067 if (result == 0) { // no time added, no remTime added 3068 pStack->remTimes[ii] += currentTime - pStack->lastEventTime; 3069 } else if (result == 3 || result == 4) { // remTime added 3070 // Reset remTime, since it's been added 3071 pStack->remTimes[ii] = 0; 3072 } 3073 */ 3074 } 3075 3076 } else { 3077 /* This is a method exit */ 3078 uint64_t entryTime = 0; 3079 3080 /* Pop the method off the stack for this thread */ 3081 if (pStack->top > 0) { 3082 pStack->top -= 1; 3083 entryTime = pStack->calls[pStack->top].entryTime; 3084 if (method != pStack->calls[pStack->top].method) { 3085 if (method->methodName) { 3086 fprintf(stderr, 3087 "Exit from method %s.%s %s does not match stack:\n", 3088 method->className, method->methodName, 3089 method->signature); 3090 } else { 3091 fprintf(stderr, 3092 "Exit from method %s does not match stack:\n", 3093 method->className); 3094 } 3095 stackDump(pStack, pStack->top + 1); 3096 exit(1); 3097 } 3098 } 3099 3100 /* Get the caller method */ 3101 if (pStack->top >= 1) 3102 caller = pStack->calls[pStack->top - 1].method; 3103 else 3104 caller = &dataKeys->methods[TOPLEVEL_INDEX]; 3105 countRecursiveEntries(pStack, pStack->top, caller); 3106 countRecursiveEntries(pStack, pStack->top, method); 3107 uint64_t elapsed = currentTime - entryTime; 3108 addInclusiveTime(caller, method, elapsed); 3109 method->elapsedExclusive += currentTime - pStack->lastEventTime; 3110 if (method->recursiveEntries == 0) { 3111 method->topExclusive += currentTime - pStack->lastEventTime; 3112 } 3113 3114 // For each filter 3115 int result = 0; 3116 for (ii = 0; ii < numFilters; ii++) { 3117 result = filterMethod(method, filters[ii], 0, threadId, numThreads, 3118 currentTime - pStack->lastEventTime, pStack->remTimes[ii]); 3119 3120 // TODO: make remTimes work properly 3121 /* 3122 if (result == 0) { // no time added, no remTime added 3123 pStack->remTimes[ii] += currentTime - pStack->lastEventTime; 3124 } else if (result == 3 || result == 4) { // remTime added 3125 // Reset remTime, since it's been added 3126 pStack->remTimes[ii] = 0; 3127 } 3128 */ 3129 } 3130 3131 } 3132 /* Remember the time of the last entry or exit event */ 3133 pStack->lastEventTime = currentTime; 3134 } 3135 3136 /* If we have calls on the stack when the trace ends, then clean 3137 * up the stack and add time to the callers by pretending that we 3138 * are exiting from their methods now. 3139 */ 3140 CallStack *pStack; 3141 int threadId; 3142 uint64_t elapsedTime = 0; 3143 uint64_t sumThreadTime = 0; 3144 for (threadId = 0; threadId < MAX_THREADS; ++threadId) { 3145 3146 pStack = traceData->stacks[threadId]; 3147 3148 /* If this thread never existed, then continue with next thread */ 3149 if (pStack == NULL) 3150 continue; 3151 3152 /* Calculate times spent in thread, and add it to total time */ 3153 elapsedTime = pStack->lastEventTime - pStack->threadStartTime; 3154 sumThreadTime += elapsedTime; 3155 3156 for (ii = 0; ii < pStack->top; ++ii) { 3157 //printf("in loop\n"); 3158 3159 if (ii == 0) 3160 caller = &dataKeys->methods[TOPLEVEL_INDEX]; 3161 else 3162 caller = pStack->calls[ii - 1].method; 3163 method = pStack->calls[ii].method; 3164 countRecursiveEntries(pStack, ii, caller); 3165 countRecursiveEntries(pStack, ii, method); 3166 3167 uint64_t entryTime = pStack->calls[ii].entryTime; 3168 uint64_t elapsed = pStack->lastEventTime - entryTime; 3169 addInclusiveTime(caller, method, elapsed); 3170 3171 // For each filter 3172 int result = 0; 3173 for (ii = 0; ii < numFilters; ii++) { 3174 result = filterMethod(method, filters[ii], 0, threadId, numThreads, 3175 currentTime - pStack->lastEventTime, pStack->remTimes[ii]); 3176 3177 // TODO: make remTimes work properly 3178 /* 3179 if (result == 0) { // no time added, no remTime added 3180 pStack->remTimes[ii] += currentTime - pStack->lastEventTime; 3181 } else if (result == 3 || result == 4) { // remTime added 3182 // Reset remTime, since it's been added 3183 pStack->remTimes[ii] = 0; 3184 } 3185 */ 3186 } 3187 } 3188 3189 /* Save the per-thread elapsed time in the DataKeys struct */ 3190 for (ii = 0; ii < dataKeys->numThreads; ++ii) { 3191 if (dataKeys->threads[ii].threadId == threadId) { 3192 dataKeys->threads[ii].elapsedTime = elapsedTime; 3193 } 3194 } 3195 3196 3197 } 3198 caller = &dataKeys->methods[TOPLEVEL_INDEX]; 3199 caller->elapsedInclusive = sumThreadTime; 3200 3201#if 0 3202 fclose(dumpStream); 3203#endif 3204 3205 if (threadTime != NULL) { 3206 *threadTime = sumThreadTime; 3207 } 3208 3209bail: 3210 if (dataFp != NULL) 3211 fclose(dataFp); 3212 3213 return dataKeys; 3214} 3215 3216MethodEntry** parseMethodEntries(DataKeys* dataKeys) 3217{ 3218 int ii; 3219 /* Create a new array of pointers to the methods and sort the pointers 3220 * instead of the actual MethodEntry structs. We need to do this 3221 * because there are other lists that contain pointers to the 3222 * MethodEntry structs. 3223 */ 3224 MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods); 3225 for (ii = 0; ii < dataKeys->numMethods; ++ii) { 3226 MethodEntry* entry = &dataKeys->methods[ii]; 3227 pMethods[ii] = entry; 3228 } 3229 3230 return pMethods; 3231} 3232 3233 3234/* 3235 * Produce a function profile from the following methods 3236 */ 3237void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime, 3238 ThreadEntry *pThreads, int numThreads, Filter** filters) 3239{ 3240 /* Print the html header, if necessary */ 3241 if (gOptions.outputHtml) { 3242 printf(htmlHeader, gOptions.sortableUrl); 3243 outputTableOfContents(); 3244 } 3245 3246 printExclusiveProfile(pMethods, numMethods, sumThreadTime); 3247 printInclusiveProfile(pMethods, numMethods, sumThreadTime); 3248 3249 printThreadProfile(pThreads, numThreads, sumThreadTime, filters); 3250 3251 createClassList(traceData, pMethods, numMethods); 3252 printClassProfiles(traceData, sumThreadTime); 3253 3254 createUniqueMethodList(traceData, pMethods, numMethods); 3255 printMethodProfiles(traceData, sumThreadTime); 3256 3257 if (gOptions.outputHtml) { 3258 printf("%s", htmlFooter); 3259 } 3260} 3261 3262int compareMethodNamesForDiff(const void *a, const void *b) 3263{ 3264 int result; 3265 3266 const MethodEntry *methodA = *(const MethodEntry**)a; 3267 const MethodEntry *methodB = *(const MethodEntry**)b; 3268 if (methodA->methodName == NULL || methodB->methodName == NULL) { 3269 return compareClassNames(a, b); 3270 } 3271 result = strcmp(methodA->methodName, methodB->methodName); 3272 if (result == 0) { 3273 result = strcmp(methodA->signature, methodB->signature); 3274 if (result == 0) { 3275 return strcmp(methodA->className, methodB->className); 3276 } 3277 } 3278 return result; 3279} 3280 3281int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis) 3282{ 3283 int i; 3284 3285 for (i = 0 ; i < size ; i++) { 3286 MethodEntry* method = methods[i]; 3287 3288 if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) { 3289// printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName, 3290 // method->className, method->methodName); 3291 3292 return i; 3293/* if (!compareMethodNames(&method, &matchThis)) { 3294 return i; 3295 } 3296*/ } 3297 } 3298 3299 return -1; 3300} 3301 3302int compareDiffEntriesExculsive(const void *a, const void *b) 3303{ 3304 int result; 3305 3306 const DiffEntry* entryA = (const DiffEntry*)a; 3307 const DiffEntry* entryB = (const DiffEntry*)b; 3308 3309 if (entryA->differenceExclusive < entryB->differenceExclusive) { 3310 return 1; 3311 } else if (entryA->differenceExclusive > entryB->differenceExclusive) { 3312 return -1; 3313 } 3314 3315 return 0; 3316} 3317 3318int compareDiffEntriesInculsive(const void *a, const void *b) 3319{ 3320 int result; 3321 3322 const DiffEntry* entryA = (const DiffEntry*)a; 3323 const DiffEntry* entryB = (const DiffEntry*)b; 3324 3325 if (entryA->differenceInclusive < entryB->differenceInclusive) { 3326 return 1; 3327 } else if (entryA->differenceInclusive > entryB->differenceInclusive) { 3328 return -1; 3329 } 3330 3331 return 0; 3332} 3333 3334void printMissingMethod(MethodEntry* method) 3335{ 3336 char classBuf[HTML_BUFSIZE]; 3337 char methodBuf[HTML_BUFSIZE]; 3338 char* className; 3339 char* methodName; 3340 3341 className = htmlEscape(method->className, classBuf, HTML_BUFSIZE); 3342 methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE); 3343 3344 if (gOptions.outputHtml) printf("<tr><td>\n"); 3345 3346 printf("%s.%s ", className, methodName); 3347 if (gOptions.outputHtml) printf("</td><td>"); 3348 3349 printf("%lld ", method->elapsedExclusive); 3350 if (gOptions.outputHtml) printf("</td><td>"); 3351 3352 printf("%lld ", method->elapsedInclusive); 3353 if (gOptions.outputHtml) printf("</td><td>"); 3354 3355 printf("%d\n", method->numCalls[0]); 3356 if (gOptions.outputHtml) printf("</td><td>\n"); 3357} 3358 3359 3360void createDiff(DataKeys* d1, uint64_t sum1, DataKeys* d2, uint64_t sum2) 3361{ 3362 MethodEntry** methods1 = parseMethodEntries(d1); 3363 MethodEntry** methods2 = parseMethodEntries(d2); 3364 3365 // sort and assign the indicies 3366 int i; 3367 qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); 3368 for (i = 0; i < d1->numMethods; ++i) { 3369 methods1[i]->index = i; 3370 } 3371 3372 qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); 3373 for (i = 0; i < d2->numMethods; ++i) { 3374 methods2[i]->index = i; 3375 } 3376 3377 int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods; 3378 max++; 3379 DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry)); 3380 memset(diffs, 0, max * sizeof(DiffEntry)); 3381 DiffEntry* ptr = diffs; 3382 3383// printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods); 3384 3385 int matches = 0; 3386 3387 for (i = 0 ; i < d1->numMethods ; i++) { 3388 int match = findMatch(methods2, d2->numMethods, methods1[i]); 3389 if (match >= 0) { 3390 ptr->method1 = methods1[i]; 3391 ptr->method2 = methods2[match]; 3392 3393 uint64_t e1 = ptr->method1->elapsedExclusive; 3394 uint64_t e2 = ptr->method2->elapsedExclusive; 3395 if (e1 > 0) { 3396 ptr->differenceExclusive = e2 - e1; 3397 ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0; 3398 } 3399 3400 uint64_t i1 = ptr->method1->elapsedInclusive; 3401 uint64_t i2 = ptr->method2->elapsedInclusive; 3402 if (i1 > 0) { 3403 ptr->differenceInclusive = i2 - i1; 3404 ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0; 3405 } 3406 3407 // clear these out so we don't find them again and we know which ones 3408 // we have left over 3409 methods1[i] = NULL; 3410 methods2[match] = NULL; 3411 ptr++; 3412 3413 matches++; 3414 } 3415 } 3416 ptr->method1 = NULL; 3417 ptr->method2 = NULL; 3418 3419 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive); 3420 ptr = diffs; 3421 3422 if (gOptions.outputHtml) { 3423 printf(htmlHeader, gOptions.sortableUrl); 3424 printf("<h3>Table of Contents</h3>\n"); 3425 printf("<ul>\n"); 3426 printf("<li><a href='#exclusive'>Exclusive</a>\n"); 3427 printf("<li><a href='#inclusive'>Inclusive</a>\n"); 3428 printf("</ul>\n"); 3429 printf("Run 1: %s<br>\n", gOptions.diffFileName); 3430 printf("Run 2: %s<br>\n", gOptions.traceFileName); 3431 printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n"); 3432 printf(tableHeader, "exclusive_table"); 3433 } 3434 3435 char classBuf[HTML_BUFSIZE]; 3436 char methodBuf[HTML_BUFSIZE]; 3437 char* className; 3438 char* methodName; 3439 3440 while (ptr->method1 != NULL && ptr->method2 != NULL) { 3441 if (gOptions.outputHtml) printf("<tr><td>\n"); 3442 3443 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); 3444 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); 3445 3446 printf("%s.%s ", className, methodName); 3447 if (gOptions.outputHtml) printf("</td><td>"); 3448 3449 printf("%lld ", ptr->method1->elapsedExclusive); 3450 if (gOptions.outputHtml) printf("</td><td>"); 3451 3452 printf("%llu ", ptr->method2->elapsedExclusive); 3453 if (gOptions.outputHtml) printf("</td><td>"); 3454 3455 printf("%lld ", ptr->differenceExclusive); 3456 if (gOptions.outputHtml) printf("</td><td>"); 3457 3458 printf("%.2f\n", ptr->differenceExclusivePercentage); 3459 if (gOptions.outputHtml) printf("</td><td>\n"); 3460 3461 printf("%d\n", ptr->method1->numCalls[0]); 3462 if (gOptions.outputHtml) printf("</td><td>\n"); 3463 3464 printf("%d\n", ptr->method2->numCalls[0]); 3465 if (gOptions.outputHtml) printf("</td></tr>\n"); 3466 3467 ptr++; 3468 } 3469 3470 if (gOptions.outputHtml) printf("</table>\n"); 3471 3472 if (gOptions.outputHtml) { 3473 printf(htmlHeader, gOptions.sortableUrl); 3474 printf("Run 1: %s<br>\n", gOptions.diffFileName); 3475 printf("Run 2: %s<br>\n", gOptions.traceFileName); 3476 printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n"); 3477 printf(tableHeader, "inclusive_table"); 3478 } 3479 3480 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive); 3481 ptr = diffs; 3482 3483 while (ptr->method1 != NULL && ptr->method2 != NULL) { 3484 if (gOptions.outputHtml) printf("<tr><td>\n"); 3485 3486 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); 3487 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); 3488 3489 printf("%s.%s ", className, methodName); 3490 if (gOptions.outputHtml) printf("</td><td>"); 3491 3492 printf("%lld ", ptr->method1->elapsedInclusive); 3493 if (gOptions.outputHtml) printf("</td><td>"); 3494 3495 printf("%llu ", ptr->method2->elapsedInclusive); 3496 if (gOptions.outputHtml) printf("</td><td>"); 3497 3498 printf("%lld ", ptr->differenceInclusive); 3499 if (gOptions.outputHtml) printf("</td><td>"); 3500 3501 printf("%.2f\n", ptr->differenceInclusivePercentage); 3502 if (gOptions.outputHtml) printf("</td><td>\n"); 3503 3504 printf("%d\n", ptr->method1->numCalls[0]); 3505 if (gOptions.outputHtml) printf("</td><td>\n"); 3506 3507 printf("%d\n", ptr->method2->numCalls[0]); 3508 if (gOptions.outputHtml) printf("</td></tr>\n"); 3509 3510 ptr++; 3511 } 3512 3513 if (gOptions.outputHtml) { 3514 printf("</table>\n"); 3515 printf("<h3>Run 1 methods not found in Run 2</h3>"); 3516 printf(tableHeaderMissing); 3517 } 3518 3519 for (i = 0; i < d1->numMethods; ++i) { 3520 if (methods1[i] != NULL) { 3521 printMissingMethod(methods1[i]); 3522 } 3523 } 3524 3525 if (gOptions.outputHtml) { 3526 printf("</table>\n"); 3527 printf("<h3>Run 2 methods not found in Run 1</h3>"); 3528 printf(tableHeaderMissing); 3529 } 3530 3531 for (i = 0; i < d2->numMethods; ++i) { 3532 if (methods2[i] != NULL) { 3533 printMissingMethod(methods2[i]); 3534 } 3535 } 3536 3537 if (gOptions.outputHtml) printf("</body></html\n"); 3538} 3539 3540int usage(const char *program) 3541{ 3542 fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] [-f filter-file] trace-file-name\n", program); 3543 fprintf(stderr, " -d trace-file-name - Diff with this trace\n"); 3544 fprintf(stderr, " -g outfile - Write graph to 'outfile'\n"); 3545 fprintf(stderr, " -f filter-file - Filter functions as specified in file\n"); 3546 fprintf(stderr, " -k - When writing a graph, keep the intermediate DOT file\n"); 3547 fprintf(stderr, " -h - Turn on HTML output\n"); 3548 fprintf(stderr, " -o - Dump the dmtrace file instead of profiling\n"); 3549 fprintf(stderr, " -s - URL base to where the sortable javascript file\n"); 3550 fprintf(stderr, " -t threshold - Threshold percentage for including nodes in the graph\n"); 3551 return 2; 3552} 3553 3554// Returns true if there was an error 3555int parseOptions(int argc, char **argv) 3556{ 3557 while (1) { 3558 int opt = getopt(argc, argv, "d:hg:kos:t:f:"); 3559 if (opt == -1) 3560 break; 3561 switch (opt) { 3562 case 'd': 3563 gOptions.diffFileName = optarg; 3564 break; 3565 case 'g': 3566 gOptions.graphFileName = optarg; 3567 break; 3568 case 'f': 3569 gOptions.filterFileName = optarg; 3570 break; 3571 case 'k': 3572 gOptions.keepDotFile = 1; 3573 break; 3574 case 'h': 3575 gOptions.outputHtml = 1; 3576 break; 3577 case 'o': 3578 gOptions.dump = 1; 3579 break; 3580 case 's': 3581 gOptions.sortableUrl = optarg; 3582 break; 3583 case 't': 3584 gOptions.threshold = atoi(optarg); 3585 break; 3586 default: 3587 return 1; 3588 } 3589 } 3590 return 0; 3591} 3592 3593/* 3594 * Parse args. 3595 */ 3596int main(int argc, char** argv) 3597{ 3598 3599 gOptions.threshold = -1; 3600 3601 // Parse the options 3602 if (parseOptions(argc, argv) || argc - optind != 1) 3603 return usage(argv[0]); 3604 3605 gOptions.traceFileName = argv[optind]; 3606 3607 if (gOptions.threshold < 0 || 100 <= gOptions.threshold) { 3608 gOptions.threshold = 20; 3609 } 3610 3611 if (gOptions.dump) { 3612 dumpTrace(); 3613 return 0; 3614 } 3615 3616 uint64_t sumThreadTime = 0; 3617 3618 Filter** filters = NULL; 3619 if (gOptions.filterFileName != NULL) { 3620 filters = parseFilters(gOptions.filterFileName); 3621 } 3622 3623 TraceData data1; 3624 memset(&data1, 0, sizeof(data1)); 3625 DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName, 3626 &sumThreadTime, filters); 3627 if (dataKeys == NULL) { 3628 fprintf(stderr, "Cannot read trace.\n"); 3629 exit(1); 3630 } 3631 3632 if (gOptions.diffFileName != NULL) { 3633 uint64_t sum2; 3634 TraceData data2; 3635 DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2, filters); 3636 3637 createDiff(d2, sum2, dataKeys, sumThreadTime); 3638 3639 freeDataKeys(d2); 3640 } else { 3641 MethodEntry** methods = parseMethodEntries(dataKeys); 3642 profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime, 3643 dataKeys->threads, dataKeys->numThreads, filters); 3644 if (gOptions.graphFileName != NULL) { 3645 createInclusiveProfileGraphNew(dataKeys); 3646 } 3647 free(methods); 3648 } 3649 3650 freeDataKeys(dataKeys); 3651 3652 return 0; 3653} 3654