Home | History | Annotate | Download | only in dmtracedump
      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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    512 void freeDataKeys(DataKeys* pKeys) {
    513   if (pKeys == nullptr) return;
    514 
    515   free(pKeys->fileData);
    516   free(pKeys->threads);
    517   free(pKeys->methods);
    518   free(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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    795 static int32_t compareThreads(const void* thread1, const void* thread2) {
    796   return ((const ThreadEntry*) thread1)->threadId -
    797          ((const ThreadEntry*) thread2)->threadId;
    798 }
    799 
    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  */
    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 
    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  */
    822 DataKeys* parseKeys(FILE* fp, int32_t verbose) {
    823   int64_t offset;
    824   DataKeys* pKeys = new DataKeys();
    825   memset(pKeys, 0, sizeof(DataKeys));
    826   if (pKeys == nullptr) return nullptr;
    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   /* Reduce our allocation now that we know where the end of the key section is. */
    869   pKeys->fileData = reinterpret_cast<char*>(realloc(pKeys->fileData, offset));
    870   pKeys->fileLen = offset;
    871   /* Leave fp pointing to the beginning of the data section. */
    872   fseek(fp, offset, SEEK_SET);
    873 
    874   sortThreadList(pKeys);
    875   sortMethodList(pKeys);
    876 
    877   /*
    878    * Dump list of threads.
    879    */
    880   if (verbose) {
    881     printf("Threads (%d):\n", pKeys->numThreads);
    882     for (int32_t i = 0; i < pKeys->numThreads; i++) {
    883       printf("%2d %s\n", pKeys->threads[i].threadId, pKeys->threads[i].threadName);
    884     }
    885   }
    886 
    887 #if 0
    888   /*
    889    * Dump list of methods.
    890    */
    891   if (verbose) {
    892     printf("Methods (%d):\n", pKeys->numMethods);
    893     for (int32_t i = 0; i < pKeys->numMethods; i++) {
    894       printf("0x%08x %s : %s : %s\n",
    895              pKeys->methods[i].methodId, pKeys->methods[i].className,
    896              pKeys->methods[i].methodName, pKeys->methods[i].signature);
    897     }
    898   }
    899 #endif
    900 
    901   return pKeys;
    902 }
    903 
    904 /*
    905  * Read values from the binary data file.
    906  */
    907 
    908 /*
    909  * Make the return value "uint32_t" instead of "uint16_t" so that we can detect EOF.
    910  */
    911 uint32_t read2LE(FILE* fp) {
    912   uint32_t val = getc(fp);
    913   val |= getc(fp) << 8;
    914   return val;
    915 }
    916 uint32_t read4LE(FILE* fp) {
    917   uint32_t val = getc(fp);
    918   val |= getc(fp) << 8;
    919   val |= getc(fp) << 16;
    920   val |= getc(fp) << 24;
    921   return val;
    922 }
    923 uint64_t read8LE(FILE* fp) {
    924   uint64_t val = getc(fp);
    925   val |= (uint64_t) getc(fp) << 8;
    926   val |= (uint64_t) getc(fp) << 16;
    927   val |= (uint64_t) getc(fp) << 24;
    928   val |= (uint64_t) getc(fp) << 32;
    929   val |= (uint64_t) getc(fp) << 40;
    930   val |= (uint64_t) getc(fp) << 48;
    931   val |= (uint64_t) getc(fp) << 56;
    932   return val;
    933 }
    934 
    935 /*
    936  * Parse the header of the data section.
    937  *
    938  * Returns with the file positioned at the start of the record data.
    939  */
    940 int32_t parseDataHeader(FILE* fp, DataHeader* pHeader) {
    941   pHeader->magic = read4LE(fp);
    942   pHeader->version = read2LE(fp);
    943   pHeader->offsetToData = read2LE(fp);
    944   pHeader->startWhen = read8LE(fp);
    945   int32_t bytesToRead = pHeader->offsetToData - 16;
    946   if (pHeader->version == 1) {
    947     pHeader->recordSize = 9;
    948   } else if (pHeader->version == 2) {
    949     pHeader->recordSize = 10;
    950   } else if (pHeader->version == 3) {
    951     pHeader->recordSize = read2LE(fp);
    952     bytesToRead -= 2;
    953   } else {
    954     fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version);
    955     return -1;
    956   }
    957 
    958   if (fseek(fp, bytesToRead, SEEK_CUR) != 0) {
    959     return -1;
    960   }
    961 
    962   return 0;
    963 }
    964 
    965 /*
    966  * Look up a method by it's method ID.
    967  *
    968  * Returns nullptr if no matching method was found.
    969  */
    970 MethodEntry* lookupMethod(DataKeys* pKeys, int64_t methodId) {
    971   int32_t lo = 0;
    972   int32_t hi = pKeys->numMethods - 1;
    973 
    974   while (hi >= lo) {
    975     int32_t mid = (hi + lo) / 2;
    976 
    977     int64_t id = pKeys->methods[mid].methodId;
    978     if (id == methodId) /* match */
    979       return &pKeys->methods[mid];
    980     else if (id < methodId) /* too low */
    981       lo = mid + 1;
    982     else /* too high */
    983       hi = mid - 1;
    984   }
    985 
    986   return nullptr;
    987 }
    988 
    989 /*
    990  * Reads the next data record, and assigns the data values to threadId,
    991  * methodVal and elapsedTime.  On end-of-file, the threadId, methodVal,
    992  * and elapsedTime are unchanged.  Returns 1 on end-of-file, otherwise
    993  * returns 0.
    994  */
    995 int32_t readDataRecord(FILE* dataFp, DataHeader* dataHeader, int32_t* threadId,
    996                    uint32_t* methodVal, uint64_t* elapsedTime) {
    997   int32_t id;
    998   int32_t bytesToRead = dataHeader->recordSize;
    999   if (dataHeader->version == 1) {
   1000     id = getc(dataFp);
   1001     bytesToRead -= 1;
   1002   } else {
   1003     id = read2LE(dataFp);
   1004     bytesToRead -= 2;
   1005   }
   1006   if (id == EOF) return 1;
   1007   *threadId = id;
   1008 
   1009   *methodVal = read4LE(dataFp);
   1010   *elapsedTime = read4LE(dataFp);
   1011   bytesToRead -= 8;
   1012 
   1013   while (bytesToRead-- > 0) {
   1014     getc(dataFp);
   1015   }
   1016 
   1017   if (feof(dataFp)) {
   1018     fprintf(stderr, "WARNING: hit EOF mid-record\n");
   1019     return 1;
   1020   }
   1021   return 0;
   1022 }
   1023 
   1024 /*
   1025  * Read the key file and use it to produce formatted output from the
   1026  * data file.
   1027  */
   1028 void dumpTrace() {
   1029   static const char* actionStr[] = {"ent", "xit", "unr", "???"};
   1030   MethodEntry bogusMethod = {
   1031       0, "???", "???",        "???",        "???",  -1, 0, 0,
   1032       0, 0,     {nullptr, nullptr}, {nullptr, nullptr}, {0, 0}, 0,  0, -1};
   1033   char bogusBuf[80];
   1034   TraceData traceData;
   1035 
   1036   // printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
   1037 
   1038   char spaces[MAX_STACK_DEPTH + 1];
   1039   memset(spaces, '.', MAX_STACK_DEPTH);
   1040   spaces[MAX_STACK_DEPTH] = '\0';
   1041 
   1042   for (int32_t i = 0; i < MAX_THREADS; i++)
   1043     traceData.depth[i] = 2;  // adjust for return from start function
   1044 
   1045   FILE* dataFp = fopen(gOptions.traceFileName, "rb");
   1046   if (dataFp == nullptr) return;
   1047 
   1048   DataKeys* pKeys = parseKeys(dataFp, 1);
   1049   if (pKeys == nullptr) {
   1050     fclose(dataFp);
   1051     return;
   1052   }
   1053 
   1054   DataHeader dataHeader;
   1055   if (parseDataHeader(dataFp, &dataHeader) < 0) {
   1056     fclose(dataFp);
   1057     freeDataKeys(pKeys);
   1058     return;
   1059   }
   1060 
   1061   printf("Trace (threadID action usecs class.method signature):\n");
   1062 
   1063   while (1) {
   1064     /*
   1065      * Extract values from file.
   1066      */
   1067     int32_t threadId;
   1068     uint32_t methodVal;
   1069     uint64_t elapsedTime;
   1070     if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime))
   1071       break;
   1072 
   1073     int32_t action = METHOD_ACTION(methodVal);
   1074     int64_t methodId = METHOD_ID(methodVal);
   1075 
   1076     /*
   1077      * Generate a line of output.
   1078      */
   1079     int64_t lastEnter = 0;
   1080     int32_t mismatch = 0;
   1081     if (action == METHOD_TRACE_ENTER) {
   1082       traceData.depth[threadId]++;
   1083       lastEnter = methodId;
   1084     } else {
   1085       /* quick test for mismatched adjacent enter/exit */
   1086       if (lastEnter != 0 && lastEnter != methodId) mismatch = 1;
   1087     }
   1088 
   1089     int32_t printDepth = traceData.depth[threadId];
   1090     char depthNote = ' ';
   1091     if (printDepth < 0) {
   1092       printDepth = 0;
   1093       depthNote = '-';
   1094     } else if (printDepth > MAX_STACK_DEPTH) {
   1095       printDepth = MAX_STACK_DEPTH;
   1096       depthNote = '+';
   1097     }
   1098 
   1099     MethodEntry* method = lookupMethod(pKeys, methodId);
   1100     if (method == nullptr) {
   1101       method = &bogusMethod;
   1102       sprintf(bogusBuf, "methodId: %#" PRIx64 "", methodId);
   1103       method->signature = bogusBuf;
   1104     }
   1105 
   1106     if (method->methodName) {
   1107       printf("%2d %s%c %8" PRIu64 "%c%s%s.%s %s\n", threadId, actionStr[action],
   1108              mismatch ? '!' : ' ', elapsedTime, depthNote,
   1109              spaces + (MAX_STACK_DEPTH - printDepth), method->className,
   1110              method->methodName, method->signature);
   1111     } else {
   1112       printf("%2d %s%c %8" PRIu64 "%c%s%s\n", threadId, actionStr[action],
   1113              mismatch ? '!' : ' ', elapsedTime, depthNote,
   1114              spaces + (MAX_STACK_DEPTH - printDepth), method->className);
   1115     }
   1116 
   1117     if (action != METHOD_TRACE_ENTER) {
   1118       traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */
   1119       lastEnter = 0;
   1120     }
   1121 
   1122     mismatch = 0;
   1123   }
   1124 
   1125   fclose(dataFp);
   1126   freeDataKeys(pKeys);
   1127 }
   1128 
   1129 /* This routine adds the given time to the parent and child methods.
   1130  * This is called when the child routine exits, after the child has
   1131  * been popped from the stack.  The elapsedTime parameter is the
   1132  * duration of the child routine, including time spent in called routines.
   1133  */
   1134 void addInclusiveTime(MethodEntry* parent, MethodEntry* child, uint64_t elapsedTime) {
   1135 #if 0
   1136   bool verbose = false;
   1137   if (strcmp(child->className, debugClassName) == 0)
   1138     verbose = true;
   1139 #endif
   1140 
   1141   int32_t childIsRecursive = (child->recursiveEntries > 0);
   1142   int32_t parentIsRecursive = (parent->recursiveEntries > 1);
   1143 
   1144   if (child->recursiveEntries == 0) {
   1145     child->elapsedInclusive += elapsedTime;
   1146   } else if (child->recursiveEntries == 1) {
   1147     child->recursiveInclusive += elapsedTime;
   1148   }
   1149   child->numCalls[childIsRecursive] += 1;
   1150 
   1151 #if 0
   1152   if (verbose) {
   1153     fprintf(stderr,
   1154             "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n",
   1155             child->className, child->recursiveEntries,
   1156             elapsedTime, child->elapsedInclusive,
   1157             child->recursiveInclusive);
   1158   }
   1159 #endif
   1160 
   1161   /* Find the child method in the parent */
   1162   TimedMethod* pTimed;
   1163   TimedMethod* children = parent->children[parentIsRecursive];
   1164   for (pTimed = children; pTimed; pTimed = pTimed->next) {
   1165     if (pTimed->method == child) {
   1166       pTimed->elapsedInclusive += elapsedTime;
   1167       pTimed->numCalls += 1;
   1168       break;
   1169     }
   1170   }
   1171   if (pTimed == nullptr) {
   1172     /* Allocate a new TimedMethod */
   1173     pTimed = new TimedMethod();
   1174     pTimed->elapsedInclusive = elapsedTime;
   1175     pTimed->numCalls = 1;
   1176     pTimed->method = child;
   1177 
   1178     /* Add it to the front of the list */
   1179     pTimed->next = children;
   1180     parent->children[parentIsRecursive] = pTimed;
   1181   }
   1182 
   1183   /* Find the parent method in the child */
   1184   TimedMethod* parents = child->parents[childIsRecursive];
   1185   for (pTimed = parents; pTimed; pTimed = pTimed->next) {
   1186     if (pTimed->method == parent) {
   1187       pTimed->elapsedInclusive += elapsedTime;
   1188       pTimed->numCalls += 1;
   1189       break;
   1190     }
   1191   }
   1192   if (pTimed == nullptr) {
   1193     /* Allocate a new TimedMethod */
   1194     pTimed = new TimedMethod();
   1195     pTimed->elapsedInclusive = elapsedTime;
   1196     pTimed->numCalls = 1;
   1197     pTimed->method = parent;
   1198 
   1199     /* Add it to the front of the list */
   1200     pTimed->next = parents;
   1201     child->parents[childIsRecursive] = pTimed;
   1202   }
   1203 
   1204 #if 0
   1205   if (verbose) {
   1206     fprintf(stderr,
   1207             "  %s %d eI: %lld\n",
   1208             parent->className, parent->recursiveEntries,
   1209             pTimed->elapsedInclusive);
   1210   }
   1211 #endif
   1212 }
   1213 
   1214 /* Sorts a linked list and returns a newly allocated array containing
   1215  * the sorted entries.
   1216  */
   1217 TimedMethod* sortTimedMethodList(TimedMethod* list, int32_t* num) {
   1218   /* Count the elements */
   1219   TimedMethod* pTimed;
   1220   int32_t num_entries = 0;
   1221   for (pTimed = list; pTimed; pTimed = pTimed->next) num_entries += 1;
   1222   *num = num_entries;
   1223   if (num_entries == 0) return nullptr;
   1224 
   1225   /* Copy all the list elements to a new array and sort them */
   1226   int32_t ii;
   1227   TimedMethod* sorted = new TimedMethod[num_entries];
   1228   for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii)
   1229     memcpy(&sorted[ii], pTimed, sizeof(TimedMethod));
   1230   qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod);
   1231 
   1232   /* Fix up the "next" pointers so that they work. */
   1233   for (ii = 0; ii < num_entries - 1; ++ii) sorted[ii].next = &sorted[ii + 1];
   1234   sorted[num_entries - 1].next = nullptr;
   1235 
   1236   return sorted;
   1237 }
   1238 
   1239 /* Define flag values for printInclusiveMethod() */
   1240 static const int32_t kIsRecursive = 1;
   1241 
   1242 /* This prints the inclusive stats for all the parents or children of a
   1243  * method, depending on the list that is passed in.
   1244  */
   1245 void printInclusiveMethod(MethodEntry* method, TimedMethod* list, int32_t numCalls, int32_t flags) {
   1246   char buf[80];
   1247   const char* anchor_close = "";
   1248   const char* spaces = "      "; /* 6 spaces */
   1249   int32_t num_spaces = strlen(spaces);
   1250   const char* space_ptr = &spaces[num_spaces];
   1251   char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
   1252   char signatureBuf[HTML_BUFSIZE];
   1253 
   1254   if (gOptions.outputHtml) anchor_close = "</a>";
   1255 
   1256   int32_t num;
   1257   TimedMethod* sorted = sortTimedMethodList(list, &num);
   1258   double methodTotal = method->elapsedInclusive;
   1259   for (TimedMethod* pTimed = sorted; pTimed; pTimed = pTimed->next) {
   1260     MethodEntry* relative = pTimed->method;
   1261     const char* className = relative->className;
   1262     const char* methodName = relative->methodName;
   1263     const char* signature = relative->signature;
   1264     double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
   1265     sprintf(buf, "[%d]", relative->index);
   1266     if (gOptions.outputHtml) {
   1267       int32_t len = strlen(buf);
   1268       if (len > num_spaces) len = num_spaces;
   1269       sprintf(buf, "<a href=\"#m%d\">[%d]", relative->index, relative->index);
   1270       space_ptr = &spaces[len];
   1271       className = htmlEscape(className, classBuf, HTML_BUFSIZE);
   1272       methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
   1273       signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
   1274     }
   1275     int32_t nCalls = numCalls;
   1276     if (nCalls == 0) nCalls = relative->numCalls[0] + relative->numCalls[1];
   1277     if (relative->methodName) {
   1278       if (flags & kIsRecursive) {
   1279         // Don't display percentages for recursive functions
   1280         printf("%6s %5s   %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s.%s %s\n", "", "",
   1281                "", space_ptr, buf, anchor_close, pTimed->numCalls, nCalls,
   1282                pTimed->elapsedInclusive, className, methodName, signature);
   1283       } else {
   1284         printf("%6s %5s   %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 " %s.%s %s\n", "",
   1285                "", per, space_ptr, buf, anchor_close, pTimed->numCalls, nCalls,
   1286                pTimed->elapsedInclusive, className, methodName, signature);
   1287       }
   1288     } else {
   1289       if (flags & kIsRecursive) {
   1290         // Don't display percentages for recursive functions
   1291         printf("%6s %5s   %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", "", "", "",
   1292                space_ptr, buf, anchor_close, pTimed->numCalls, nCalls,
   1293                pTimed->elapsedInclusive, className);
   1294       } else {
   1295         printf("%6s %5s   %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", "", "",
   1296                per, space_ptr, buf, anchor_close, pTimed->numCalls, nCalls,
   1297                pTimed->elapsedInclusive, className);
   1298       }
   1299     }
   1300   }
   1301 }
   1302 
   1303 void countRecursiveEntries(CallStack* pStack, int32_t top, MethodEntry* method) {
   1304   method->recursiveEntries = 0;
   1305   for (int32_t ii = 0; ii < top; ++ii) {
   1306     if (pStack->calls[ii].method == method) method->recursiveEntries += 1;
   1307   }
   1308 }
   1309 
   1310 void stackDump(CallStack* pStack, int32_t top) {
   1311   for (int32_t ii = 0; ii < top; ++ii) {
   1312     MethodEntry* method = pStack->calls[ii].method;
   1313     uint64_t entryTime = pStack->calls[ii].entryTime;
   1314     if (method->methodName) {
   1315       fprintf(stderr, "  %2d: %8" PRIu64 " %s.%s %s\n", ii, entryTime,
   1316               method->className, method->methodName, method->signature);
   1317     } else {
   1318       fprintf(stderr, "  %2d: %8" PRIu64 " %s\n", ii, entryTime, method->className);
   1319     }
   1320   }
   1321 }
   1322 
   1323 void outputTableOfContents() {
   1324   printf("<a name=\"contents\"></a>\n");
   1325   printf("<h2>Table of Contents</h2>\n");
   1326   printf("<ul>\n");
   1327   printf("  <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
   1328   printf("  <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
   1329   printf("  <li><a href=\"#class\">Class/method profile</a></li>\n");
   1330   printf("  <li><a href=\"#method\">Method/class profile</a></li>\n");
   1331   printf("</ul>\n\n");
   1332 }
   1333 
   1334 void outputNavigationBar() {
   1335   printf("<a href=\"#contents\">[Top]</a>\n");
   1336   printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
   1337   printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
   1338   printf("<a href=\"#class\">[Class]</a>\n");
   1339   printf("<a href=\"#method\">[Method]</a>\n");
   1340   printf("<br><br>\n");
   1341 }
   1342 
   1343 void printExclusiveProfile(MethodEntry** pMethods, int32_t numMethods, uint64_t sumThreadTime) {
   1344   char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
   1345   char signatureBuf[HTML_BUFSIZE];
   1346   const char* anchor_close = "";
   1347   char anchor_buf[80];
   1348   anchor_buf[0] = 0;
   1349   if (gOptions.outputHtml) {
   1350     anchor_close = "</a>";
   1351     printf("<a name=\"exclusive\"></a>\n");
   1352     printf("<hr>\n");
   1353     outputNavigationBar();
   1354   } else {
   1355     printf("\n%s\n", profileSeparator);
   1356   }
   1357 
   1358   /* First, sort the methods into decreasing order of inclusive
   1359    * elapsed time so that we can assign the method indices.
   1360    */
   1361   qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
   1362 
   1363   for (int32_t ii = 0; ii < numMethods; ++ii) pMethods[ii]->index = ii;
   1364 
   1365   /* Sort the methods into decreasing order of exclusive elapsed time. */
   1366   qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive);
   1367 
   1368   printf("Total cycles: %" PRIu64 "\n\n", sumThreadTime);
   1369   if (gOptions.outputHtml) {
   1370     printf("<br><br>\n");
   1371   }
   1372   printf("Exclusive elapsed times for each method, not including time spent in\n");
   1373   printf("children, sorted by exclusive time.\n\n");
   1374   if (gOptions.outputHtml) {
   1375     printf("<br><br>\n<pre>\n");
   1376   }
   1377 
   1378   printf("    Usecs  self %%  sum %%  Method\n");
   1379 
   1380   double sum = 0;
   1381   double total = sumThreadTime;
   1382   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1383     MethodEntry* method = pMethods[ii];
   1384     /* Don't show methods with zero cycles */
   1385     if (method->elapsedExclusive == 0) break;
   1386     const char* className = method->className;
   1387     const char* methodName = method->methodName;
   1388     const char* signature = method->signature;
   1389     sum += method->elapsedExclusive;
   1390     double per = 100.0 * method->elapsedExclusive / total;
   1391     double sum_per = 100.0 * sum / total;
   1392     if (gOptions.outputHtml) {
   1393       sprintf(anchor_buf, "<a href=\"#m%d\">", method->index);
   1394       className = htmlEscape(className, classBuf, HTML_BUFSIZE);
   1395       methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
   1396       signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
   1397     }
   1398     if (method->methodName) {
   1399       printf("%9" PRIu64 "  %6.2f %6.2f  %s[%d]%s %s.%s %s\n",
   1400              method->elapsedExclusive, per, sum_per, anchor_buf, method->index,
   1401              anchor_close, className, methodName, signature);
   1402     } else {
   1403       printf("%9" PRIu64 "  %6.2f %6.2f  %s[%d]%s %s\n",
   1404              method->elapsedExclusive, per, sum_per, anchor_buf, method->index,
   1405              anchor_close, className);
   1406     }
   1407   }
   1408   if (gOptions.outputHtml) {
   1409     printf("</pre>\n");
   1410   }
   1411 }
   1412 
   1413 /* check to make sure that the child method meets the threshold of the parent */
   1414 int32_t checkThreshold(MethodEntry* parent, MethodEntry* child) {
   1415   double parentTime = parent->elapsedInclusive;
   1416   double childTime = child->elapsedInclusive;
   1417   int64_t percentage = (childTime / parentTime) * 100.0;
   1418   return (percentage < gOptions.threshold) ? 0 : 1;
   1419 }
   1420 
   1421 void createLabels(FILE* file, MethodEntry* method) {
   1422   fprintf(file,
   1423           "node%d[label = \"[%d] %s.%s (%" PRIu64 ", %" PRIu64 ", %d)\"]\n",
   1424           method->index, method->index, method->className, method->methodName,
   1425           method->elapsedInclusive / 1000, method->elapsedExclusive / 1000,
   1426           method->numCalls[0]);
   1427 
   1428   method->graphState = GRAPH_LABEL_VISITED;
   1429 
   1430   for (TimedMethod* child = method->children[0]; child; child = child->next) {
   1431     MethodEntry* childMethod = child->method;
   1432 
   1433     if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 &&
   1434         checkThreshold(method, childMethod)) {
   1435       createLabels(file, child->method);
   1436     }
   1437   }
   1438 }
   1439 
   1440 void createLinks(FILE* file, MethodEntry* method) {
   1441   method->graphState |= GRAPH_NODE_VISITED;
   1442 
   1443   for (TimedMethod* child = method->children[0]; child; child = child->next) {
   1444     MethodEntry* childMethod = child->method;
   1445     if (checkThreshold(method, child->method)) {
   1446       fprintf(file, "node%d -> node%d\n", method->index, child->method->index);
   1447       // only visit children that haven't been visited before
   1448       if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) {
   1449         createLinks(file, child->method);
   1450       }
   1451     }
   1452   }
   1453 }
   1454 
   1455 void createInclusiveProfileGraphNew(DataKeys* dataKeys) {
   1456   // create a temporary file in /tmp
   1457   char path[FILENAME_MAX];
   1458   if (gOptions.keepDotFile) {
   1459     snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName);
   1460   } else {
   1461     snprintf(path, FILENAME_MAX, "dot-%d-%d.dot", (int32_t)time(nullptr), rand());
   1462   }
   1463 
   1464   FILE* file = fopen(path, "w+");
   1465 
   1466   fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n");
   1467 
   1468   createLabels(file, dataKeys->methods);
   1469   createLinks(file, dataKeys->methods);
   1470 
   1471   fprintf(file, "}");
   1472   fclose(file);
   1473 
   1474   // now that we have the dot file generate the image
   1475   char command[1024];
   1476   snprintf(command, 1024, "dot -Tpng -o \"%s\" \"%s\"", gOptions.graphFileName, path);
   1477 
   1478   system(command);
   1479 
   1480   if (!gOptions.keepDotFile) {
   1481     remove(path);
   1482   }
   1483 }
   1484 
   1485 void printInclusiveProfile(MethodEntry** pMethods, int32_t numMethods, uint64_t sumThreadTime) {
   1486   char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
   1487   char signatureBuf[HTML_BUFSIZE];
   1488   char anchor_buf[80];
   1489   const char* anchor_close = "";
   1490   anchor_buf[0] = 0;
   1491   if (gOptions.outputHtml) {
   1492     anchor_close = "</a>";
   1493     printf("<a name=\"inclusive\"></a>\n");
   1494     printf("<hr>\n");
   1495     outputNavigationBar();
   1496   } else {
   1497     printf("\n%s\n", profileSeparator);
   1498   }
   1499 
   1500   /* Sort the methods into decreasing order of inclusive elapsed time. */
   1501   qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
   1502 
   1503   printf("\nInclusive elapsed times for each method and its parents and children,\n");
   1504   printf("sorted by inclusive time.\n\n");
   1505 
   1506   if (gOptions.outputHtml) {
   1507     printf("<br><br>\n<pre>\n");
   1508   }
   1509 
   1510   printf("index  %%/total %%/self  index     calls         usecs name\n");
   1511 
   1512   double total = sumThreadTime;
   1513   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1514     char buf[40];
   1515 
   1516     MethodEntry* method = pMethods[ii];
   1517     /* Don't show methods with zero cycles */
   1518     if (method->elapsedInclusive == 0) break;
   1519 
   1520     const char* className = method->className;
   1521     const char* methodName = method->methodName;
   1522     const char* signature = method->signature;
   1523 
   1524     if (gOptions.outputHtml) {
   1525       printf("<a name=\"m%d\"></a>", method->index);
   1526       className = htmlEscape(className, classBuf, HTML_BUFSIZE);
   1527       methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
   1528       signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
   1529     }
   1530     printf("----------------------------------------------------\n");
   1531 
   1532     /* Sort and print the parents */
   1533     int32_t numCalls = method->numCalls[0] + method->numCalls[1];
   1534     printInclusiveMethod(method, method->parents[0], numCalls, 0);
   1535     if (method->parents[1]) {
   1536       printf("               +++++++++++++++++++++++++\n");
   1537       printInclusiveMethod(method, method->parents[1], numCalls, kIsRecursive);
   1538     }
   1539 
   1540     double per = 100.0 * method->elapsedInclusive / total;
   1541     sprintf(buf, "[%d]", ii);
   1542     if (method->methodName) {
   1543       printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9" PRIu64 " %s.%s %s\n", buf,
   1544              per, "", "", method->numCalls[0], method->numCalls[1],
   1545              method->elapsedInclusive, className, methodName, signature);
   1546     } else {
   1547       printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9" PRIu64 " %s\n", buf, per, "",
   1548              "", method->numCalls[0], method->numCalls[1],
   1549              method->elapsedInclusive, className);
   1550     }
   1551     double excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
   1552     printf("%6s %5s   %5.1f%% %6s %6s %6s %9" PRIu64 "\n", "", "", excl_per,
   1553            "excl", "", "", method->topExclusive);
   1554 
   1555     /* Sort and print the children */
   1556     printInclusiveMethod(method, method->children[0], 0, 0);
   1557     if (method->children[1]) {
   1558       printf("               +++++++++++++++++++++++++\n");
   1559       printInclusiveMethod(method, method->children[1], 0, kIsRecursive);
   1560     }
   1561   }
   1562   if (gOptions.outputHtml) {
   1563     printf("</pre>\n");
   1564   }
   1565 }
   1566 
   1567 void createClassList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) {
   1568   /* Sort the methods into alphabetical order to find the unique class
   1569    * names.
   1570    */
   1571   qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames);
   1572 
   1573   /* Count the number of unique class names. */
   1574   const char* currentClassName = "";
   1575   const char* firstClassName = nullptr;
   1576   traceData->numClasses = 0;
   1577   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1578     if (pMethods[ii]->methodName == nullptr) {
   1579       continue;
   1580     }
   1581     if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
   1582       // Remember the first one
   1583       if (firstClassName == nullptr) {
   1584         firstClassName = pMethods[ii]->className;
   1585       }
   1586       traceData->numClasses += 1;
   1587       currentClassName = pMethods[ii]->className;
   1588     }
   1589   }
   1590 
   1591   if (traceData->numClasses == 0) {
   1592     traceData->classes = nullptr;
   1593     return;
   1594   }
   1595 
   1596   /* Allocate space for all of the unique class names */
   1597   traceData->classes = new ClassEntry[traceData->numClasses];
   1598 
   1599   /* Initialize the classes array */
   1600   memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
   1601   ClassEntry* pClass = traceData->classes;
   1602   pClass->className = currentClassName = firstClassName;
   1603   int32_t prevNumMethods = 0;
   1604   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1605     if (pMethods[ii]->methodName == nullptr) {
   1606       continue;
   1607     }
   1608     if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
   1609       pClass->numMethods = prevNumMethods;
   1610       (++pClass)->className = currentClassName = pMethods[ii]->className;
   1611       prevNumMethods = 0;
   1612     }
   1613     prevNumMethods += 1;
   1614   }
   1615   pClass->numMethods = prevNumMethods;
   1616 
   1617   /* Create the array of MethodEntry pointers for each class */
   1618   pClass = nullptr;
   1619   currentClassName = "";
   1620   int32_t nextMethod = 0;
   1621   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1622     if (pMethods[ii]->methodName == nullptr) {
   1623       continue;
   1624     }
   1625     if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
   1626       currentClassName = pMethods[ii]->className;
   1627       if (pClass == nullptr)
   1628         pClass = traceData->classes;
   1629       else
   1630         pClass++;
   1631       /* Allocate space for the methods array */
   1632       pClass->methods = new MethodEntry*[pClass->numMethods];
   1633       nextMethod = 0;
   1634     }
   1635     pClass->methods[nextMethod++] = pMethods[ii];
   1636   }
   1637 }
   1638 
   1639 /* Prints a number of html non-breaking spaces according so that the length
   1640  * of the string "buf" is at least "width" characters wide.  If width is
   1641  * negative, then trailing spaces are added instead of leading spaces.
   1642  */
   1643 void printHtmlField(char* buf, int32_t width) {
   1644   int32_t leadingSpaces = 1;
   1645   if (width < 0) {
   1646     width = -width;
   1647     leadingSpaces = 0;
   1648   }
   1649   int32_t len = strlen(buf);
   1650   int32_t numSpaces = width - len;
   1651   if (numSpaces <= 0) {
   1652     printf("%s", buf);
   1653     return;
   1654   }
   1655   if (leadingSpaces == 0) printf("%s", buf);
   1656   for (int32_t ii = 0; ii < numSpaces; ++ii) printf("&nbsp;");
   1657   if (leadingSpaces == 1) printf("%s", buf);
   1658 }
   1659 
   1660 void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime) {
   1661   char classBuf[HTML_BUFSIZE];
   1662   char methodBuf[HTML_BUFSIZE];
   1663   char signatureBuf[HTML_BUFSIZE];
   1664 
   1665   if (gOptions.outputHtml) {
   1666     printf("<a name=\"class\"></a>\n");
   1667     printf("<hr>\n");
   1668     outputNavigationBar();
   1669   } else {
   1670     printf("\n%s\n", profileSeparator);
   1671   }
   1672 
   1673   if (traceData->numClasses == 0) {
   1674     printf("\nNo classes.\n");
   1675     if (gOptions.outputHtml) {
   1676       printf("<br><br>\n");
   1677     }
   1678     return;
   1679   }
   1680 
   1681   printf("\nExclusive elapsed time for each class, summed over all the methods\n");
   1682   printf("in the class.\n\n");
   1683   if (gOptions.outputHtml) {
   1684     printf("<br><br>\n");
   1685   }
   1686 
   1687   /* For each class, sum the exclusive times in all of the methods
   1688    * in that class.  Also sum the number of method calls.  Also
   1689    * sort the methods so the most expensive appear at the top.
   1690    */
   1691   ClassEntry* pClass = traceData->classes;
   1692   for (int32_t ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
   1693     // printf("%s %d methods\n", pClass->className, pClass->numMethods);
   1694     int32_t numMethods = pClass->numMethods;
   1695     for (int32_t jj = 0; jj < numMethods; ++jj) {
   1696       MethodEntry* method = pClass->methods[jj];
   1697       pClass->elapsedExclusive += method->elapsedExclusive;
   1698       pClass->numCalls[0] += method->numCalls[0];
   1699       pClass->numCalls[1] += method->numCalls[1];
   1700     }
   1701 
   1702     /* Sort the methods into decreasing order of exclusive time */
   1703     qsort(pClass->methods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive);
   1704   }
   1705 
   1706   /* Allocate an array of pointers to the classes for more efficient sorting. */
   1707   ClassEntry** pClasses = new ClassEntry*[traceData->numClasses];
   1708   for (int32_t ii = 0; ii < traceData->numClasses; ++ii)
   1709     pClasses[ii] = &traceData->classes[ii];
   1710 
   1711   /* Sort the classes into decreasing order of exclusive time */
   1712   qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive);
   1713 
   1714   if (gOptions.outputHtml) {
   1715     printf(
   1716         "<div class=\"header\"><span "
   1717         "class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
   1718     printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Class</div>\n");
   1719   } else {
   1720     printf("   Cycles %%/total Cumul.%%  Calls+Recur  Class\n");
   1721   }
   1722 
   1723   double sum = 0;
   1724   double total = sumThreadTime;
   1725   for (int32_t ii = 0; ii < traceData->numClasses; ++ii) {
   1726     /* Skip classes with zero cycles */
   1727     pClass = pClasses[ii];
   1728     if (pClass->elapsedExclusive == 0) break;
   1729 
   1730     sum += pClass->elapsedExclusive;
   1731     double per = 100.0 * pClass->elapsedExclusive / total;
   1732     double sum_per = 100.0 * sum / total;
   1733     const char* className = pClass->className;
   1734     if (gOptions.outputHtml) {
   1735       char buf[80];
   1736 
   1737       className = htmlEscape(className, classBuf, HTML_BUFSIZE);
   1738       printf(
   1739           "<div class=\"link\" onClick=\"javascript:toggle('d%d')\" "
   1740           "onMouseOver=\"javascript:onMouseOver(this)\" "
   1741           "onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" "
   1742           "id=\"xd%d\">+</span>",
   1743           ii, ii);
   1744       sprintf(buf, "%" PRIu64, pClass->elapsedExclusive);
   1745       printHtmlField(buf, 9);
   1746       printf(" ");
   1747       sprintf(buf, "%.1f", per);
   1748       printHtmlField(buf, 7);
   1749       printf(" ");
   1750       sprintf(buf, "%.1f", sum_per);
   1751       printHtmlField(buf, 7);
   1752       printf(" ");
   1753       sprintf(buf, "%d", pClass->numCalls[0]);
   1754       printHtmlField(buf, 6);
   1755       printf("+");
   1756       sprintf(buf, "%d", pClass->numCalls[1]);
   1757       printHtmlField(buf, -6);
   1758       printf(" ");
   1759       printf("%s", className);
   1760       printf("</div>\n");
   1761       printf("<div class=\"parent\" id=\"d%d\">\n", ii);
   1762     } else {
   1763       printf("---------------------------------------------\n");
   1764       printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n", pClass->elapsedExclusive,
   1765              per, sum_per, pClass->numCalls[0], pClass->numCalls[1], className);
   1766     }
   1767 
   1768     int32_t numMethods = pClass->numMethods;
   1769     double classExclusive = pClass->elapsedExclusive;
   1770     double sumMethods = 0;
   1771     for (int32_t jj = 0; jj < numMethods; ++jj) {
   1772       MethodEntry* method = pClass->methods[jj];
   1773       const char* methodName = method->methodName;
   1774       const char* signature = method->signature;
   1775       per = 100.0 * method->elapsedExclusive / classExclusive;
   1776       sumMethods += method->elapsedExclusive;
   1777       sum_per = 100.0 * sumMethods / classExclusive;
   1778       if (gOptions.outputHtml) {
   1779         char buf[80];
   1780 
   1781         methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
   1782         signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
   1783         printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
   1784         sprintf(buf, "%" PRIu64, method->elapsedExclusive);
   1785         printHtmlField(buf, 9);
   1786         printf("&nbsp;");
   1787         sprintf(buf, "%" PRIu64, method->elapsedInclusive);
   1788         printHtmlField(buf, 9);
   1789         printf("&nbsp;");
   1790         sprintf(buf, "%.1f", per);
   1791         printHtmlField(buf, 7);
   1792         printf("&nbsp;");
   1793         sprintf(buf, "%.1f", sum_per);
   1794         printHtmlField(buf, 7);
   1795         printf("&nbsp;");
   1796         sprintf(buf, "%d", method->numCalls[0]);
   1797         printHtmlField(buf, 6);
   1798         printf("+");
   1799         sprintf(buf, "%d", method->numCalls[1]);
   1800         printHtmlField(buf, -6);
   1801         printf("&nbsp;");
   1802         printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s&nbsp;%s", method->index,
   1803                method->index, methodName, signature);
   1804         printf("</div>\n");
   1805       } else {
   1806         printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s %s\n",
   1807                method->elapsedExclusive, method->elapsedInclusive, per, sum_per,
   1808                method->numCalls[0], method->numCalls[1], method->index,
   1809                methodName, signature);
   1810       }
   1811     }
   1812     if (gOptions.outputHtml) {
   1813       printf("</div>\n");
   1814     }
   1815   }
   1816 }
   1817 
   1818 void createUniqueMethodList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) {
   1819   /* Sort the methods into alphabetical order of method names
   1820    * to find the unique method names.
   1821    */
   1822   qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames);
   1823 
   1824   /* Count the number of unique method names, ignoring class and signature. */
   1825   const char* currentMethodName = "";
   1826   traceData->numUniqueMethods = 0;
   1827   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1828     if (pMethods[ii]->methodName == nullptr) continue;
   1829     if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
   1830       traceData->numUniqueMethods += 1;
   1831       currentMethodName = pMethods[ii]->methodName;
   1832     }
   1833   }
   1834   if (traceData->numUniqueMethods == 0) return;
   1835 
   1836   /* Allocate space for pointers to all of the unique methods */
   1837   traceData->uniqueMethods = new UniqueMethodEntry[traceData->numUniqueMethods];
   1838 
   1839   /* Initialize the uniqueMethods array */
   1840   memset(traceData->uniqueMethods, 0, sizeof(UniqueMethodEntry) * traceData->numUniqueMethods);
   1841   UniqueMethodEntry* pUnique = traceData->uniqueMethods;
   1842   currentMethodName = nullptr;
   1843   int32_t prevNumMethods = 0;
   1844   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1845     if (pMethods[ii]->methodName == nullptr) continue;
   1846     if (currentMethodName == nullptr) currentMethodName = pMethods[ii]->methodName;
   1847     if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
   1848       currentMethodName = pMethods[ii]->methodName;
   1849       pUnique->numMethods = prevNumMethods;
   1850       pUnique++;
   1851       prevNumMethods = 0;
   1852     }
   1853     prevNumMethods += 1;
   1854   }
   1855   pUnique->numMethods = prevNumMethods;
   1856 
   1857   /* Create the array of MethodEntry pointers for each unique method */
   1858   pUnique = nullptr;
   1859   currentMethodName = "";
   1860   int32_t nextMethod = 0;
   1861   for (int32_t ii = 0; ii < numMethods; ++ii) {
   1862     if (pMethods[ii]->methodName == nullptr) continue;
   1863     if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
   1864       currentMethodName = pMethods[ii]->methodName;
   1865       if (pUnique == nullptr)
   1866         pUnique = traceData->uniqueMethods;
   1867       else
   1868         pUnique++;
   1869       /* Allocate space for the methods array */
   1870       pUnique->methods = new MethodEntry*[pUnique->numMethods];
   1871       nextMethod = 0;
   1872     }
   1873     pUnique->methods[nextMethod++] = pMethods[ii];
   1874   }
   1875 }
   1876 
   1877 void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime) {
   1878   char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
   1879   char signatureBuf[HTML_BUFSIZE];
   1880 
   1881   if (traceData->numUniqueMethods == 0) return;
   1882 
   1883   if (gOptions.outputHtml) {
   1884     printf("<a name=\"method\"></a>\n");
   1885     printf("<hr>\n");
   1886     outputNavigationBar();
   1887   } else {
   1888     printf("\n%s\n", profileSeparator);
   1889   }
   1890 
   1891   printf("\nExclusive elapsed time for each method, summed over all the classes\n");
   1892   printf("that contain a method with the same name.\n\n");
   1893   if (gOptions.outputHtml) {
   1894     printf("<br><br>\n");
   1895   }
   1896 
   1897   /* For each unique method, sum the exclusive times in all of the methods
   1898    * with the same name.  Also sum the number of method calls.  Also
   1899    * sort the methods so the most expensive appear at the top.
   1900    */
   1901   UniqueMethodEntry* pUnique = traceData->uniqueMethods;
   1902   for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
   1903     int32_t numMethods = pUnique->numMethods;
   1904     for (int32_t jj = 0; jj < numMethods; ++jj) {
   1905       MethodEntry* method = pUnique->methods[jj];
   1906       pUnique->elapsedExclusive += method->elapsedExclusive;
   1907       pUnique->numCalls[0] += method->numCalls[0];
   1908       pUnique->numCalls[1] += method->numCalls[1];
   1909     }
   1910 
   1911     /* Sort the methods into decreasing order of exclusive time */
   1912     qsort(pUnique->methods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive);
   1913   }
   1914 
   1915   /* Allocate an array of pointers to the methods for more efficient sorting. */
   1916   UniqueMethodEntry** pUniqueMethods = new UniqueMethodEntry*[traceData->numUniqueMethods];
   1917   for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii)
   1918     pUniqueMethods[ii] = &traceData->uniqueMethods[ii];
   1919 
   1920   /* Sort the methods into decreasing order of exclusive time */
   1921   qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*),
   1922         compareUniqueExclusive);
   1923 
   1924   if (gOptions.outputHtml) {
   1925     printf(
   1926         "<div class=\"header\"><span "
   1927         "class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
   1928     printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Method</div>\n");
   1929   } else {
   1930     printf("   Cycles %%/total Cumul.%%  Calls+Recur  Method\n");
   1931   }
   1932 
   1933   double sum = 0;
   1934   double total = sumThreadTime;
   1935   for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii) {
   1936     /* Skip methods with zero cycles */
   1937     pUnique = pUniqueMethods[ii];
   1938     if (pUnique->elapsedExclusive == 0) break;
   1939 
   1940     sum += pUnique->elapsedExclusive;
   1941     double per = 100.0 * pUnique->elapsedExclusive / total;
   1942     double sum_per = 100.0 * sum / total;
   1943     const char* methodName = pUnique->methods[0]->methodName;
   1944     if (gOptions.outputHtml) {
   1945       char buf[80];
   1946 
   1947       methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
   1948       printf(
   1949           "<div class=\"link\" onClick=\"javascript:toggle('e%d')\" "
   1950           "onMouseOver=\"javascript:onMouseOver(this)\" "
   1951           "onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" "
   1952           "id=\"xe%d\">+</span>",
   1953           ii, ii);
   1954       sprintf(buf, "%" PRIu64, pUnique->elapsedExclusive);
   1955       printHtmlField(buf, 9);
   1956       printf(" ");
   1957       sprintf(buf, "%.1f", per);
   1958       printHtmlField(buf, 7);
   1959       printf(" ");
   1960       sprintf(buf, "%.1f", sum_per);
   1961       printHtmlField(buf, 7);
   1962       printf(" ");
   1963       sprintf(buf, "%d", pUnique->numCalls[0]);
   1964       printHtmlField(buf, 6);
   1965       printf("+");
   1966       sprintf(buf, "%d", pUnique->numCalls[1]);
   1967       printHtmlField(buf, -6);
   1968       printf(" ");
   1969       printf("%s", methodName);
   1970       printf("</div>\n");
   1971       printf("<div class=\"parent\" id=\"e%d\">\n", ii);
   1972     } else {
   1973       printf("---------------------------------------------\n");
   1974       printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n",
   1975              pUnique->elapsedExclusive, per, sum_per, pUnique->numCalls[0],
   1976              pUnique->numCalls[1], methodName);
   1977     }
   1978     int32_t numMethods = pUnique->numMethods;
   1979     double methodExclusive = pUnique->elapsedExclusive;
   1980     double sumMethods = 0;
   1981     for (int32_t jj = 0; jj < numMethods; ++jj) {
   1982       MethodEntry* method = pUnique->methods[jj];
   1983       const char* className = method->className;
   1984       const char* signature = method->signature;
   1985       per = 100.0 * method->elapsedExclusive / methodExclusive;
   1986       sumMethods += method->elapsedExclusive;
   1987       sum_per = 100.0 * sumMethods / methodExclusive;
   1988       if (gOptions.outputHtml) {
   1989         char buf[80];
   1990 
   1991         className = htmlEscape(className, classBuf, HTML_BUFSIZE);
   1992         signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
   1993         printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
   1994         sprintf(buf, "%" PRIu64, method->elapsedExclusive);
   1995         printHtmlField(buf, 9);
   1996         printf("&nbsp;");
   1997         sprintf(buf, "%" PRIu64, method->elapsedInclusive);
   1998         printHtmlField(buf, 9);
   1999         printf("&nbsp;");
   2000         sprintf(buf, "%.1f", per);
   2001         printHtmlField(buf, 7);
   2002         printf("&nbsp;");
   2003         sprintf(buf, "%.1f", sum_per);
   2004         printHtmlField(buf, 7);
   2005         printf("&nbsp;");
   2006         sprintf(buf, "%d", method->numCalls[0]);
   2007         printHtmlField(buf, 6);
   2008         printf("+");
   2009         sprintf(buf, "%d", method->numCalls[1]);
   2010         printHtmlField(buf, -6);
   2011         printf("&nbsp;");
   2012         printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s.%s&nbsp;%s", method->index,
   2013                method->index, className, methodName, signature);
   2014         printf("</div>\n");
   2015       } else {
   2016         printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n",
   2017                method->elapsedExclusive, method->elapsedInclusive, per, sum_per,
   2018                method->numCalls[0], method->numCalls[1], method->index,
   2019                className, methodName, signature);
   2020       }
   2021     }
   2022     if (gOptions.outputHtml) {
   2023       printf("</div>\n");
   2024     }
   2025   }
   2026 }
   2027 
   2028 /*
   2029  * Read the key and data files and return the MethodEntries for those files
   2030  */
   2031 DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime) {
   2032   MethodEntry* caller;
   2033 
   2034   FILE* dataFp = fopen(traceFileName, "rb");
   2035   if (dataFp == nullptr) return nullptr;
   2036 
   2037   DataKeys* dataKeys = parseKeys(dataFp, 0);
   2038   if (dataKeys == nullptr) {
   2039     fclose(dataFp);
   2040     return nullptr;
   2041   }
   2042 
   2043   DataHeader dataHeader;
   2044   if (parseDataHeader(dataFp, &dataHeader) < 0) {
   2045     fclose(dataFp);
   2046     return dataKeys;
   2047   }
   2048 
   2049 #if 0
   2050   FILE* dumpStream = fopen("debug", "w");
   2051 #endif
   2052   while (1) {
   2053     /*
   2054      * Extract values from file.
   2055      */
   2056     int32_t threadId;
   2057     uint32_t methodVal;
   2058     uint64_t currentTime;
   2059     if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &currentTime))
   2060       break;
   2061 
   2062     int32_t action = METHOD_ACTION(methodVal);
   2063     int64_t methodId = METHOD_ID(methodVal);
   2064 
   2065     /* Get the call stack for this thread */
   2066     CallStack* pStack = traceData->stacks[threadId];
   2067 
   2068     /* If there is no call stack yet for this thread, then allocate one */
   2069     if (pStack == nullptr) {
   2070       pStack = new CallStack();
   2071       pStack->top = 0;
   2072       pStack->lastEventTime = currentTime;
   2073       pStack->threadStartTime = currentTime;
   2074       traceData->stacks[threadId] = pStack;
   2075     }
   2076 
   2077     /* Lookup the current method */
   2078     MethodEntry* method = lookupMethod(dataKeys, methodId);
   2079     if (method == nullptr) method = &dataKeys->methods[UNKNOWN_INDEX];
   2080 
   2081 #if 0
   2082     if (method->methodName) {
   2083       fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
   2084               threadId, currentTime, action, pStack->threadStartTime,
   2085               method->recursiveEntries,
   2086               pStack->top, method->className, method->methodName,
   2087               method->signature);
   2088     } else {
   2089       fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
   2090               threadId, currentTime, action, pStack->threadStartTime,
   2091               method->recursiveEntries,
   2092               pStack->top, method->className);
   2093     }
   2094 #endif
   2095 
   2096     if (action == METHOD_TRACE_ENTER) {
   2097       /* This is a method entry */
   2098       if (pStack->top >= MAX_STACK_DEPTH) {
   2099         fprintf(stderr, "Stack overflow (exceeded %d frames)\n",
   2100                 MAX_STACK_DEPTH);
   2101         exit(1);
   2102       }
   2103 
   2104       /* Get the caller method */
   2105       if (pStack->top >= 1)
   2106         caller = pStack->calls[pStack->top - 1].method;
   2107       else
   2108         caller = &dataKeys->methods[TOPLEVEL_INDEX];
   2109       countRecursiveEntries(pStack, pStack->top, caller);
   2110       caller->elapsedExclusive += currentTime - pStack->lastEventTime;
   2111 #if 0
   2112       if (caller->elapsedExclusive > 10000000)
   2113         fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n",
   2114                 caller->elapsedExclusive, currentTime,
   2115                 pStack->lastEventTime,
   2116                 currentTime - pStack->lastEventTime);
   2117 #endif
   2118       if (caller->recursiveEntries <= 1) {
   2119         caller->topExclusive += currentTime - pStack->lastEventTime;
   2120       }
   2121 
   2122       /* Push the method on the stack for this thread */
   2123       pStack->calls[pStack->top].method = method;
   2124       pStack->calls[pStack->top++].entryTime = currentTime;
   2125     } else {
   2126       /* This is a method exit */
   2127       uint64_t entryTime = 0;
   2128 
   2129       /* Pop the method off the stack for this thread */
   2130       if (pStack->top > 0) {
   2131         pStack->top -= 1;
   2132         entryTime = pStack->calls[pStack->top].entryTime;
   2133         if (method != pStack->calls[pStack->top].method) {
   2134           if (method->methodName) {
   2135             fprintf(stderr, "Exit from method %s.%s %s does not match stack:\n",
   2136                     method->className, method->methodName, method->signature);
   2137           } else {
   2138             fprintf(stderr, "Exit from method %s does not match stack:\n",
   2139                     method->className);
   2140           }
   2141           stackDump(pStack, pStack->top + 1);
   2142           exit(1);
   2143         }
   2144       }
   2145 
   2146       /* Get the caller method */
   2147       if (pStack->top >= 1)
   2148         caller = pStack->calls[pStack->top - 1].method;
   2149       else
   2150         caller = &dataKeys->methods[TOPLEVEL_INDEX];
   2151       countRecursiveEntries(pStack, pStack->top, caller);
   2152       countRecursiveEntries(pStack, pStack->top, method);
   2153       uint64_t elapsed = currentTime - entryTime;
   2154       addInclusiveTime(caller, method, elapsed);
   2155       method->elapsedExclusive += currentTime - pStack->lastEventTime;
   2156       if (method->recursiveEntries == 0) {
   2157         method->topExclusive += currentTime - pStack->lastEventTime;
   2158       }
   2159     }
   2160     /* Remember the time of the last entry or exit event */
   2161     pStack->lastEventTime = currentTime;
   2162   }
   2163 
   2164   /* If we have calls on the stack when the trace ends, then clean
   2165    * up the stack and add time to the callers by pretending that we
   2166    * are exiting from their methods now.
   2167    */
   2168   uint64_t sumThreadTime = 0;
   2169   for (int32_t threadId = 0; threadId < MAX_THREADS; ++threadId) {
   2170     CallStack* pStack = traceData->stacks[threadId];
   2171 
   2172     /* If this thread never existed, then continue with next thread */
   2173     if (pStack == nullptr) continue;
   2174 
   2175     /* Also, add up the time taken by all of the threads */
   2176     sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
   2177 
   2178     for (int32_t ii = 0; ii < pStack->top; ++ii) {
   2179       if (ii == 0)
   2180         caller = &dataKeys->methods[TOPLEVEL_INDEX];
   2181       else
   2182         caller = pStack->calls[ii - 1].method;
   2183       MethodEntry* method = pStack->calls[ii].method;
   2184       countRecursiveEntries(pStack, ii, caller);
   2185       countRecursiveEntries(pStack, ii, method);
   2186 
   2187       uint64_t entryTime = pStack->calls[ii].entryTime;
   2188       uint64_t elapsed = pStack->lastEventTime - entryTime;
   2189       addInclusiveTime(caller, method, elapsed);
   2190     }
   2191   }
   2192   caller = &dataKeys->methods[TOPLEVEL_INDEX];
   2193   caller->elapsedInclusive = sumThreadTime;
   2194 
   2195 #if 0
   2196   fclose(dumpStream);
   2197 #endif
   2198 
   2199   if (threadTime != nullptr) {
   2200     *threadTime = sumThreadTime;
   2201   }
   2202 
   2203   fclose(dataFp);
   2204   return dataKeys;
   2205 }
   2206 
   2207 MethodEntry** parseMethodEntries(DataKeys* dataKeys) {
   2208   /* Create a new array of pointers to the methods and sort the pointers
   2209    * instead of the actual MethodEntry structs.  We need to do this
   2210    * because there are other lists that contain pointers to the
   2211    * MethodEntry structs.
   2212    */
   2213   MethodEntry** pMethods = new MethodEntry*[dataKeys->numMethods];
   2214   for (int32_t ii = 0; ii < dataKeys->numMethods; ++ii) {
   2215     MethodEntry* entry = &dataKeys->methods[ii];
   2216     pMethods[ii] = entry;
   2217   }
   2218 
   2219   return pMethods;
   2220 }
   2221 
   2222 /*
   2223  * Produce a function profile from the following methods
   2224  */
   2225 void profileTrace(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods,
   2226                   uint64_t sumThreadTime) {
   2227   /* Print the html header, if necessary */
   2228   if (gOptions.outputHtml) {
   2229     printf(htmlHeader, gOptions.sortableUrl);
   2230     outputTableOfContents();
   2231   }
   2232 
   2233   printExclusiveProfile(pMethods, numMethods, sumThreadTime);
   2234   printInclusiveProfile(pMethods, numMethods, sumThreadTime);
   2235 
   2236   createClassList(traceData, pMethods, numMethods);
   2237   printClassProfiles(traceData, sumThreadTime);
   2238 
   2239   createUniqueMethodList(traceData, pMethods, numMethods);
   2240   printMethodProfiles(traceData, sumThreadTime);
   2241 
   2242   if (gOptions.outputHtml) {
   2243     printf("%s", htmlFooter);
   2244   }
   2245 }
   2246 
   2247 int32_t compareMethodNamesForDiff(const void* a, const void* b) {
   2248   const MethodEntry* methodA = *(const MethodEntry**) a;
   2249   const MethodEntry* methodB = *(const MethodEntry**) b;
   2250   if (methodA->methodName == nullptr || methodB->methodName == nullptr) {
   2251     return compareClassNames(a, b);
   2252   }
   2253   int32_t result = strcmp(methodA->methodName, methodB->methodName);
   2254   if (result == 0) {
   2255     result = strcmp(methodA->signature, methodB->signature);
   2256     if (result == 0) {
   2257       return strcmp(methodA->className, methodB->className);
   2258     }
   2259   }
   2260   return result;
   2261 }
   2262 
   2263 int32_t findMatch(MethodEntry** methods, int32_t size, MethodEntry* matchThis) {
   2264   for (int32_t i = 0; i < size; i++) {
   2265     MethodEntry* method = methods[i];
   2266 
   2267     if (method != nullptr && !compareMethodNamesForDiff(&method, &matchThis)) {
   2268       // printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName,
   2269       //        method->className, method->methodName);
   2270 
   2271       return i;
   2272       // if (!compareMethodNames(&method, &matchThis)) return i;
   2273     }
   2274   }
   2275 
   2276   return -1;
   2277 }
   2278 
   2279 int32_t compareDiffEntriesExculsive(const void* a, const void* b) {
   2280   const DiffEntry* entryA = (const DiffEntry*) a;
   2281   const DiffEntry* entryB = (const DiffEntry*) b;
   2282 
   2283   if (entryA->differenceExclusive < entryB->differenceExclusive) {
   2284     return 1;
   2285   } else if (entryA->differenceExclusive > entryB->differenceExclusive) {
   2286     return -1;
   2287   }
   2288 
   2289   return 0;
   2290 }
   2291 
   2292 int32_t compareDiffEntriesInculsive(const void* a, const void* b) {
   2293   const DiffEntry* entryA = (const DiffEntry*) a;
   2294   const DiffEntry* entryB = (const DiffEntry*) b;
   2295 
   2296   if (entryA->differenceInclusive < entryB->differenceInclusive) {
   2297     return 1;
   2298   } else if (entryA->differenceInclusive > entryB->differenceInclusive) {
   2299     return -1;
   2300   }
   2301 
   2302   return 0;
   2303 }
   2304 
   2305 void printMissingMethod(MethodEntry* method) {
   2306   char classBuf[HTML_BUFSIZE];
   2307   char methodBuf[HTML_BUFSIZE];
   2308 
   2309   char* className = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
   2310   char* methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
   2311 
   2312   if (gOptions.outputHtml) printf("<tr><td>\n");
   2313 
   2314   printf("%s.%s ", className, methodName);
   2315   if (gOptions.outputHtml) printf("</td><td>");
   2316 
   2317   printf("%" PRIu64 " ", method->elapsedExclusive);
   2318   if (gOptions.outputHtml) printf("</td><td>");
   2319 
   2320   printf("%" PRIu64 " ", method->elapsedInclusive);
   2321   if (gOptions.outputHtml) printf("</td><td>");
   2322 
   2323   printf("%d\n", method->numCalls[0]);
   2324   if (gOptions.outputHtml) printf("</td><td>\n");
   2325 }
   2326 
   2327 void createDiff(DataKeys* d1, DataKeys* d2) {
   2328   MethodEntry** methods1 = parseMethodEntries(d1);
   2329   MethodEntry** methods2 = parseMethodEntries(d2);
   2330 
   2331   // sort and assign the indicies
   2332   qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
   2333   for (int32_t i = 0; i < d1->numMethods; ++i) {
   2334     methods1[i]->index = i;
   2335   }
   2336 
   2337   qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
   2338   for (int32_t i = 0; i < d2->numMethods; ++i) {
   2339     methods2[i]->index = i;
   2340   }
   2341 
   2342   int32_t max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
   2343   max++;
   2344   DiffEntry* diffs = new DiffEntry[max];
   2345   memset(diffs, 0, max * sizeof(DiffEntry));
   2346   DiffEntry* ptr = diffs;
   2347 
   2348   // printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n",
   2349   //        d1->numMethods, d2->numMethods);
   2350 
   2351   int32_t matches = 0;
   2352 
   2353   for (int32_t i = 0; i < d1->numMethods; i++) {
   2354     int32_t match = findMatch(methods2, d2->numMethods, methods1[i]);
   2355     if (match >= 0) {
   2356       ptr->method1 = methods1[i];
   2357       ptr->method2 = methods2[match];
   2358 
   2359       uint64_t e1 = ptr->method1->elapsedExclusive;
   2360       uint64_t e2 = ptr->method2->elapsedExclusive;
   2361       if (e1 > 0) {
   2362         ptr->differenceExclusive = e2 - e1;
   2363         ptr->differenceExclusivePercentage = (static_cast<double>(e2) /
   2364                                               static_cast<double>(e1)) * 100.0;
   2365       }
   2366 
   2367       uint64_t i1 = ptr->method1->elapsedInclusive;
   2368       uint64_t i2 = ptr->method2->elapsedInclusive;
   2369       if (i1 > 0) {
   2370         ptr->differenceInclusive = i2 - i1;
   2371         ptr->differenceInclusivePercentage = (static_cast<double>(i2) /
   2372                                               static_cast<double>(i1)) * 100.0;
   2373       }
   2374 
   2375       // clear these out so we don't find them again and we know which ones
   2376       // we have left over
   2377       methods1[i] = nullptr;
   2378       methods2[match] = nullptr;
   2379       ptr++;
   2380 
   2381       matches++;
   2382     }
   2383   }
   2384   ptr->method1 = nullptr;
   2385   ptr->method2 = nullptr;
   2386 
   2387   qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
   2388   ptr = diffs;
   2389 
   2390   if (gOptions.outputHtml) {
   2391     printf(htmlHeader, gOptions.sortableUrl);
   2392     printf("<h3>Table of Contents</h3>\n");
   2393     printf("<ul>\n");
   2394     printf("<li><a href='#exclusive'>Exclusive</a>\n");
   2395     printf("<li><a href='#inclusive'>Inclusive</a>\n");
   2396     printf("</ul>\n");
   2397     printf("Run 1: %s<br>\n", gOptions.diffFileName);
   2398     printf("Run 2: %s<br>\n", gOptions.traceFileName);
   2399     printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n");
   2400     printf(tableHeader, "exclusive_table");
   2401   }
   2402 
   2403   char classBuf[HTML_BUFSIZE];
   2404   char methodBuf[HTML_BUFSIZE];
   2405   while (ptr->method1 != nullptr && ptr->method2 != nullptr) {
   2406     if (gOptions.outputHtml) printf("<tr><td>\n");
   2407 
   2408     char* className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
   2409     char* methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
   2410 
   2411     printf("%s.%s ", className, methodName);
   2412     if (gOptions.outputHtml) printf("</td><td>");
   2413 
   2414     printf("%" PRIu64 " ", ptr->method1->elapsedExclusive);
   2415     if (gOptions.outputHtml) printf("</td><td>");
   2416 
   2417     printf("%" PRIu64 " ", ptr->method2->elapsedExclusive);
   2418     if (gOptions.outputHtml) printf("</td><td>");
   2419 
   2420     printf("%" PRIu64 " ", ptr->differenceExclusive);
   2421     if (gOptions.outputHtml) printf("</td><td>");
   2422 
   2423     printf("%.2f\n", ptr->differenceExclusivePercentage);
   2424     if (gOptions.outputHtml) printf("</td><td>\n");
   2425 
   2426     printf("%d\n", ptr->method1->numCalls[0]);
   2427     if (gOptions.outputHtml) printf("</td><td>\n");
   2428 
   2429     printf("%d\n", ptr->method2->numCalls[0]);
   2430     if (gOptions.outputHtml) printf("</td></tr>\n");
   2431 
   2432     ptr++;
   2433   }
   2434 
   2435   if (gOptions.outputHtml) printf("</table>\n");
   2436 
   2437   if (gOptions.outputHtml) {
   2438     printf(htmlHeader, gOptions.sortableUrl);
   2439     printf("Run 1: %s<br>\n", gOptions.diffFileName);
   2440     printf("Run 2: %s<br>\n", gOptions.traceFileName);
   2441     printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n");
   2442     printf(tableHeader, "inclusive_table");
   2443   }
   2444 
   2445   qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive);
   2446   ptr = diffs;
   2447 
   2448   while (ptr->method1 != nullptr && ptr->method2 != nullptr) {
   2449     if (gOptions.outputHtml) printf("<tr><td>\n");
   2450 
   2451     char* className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
   2452     char* methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
   2453 
   2454     printf("%s.%s ", className, methodName);
   2455     if (gOptions.outputHtml) printf("</td><td>");
   2456 
   2457     printf("%" PRIu64 " ", ptr->method1->elapsedInclusive);
   2458     if (gOptions.outputHtml) printf("</td><td>");
   2459 
   2460     printf("%" PRIu64 " ", ptr->method2->elapsedInclusive);
   2461     if (gOptions.outputHtml) printf("</td><td>");
   2462 
   2463     printf("%" PRIu64 " ", ptr->differenceInclusive);
   2464     if (gOptions.outputHtml) printf("</td><td>");
   2465 
   2466     printf("%.2f\n", ptr->differenceInclusivePercentage);
   2467     if (gOptions.outputHtml) printf("</td><td>\n");
   2468 
   2469     printf("%d\n", ptr->method1->numCalls[0]);
   2470     if (gOptions.outputHtml) printf("</td><td>\n");
   2471 
   2472     printf("%d\n", ptr->method2->numCalls[0]);
   2473     if (gOptions.outputHtml) printf("</td></tr>\n");
   2474 
   2475     ptr++;
   2476   }
   2477 
   2478   if (gOptions.outputHtml) {
   2479     printf("</table>\n");
   2480     printf("<h3>Run 1 methods not found in Run 2</h3>");
   2481     printf(tableHeaderMissing, "?");
   2482   }
   2483 
   2484   for (int32_t i = 0; i < d1->numMethods; ++i) {
   2485     if (methods1[i] != nullptr) {
   2486       printMissingMethod(methods1[i]);
   2487     }
   2488   }
   2489 
   2490   if (gOptions.outputHtml) {
   2491     printf("</table>\n");
   2492     printf("<h3>Run 2 methods not found in Run 1</h3>");
   2493     printf(tableHeaderMissing, "?");
   2494   }
   2495 
   2496   for (int32_t i = 0; i < d2->numMethods; ++i) {
   2497     if (methods2[i] != nullptr) {
   2498       printMissingMethod(methods2[i]);
   2499     }
   2500   }
   2501 
   2502   if (gOptions.outputHtml) printf("</body></html\n");
   2503 }
   2504 
   2505 int32_t usage(const char* program) {
   2506   fprintf(stderr, "Copyright (C) 2006 The Android Open Source Project\n\n");
   2507   fprintf(stderr,
   2508           "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] "
   2509           "trace-file-name\n",
   2510           program);
   2511   fprintf(stderr, "  -d trace-file-name  - Diff with this trace\n");
   2512   fprintf(stderr, "  -g outfile          - Write graph to 'outfile'\n");
   2513   fprintf(stderr,
   2514           "  -k                  - When writing a graph, keep the intermediate "
   2515           "DOT file\n");
   2516   fprintf(stderr, "  -h                  - Turn on HTML output\n");
   2517   fprintf(
   2518       stderr,
   2519       "  -o                  - Dump the dmtrace file instead of profiling\n");
   2520   fprintf(stderr,
   2521           "  -s                  - URL base to where the sortable javascript "
   2522           "file\n");
   2523   fprintf(stderr,
   2524           "  -t threshold        - Threshold percentage for including nodes in "
   2525           "the graph\n");
   2526   return 2;
   2527 }
   2528 
   2529 // Returns true if there was an error
   2530 int32_t parseOptions(int32_t argc, char** argv) {
   2531   while (1) {
   2532     int32_t opt = getopt(argc, argv, "d:hg:kos:t:");
   2533     if (opt == -1) break;
   2534     switch (opt) {
   2535       case 'd':
   2536         gOptions.diffFileName = optarg;
   2537         break;
   2538       case 'g':
   2539         gOptions.graphFileName = optarg;
   2540         break;
   2541       case 'k':
   2542         gOptions.keepDotFile = 1;
   2543         break;
   2544       case 'h':
   2545         gOptions.outputHtml = 1;
   2546         break;
   2547       case 'o':
   2548         gOptions.dump = 1;
   2549         break;
   2550       case 's':
   2551         gOptions.sortableUrl = optarg;
   2552         break;
   2553       case 't':
   2554         gOptions.threshold = atoi(optarg);
   2555         break;
   2556       default:
   2557         return 1;
   2558     }
   2559   }
   2560   return 0;
   2561 }
   2562 
   2563 /*
   2564  * Parse args.
   2565  */
   2566 int32_t main(int32_t argc, char** argv) {
   2567   gOptions.threshold = -1;
   2568 
   2569   // Parse the options
   2570   if (parseOptions(argc, argv) || argc - optind != 1) return usage(argv[0]);
   2571 
   2572   gOptions.traceFileName = argv[optind];
   2573 
   2574   if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
   2575     gOptions.threshold = 20;
   2576   }
   2577 
   2578   if (gOptions.dump) {
   2579     dumpTrace();
   2580     return 0;
   2581   }
   2582 
   2583   uint64_t sumThreadTime = 0;
   2584 
   2585   TraceData data1;
   2586   DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName, &sumThreadTime);
   2587   if (dataKeys == nullptr) {
   2588     fprintf(stderr, "Cannot read \"%s\".\n", gOptions.traceFileName);
   2589     exit(1);
   2590   }
   2591 
   2592   if (gOptions.diffFileName != nullptr) {
   2593     uint64_t sum2;
   2594     TraceData data2;
   2595     DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2);
   2596     if (d2 == nullptr) {
   2597       fprintf(stderr, "Cannot read \"%s\".\n", gOptions.diffFileName);
   2598       exit(1);
   2599     }
   2600 
   2601     createDiff(d2, dataKeys);
   2602 
   2603     freeDataKeys(d2);
   2604   } else {
   2605     MethodEntry** methods = parseMethodEntries(dataKeys);
   2606     profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime);
   2607     if (gOptions.graphFileName != nullptr) {
   2608       createInclusiveProfileGraphNew(dataKeys);
   2609     }
   2610     free(methods);
   2611   }
   2612 
   2613   freeDataKeys(dataKeys);
   2614 
   2615   return 0;
   2616 }
   2617