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