• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *  '<'  &lt;
243  *  '>'  &gt;
244  *  '&'  &amp;
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("&nbsp;");
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\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
2029         printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; 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\">&nbsp;</span>");
2094                 sprintf(buf, "%llu", method->elapsedExclusive);
2095                 printHtmlField(buf, 9);
2096                 printf("&nbsp;");
2097                 sprintf(buf, "%llu", method->elapsedInclusive);
2098                 printHtmlField(buf, 9);
2099                 printf("&nbsp;");
2100                 sprintf(buf, "%.1f", per);
2101                 printHtmlField(buf, 7);
2102                 printf("&nbsp;");
2103                 sprintf(buf, "%.1f", sum_per);
2104                 printHtmlField(buf, 7);
2105                 printf("&nbsp;");
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("&nbsp;");
2112                 printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s&nbsp;%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\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
2261         printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; 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\">&nbsp;</span>");
2325                 sprintf(buf, "%llu", method->elapsedExclusive);
2326                 printHtmlField(buf, 9);
2327                 printf("&nbsp;");
2328                 sprintf(buf, "%llu", method->elapsedInclusive);
2329                 printHtmlField(buf, 9);
2330                 printf("&nbsp;");
2331                 sprintf(buf, "%.1f", per);
2332                 printHtmlField(buf, 7);
2333                 printf("&nbsp;");
2334                 sprintf(buf, "%.1f", sum_per);
2335                 printHtmlField(buf, 7);
2336                 printf("&nbsp;");
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("&nbsp;");
2343                 printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s.%s&nbsp;%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, &currentTime))
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