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