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