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. Version 1 uses one byte for the thread id.
37 * Version 2 uses two bytes for the thread ids.
38 */
39 int versionNumber;
40
41 /* arbitrarily limit indentation */
42 #define MAX_STACK_DEPTH 10000
43
44 /* thread list in key file is not reliable, so just max out */
45 #define MAX_THREADS 32768
46
47 /* Size of temporary buffers for escaping html strings */
48 #define HTML_BUFSIZE 10240
49
50 char *htmlHeader =
51 "<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n"
52 "<script langugage=\"javascript\">\n"
53 "function toggle(item) {\n"
54 " obj=document.getElementById(item);\n"
55 " visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n"
56 " key=document.getElementById(\"x\" + item);\n"
57 " if (visible) {\n"
58 " obj.style.display=\"none\";\n"
59 " key.innerHTML=\"+\";\n"
60 " } else {\n"
61 " obj.style.display=\"block\";\n"
62 " key.innerHTML=\"-\";\n"
63 " }\n"
64 "}\n"
65 "function onMouseOver(obj) {\n"
66 " obj.style.background=\"lightblue\";\n"
67 "}\n"
68 "function onMouseOut(obj) {\n"
69 " obj.style.background=\"white\";\n"
70 "}\n"
71 "</script>\n"
72 "<style type=\"text/css\">\n"
73 "div { font-family: courier; font-size: 13 }\n"
74 "div.parent { margin-left: 15; display: none }\n"
75 "div.leaf { margin-left: 10 }\n"
76 "div.header { margin-left: 10 }\n"
77 "div.link { margin-left: 10; cursor: move }\n"
78 "span.parent { padding-right: 10; }\n"
79 "span.leaf { padding-right: 10; }\n"
80 "a img { border: 0;}\n"
81 "table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n"
82 "a { text-decoration: none; }\n"
83 "a:hover { text-decoration: underline; }\n"
84 "table.sortable th, table.sortable td { text-align: left;}"
85 "table.sortable tr.odd td { background-color: #ddd; }\n"
86 "table.sortable tr.even td { background-color: #fff; }\n"
87 "</style>\n"
88 "</head><body>\n\n";
89
90 char *htmlFooter = "\n</body>\n</html>\n";
91 char *profileSeparator =
92 "======================================================================";
93
94 const char* tableHeader =
95 "<table class='sortable' id='%s'><tr>\n"
96 "<th>Method</th>\n"
97 "<th>Run 1 (us)</th>\n"
98 "<th>Run 2 (us)</th>\n"
99 "<th>Diff (us)</th>\n"
100 "<th>Diff (%%)</th>\n"
101 "<th>1: # calls</th>\n"
102 "<th>2: # calls</th>\n"
103 "</tr>\n";
104
105 const char* tableHeaderMissing =
106 "<table class='sortable' id='%s'>\n"
107 "<th>Method</th>\n"
108 "<th>Exclusive</th>\n"
109 "<th>Inclusive</th>\n"
110 "<th># calls</th>\n";
111
112 #define GRAPH_LABEL_VISITED 0x0001
113 #define GRAPH_NODE_VISITED 0x0002
114
115 /*
116 * Values from the header of the data file.
117 */
118 typedef struct DataHeader {
119 unsigned int magic;
120 short version;
121 short offsetToData;
122 long long startWhen;
123 } DataHeader;
124
125 /*
126 * Entry from the thread list.
127 */
128 typedef struct ThreadEntry {
129 int threadId;
130 const char* threadName;
131 uint64_t elapsedTime;
132 } ThreadEntry;
133
134 struct MethodEntry;
135 typedef struct TimedMethod {
136 struct TimedMethod *next;
137 uint64_t elapsedInclusive;
138 int numCalls;
139 struct MethodEntry *method;
140 } TimedMethod;
141
142 typedef struct ClassEntry {
143 const char *className;
144 uint64_t elapsedExclusive;
145 int numMethods;
146 struct MethodEntry **methods; /* list of methods in this class */
147 int numCalls[2]; /* 0=normal, 1=recursive */
148 } ClassEntry;
149
150 typedef struct UniqueMethodEntry {
151 uint64_t elapsedExclusive;
152 int numMethods;
153 struct MethodEntry **methods; /* list of methods with same name */
154 int numCalls[2]; /* 0=normal, 1=recursive */
155 } UniqueMethodEntry;
156
157 /*
158 * Entry from the method list.
159 */
160 typedef struct MethodEntry {
161 unsigned int methodId;
162 const char* className;
163 const char* methodName;
164 const char* signature;
165 const char* fileName;
166 int lineNum;
167 uint64_t elapsedExclusive;
168 uint64_t elapsedInclusive;
169 uint64_t topExclusive; /* non-recursive exclusive time */
170 uint64_t recursiveInclusive;
171 struct TimedMethod *parents[2]; /* 0=normal, 1=recursive */
172 struct TimedMethod *children[2]; /* 0=normal, 1=recursive */
173 int numCalls[2]; /* 0=normal, 1=recursive */
174 int index; /* used after sorting to number methods */
175 int recursiveEntries; /* number of entries on the stack */
176 int graphState; /* used when graphing to see if this method has been visited before */
177 } MethodEntry;
178
179 /*
180 * The parsed contents of the key file.
181 */
182 typedef struct DataKeys {
183 char* fileData; /* contents of the entire file */
184 long fileLen;
185 int numThreads;
186 ThreadEntry* threads;
187 int numMethods;
188 MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */
189 } DataKeys;
190
191 #define TOPLEVEL_INDEX 0
192 #define UNKNOWN_INDEX 1
193
194 typedef struct StackEntry {
195 MethodEntry *method;
196 uint64_t entryTime;
197 } StackEntry;
198
199 typedef struct CallStack {
200 int top;
201 StackEntry calls[MAX_STACK_DEPTH];
202 uint64_t lastEventTime;
203 uint64_t threadStartTime;
204 } CallStack;
205
206 typedef struct DiffEntry {
207 MethodEntry* method1;
208 MethodEntry* method2;
209 int64_t differenceExclusive;
210 int64_t differenceInclusive;
211 double differenceExclusivePercentage;
212 double differenceInclusivePercentage;
213 } DiffEntry;
214
215 // Global options
216 typedef struct Options {
217 const char* traceFileName;
218 const char* diffFileName;
219 const char* graphFileName;
220 int keepDotFile;
221 int dump;
222 int outputHtml;
223 const char* sortableUrl;
224 int threshold;
225 } Options;
226
227 typedef struct TraceData {
228 int numClasses;
229 ClassEntry *classes;
230 CallStack *stacks[MAX_THREADS];
231 int depth[MAX_THREADS];
232 int numUniqueMethods;
233 UniqueMethodEntry *uniqueMethods;
234 } TraceData;
235
236 static Options gOptions;
237
238 /* Escapes characters in the source string that are html special entities.
239 * The escaped string is written to "dest" which must be large enough to
240 * hold the result. A pointer to "dest" is returned. The characters and
241 * their corresponding escape sequences are:
242 * '<' <
243 * '>' >
244 * '&' &
245 */
htmlEscape(const char * src,char * dest,int len)246 char *htmlEscape(const char *src, char *dest, int len)
247 {
248 char *destStart = dest;
249
250 if (src == NULL)
251 return NULL;
252
253 int nbytes = 0;
254 while (*src) {
255 if (*src == '<') {
256 nbytes += 4;
257 if (nbytes >= len)
258 break;
259 *dest++ = '&';
260 *dest++ = 'l';
261 *dest++ = 't';
262 *dest++ = ';';
263 } else if (*src == '>') {
264 nbytes += 4;
265 if (nbytes >= len)
266 break;
267 *dest++ = '&';
268 *dest++ = 'g';
269 *dest++ = 't';
270 *dest++ = ';';
271 } else if (*src == '&') {
272 nbytes += 5;
273 if (nbytes >= len)
274 break;
275 *dest++ = '&';
276 *dest++ = 'a';
277 *dest++ = 'm';
278 *dest++ = 'p';
279 *dest++ = ';';
280 } else {
281 nbytes += 1;
282 if (nbytes >= len)
283 break;
284 *dest++ = *src;
285 }
286 src += 1;
287 }
288 if (nbytes >= len) {
289 fprintf(stderr, "htmlEscape(): buffer overflow\n");
290 exit(1);
291 }
292 *dest = 0;
293
294 return destStart;
295 }
296
297 /* Initializes a MethodEntry
298 */
initMethodEntry(MethodEntry * method,unsigned int methodId,const char * className,const char * methodName,const char * signature,const char * fileName,const char * lineNumStr)299 void initMethodEntry(MethodEntry *method, unsigned int methodId,
300 const char *className, const char *methodName,
301 const char *signature, const char* fileName,
302 const char* lineNumStr)
303 {
304 method->methodId = methodId;
305 method->className = className;
306 method->methodName = methodName;
307 method->signature = signature;
308 method->fileName = fileName;
309 method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1;
310 method->elapsedExclusive = 0;
311 method->elapsedInclusive = 0;
312 method->topExclusive = 0;
313 method->recursiveInclusive = 0;
314 method->parents[0] = NULL;
315 method->parents[1] = NULL;
316 method->children[0] = NULL;
317 method->children[1] = NULL;
318 method->numCalls[0] = 0;
319 method->numCalls[1] = 0;
320 method->index = 0;
321 method->recursiveEntries = 0;
322 }
323
324 /*
325 * This comparison function is called from qsort() to sort
326 * methods into decreasing order of exclusive elapsed time.
327 */
compareElapsedExclusive(const void * a,const void * b)328 int compareElapsedExclusive(const void *a, const void *b) {
329 uint64_t elapsed1, elapsed2;
330 int result;
331
332 const MethodEntry *methodA = *(const MethodEntry**)a;
333 const MethodEntry *methodB = *(const MethodEntry**)b;
334 elapsed1 = methodA->elapsedExclusive;
335 elapsed2 = methodB->elapsedExclusive;
336 if (elapsed1 < elapsed2)
337 return 1;
338 if (elapsed1 > elapsed2)
339 return -1;
340
341 /* If the elapsed times of two methods are equal, then sort them
342 * into alphabetical order.
343 */
344 result = strcmp(methodA->className, methodB->className);
345 if (result == 0) {
346 if (methodA->methodName == NULL || methodB->methodName == NULL) {
347 unsigned int idA = methodA->methodId;
348 unsigned int idB = methodB->methodId;
349 if (idA < idB)
350 return -1;
351 if (idA > idB)
352 return 1;
353 return 0;
354 }
355 result = strcmp(methodA->methodName, methodB->methodName);
356 if (result == 0)
357 result = strcmp(methodA->signature, methodB->signature);
358 }
359 return result;
360 }
361
362 /*
363 * This comparison function is called from qsort() to sort
364 * methods into decreasing order of inclusive elapsed time.
365 */
compareElapsedInclusive(const void * a,const void * b)366 int compareElapsedInclusive(const void *a, const void *b) {
367 const MethodEntry *methodA, *methodB;
368 uint64_t elapsed1, elapsed2;
369 int result;
370
371 methodA = *(MethodEntry const **)a;
372 methodB = *(MethodEntry const **)b;
373 elapsed1 = methodA->elapsedInclusive;
374 elapsed2 = methodB->elapsedInclusive;
375 if (elapsed1 < elapsed2)
376 return 1;
377 if (elapsed1 > elapsed2)
378 return -1;
379
380 /* If the elapsed times of two methods are equal, then sort them
381 * into alphabetical order.
382 */
383 result = strcmp(methodA->className, methodB->className);
384 if (result == 0) {
385 if (methodA->methodName == NULL || methodB->methodName == NULL) {
386 unsigned int idA = methodA->methodId;
387 unsigned int idB = methodB->methodId;
388 if (idA < idB)
389 return -1;
390 if (idA > idB)
391 return 1;
392 return 0;
393 }
394 result = strcmp(methodA->methodName, methodB->methodName);
395 if (result == 0)
396 result = strcmp(methodA->signature, methodB->signature);
397 }
398 return result;
399 }
400
401 /*
402 * This comparison function is called from qsort() to sort
403 * threads into decreasing order of elapsed time.
404 */
compareElapsed(const void * a,const void * b)405 int compareElapsed(const void *a, const void *b) {
406 const ThreadEntry *threadA, *threadB;
407 uint64_t elapsed1, elapsed2;
408 int result = 0;
409
410 threadA = (ThreadEntry const *)a;
411 threadB = (ThreadEntry const *)b;
412 elapsed1 = threadA->elapsedTime;
413 elapsed2 = threadB->elapsedTime;
414 if (elapsed1 < elapsed2)
415 return 1;
416 if (elapsed1 > elapsed2)
417 return -1;
418
419 /* If the elapsed times of two threads are equal, then sort them
420 * by thread id.
421 */
422 int idA = threadA->threadId;
423 int idB = threadB->threadId;
424 if (idA < idB)
425 result = -1;
426 if (idA > idB)
427 result = 1;
428
429 return result;
430 }
431
432 /*
433 * This comparison function is called from qsort() to sort
434 * TimedMethods into decreasing order of inclusive elapsed time.
435 */
compareTimedMethod(const void * a,const void * b)436 int compareTimedMethod(const void *a, const void *b) {
437 const TimedMethod *timedA, *timedB;
438 uint64_t elapsed1, elapsed2;
439 int result;
440
441 timedA = (TimedMethod const *)a;
442 timedB = (TimedMethod const *)b;
443 elapsed1 = timedA->elapsedInclusive;
444 elapsed2 = timedB->elapsedInclusive;
445 if (elapsed1 < elapsed2)
446 return 1;
447 if (elapsed1 > elapsed2)
448 return -1;
449
450 /* If the elapsed times of two methods are equal, then sort them
451 * into alphabetical order.
452 */
453 MethodEntry *methodA = timedA->method;
454 MethodEntry *methodB = timedB->method;
455 result = strcmp(methodA->className, methodB->className);
456 if (result == 0) {
457 if (methodA->methodName == NULL || methodB->methodName == NULL) {
458 unsigned int idA = methodA->methodId;
459 unsigned int idB = methodB->methodId;
460 if (idA < idB)
461 return -1;
462 if (idA > idB)
463 return 1;
464 return 0;
465 }
466 result = strcmp(methodA->methodName, methodB->methodName);
467 if (result == 0)
468 result = strcmp(methodA->signature, methodB->signature);
469 }
470 return result;
471 }
472
473 /*
474 * This comparison function is called from qsort() to sort
475 * MethodEntry pointers into alphabetical order of class names.
476 */
compareClassNames(const void * a,const void * b)477 int compareClassNames(const void *a, const void *b) {
478 int result;
479
480 const MethodEntry *methodA = *(const MethodEntry**)a;
481 const MethodEntry *methodB = *(const MethodEntry**)b;
482 result = strcmp(methodA->className, methodB->className);
483 if (result == 0) {
484 unsigned int idA = methodA->methodId;
485 unsigned int idB = methodB->methodId;
486 if (idA < idB)
487 return -1;
488 if (idA > idB)
489 return 1;
490 return 0;
491 }
492 return result;
493 }
494
495 /*
496 * This comparison function is called from qsort() to sort
497 * classes into decreasing order of exclusive elapsed time.
498 */
compareClassExclusive(const void * a,const void * b)499 int compareClassExclusive(const void *a, const void *b) {
500 uint64_t elapsed1, elapsed2;
501 int result;
502
503 const ClassEntry *classA = *(const ClassEntry**)a;
504 const ClassEntry *classB = *(const ClassEntry**)b;
505 elapsed1 = classA->elapsedExclusive;
506 elapsed2 = classB->elapsedExclusive;
507 if (elapsed1 < elapsed2)
508 return 1;
509 if (elapsed1 > elapsed2)
510 return -1;
511
512 /* If the elapsed times of two classs are equal, then sort them
513 * into alphabetical order.
514 */
515 result = strcmp(classA->className, classB->className);
516 if (result == 0) {
517 /* Break ties with the first method id. This is probably not
518 * needed.
519 */
520 unsigned int idA = classA->methods[0]->methodId;
521 unsigned int idB = classB->methods[0]->methodId;
522 if (idA < idB)
523 return -1;
524 if (idA > idB)
525 return 1;
526 return 0;
527 }
528 return result;
529 }
530
531 /*
532 * This comparison function is called from qsort() to sort
533 * MethodEntry pointers into alphabetical order by method name,
534 * then by class name.
535 */
compareMethodNames(const void * a,const void * b)536 int compareMethodNames(const void *a, const void *b) {
537 int result;
538
539 const MethodEntry *methodA = *(const MethodEntry**)a;
540 const MethodEntry *methodB = *(const MethodEntry**)b;
541 if (methodA->methodName == NULL || methodB->methodName == NULL) {
542 return compareClassNames(a, b);
543 }
544 result = strcmp(methodA->methodName, methodB->methodName);
545 if (result == 0) {
546 result = strcmp(methodA->className, methodB->className);
547 if (result == 0) {
548 unsigned int idA = methodA->methodId;
549 unsigned int idB = methodB->methodId;
550 if (idA < idB)
551 return -1;
552 if (idA > idB)
553 return 1;
554 return 0;
555 }
556 }
557 return result;
558 }
559
560 /*
561 * This comparison function is called from qsort() to sort
562 * unique methods into decreasing order of exclusive elapsed time.
563 */
compareUniqueExclusive(const void * a,const void * b)564 int compareUniqueExclusive(const void *a, const void *b) {
565 uint64_t elapsed1, elapsed2;
566 int result;
567
568 const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a;
569 const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b;
570 elapsed1 = uniqueA->elapsedExclusive;
571 elapsed2 = uniqueB->elapsedExclusive;
572 if (elapsed1 < elapsed2)
573 return 1;
574 if (elapsed1 > elapsed2)
575 return -1;
576
577 /* If the elapsed times of two methods are equal, then sort them
578 * into alphabetical order.
579 */
580 result = strcmp(uniqueA->methods[0]->className,
581 uniqueB->methods[0]->className);
582 if (result == 0) {
583 unsigned int idA = uniqueA->methods[0]->methodId;
584 unsigned int idB = uniqueB->methods[0]->methodId;
585 if (idA < idB)
586 return -1;
587 if (idA > idB)
588 return 1;
589 return 0;
590 }
591 return result;
592 }
593
594 /*
595 * Free a DataKeys struct.
596 */
freeDataKeys(DataKeys * pKeys)597 void freeDataKeys(DataKeys* pKeys)
598 {
599 if (pKeys == NULL)
600 return;
601
602 free(pKeys->fileData);
603 free(pKeys->threads);
604 free(pKeys->methods);
605 free(pKeys);
606 }
607
608 /*
609 * Find the offset to the next occurrence of the specified character.
610 *
611 * "data" should point somewhere within the current line. "len" is the
612 * number of bytes left in the buffer.
613 *
614 * Returns -1 if we hit the end of the buffer.
615 */
findNextChar(const char * data,int len,char lookFor)616 int findNextChar(const char* data, int len, char lookFor)
617 {
618 const char* start = data;
619
620 while (len > 0) {
621 if (*data == lookFor)
622 return data - start;
623
624 data++;
625 len--;
626 }
627
628 return -1;
629 }
630
631 /*
632 * Count the number of lines until the next token.
633 *
634 * Returns -1 if none found before EOF.
635 */
countLinesToToken(const char * data,int len)636 int countLinesToToken(const char* data, int len)
637 {
638 int count = 0;
639 int next;
640
641 while (*data != TOKEN_CHAR) {
642 next = findNextChar(data, len, '\n');
643 if (next < 0)
644 return -1;
645 count++;
646 data += next+1;
647 len -= next+1;
648 }
649
650 return count;
651 }
652
653 /*
654 * Make sure we're at the start of the right section.
655 *
656 * Returns the length of the token line, or -1 if something is wrong.
657 */
checkToken(const char * data,int len,const char * cmpStr)658 int checkToken(const char* data, int len, const char* cmpStr)
659 {
660 int cmpLen = strlen(cmpStr);
661 int next;
662
663 if (*data != TOKEN_CHAR) {
664 fprintf(stderr,
665 "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data);
666 return -1;
667 }
668
669 next = findNextChar(data, len, '\n');
670 if (next < cmpLen+1)
671 return -1;
672
673 if (strncmp(data+1, cmpStr, cmpLen) != 0) {
674 fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1);
675 return -1;
676 }
677
678 return next+1;
679 }
680
681 /*
682 * Parse the "*version" section.
683 */
parseVersion(DataKeys * pKeys,long offset,int verbose)684 long parseVersion(DataKeys* pKeys, long offset, int verbose)
685 {
686 char* data;
687 char* dataEnd;
688 int i, count, next;
689
690 if (offset < 0)
691 return -1;
692
693 data = pKeys->fileData + offset;
694 dataEnd = pKeys->fileData + pKeys->fileLen;
695 next = checkToken(data, dataEnd - data, "version");
696 if (next <= 0)
697 return -1;
698
699 data += next;
700
701 /*
702 * Count the number of items in the "version" section.
703 */
704 count = countLinesToToken(data, dataEnd - data);
705 if (count <= 0) {
706 fprintf(stderr,
707 "ERROR: failed while reading version (found %d)\n", count);
708 return -1;
709 }
710
711 /* find the end of the line */
712 next = findNextChar(data, dataEnd - data, '\n');
713 if (next < 0)
714 return -1;
715
716 data[next] = '\0';
717 versionNumber = strtoul(data, NULL, 0);
718 if (verbose)
719 printf("VERSION: %d\n", versionNumber);
720
721 data += next+1;
722
723 /* skip over the rest of the stuff, which is "name=value" lines */
724 for (i = 1; i < count; i++) {
725 next = findNextChar(data, dataEnd - data, '\n');
726 if (next < 0)
727 return -1;
728 //data[next] = '\0';
729 //printf("IGNORING: '%s'\n", data);
730 data += next+1;
731 }
732
733 return data - pKeys->fileData;
734 }
735
736 /*
737 * Parse the "*threads" section.
738 */
parseThreads(DataKeys * pKeys,long offset)739 long parseThreads(DataKeys* pKeys, long offset)
740 {
741 char* data;
742 char* dataEnd;
743 int i, next, tab, count;
744
745 if (offset < 0)
746 return -1;
747
748 data = pKeys->fileData + offset;
749 dataEnd = pKeys->fileData + pKeys->fileLen;
750 next = checkToken(data, dataEnd - data, "threads");
751
752 data += next;
753
754 /*
755 * Count the number of thread entries (one per line).
756 */
757 count = countLinesToToken(data, dataEnd - data);
758 if (count <= 0) {
759 fprintf(stderr,
760 "ERROR: failed while reading threads (found %d)\n", count);
761 return -1;
762 }
763
764 //printf("+++ found %d threads\n", count);
765 pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count);
766 if (pKeys->threads == NULL)
767 return -1;
768
769 /*
770 * Extract all entries.
771 */
772 for (i = 0; i < count; i++) {
773 next = findNextChar(data, dataEnd - data, '\n');
774 assert(next > 0);
775 data[next] = '\0';
776
777 tab = findNextChar(data, next, '\t');
778 data[tab] = '\0';
779
780 pKeys->threads[i].threadId = atoi(data);
781 pKeys->threads[i].threadName = data + tab +1;
782
783 data += next+1;
784 }
785
786 pKeys->numThreads = count;
787 return data - pKeys->fileData;
788 }
789
790 /*
791 * Parse the "*methods" section.
792 */
parseMethods(DataKeys * pKeys,long offset)793 long parseMethods(DataKeys* pKeys, long offset)
794 {
795 char* data;
796 char* dataEnd;
797 int i, next, 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, "methods");
805 if (next < 0)
806 return -1;
807
808 data += next;
809
810 /*
811 * Count the number of method entries (one per line).
812 */
813 count = countLinesToToken(data, dataEnd - data);
814 if (count <= 0) {
815 fprintf(stderr,
816 "ERROR: failed while reading methods (found %d)\n", count);
817 return -1;
818 }
819
820 /* Reserve an extra method at location 0 for the "toplevel" method,
821 * and another extra method for all other "unknown" methods.
822 */
823 count += 2;
824 pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count);
825 if (pKeys->methods == NULL)
826 return -1;
827 initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], 0, "(toplevel)",
828 NULL, NULL, NULL, NULL);
829 initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], 0, "(unknown)",
830 NULL, NULL, NULL, NULL);
831
832 /*
833 * Extract all entries, starting with index 2.
834 */
835 for (i = UNKNOWN_INDEX + 1; i < count; i++) {
836 int tab1, tab2, tab3, tab4, tab5;
837 unsigned int id;
838 char* endptr;
839
840 next = findNextChar(data, dataEnd - data, '\n');
841 assert(next > 0);
842 data[next] = '\0';
843
844 tab1 = findNextChar(data, next, '\t');
845 tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t');
846 tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t');
847 tab4 = findNextChar(data+(tab1+tab2+tab3+3),
848 next-(tab1+tab2+tab3+3), '\t');
849 tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4),
850 next-(tab1+tab2+tab3+tab4+4), '\t');
851 if (tab1 < 0) {
852 fprintf(stderr, "ERROR: missing field on method line: '%s'\n",
853 data);
854 return -1;
855 }
856 assert(data[tab1] == '\t');
857 data[tab1] = '\0';
858
859 id = strtoul(data, &endptr, 0);
860 if (*endptr != '\0') {
861 fprintf(stderr, "ERROR: bad method ID '%s'\n", data);
862 return -1;
863 }
864
865 // Allow files that specify just a function name, instead of requiring
866 // "class \t method \t signature"
867 if (tab2 > 0 && tab3 > 0) {
868 tab2 += tab1+1;
869 tab3 += tab2+1;
870 assert(data[tab2] == '\t');
871 assert(data[tab3] == '\t');
872 data[tab2] = data[tab3] = '\0';
873
874 // This is starting to get awkward. Allow filename and line #.
875 if (tab4 > 0 && tab5 > 0) {
876 tab4 += tab3+1;
877 tab5 += tab4+1;
878
879 assert(data[tab4] == '\t');
880 assert(data[tab5] == '\t');
881 data[tab4] = data[tab5] = '\0';
882
883 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
884 data + tab2 +1, data + tab3 +1, data + tab4 +1,
885 data + tab5 +1);
886 } else {
887 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
888 data + tab2 +1, data + tab3 +1, NULL, NULL);
889 }
890 } else {
891 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
892 NULL, NULL, NULL, NULL);
893 }
894
895 data += next+1;
896 }
897
898 pKeys->numMethods = count;
899 return data - pKeys->fileData;
900 }
901
902 /*
903 * Parse the "*end" section.
904 */
parseEnd(DataKeys * pKeys,long offset)905 long parseEnd(DataKeys* pKeys, long offset)
906 {
907 char* data;
908 char* dataEnd;
909 int next;
910
911 if (offset < 0)
912 return -1;
913
914 data = pKeys->fileData + offset;
915 dataEnd = pKeys->fileData + pKeys->fileLen;
916 next = checkToken(data, dataEnd - data, "end");
917 if (next < 0)
918 return -1;
919
920 data += next;
921
922 return data - pKeys->fileData;
923 }
924
925 /*
926 * Sort the thread list entries.
927 */
compareThreads(const void * thread1,const void * thread2)928 static int compareThreads(const void* thread1, const void* thread2)
929 {
930 return ((const ThreadEntry*) thread1)->threadId -
931 ((const ThreadEntry*) thread2)->threadId;
932 }
933
sortThreadList(DataKeys * pKeys)934 void sortThreadList(DataKeys* pKeys)
935 {
936 qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]),
937 compareThreads);
938 }
939
940 /*
941 * Sort the method list entries.
942 */
compareMethods(const void * meth1,const void * meth2)943 static int compareMethods(const void* meth1, const void* meth2)
944 {
945 unsigned int id1, id2;
946
947 id1 = ((const MethodEntry*) meth1)->methodId;
948 id2 = ((const MethodEntry*) meth2)->methodId;
949 if (id1 < id2)
950 return -1;
951 if (id1 > id2)
952 return 1;
953 return 0;
954 }
955
sortMethodList(DataKeys * pKeys)956 void sortMethodList(DataKeys* pKeys)
957 {
958 qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry),
959 compareMethods);
960 }
961
962 /*
963 * Parse the key section, and return a copy of the parsed contents.
964 */
parseKeys(FILE * fp,int verbose)965 DataKeys* parseKeys(FILE *fp, int verbose)
966 {
967 DataKeys* pKeys = NULL;
968 long offset;
969 int i;
970
971 pKeys = (DataKeys*) calloc(1, sizeof(DataKeys));
972 if (pKeys == NULL)
973 goto fail;
974
975 /*
976 * We load the entire file into memory. We do this, rather than memory-
977 * mapping it, because we want to change some whitespace to NULs.
978 */
979 if (fseek(fp, 0L, SEEK_END) != 0) {
980 perror("fseek");
981 goto fail;
982 }
983 pKeys->fileLen = ftell(fp);
984 if (pKeys->fileLen == 0) {
985 fprintf(stderr, "Key file is empty.\n");
986 goto fail;
987 }
988 rewind(fp);
989
990 pKeys->fileData = (char*) malloc(pKeys->fileLen);
991 if (pKeys->fileData == NULL) {
992 fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen);
993 goto fail;
994 }
995
996 if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen)
997 {
998 fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n",
999 pKeys->fileLen);
1000 goto fail;
1001 }
1002
1003 offset = 0;
1004
1005 offset = parseVersion(pKeys, offset, verbose);
1006 offset = parseThreads(pKeys, offset);
1007 offset = parseMethods(pKeys, offset);
1008 offset = parseEnd(pKeys, offset);
1009 if (offset < 0)
1010 goto fail;
1011
1012 /* Reduce our allocation now that we know where the end of the key section is. */
1013 pKeys->fileData = (char *)realloc(pKeys->fileData, offset);
1014 pKeys->fileLen = offset;
1015 /* Leave fp pointing to the beginning of the data section. */
1016 fseek(fp, offset, SEEK_SET);
1017
1018 sortThreadList(pKeys);
1019 sortMethodList(pKeys);
1020
1021 /*
1022 * Dump list of threads.
1023 */
1024 if (verbose) {
1025 printf("Threads (%d):\n", pKeys->numThreads);
1026 for (i = 0; i < pKeys->numThreads; i++) {
1027 printf("%2d %s\n",
1028 pKeys->threads[i].threadId, pKeys->threads[i].threadName);
1029 }
1030 }
1031
1032 #if 0
1033 /*
1034 * Dump list of methods.
1035 */
1036 if (verbose) {
1037 printf("Methods (%d):\n", pKeys->numMethods);
1038 for (i = 0; i < pKeys->numMethods; i++) {
1039 printf("0x%08x %s : %s : %s\n",
1040 pKeys->methods[i].methodId, pKeys->methods[i].className,
1041 pKeys->methods[i].methodName, pKeys->methods[i].signature);
1042 }
1043 }
1044 #endif
1045
1046 return pKeys;
1047
1048 fail:
1049 freeDataKeys(pKeys);
1050 return NULL;
1051 }
1052
1053
1054 /*
1055 * Read values from the binary data file.
1056 */
1057
1058 /* Make the return value "unsigned int" instead of "unsigned short" so that
1059 * we can detect EOF.
1060 */
read2LE(FILE * fp)1061 unsigned int read2LE(FILE* fp)
1062 {
1063 unsigned int val;
1064
1065 val = getc(fp);
1066 val |= getc(fp) << 8;
1067 return val;
1068 }
read4LE(FILE * fp)1069 unsigned int read4LE(FILE* fp)
1070 {
1071 unsigned int val;
1072
1073 val = getc(fp);
1074 val |= getc(fp) << 8;
1075 val |= getc(fp) << 16;
1076 val |= getc(fp) << 24;
1077 return val;
1078 }
read8LE(FILE * fp)1079 unsigned long long read8LE(FILE* fp)
1080 {
1081 unsigned long long val;
1082
1083 val = getc(fp);
1084 val |= (unsigned long long) getc(fp) << 8;
1085 val |= (unsigned long long) getc(fp) << 16;
1086 val |= (unsigned long long) getc(fp) << 24;
1087 val |= (unsigned long long) getc(fp) << 32;
1088 val |= (unsigned long long) getc(fp) << 40;
1089 val |= (unsigned long long) getc(fp) << 48;
1090 val |= (unsigned long long) getc(fp) << 56;
1091 return val;
1092 }
1093
1094 /*
1095 * Parse the header of the data section.
1096 *
1097 * Returns with the file positioned at the start of the record data.
1098 */
parseDataHeader(FILE * fp,DataHeader * pHeader)1099 int parseDataHeader(FILE *fp, DataHeader* pHeader)
1100 {
1101 pHeader->magic = read4LE(fp);
1102 pHeader->version = read2LE(fp);
1103 pHeader->offsetToData = read2LE(fp);
1104 pHeader->startWhen = read8LE(fp);
1105
1106 if (fseek(fp, pHeader->offsetToData - 16, SEEK_CUR) != 0) {
1107 return -1;
1108 }
1109
1110 return 0;
1111 }
1112
1113 /*
1114 * Look up a method by its method ID (using binary search).
1115 *
1116 * Returns NULL if no matching method was found.
1117 */
lookupMethod(DataKeys * pKeys,unsigned int methodId)1118 MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId)
1119 {
1120 int hi, lo, mid;
1121 unsigned int id;
1122
1123 lo = 0;
1124 hi = pKeys->numMethods - 1;
1125
1126 while (hi >= lo) {
1127 mid = (hi + lo) / 2;
1128
1129 id = pKeys->methods[mid].methodId;
1130 if (id == methodId) /* match */
1131 return &pKeys->methods[mid];
1132 else if (id < methodId) /* too low */
1133 lo = mid + 1;
1134 else /* too high */
1135 hi = mid - 1;
1136 }
1137
1138 return NULL;
1139 }
1140
1141 /*
1142 * Reads the next data record, and assigns the data values to threadId,
1143 * methodVal and elapsedTime. On end-of-file, the threadId, methodVal,
1144 * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise
1145 * returns 0.
1146 */
readDataRecord(FILE * dataFp,int * threadId,unsigned int * methodVal,uint64_t * elapsedTime)1147 int readDataRecord(FILE *dataFp, int *threadId, unsigned int *methodVal,
1148 uint64_t *elapsedTime)
1149 {
1150 int id;
1151
1152 /*
1153 * TODO:
1154 * This SHOULD NOT be keyed off of the global version number! Use
1155 * a name=value setting in the version area instead!
1156 */
1157 if (versionNumber == 1) {
1158 id = getc(dataFp);
1159 } else {
1160 id = read2LE(dataFp);
1161 }
1162 if (id == EOF)
1163 return 1;
1164 *threadId = id;
1165
1166 *methodVal = read4LE(dataFp);
1167 *elapsedTime = read4LE(dataFp);
1168 if (feof(dataFp)) {
1169 fprintf(stderr, "WARNING: hit EOF mid-record\n");
1170 return 1;
1171 }
1172 return 0;
1173 }
1174
1175 /*
1176 * Read the key file and use it to produce formatted output from the
1177 * data file.
1178 */
dumpTrace()1179 void dumpTrace()
1180 {
1181 static const char* actionStr[] = { "ent", "xit", "unr", "???" };
1182 MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0,
1183 {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 };
1184 char bogusBuf[80];
1185 char spaces[MAX_STACK_DEPTH+1];
1186 FILE* dataFp = NULL;
1187 DataHeader dataHeader;
1188 DataKeys* pKeys = NULL;
1189 int i;
1190 TraceData traceData;
1191
1192 //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
1193
1194 memset(spaces, '.', MAX_STACK_DEPTH);
1195 spaces[MAX_STACK_DEPTH] = '\0';
1196
1197 for (i = 0; i < MAX_THREADS; i++)
1198 traceData.depth[i] = 2; // adjust for return from start function
1199
1200 dataFp = fopen(gOptions.traceFileName, "r");
1201 if (dataFp == NULL)
1202 goto bail;
1203
1204 if ((pKeys = parseKeys(dataFp, 1)) == NULL)
1205 goto bail;
1206
1207 if (parseDataHeader(dataFp, &dataHeader) < 0)
1208 goto bail;
1209
1210 printf("Trace (threadID action usecs class.method signature):\n");
1211
1212 while (1) {
1213 MethodEntry* method;
1214 int threadId;
1215 unsigned int methodVal;
1216 uint64_t elapsedTime;
1217 int action, printDepth;
1218 unsigned int methodId, lastEnter = 0;
1219 int mismatch = 0;
1220 char depthNote;
1221
1222 /*
1223 * Extract values from file.
1224 */
1225 if (readDataRecord(dataFp, &threadId, &methodVal, &elapsedTime))
1226 break;
1227
1228 action = METHOD_ACTION(methodVal);
1229 methodId = METHOD_ID(methodVal);
1230
1231 /*
1232 * Generate a line of output.
1233 */
1234 if (action == METHOD_TRACE_ENTER) {
1235 traceData.depth[threadId]++;
1236 lastEnter = methodId;
1237 } else {
1238 /* quick test for mismatched adjacent enter/exit */
1239 if (lastEnter != 0 && lastEnter != methodId)
1240 mismatch = 1;
1241 }
1242
1243 printDepth = traceData.depth[threadId];
1244 depthNote = ' ';
1245 if (printDepth < 0) {
1246 printDepth = 0;
1247 depthNote = '-';
1248 } else if (printDepth > MAX_STACK_DEPTH) {
1249 printDepth = MAX_STACK_DEPTH;
1250 depthNote = '+';
1251 }
1252
1253 method = lookupMethod(pKeys, methodId);
1254 if (method == NULL) {
1255 method = &bogusMethod;
1256 sprintf(bogusBuf, "methodId: 0x%x", methodId);
1257 method->signature = bogusBuf;
1258 }
1259
1260 if (method->methodName) {
1261 printf("%2d %s%c %8lld%c%s%s.%s %s\n", threadId,
1262 actionStr[action], mismatch ? '!' : ' ',
1263 elapsedTime, depthNote,
1264 spaces + (MAX_STACK_DEPTH - printDepth),
1265 method->className, method->methodName, method->signature);
1266 } else {
1267 printf("%2d %s%c %8lld%c%s%s\n", threadId,
1268 actionStr[action], mismatch ? '!' : ' ',
1269 elapsedTime, depthNote,
1270 spaces + (MAX_STACK_DEPTH - printDepth),
1271 method->className);
1272 }
1273
1274 if (action != METHOD_TRACE_ENTER) {
1275 traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */
1276 lastEnter = 0;
1277 }
1278
1279 mismatch = 0;
1280 }
1281
1282 bail:
1283 if (dataFp != NULL)
1284 fclose(dataFp);
1285 if (pKeys != NULL)
1286 freeDataKeys(pKeys);
1287 }
1288
1289 /* This routine adds the given time to the parent and child methods.
1290 * This is called when the child routine exits, after the child has
1291 * been popped from the stack. The elapsedTime parameter is the
1292 * duration of the child routine, including time spent in called routines.
1293 */
addInclusiveTime(MethodEntry * parent,MethodEntry * child,uint64_t elapsedTime)1294 void addInclusiveTime(MethodEntry *parent, MethodEntry *child,
1295 uint64_t elapsedTime)
1296 {
1297 TimedMethod *pTimed;
1298
1299 #if 0
1300 bool verbose = false;
1301 if (strcmp(child->className, debugClassName) == 0)
1302 verbose = true;
1303 #endif
1304
1305 int childIsRecursive = (child->recursiveEntries > 0);
1306 int parentIsRecursive = (parent->recursiveEntries > 1);
1307
1308 if (child->recursiveEntries == 0) {
1309 child->elapsedInclusive += elapsedTime;
1310 } else if (child->recursiveEntries == 1) {
1311 child->recursiveInclusive += elapsedTime;
1312 }
1313 child->numCalls[childIsRecursive] += 1;
1314
1315 #if 0
1316 if (verbose) {
1317 fprintf(stderr,
1318 "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n",
1319 child->className, child->recursiveEntries,
1320 elapsedTime, child->elapsedInclusive,
1321 child->recursiveInclusive);
1322 }
1323 #endif
1324
1325 /* Find the child method in the parent */
1326 TimedMethod *children = parent->children[parentIsRecursive];
1327 for (pTimed = children; pTimed; pTimed = pTimed->next) {
1328 if (pTimed->method == child) {
1329 pTimed->elapsedInclusive += elapsedTime;
1330 pTimed->numCalls += 1;
1331 break;
1332 }
1333 }
1334 if (pTimed == NULL) {
1335 /* Allocate a new TimedMethod */
1336 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
1337 pTimed->elapsedInclusive = elapsedTime;
1338 pTimed->numCalls = 1;
1339 pTimed->method = child;
1340
1341 /* Add it to the front of the list */
1342 pTimed->next = children;
1343 parent->children[parentIsRecursive] = pTimed;
1344 }
1345
1346 /* Find the parent method in the child */
1347 TimedMethod *parents = child->parents[childIsRecursive];
1348 for (pTimed = parents; pTimed; pTimed = pTimed->next) {
1349 if (pTimed->method == parent) {
1350 pTimed->elapsedInclusive += elapsedTime;
1351 pTimed->numCalls += 1;
1352 break;
1353 }
1354 }
1355 if (pTimed == NULL) {
1356 /* Allocate a new TimedMethod */
1357 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
1358 pTimed->elapsedInclusive = elapsedTime;
1359 pTimed->numCalls = 1;
1360 pTimed->method = parent;
1361
1362 /* Add it to the front of the list */
1363 pTimed->next = parents;
1364 child->parents[childIsRecursive] = pTimed;
1365 }
1366
1367 #if 0
1368 if (verbose) {
1369 fprintf(stderr,
1370 " %s %d eI: %lld\n",
1371 parent->className, parent->recursiveEntries,
1372 pTimed->elapsedInclusive);
1373 }
1374 #endif
1375 }
1376
1377 /* Sorts a linked list and returns a newly allocated array containing
1378 * the sorted entries.
1379 */
sortTimedMethodList(TimedMethod * list,int * num)1380 TimedMethod *sortTimedMethodList(TimedMethod *list, int *num)
1381 {
1382 int ii;
1383 TimedMethod *pTimed, *sorted;
1384
1385 /* Count the elements */
1386 int num_entries = 0;
1387 for (pTimed = list; pTimed; pTimed = pTimed->next)
1388 num_entries += 1;
1389 *num = num_entries;
1390 if (num_entries == 0)
1391 return NULL;
1392
1393 /* Copy all the list elements to a new array and sort them */
1394 sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries);
1395 for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii)
1396 memcpy(&sorted[ii], pTimed, sizeof(TimedMethod));
1397 qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod);
1398
1399 /* Fix up the "next" pointers so that they work. */
1400 for (ii = 0; ii < num_entries - 1; ++ii)
1401 sorted[ii].next = &sorted[ii + 1];
1402 sorted[num_entries - 1].next = NULL;
1403
1404 return sorted;
1405 }
1406
1407 /* Define flag values for printInclusiveMethod() */
1408 static const int kIsRecursive = 1;
1409
1410 /* This prints the inclusive stats for all the parents or children of a
1411 * method, depending on the list that is passed in.
1412 */
printInclusiveMethod(MethodEntry * method,TimedMethod * list,int numCalls,int flags)1413 void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls,
1414 int flags)
1415 {
1416 int num;
1417 TimedMethod *pTimed;
1418 char buf[80];
1419 char *anchor_close;
1420 char *spaces = " "; /* 6 spaces */
1421 int num_spaces = strlen(spaces);
1422 char *space_ptr = &spaces[num_spaces];
1423 char *className, *methodName, *signature;
1424 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1425 char signatureBuf[HTML_BUFSIZE];
1426
1427 anchor_close = "";
1428 if (gOptions.outputHtml)
1429 anchor_close = "</a>";
1430
1431 TimedMethod *sorted = sortTimedMethodList(list, &num);
1432 double methodTotal = method->elapsedInclusive;
1433 for (pTimed = sorted; pTimed; pTimed = pTimed->next) {
1434 MethodEntry *relative = pTimed->method;
1435 className = (char*)(relative->className);
1436 methodName = (char*)(relative->methodName);
1437 signature = (char*)(relative->signature);
1438 double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
1439 sprintf(buf, "[%d]", relative->index);
1440 if (gOptions.outputHtml) {
1441 int len = strlen(buf);
1442 if (len > num_spaces)
1443 len = num_spaces;
1444 sprintf(buf, "<a href=\"#m%d\">[%d]",
1445 relative->index, relative->index);
1446 space_ptr = &spaces[len];
1447 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1448 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1449 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1450 }
1451 int nCalls = numCalls;
1452 if (nCalls == 0)
1453 nCalls = relative->numCalls[0] + relative->numCalls[1];
1454 if (relative->methodName) {
1455 if (flags & kIsRecursive) {
1456 // Don't display percentages for recursive functions
1457 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
1458 "", "", "",
1459 space_ptr, buf, anchor_close,
1460 pTimed->numCalls, nCalls,
1461 pTimed->elapsedInclusive,
1462 className, methodName, signature);
1463 } else {
1464 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
1465 "", "", per,
1466 space_ptr, buf, anchor_close,
1467 pTimed->numCalls, nCalls,
1468 pTimed->elapsedInclusive,
1469 className, methodName, signature);
1470 }
1471 } else {
1472 if (flags & kIsRecursive) {
1473 // Don't display percentages for recursive functions
1474 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s\n",
1475 "", "", "",
1476 space_ptr, buf, anchor_close,
1477 pTimed->numCalls, nCalls,
1478 pTimed->elapsedInclusive,
1479 className);
1480 } else {
1481 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s\n",
1482 "", "", per,
1483 space_ptr, buf, anchor_close,
1484 pTimed->numCalls, nCalls,
1485 pTimed->elapsedInclusive,
1486 className);
1487 }
1488 }
1489 }
1490 }
1491
countRecursiveEntries(CallStack * pStack,int top,MethodEntry * method)1492 void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method)
1493 {
1494 int ii;
1495
1496 method->recursiveEntries = 0;
1497 for (ii = 0; ii < top; ++ii) {
1498 if (pStack->calls[ii].method == method)
1499 method->recursiveEntries += 1;
1500 }
1501 }
1502
stackDump(CallStack * pStack,int top)1503 void stackDump(CallStack *pStack, int top)
1504 {
1505 int ii;
1506
1507 for (ii = 0; ii < top; ++ii) {
1508 MethodEntry *method = pStack->calls[ii].method;
1509 uint64_t entryTime = pStack->calls[ii].entryTime;
1510 if (method->methodName) {
1511 fprintf(stderr, " %2d: %8llu %s.%s %s\n", ii, entryTime,
1512 method->className, method->methodName, method->signature);
1513 } else {
1514 fprintf(stderr, " %2d: %8llu %s\n", ii, entryTime, method->className);
1515 }
1516 }
1517 }
1518
outputTableOfContents()1519 void outputTableOfContents()
1520 {
1521 printf("<a name=\"contents\"></a>\n");
1522 printf("<h2>Table of Contents</h2>\n");
1523 printf("<ul>\n");
1524 printf(" <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
1525 printf(" <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
1526 printf(" <li><a href=\"#thread\">Thread profile</a></li>\n");
1527 printf(" <li><a href=\"#class\">Class/method profile</a></li>\n");
1528 printf(" <li><a href=\"#method\">Method/class profile</a></li>\n");
1529 printf("</ul>\n\n");
1530 }
1531
outputNavigationBar()1532 void outputNavigationBar()
1533 {
1534 printf("<a href=\"#contents\">[Top]</a>\n");
1535 printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
1536 printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
1537 printf("<a href=\"#thread\">[Thread]</a>\n");
1538 printf("<a href=\"#class\">[Class]</a>\n");
1539 printf("<a href=\"#method\">[Method]</a>\n");
1540 printf("<br><br>\n");
1541 }
1542
printExclusiveProfile(MethodEntry ** pMethods,int numMethods,uint64_t sumThreadTime)1543 void printExclusiveProfile(MethodEntry **pMethods, int numMethods,
1544 uint64_t sumThreadTime)
1545 {
1546 int ii;
1547 MethodEntry* method;
1548 double total, sum, per, sum_per;
1549 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1550 char signatureBuf[HTML_BUFSIZE];
1551 char anchor_buf[80];
1552 char *anchor_close = "";
1553
1554 total = sumThreadTime;
1555 anchor_buf[0] = 0;
1556 if (gOptions.outputHtml) {
1557 anchor_close = "</a>";
1558 printf("<a name=\"exclusive\"></a>\n");
1559 printf("<hr>\n");
1560 outputNavigationBar();
1561 } else {
1562 printf("\n%s\n", profileSeparator);
1563 }
1564
1565 /* First, sort the methods into decreasing order of inclusive
1566 * elapsed time so that we can assign the method indices.
1567 */
1568 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
1569
1570 for (ii = 0; ii < numMethods; ++ii)
1571 pMethods[ii]->index = ii;
1572
1573 /* Sort the methods into decreasing order of exclusive elapsed time.
1574 */
1575 qsort(pMethods, numMethods, sizeof(MethodEntry*),
1576 compareElapsedExclusive);
1577
1578 printf("Total cycles: %llu\n\n", sumThreadTime);
1579 if (gOptions.outputHtml) {
1580 printf("<br><br>\n");
1581 }
1582 printf("Exclusive elapsed times for each method, not including time spent in\n");
1583 printf("children, sorted by exclusive time.\n\n");
1584 if (gOptions.outputHtml) {
1585 printf("<br><br>\n<pre>\n");
1586 }
1587
1588 printf(" Usecs self %% sum %% Method\n");
1589 sum = 0;
1590
1591 for (ii = 0; ii < numMethods; ++ii) {
1592 char *className, *methodName, *signature;
1593
1594 method = pMethods[ii];
1595 /* Don't show methods with zero cycles */
1596 if (method->elapsedExclusive == 0)
1597 break;
1598 className = (char*)(method->className);
1599 methodName = (char*)(method->methodName);
1600 signature = (char*)(method->signature);
1601 sum += method->elapsedExclusive;
1602 per = 100.0 * method->elapsedExclusive / total;
1603 sum_per = 100.0 * sum / total;
1604 if (gOptions.outputHtml) {
1605 sprintf(anchor_buf, "<a href=\"#m%d\">", method->index);
1606 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1607 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1608 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1609 }
1610 if (method->methodName) {
1611 printf("%9llu %6.2f %6.2f %s[%d]%s %s.%s %s\n",
1612 method->elapsedExclusive, per, sum_per,
1613 anchor_buf, method->index, anchor_close,
1614 className, methodName, signature);
1615 } else {
1616 printf("%9llu %6.2f %6.2f %s[%d]%s %s\n",
1617 method->elapsedExclusive, per, sum_per,
1618 anchor_buf, method->index, anchor_close,
1619 className);
1620 }
1621 }
1622 if (gOptions.outputHtml) {
1623 printf("</pre>\n");
1624 }
1625 }
1626
1627 /* check to make sure that the child method meets the threshold of the parent */
checkThreshold(MethodEntry * parent,MethodEntry * child)1628 int checkThreshold(MethodEntry* parent, MethodEntry* child)
1629 {
1630 double parentTime = parent->elapsedInclusive;
1631 double childTime = child->elapsedInclusive;
1632 int64_t percentage = (childTime / parentTime) * 100.0;
1633 return (percentage < gOptions.threshold) ? 0 : 1;
1634 }
1635
createLabels(FILE * file,MethodEntry * method)1636 void createLabels(FILE* file, MethodEntry* method)
1637 {
1638 fprintf(file, "node%d[label = \"[%d] %s.%s (%llu, %llu, %d)\"]\n",
1639 method->index, method->index, method->className, method->methodName,
1640 method->elapsedInclusive / 1000,
1641 method->elapsedExclusive / 1000,
1642 method->numCalls[0]);
1643
1644 method->graphState = GRAPH_LABEL_VISITED;
1645
1646 TimedMethod* child;
1647 for (child = method->children[0] ; child ; child = child->next) {
1648 MethodEntry* childMethod = child->method;
1649
1650 if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) {
1651 createLabels(file, child->method);
1652 }
1653 }
1654 }
1655
createLinks(FILE * file,MethodEntry * method)1656 void createLinks(FILE* file, MethodEntry* method)
1657 {
1658 method->graphState |= GRAPH_NODE_VISITED;
1659
1660 TimedMethod* child;
1661 for (child = method->children[0] ; child ; child = child->next) {
1662 MethodEntry* childMethod = child->method;
1663 if (checkThreshold(method, child->method)) {
1664 fprintf(file, "node%d -> node%d\n", method->index, child->method->index);
1665 // only visit children that haven't been visited before
1666 if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) {
1667 createLinks(file, child->method);
1668 }
1669 }
1670 }
1671 }
1672
createInclusiveProfileGraphNew(DataKeys * dataKeys)1673 void createInclusiveProfileGraphNew(DataKeys* dataKeys)
1674 {
1675 // create a temporary file in /tmp
1676 char path[FILENAME_MAX];
1677 if (gOptions.keepDotFile) {
1678 snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName);
1679 } else {
1680 snprintf(path, FILENAME_MAX, "/tmp/dot-%d-%d.dot", (int)time(NULL), rand());
1681 }
1682
1683 FILE* file = fopen(path, "w+");
1684
1685 fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n");
1686
1687 createLabels(file, dataKeys->methods);
1688 createLinks(file, dataKeys->methods);
1689
1690 fprintf(file, "}");
1691 fclose(file);
1692
1693 // now that we have the dot file generate the image
1694 char command[1024];
1695 snprintf(command, 1024, "dot -Tpng -o '%s' '%s'", gOptions.graphFileName, path);
1696
1697 system(command);
1698
1699 if (! gOptions.keepDotFile) {
1700 remove(path);
1701 }
1702 }
1703
printInclusiveProfile(MethodEntry ** pMethods,int numMethods,uint64_t sumThreadTime)1704 void printInclusiveProfile(MethodEntry **pMethods, int numMethods,
1705 uint64_t sumThreadTime)
1706 {
1707 int ii;
1708 MethodEntry* method;
1709 double total, sum, per, sum_per;
1710 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1711 char signatureBuf[HTML_BUFSIZE];
1712 char anchor_buf[80];
1713 char *anchor_close = "";
1714
1715 total = sumThreadTime;
1716 anchor_buf[0] = 0;
1717 if (gOptions.outputHtml) {
1718 anchor_close = "</a>";
1719 printf("<a name=\"inclusive\"></a>\n");
1720 printf("<hr>\n");
1721 outputNavigationBar();
1722 } else {
1723 printf("\n%s\n", profileSeparator);
1724 }
1725
1726 /* Sort the methods into decreasing order of inclusive elapsed time. */
1727 qsort(pMethods, numMethods, sizeof(MethodEntry*),
1728 compareElapsedInclusive);
1729
1730 printf("\nInclusive elapsed times for each method and its parents and children,\n");
1731 printf("sorted by inclusive time.\n\n");
1732
1733 if (gOptions.outputHtml) {
1734 printf("<br><br>\n<pre>\n");
1735 }
1736
1737 printf("index %%/total %%/self index calls usecs name\n");
1738 for (ii = 0; ii < numMethods; ++ii) {
1739 int num;
1740 TimedMethod *pTimed;
1741 double excl_per;
1742 char buf[40];
1743 char *className, *methodName, *signature;
1744
1745 method = pMethods[ii];
1746 /* Don't show methods with zero cycles */
1747 if (method->elapsedInclusive == 0)
1748 break;
1749
1750 className = (char*)(method->className);
1751 methodName = (char*)(method->methodName);
1752 signature = (char*)(method->signature);
1753
1754 if (gOptions.outputHtml) {
1755 printf("<a name=\"m%d\"></a>", method->index);
1756 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1757 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1758 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1759 }
1760 printf("----------------------------------------------------\n");
1761
1762 /* Sort and print the parents */
1763 int numCalls = method->numCalls[0] + method->numCalls[1];
1764 printInclusiveMethod(method, method->parents[0], numCalls, 0);
1765 if (method->parents[1]) {
1766 printf(" +++++++++++++++++++++++++\n");
1767 printInclusiveMethod(method, method->parents[1], numCalls,
1768 kIsRecursive);
1769 }
1770
1771 per = 100.0 * method->elapsedInclusive / total;
1772 sprintf(buf, "[%d]", ii);
1773 if (method->methodName) {
1774 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s.%s %s\n",
1775 buf,
1776 per, "", "", method->numCalls[0], method->numCalls[1],
1777 method->elapsedInclusive,
1778 className, methodName, signature);
1779 } else {
1780 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s\n",
1781 buf,
1782 per, "", "", method->numCalls[0], method->numCalls[1],
1783 method->elapsedInclusive,
1784 className);
1785 }
1786 excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
1787 printf("%6s %5s %5.1f%% %6s %6s %6s %9llu\n",
1788 "", "", excl_per, "excl", "", "", method->topExclusive);
1789
1790 /* Sort and print the children */
1791 printInclusiveMethod(method, method->children[0], 0, 0);
1792 if (method->children[1]) {
1793 printf(" +++++++++++++++++++++++++\n");
1794 printInclusiveMethod(method, method->children[1], 0,
1795 kIsRecursive);
1796 }
1797 }
1798 if (gOptions.outputHtml) {
1799 printf("</pre>\n");
1800 }
1801 }
1802
printThreadProfile(ThreadEntry * pThreads,int numThreads,uint64_t sumThreadTime)1803 void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime)
1804 {
1805 int ii;
1806 ThreadEntry thread;
1807 double total, per, sum_per;
1808 uint64_t sum;
1809 char threadBuf[HTML_BUFSIZE];
1810 char anchor_buf[80];
1811 char *anchor_close = "";
1812
1813 total = sumThreadTime;
1814 anchor_buf[0] = 0;
1815 if (gOptions.outputHtml) {
1816 anchor_close = "</a>";
1817 printf("<a name=\"thread\"></a>\n");
1818 printf("<hr>\n");
1819 outputNavigationBar();
1820 } else {
1821 printf("\n%s\n", profileSeparator);
1822 }
1823
1824 /* Sort the threads into decreasing order of elapsed time. */
1825 qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed);
1826
1827 printf("\nElapsed times for each thread, sorted by elapsed time.\n\n");
1828
1829 if (gOptions.outputHtml) {
1830 printf("<br><br>\n<pre>\n");
1831 }
1832
1833 printf(" Usecs self %% sum %% tid ThreadName\n");
1834 sum = 0;
1835
1836 for (ii = 0; ii < numThreads; ++ii) {
1837 int threadId;
1838 char *threadName;
1839 uint64_t time;
1840
1841 thread = pThreads[ii];
1842
1843 threadId = thread.threadId;
1844 threadName = (char*)(thread.threadName);
1845 time = thread.elapsedTime;
1846
1847 sum += time;
1848 per = 100.0 * time / total;
1849 sum_per = 100.0 * sum / total;
1850
1851 if (gOptions.outputHtml) {
1852 threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
1853 }
1854 printf("%9llu %6.2f %6.2f %3d %s\n", time, per, sum_per, threadId, threadName);
1855 }
1856
1857 if (gOptions.outputHtml)
1858 printf("</pre>\n");
1859
1860 }
1861
createClassList(TraceData * traceData,MethodEntry ** pMethods,int numMethods)1862 void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
1863 {
1864 int ii;
1865
1866 /* Sort the methods into alphabetical order to find the unique class
1867 * names.
1868 */
1869 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames);
1870
1871 /* Count the number of unique class names. */
1872 const char *currentClassName = "";
1873 const char *firstClassName = NULL;
1874 traceData->numClasses = 0;
1875 for (ii = 0; ii < numMethods; ++ii) {
1876 if (pMethods[ii]->methodName == NULL) {
1877 continue;
1878 }
1879 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1880 // Remember the first one
1881 if (firstClassName == NULL) {
1882 firstClassName = pMethods[ii]->className;
1883 }
1884 traceData->numClasses += 1;
1885 currentClassName = pMethods[ii]->className;
1886 }
1887 }
1888
1889 if (traceData->numClasses == 0) {
1890 traceData->classes = NULL;
1891 return;
1892 }
1893
1894 /* Allocate space for all of the unique class names */
1895 traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses);
1896
1897 /* Initialize the classes array */
1898 memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
1899 ClassEntry *pClass = traceData->classes;
1900 pClass->className = currentClassName = firstClassName;
1901 int prevNumMethods = 0;
1902 for (ii = 0; ii < numMethods; ++ii) {
1903 if (pMethods[ii]->methodName == NULL) {
1904 continue;
1905 }
1906 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1907 pClass->numMethods = prevNumMethods;
1908 (++pClass)->className = currentClassName = pMethods[ii]->className;
1909 prevNumMethods = 0;
1910 }
1911 prevNumMethods += 1;
1912 }
1913 pClass->numMethods = prevNumMethods;
1914
1915 /* Create the array of MethodEntry pointers for each class */
1916 pClass = NULL;
1917 currentClassName = "";
1918 int nextMethod = 0;
1919 for (ii = 0; ii < numMethods; ++ii) {
1920 if (pMethods[ii]->methodName == NULL) {
1921 continue;
1922 }
1923 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1924 currentClassName = pMethods[ii]->className;
1925 if (pClass == NULL)
1926 pClass = traceData->classes;
1927 else
1928 pClass++;
1929 /* Allocate space for the methods array */
1930 int nbytes = sizeof(MethodEntry*) * pClass->numMethods;
1931 pClass->methods = (MethodEntry**) malloc(nbytes);
1932 nextMethod = 0;
1933 }
1934 pClass->methods[nextMethod++] = pMethods[ii];
1935 }
1936 }
1937
1938 /* Prints a number of html non-breaking spaces according so that the length
1939 * of the string "buf" is at least "width" characters wide. If width is
1940 * negative, then trailing spaces are added instead of leading spaces.
1941 */
printHtmlField(char * buf,int width)1942 void printHtmlField(char *buf, int width)
1943 {
1944 int ii;
1945
1946 int leadingSpaces = 1;
1947 if (width < 0) {
1948 width = -width;
1949 leadingSpaces = 0;
1950 }
1951 int len = strlen(buf);
1952 int numSpaces = width - len;
1953 if (numSpaces <= 0) {
1954 printf("%s", buf);
1955 return;
1956 }
1957 if (leadingSpaces == 0)
1958 printf("%s", buf);
1959 for (ii = 0; ii < numSpaces; ++ii)
1960 printf(" ");
1961 if (leadingSpaces == 1)
1962 printf("%s", buf);
1963 }
1964
printClassProfiles(TraceData * traceData,uint64_t sumThreadTime)1965 void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime)
1966 {
1967 int ii, jj;
1968 MethodEntry* method;
1969 double total, sum, per, sum_per;
1970 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1971 char signatureBuf[HTML_BUFSIZE];
1972
1973 total = sumThreadTime;
1974 if (gOptions.outputHtml) {
1975 printf("<a name=\"class\"></a>\n");
1976 printf("<hr>\n");
1977 outputNavigationBar();
1978 } else {
1979 printf("\n%s\n", profileSeparator);
1980 }
1981
1982 if (traceData->numClasses == 0) {
1983 printf("\nNo classes.\n");
1984 if (gOptions.outputHtml) {
1985 printf("<br><br>\n");
1986 }
1987 return;
1988 }
1989
1990 printf("\nExclusive elapsed time for each class, summed over all the methods\n");
1991 printf("in the class.\n\n");
1992 if (gOptions.outputHtml) {
1993 printf("<br><br>\n");
1994 }
1995
1996 /* For each class, sum the exclusive times in all of the methods
1997 * in that class. Also sum the number of method calls. Also
1998 * sort the methods so the most expensive appear at the top.
1999 */
2000 ClassEntry *pClass = traceData->classes;
2001 for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
2002 //printf("%s %d methods\n", pClass->className, pClass->numMethods);
2003 int numMethods = pClass->numMethods;
2004 for (jj = 0; jj < numMethods; ++jj) {
2005 method = pClass->methods[jj];
2006 pClass->elapsedExclusive += method->elapsedExclusive;
2007 pClass->numCalls[0] += method->numCalls[0];
2008 pClass->numCalls[1] += method->numCalls[1];
2009 }
2010
2011 /* Sort the methods into decreasing order of exclusive time */
2012 qsort(pClass->methods, numMethods, sizeof(MethodEntry*),
2013 compareElapsedExclusive);
2014 }
2015
2016 /* Allocate an array of pointers to the classes for more efficient
2017 * sorting.
2018 */
2019 ClassEntry **pClasses;
2020 pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses);
2021 for (ii = 0; ii < traceData->numClasses; ++ii)
2022 pClasses[ii] = &traceData->classes[ii];
2023
2024 /* Sort the classes into decreasing order of exclusive time */
2025 qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive);
2026
2027 if (gOptions.outputHtml) {
2028 printf("<div class=\"header\"><span class=\"parent\"> </span> ");
2029 printf("Cycles %%/total Cumul.%% Calls+Recur Class</div>\n");
2030 } else {
2031 printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n");
2032 }
2033
2034 sum = 0;
2035 for (ii = 0; ii < traceData->numClasses; ++ii) {
2036 char *className, *methodName, *signature;
2037
2038 /* Skip classes with zero cycles */
2039 pClass = pClasses[ii];
2040 if (pClass->elapsedExclusive == 0)
2041 break;
2042
2043 per = 100.0 * pClass->elapsedExclusive / total;
2044 sum += pClass->elapsedExclusive;
2045 sum_per = 100.0 * sum / total;
2046 className = (char*)(pClass->className);
2047 if (gOptions.outputHtml) {
2048 char buf[80];
2049
2050 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
2051 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);
2052 sprintf(buf, "%llu", pClass->elapsedExclusive);
2053 printHtmlField(buf, 9);
2054 printf(" ");
2055 sprintf(buf, "%.1f", per);
2056 printHtmlField(buf, 7);
2057 printf(" ");
2058 sprintf(buf, "%.1f", sum_per);
2059 printHtmlField(buf, 7);
2060 printf(" ");
2061 sprintf(buf, "%d", pClass->numCalls[0]);
2062 printHtmlField(buf, 6);
2063 printf("+");
2064 sprintf(buf, "%d", pClass->numCalls[1]);
2065 printHtmlField(buf, -6);
2066 printf(" ");
2067 printf("%s", className);
2068 printf("</div>\n");
2069 printf("<div class=\"parent\" id=\"d%d\">\n", ii);
2070 } else {
2071 printf("---------------------------------------------\n");
2072 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
2073 pClass->elapsedExclusive, per, sum_per,
2074 pClass->numCalls[0], pClass->numCalls[1],
2075 className);
2076 }
2077
2078 int numMethods = pClass->numMethods;
2079 double classExclusive = pClass->elapsedExclusive;
2080 double sumMethods = 0;
2081 for (jj = 0; jj < numMethods; ++jj) {
2082 method = pClass->methods[jj];
2083 methodName = (char*)(method->methodName);
2084 signature = (char*)(method->signature);
2085 per = 100.0 * method->elapsedExclusive / classExclusive;
2086 sumMethods += method->elapsedExclusive;
2087 sum_per = 100.0 * sumMethods / classExclusive;
2088 if (gOptions.outputHtml) {
2089 char buf[80];
2090
2091 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
2092 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
2093 printf("<div class=\"leaf\"><span class=\"leaf\"> </span>");
2094 sprintf(buf, "%llu", method->elapsedExclusive);
2095 printHtmlField(buf, 9);
2096 printf(" ");
2097 sprintf(buf, "%llu", method->elapsedInclusive);
2098 printHtmlField(buf, 9);
2099 printf(" ");
2100 sprintf(buf, "%.1f", per);
2101 printHtmlField(buf, 7);
2102 printf(" ");
2103 sprintf(buf, "%.1f", sum_per);
2104 printHtmlField(buf, 7);
2105 printf(" ");
2106 sprintf(buf, "%d", method->numCalls[0]);
2107 printHtmlField(buf, 6);
2108 printf("+");
2109 sprintf(buf, "%d", method->numCalls[1]);
2110 printHtmlField(buf, -6);
2111 printf(" ");
2112 printf("<a href=\"#m%d\">[%d]</a> %s %s",
2113 method->index, method->index, methodName, signature);
2114 printf("</div>\n");
2115 } else {
2116 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s %s\n",
2117 method->elapsedExclusive,
2118 method->elapsedInclusive,
2119 per, sum_per,
2120 method->numCalls[0], method->numCalls[1],
2121 method->index, methodName, signature);
2122 }
2123 }
2124 if (gOptions.outputHtml) {
2125 printf("</div>\n");
2126 }
2127 }
2128 }
2129
createUniqueMethodList(TraceData * traceData,MethodEntry ** pMethods,int numMethods)2130 void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
2131 {
2132 int ii;
2133
2134 /* Sort the methods into alphabetical order of method names
2135 * to find the unique method names.
2136 */
2137 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames);
2138
2139 /* Count the number of unique method names, ignoring class and
2140 * signature.
2141 */
2142 const char *currentMethodName = "";
2143 traceData->numUniqueMethods = 0;
2144 for (ii = 0; ii < numMethods; ++ii) {
2145 if (pMethods[ii]->methodName == NULL)
2146 continue;
2147 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2148 traceData->numUniqueMethods += 1;
2149 currentMethodName = pMethods[ii]->methodName;
2150 }
2151 }
2152 if (traceData->numUniqueMethods == 0)
2153 return;
2154
2155 /* Allocate space for pointers to all of the unique methods */
2156 int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods;
2157 traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes);
2158
2159 /* Initialize the uniqueMethods array */
2160 memset(traceData->uniqueMethods, 0, nbytes);
2161 UniqueMethodEntry *pUnique = traceData->uniqueMethods;
2162 currentMethodName = NULL;
2163 int prevNumMethods = 0;
2164 for (ii = 0; ii < numMethods; ++ii) {
2165 if (pMethods[ii]->methodName == NULL)
2166 continue;
2167 if (currentMethodName == NULL)
2168 currentMethodName = pMethods[ii]->methodName;
2169 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2170 currentMethodName = pMethods[ii]->methodName;
2171 pUnique->numMethods = prevNumMethods;
2172 pUnique++;
2173 prevNumMethods = 0;
2174 }
2175 prevNumMethods += 1;
2176 }
2177 pUnique->numMethods = prevNumMethods;
2178
2179 /* Create the array of MethodEntry pointers for each unique method */
2180 pUnique = NULL;
2181 currentMethodName = "";
2182 int nextMethod = 0;
2183 for (ii = 0; ii < numMethods; ++ii) {
2184 if (pMethods[ii]->methodName == NULL)
2185 continue;
2186 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2187 currentMethodName = pMethods[ii]->methodName;
2188 if (pUnique == NULL)
2189 pUnique = traceData->uniqueMethods;
2190 else
2191 pUnique++;
2192 /* Allocate space for the methods array */
2193 int nbytes = sizeof(MethodEntry*) * pUnique->numMethods;
2194 pUnique->methods = (MethodEntry**) malloc(nbytes);
2195 nextMethod = 0;
2196 }
2197 pUnique->methods[nextMethod++] = pMethods[ii];
2198 }
2199 }
2200
printMethodProfiles(TraceData * traceData,uint64_t sumThreadTime)2201 void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime)
2202 {
2203 int ii, jj;
2204 MethodEntry* method;
2205 double total, sum, per, sum_per;
2206 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
2207 char signatureBuf[HTML_BUFSIZE];
2208
2209 if (traceData->numUniqueMethods == 0)
2210 return;
2211
2212 total = sumThreadTime;
2213 if (gOptions.outputHtml) {
2214 printf("<a name=\"method\"></a>\n");
2215 printf("<hr>\n");
2216 outputNavigationBar();
2217 } else {
2218 printf("\n%s\n", profileSeparator);
2219 }
2220
2221 printf("\nExclusive elapsed time for each method, summed over all the classes\n");
2222 printf("that contain a method with the same name.\n\n");
2223 if (gOptions.outputHtml) {
2224 printf("<br><br>\n");
2225 }
2226
2227 /* For each unique method, sum the exclusive times in all of the methods
2228 * with the same name. Also sum the number of method calls. Also
2229 * sort the methods so the most expensive appear at the top.
2230 */
2231 UniqueMethodEntry *pUnique = traceData->uniqueMethods;
2232 for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
2233 int numMethods = pUnique->numMethods;
2234 for (jj = 0; jj < numMethods; ++jj) {
2235 method = pUnique->methods[jj];
2236 pUnique->elapsedExclusive += method->elapsedExclusive;
2237 pUnique->numCalls[0] += method->numCalls[0];
2238 pUnique->numCalls[1] += method->numCalls[1];
2239 }
2240
2241 /* Sort the methods into decreasing order of exclusive time */
2242 qsort(pUnique->methods, numMethods, sizeof(MethodEntry*),
2243 compareElapsedExclusive);
2244 }
2245
2246 /* Allocate an array of pointers to the methods for more efficient
2247 * sorting.
2248 */
2249 UniqueMethodEntry **pUniqueMethods;
2250 int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods;
2251 pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes);
2252 for (ii = 0; ii < traceData->numUniqueMethods; ++ii)
2253 pUniqueMethods[ii] = &traceData->uniqueMethods[ii];
2254
2255 /* Sort the methods into decreasing order of exclusive time */
2256 qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*),
2257 compareUniqueExclusive);
2258
2259 if (gOptions.outputHtml) {
2260 printf("<div class=\"header\"><span class=\"parent\"> </span> ");
2261 printf("Cycles %%/total Cumul.%% Calls+Recur Method</div>\n");
2262 } else {
2263 printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n");
2264 }
2265
2266 sum = 0;
2267 for (ii = 0; ii < traceData->numUniqueMethods; ++ii) {
2268 char *className, *methodName, *signature;
2269
2270 /* Skip methods with zero cycles */
2271 pUnique = pUniqueMethods[ii];
2272 if (pUnique->elapsedExclusive == 0)
2273 break;
2274
2275 per = 100.0 * pUnique->elapsedExclusive / total;
2276 sum += pUnique->elapsedExclusive;
2277 sum_per = 100.0 * sum / total;
2278 methodName = (char*)(pUnique->methods[0]->methodName);
2279 if (gOptions.outputHtml) {
2280 char buf[80];
2281
2282 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
2283 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);
2284 sprintf(buf, "%llu", pUnique->elapsedExclusive);
2285 printHtmlField(buf, 9);
2286 printf(" ");
2287 sprintf(buf, "%.1f", per);
2288 printHtmlField(buf, 7);
2289 printf(" ");
2290 sprintf(buf, "%.1f", sum_per);
2291 printHtmlField(buf, 7);
2292 printf(" ");
2293 sprintf(buf, "%d", pUnique->numCalls[0]);
2294 printHtmlField(buf, 6);
2295 printf("+");
2296 sprintf(buf, "%d", pUnique->numCalls[1]);
2297 printHtmlField(buf, -6);
2298 printf(" ");
2299 printf("%s", methodName);
2300 printf("</div>\n");
2301 printf("<div class=\"parent\" id=\"e%d\">\n", ii);
2302 } else {
2303 printf("---------------------------------------------\n");
2304 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
2305 pUnique->elapsedExclusive, per, sum_per,
2306 pUnique->numCalls[0], pUnique->numCalls[1],
2307 methodName);
2308 }
2309 int numMethods = pUnique->numMethods;
2310 double methodExclusive = pUnique->elapsedExclusive;
2311 double sumMethods = 0;
2312 for (jj = 0; jj < numMethods; ++jj) {
2313 method = pUnique->methods[jj];
2314 className = (char*)(method->className);
2315 signature = (char*)(method->signature);
2316 per = 100.0 * method->elapsedExclusive / methodExclusive;
2317 sumMethods += method->elapsedExclusive;
2318 sum_per = 100.0 * sumMethods / methodExclusive;
2319 if (gOptions.outputHtml) {
2320 char buf[80];
2321
2322 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
2323 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
2324 printf("<div class=\"leaf\"><span class=\"leaf\"> </span>");
2325 sprintf(buf, "%llu", method->elapsedExclusive);
2326 printHtmlField(buf, 9);
2327 printf(" ");
2328 sprintf(buf, "%llu", method->elapsedInclusive);
2329 printHtmlField(buf, 9);
2330 printf(" ");
2331 sprintf(buf, "%.1f", per);
2332 printHtmlField(buf, 7);
2333 printf(" ");
2334 sprintf(buf, "%.1f", sum_per);
2335 printHtmlField(buf, 7);
2336 printf(" ");
2337 sprintf(buf, "%d", method->numCalls[0]);
2338 printHtmlField(buf, 6);
2339 printf("+");
2340 sprintf(buf, "%d", method->numCalls[1]);
2341 printHtmlField(buf, -6);
2342 printf(" ");
2343 printf("<a href=\"#m%d\">[%d]</a> %s.%s %s",
2344 method->index, method->index,
2345 className, methodName, signature);
2346 printf("</div>\n");
2347 } else {
2348 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n",
2349 method->elapsedExclusive,
2350 method->elapsedInclusive,
2351 per, sum_per,
2352 method->numCalls[0], method->numCalls[1],
2353 method->index, className, methodName, signature);
2354 }
2355 }
2356 if (gOptions.outputHtml) {
2357 printf("</div>\n");
2358 }
2359 }
2360 }
2361
2362 /*
2363 * Read the key and data files and return the MethodEntries for those files
2364 */
parseDataKeys(TraceData * traceData,const char * traceFileName,uint64_t * threadTime)2365 DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime)
2366 {
2367 DataKeys* dataKeys = NULL;
2368 MethodEntry **pMethods = NULL;
2369 MethodEntry* method;
2370 FILE* dataFp = NULL;
2371 DataHeader dataHeader;
2372 int ii;
2373 uint64_t currentTime;
2374 MethodEntry* caller;
2375
2376 dataFp = fopen(traceFileName, "r");
2377 if (dataFp == NULL)
2378 goto bail;
2379
2380 if ((dataKeys = parseKeys(dataFp, 0)) == NULL)
2381 goto bail;
2382
2383 if (parseDataHeader(dataFp, &dataHeader) < 0)
2384 goto bail;
2385
2386 #if 0
2387 FILE *dumpStream = fopen("debug", "w");
2388 #endif
2389 while (1) {
2390 int threadId;
2391 unsigned int methodVal;
2392 int action;
2393 unsigned int methodId;
2394 CallStack *pStack;
2395 /*
2396 * Extract values from file.
2397 */
2398 if (readDataRecord(dataFp, &threadId, &methodVal, ¤tTime))
2399 break;
2400
2401 action = METHOD_ACTION(methodVal);
2402 methodId = METHOD_ID(methodVal);
2403
2404 /* Get the call stack for this thread */
2405 pStack = traceData->stacks[threadId];
2406
2407 /* If there is no call stack yet for this thread, then allocate one */
2408 if (pStack == NULL) {
2409 pStack = malloc(sizeof(CallStack));
2410 pStack->top = 0;
2411 pStack->lastEventTime = currentTime;
2412 pStack->threadStartTime = currentTime;
2413 traceData->stacks[threadId] = pStack;
2414 }
2415
2416 /* Lookup the current method */
2417 method = lookupMethod(dataKeys, methodId);
2418 if (method == NULL)
2419 method = &dataKeys->methods[UNKNOWN_INDEX];
2420
2421 #if 0
2422 if (method->methodName) {
2423 fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
2424 threadId, currentTime, action, pStack->threadStartTime,
2425 method->recursiveEntries,
2426 pStack->top, method->className, method->methodName,
2427 method->signature);
2428 } else {
2429 fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
2430 threadId, currentTime, action, pStack->threadStartTime,
2431 method->recursiveEntries,
2432 pStack->top, method->className);
2433 }
2434 #endif
2435
2436 if (action == METHOD_TRACE_ENTER) {
2437 /* This is a method entry */
2438 if (pStack->top >= MAX_STACK_DEPTH) {
2439 fprintf(stderr, "Stack overflow (exceeded %d frames)\n",
2440 MAX_STACK_DEPTH);
2441 exit(1);
2442 }
2443
2444 /* Get the caller method */
2445 if (pStack->top >= 1)
2446 caller = pStack->calls[pStack->top - 1].method;
2447 else
2448 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2449 countRecursiveEntries(pStack, pStack->top, caller);
2450 caller->elapsedExclusive += currentTime - pStack->lastEventTime;
2451 #if 0
2452 if (caller->elapsedExclusive > 10000000)
2453 fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n",
2454 caller->elapsedExclusive, currentTime,
2455 pStack->lastEventTime,
2456 currentTime - pStack->lastEventTime);
2457 #endif
2458 if (caller->recursiveEntries <= 1) {
2459 caller->topExclusive += currentTime - pStack->lastEventTime;
2460 }
2461
2462 /* Push the method on the stack for this thread */
2463 pStack->calls[pStack->top].method = method;
2464 pStack->calls[pStack->top++].entryTime = currentTime;
2465 } else {
2466 /* This is a method exit */
2467 uint64_t entryTime = 0;
2468
2469 /* Pop the method off the stack for this thread */
2470 if (pStack->top > 0) {
2471 pStack->top -= 1;
2472 entryTime = pStack->calls[pStack->top].entryTime;
2473 if (method != pStack->calls[pStack->top].method) {
2474 if (method->methodName) {
2475 fprintf(stderr,
2476 "Exit from method %s.%s %s does not match stack:\n",
2477 method->className, method->methodName,
2478 method->signature);
2479 } else {
2480 fprintf(stderr,
2481 "Exit from method %s does not match stack:\n",
2482 method->className);
2483 }
2484 stackDump(pStack, pStack->top + 1);
2485 exit(1);
2486 }
2487 }
2488
2489 /* Get the caller method */
2490 if (pStack->top >= 1)
2491 caller = pStack->calls[pStack->top - 1].method;
2492 else
2493 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2494 countRecursiveEntries(pStack, pStack->top, caller);
2495 countRecursiveEntries(pStack, pStack->top, method);
2496 uint64_t elapsed = currentTime - entryTime;
2497 addInclusiveTime(caller, method, elapsed);
2498 method->elapsedExclusive += currentTime - pStack->lastEventTime;
2499 if (method->recursiveEntries == 0) {
2500 method->topExclusive += currentTime - pStack->lastEventTime;
2501 }
2502 }
2503 /* Remember the time of the last entry or exit event */
2504 pStack->lastEventTime = currentTime;
2505 }
2506
2507 /* If we have calls on the stack when the trace ends, then clean
2508 * up the stack and add time to the callers by pretending that we
2509 * are exiting from their methods now.
2510 */
2511 CallStack *pStack;
2512 int threadId;
2513 uint64_t elapsedTime = 0;
2514 uint64_t sumThreadTime = 0;
2515 for (threadId = 0; threadId < MAX_THREADS; ++threadId) {
2516
2517 pStack = traceData->stacks[threadId];
2518
2519 /* If this thread never existed, then continue with next thread */
2520 if (pStack == NULL)
2521 continue;
2522
2523 /* Calculate time spent in thread, and add it to total time */
2524 elapsedTime = pStack->lastEventTime - pStack->threadStartTime;
2525 sumThreadTime += elapsedTime;
2526
2527 /* Save the per-thread elapsed time in the DataKeys struct */
2528 for (ii = 0; ii < dataKeys->numThreads; ++ii) {
2529 if (dataKeys->threads[ii].threadId == threadId)
2530 dataKeys->threads[ii].elapsedTime = elapsedTime;
2531 }
2532
2533 for (ii = 0; ii < pStack->top; ++ii) {
2534 if (ii == 0)
2535 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2536 else
2537 caller = pStack->calls[ii - 1].method;
2538 method = pStack->calls[ii].method;
2539 countRecursiveEntries(pStack, ii, caller);
2540 countRecursiveEntries(pStack, ii, method);
2541
2542 uint64_t entryTime = pStack->calls[ii].entryTime;
2543 uint64_t elapsed = pStack->lastEventTime - entryTime;
2544 addInclusiveTime(caller, method, elapsed);
2545 }
2546 }
2547 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2548 caller->elapsedInclusive = sumThreadTime;
2549
2550 #if 0
2551 fclose(dumpStream);
2552 #endif
2553
2554 if (threadTime != NULL) {
2555 *threadTime = sumThreadTime;
2556 }
2557
2558 bail:
2559 if (dataFp != NULL)
2560 fclose(dataFp);
2561
2562 return dataKeys;
2563 }
2564
parseMethodEntries(DataKeys * dataKeys)2565 MethodEntry** parseMethodEntries(DataKeys* dataKeys)
2566 {
2567 int ii;
2568 /* Create a new array of pointers to the methods and sort the pointers
2569 * instead of the actual MethodEntry structs. We need to do this
2570 * because there are other lists that contain pointers to the
2571 * MethodEntry structs.
2572 */
2573 MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods);
2574 for (ii = 0; ii < dataKeys->numMethods; ++ii) {
2575 MethodEntry* entry = &dataKeys->methods[ii];
2576 pMethods[ii] = entry;
2577 }
2578
2579 return pMethods;
2580 }
2581
2582 /*
2583 * Produce a function profile from the following methods
2584 */
profileTrace(TraceData * traceData,MethodEntry ** pMethods,int numMethods,uint64_t sumThreadTime,ThreadEntry * pThreads,int numThreads)2585 void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime,
2586 ThreadEntry *pThreads, int numThreads)
2587 {
2588 /* Print the html header, if necessary */
2589 if (gOptions.outputHtml) {
2590 printf(htmlHeader, gOptions.sortableUrl);
2591 outputTableOfContents();
2592 }
2593
2594 printExclusiveProfile(pMethods, numMethods, sumThreadTime);
2595 printInclusiveProfile(pMethods, numMethods, sumThreadTime);
2596
2597 printThreadProfile(pThreads, numThreads, sumThreadTime);
2598
2599 createClassList(traceData, pMethods, numMethods);
2600 printClassProfiles(traceData, sumThreadTime);
2601
2602 createUniqueMethodList(traceData, pMethods, numMethods);
2603 printMethodProfiles(traceData, sumThreadTime);
2604
2605 if (gOptions.outputHtml) {
2606 printf("%s", htmlFooter);
2607 }
2608 }
2609
compareMethodNamesForDiff(const void * a,const void * b)2610 int compareMethodNamesForDiff(const void *a, const void *b)
2611 {
2612 int result;
2613
2614 const MethodEntry *methodA = *(const MethodEntry**)a;
2615 const MethodEntry *methodB = *(const MethodEntry**)b;
2616 if (methodA->methodName == NULL || methodB->methodName == NULL) {
2617 return compareClassNames(a, b);
2618 }
2619 result = strcmp(methodA->methodName, methodB->methodName);
2620 if (result == 0) {
2621 result = strcmp(methodA->signature, methodB->signature);
2622 if (result == 0) {
2623 return strcmp(methodA->className, methodB->className);
2624 }
2625 }
2626 return result;
2627 }
2628
findMatch(MethodEntry ** methods,int size,MethodEntry * matchThis)2629 int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis)
2630 {
2631 int i;
2632
2633 for (i = 0 ; i < size ; i++) {
2634 MethodEntry* method = methods[i];
2635
2636 if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) {
2637 // printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName,
2638 // method->className, method->methodName);
2639
2640 return i;
2641 /* if (!compareMethodNames(&method, &matchThis)) {
2642 return i;
2643 }
2644 */ }
2645 }
2646
2647 return -1;
2648 }
2649
compareDiffEntriesExculsive(const void * a,const void * b)2650 int compareDiffEntriesExculsive(const void *a, const void *b)
2651 {
2652 int result;
2653
2654 const DiffEntry* entryA = (const DiffEntry*)a;
2655 const DiffEntry* entryB = (const DiffEntry*)b;
2656
2657 if (entryA->differenceExclusive < entryB->differenceExclusive) {
2658 return 1;
2659 } else if (entryA->differenceExclusive > entryB->differenceExclusive) {
2660 return -1;
2661 }
2662
2663 return 0;
2664 }
2665
compareDiffEntriesInculsive(const void * a,const void * b)2666 int compareDiffEntriesInculsive(const void *a, const void *b)
2667 {
2668 int result;
2669
2670 const DiffEntry* entryA = (const DiffEntry*)a;
2671 const DiffEntry* entryB = (const DiffEntry*)b;
2672
2673 if (entryA->differenceInclusive < entryB->differenceInclusive) {
2674 return 1;
2675 } else if (entryA->differenceInclusive > entryB->differenceInclusive) {
2676 return -1;
2677 }
2678
2679 return 0;
2680 }
2681
printMissingMethod(MethodEntry * method)2682 void printMissingMethod(MethodEntry* method)
2683 {
2684 char classBuf[HTML_BUFSIZE];
2685 char methodBuf[HTML_BUFSIZE];
2686 char* className;
2687 char* methodName;
2688
2689 className = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
2690 methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
2691
2692 if (gOptions.outputHtml) printf("<tr><td>\n");
2693
2694 printf("%s.%s ", className, methodName);
2695 if (gOptions.outputHtml) printf("</td><td>");
2696
2697 printf("%lld ", method->elapsedExclusive);
2698 if (gOptions.outputHtml) printf("</td><td>");
2699
2700 printf("%lld ", method->elapsedInclusive);
2701 if (gOptions.outputHtml) printf("</td><td>");
2702
2703 printf("%d\n", method->numCalls[0]);
2704 if (gOptions.outputHtml) printf("</td><td>\n");
2705 }
2706
2707
createDiff(DataKeys * d1,uint64_t sum1,DataKeys * d2,uint64_t sum2)2708 void createDiff(DataKeys* d1, uint64_t sum1, DataKeys* d2, uint64_t sum2)
2709 {
2710 MethodEntry** methods1 = parseMethodEntries(d1);
2711 MethodEntry** methods2 = parseMethodEntries(d2);
2712
2713 // sort and assign the indicies
2714 int i;
2715 qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
2716 for (i = 0; i < d1->numMethods; ++i) {
2717 methods1[i]->index = i;
2718 }
2719
2720 qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
2721 for (i = 0; i < d2->numMethods; ++i) {
2722 methods2[i]->index = i;
2723 }
2724
2725 int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
2726 max++;
2727 DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry));
2728 memset(diffs, 0, max * sizeof(DiffEntry));
2729 DiffEntry* ptr = diffs;
2730
2731 // printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods);
2732
2733 int matches = 0;
2734
2735 for (i = 0 ; i < d1->numMethods ; i++) {
2736 int match = findMatch(methods2, d2->numMethods, methods1[i]);
2737 if (match >= 0) {
2738 ptr->method1 = methods1[i];
2739 ptr->method2 = methods2[match];
2740
2741 uint64_t e1 = ptr->method1->elapsedExclusive;
2742 uint64_t e2 = ptr->method2->elapsedExclusive;
2743 if (e1 > 0) {
2744 ptr->differenceExclusive = e2 - e1;
2745 ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0;
2746 }
2747
2748 uint64_t i1 = ptr->method1->elapsedInclusive;
2749 uint64_t i2 = ptr->method2->elapsedInclusive;
2750 if (i1 > 0) {
2751 ptr->differenceInclusive = i2 - i1;
2752 ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0;
2753 }
2754
2755 // clear these out so we don't find them again and we know which ones
2756 // we have left over
2757 methods1[i] = NULL;
2758 methods2[match] = NULL;
2759 ptr++;
2760
2761 matches++;
2762 }
2763 }
2764 ptr->method1 = NULL;
2765 ptr->method2 = NULL;
2766
2767 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
2768 ptr = diffs;
2769
2770 if (gOptions.outputHtml) {
2771 printf(htmlHeader, gOptions.sortableUrl);
2772 printf("<h3>Table of Contents</h3>\n");
2773 printf("<ul>\n");
2774 printf("<li><a href='#exclusive'>Exclusive</a>\n");
2775 printf("<li><a href='#inclusive'>Inclusive</a>\n");
2776 printf("</ul>\n");
2777 printf("Run 1: %s<br>\n", gOptions.diffFileName);
2778 printf("Run 2: %s<br>\n", gOptions.traceFileName);
2779 printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n");
2780 printf(tableHeader, "exclusive_table");
2781 }
2782
2783 char classBuf[HTML_BUFSIZE];
2784 char methodBuf[HTML_BUFSIZE];
2785 char* className;
2786 char* methodName;
2787
2788 while (ptr->method1 != NULL && ptr->method2 != NULL) {
2789 if (gOptions.outputHtml) printf("<tr><td>\n");
2790
2791 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
2792 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
2793
2794 printf("%s.%s ", className, methodName);
2795 if (gOptions.outputHtml) printf("</td><td>");
2796
2797 printf("%lld ", ptr->method1->elapsedExclusive);
2798 if (gOptions.outputHtml) printf("</td><td>");
2799
2800 printf("%llu ", ptr->method2->elapsedExclusive);
2801 if (gOptions.outputHtml) printf("</td><td>");
2802
2803 printf("%lld ", ptr->differenceExclusive);
2804 if (gOptions.outputHtml) printf("</td><td>");
2805
2806 printf("%.2f\n", ptr->differenceExclusivePercentage);
2807 if (gOptions.outputHtml) printf("</td><td>\n");
2808
2809 printf("%d\n", ptr->method1->numCalls[0]);
2810 if (gOptions.outputHtml) printf("</td><td>\n");
2811
2812 printf("%d\n", ptr->method2->numCalls[0]);
2813 if (gOptions.outputHtml) printf("</td></tr>\n");
2814
2815 ptr++;
2816 }
2817
2818 if (gOptions.outputHtml) printf("</table>\n");
2819
2820 if (gOptions.outputHtml) {
2821 printf(htmlHeader, gOptions.sortableUrl);
2822 printf("Run 1: %s<br>\n", gOptions.diffFileName);
2823 printf("Run 2: %s<br>\n", gOptions.traceFileName);
2824 printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n");
2825 printf(tableHeader, "inclusive_table");
2826 }
2827
2828 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive);
2829 ptr = diffs;
2830
2831 while (ptr->method1 != NULL && ptr->method2 != NULL) {
2832 if (gOptions.outputHtml) printf("<tr><td>\n");
2833
2834 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
2835 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
2836
2837 printf("%s.%s ", className, methodName);
2838 if (gOptions.outputHtml) printf("</td><td>");
2839
2840 printf("%lld ", ptr->method1->elapsedInclusive);
2841 if (gOptions.outputHtml) printf("</td><td>");
2842
2843 printf("%llu ", ptr->method2->elapsedInclusive);
2844 if (gOptions.outputHtml) printf("</td><td>");
2845
2846 printf("%lld ", ptr->differenceInclusive);
2847 if (gOptions.outputHtml) printf("</td><td>");
2848
2849 printf("%.2f\n", ptr->differenceInclusivePercentage);
2850 if (gOptions.outputHtml) printf("</td><td>\n");
2851
2852 printf("%d\n", ptr->method1->numCalls[0]);
2853 if (gOptions.outputHtml) printf("</td><td>\n");
2854
2855 printf("%d\n", ptr->method2->numCalls[0]);
2856 if (gOptions.outputHtml) printf("</td></tr>\n");
2857
2858 ptr++;
2859 }
2860
2861 if (gOptions.outputHtml) {
2862 printf("</table>\n");
2863 printf("<h3>Run 1 methods not found in Run 2</h3>");
2864 printf(tableHeaderMissing);
2865 }
2866
2867 for (i = 0; i < d1->numMethods; ++i) {
2868 if (methods1[i] != NULL) {
2869 printMissingMethod(methods1[i]);
2870 }
2871 }
2872
2873 if (gOptions.outputHtml) {
2874 printf("</table>\n");
2875 printf("<h3>Run 2 methods not found in Run 1</h3>");
2876 printf(tableHeaderMissing);
2877 }
2878
2879 for (i = 0; i < d2->numMethods; ++i) {
2880 if (methods2[i] != NULL) {
2881 printMissingMethod(methods2[i]);
2882 }
2883 }
2884
2885 if (gOptions.outputHtml) printf("</body></html\n");
2886 }
2887
usage(const char * program)2888 int usage(const char *program)
2889 {
2890 fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] trace-file-name\n", program);
2891 fprintf(stderr, " -d trace-file-name - Diff with this trace\n");
2892 fprintf(stderr, " -g outfile - Write graph to 'outfile'\n");
2893 fprintf(stderr, " -k - When writing a graph, keep the intermediate DOT file\n");
2894 fprintf(stderr, " -h - Turn on HTML output\n");
2895 fprintf(stderr, " -o - Dump the dmtrace file instead of profiling\n");
2896 fprintf(stderr, " -s - URL base to where the sortable javascript file\n");
2897 fprintf(stderr, " -t threshold - Threshold percentage for including nodes in the graph\n");
2898 return 2;
2899 }
2900
2901 // Returns true if there was an error
parseOptions(int argc,char ** argv)2902 int parseOptions(int argc, char **argv)
2903 {
2904 while (1) {
2905 int opt = getopt(argc, argv, "d:hg:kos:t:");
2906 if (opt == -1)
2907 break;
2908 switch (opt) {
2909 case 'd':
2910 gOptions.diffFileName = optarg;
2911 break;
2912 case 'g':
2913 gOptions.graphFileName = optarg;
2914 break;
2915 case 'k':
2916 gOptions.keepDotFile = 1;
2917 break;
2918 case 'h':
2919 gOptions.outputHtml = 1;
2920 break;
2921 case 'o':
2922 gOptions.dump = 1;
2923 break;
2924 case 's':
2925 gOptions.sortableUrl = optarg;
2926 break;
2927 case 't':
2928 gOptions.threshold = atoi(optarg);
2929 break;
2930 default:
2931 return 1;
2932 }
2933 }
2934 return 0;
2935 }
2936
2937 /*
2938 * Parse args.
2939 */
main(int argc,char ** argv)2940 int main(int argc, char** argv)
2941 {
2942 gOptions.threshold = -1;
2943
2944 // Parse the options
2945 if (parseOptions(argc, argv) || argc - optind != 1)
2946 return usage(argv[0]);
2947
2948 gOptions.traceFileName = argv[optind];
2949
2950 if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
2951 gOptions.threshold = 20;
2952 }
2953
2954 if (gOptions.dump) {
2955 dumpTrace();
2956 return 0;
2957 }
2958
2959 uint64_t sumThreadTime = 0;
2960
2961 TraceData data1;
2962 DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
2963 &sumThreadTime);
2964 if (dataKeys == NULL) {
2965 fprintf(stderr, "Cannot read trace.\n");
2966 exit(1);
2967 }
2968
2969 if (gOptions.diffFileName != NULL) {
2970 uint64_t sum2;
2971 TraceData data2;
2972 DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2);
2973
2974 createDiff(d2, sum2, dataKeys, sumThreadTime);
2975
2976 freeDataKeys(d2);
2977 } else {
2978 MethodEntry** methods = parseMethodEntries(dataKeys);
2979 profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime,
2980 dataKeys->threads, dataKeys->numThreads);
2981 if (gOptions.graphFileName != NULL) {
2982 createInclusiveProfileGraphNew(dataKeys);
2983 }
2984 free(methods);
2985 }
2986
2987 freeDataKeys(dataKeys);
2988
2989 return 0;
2990 }
2991