Home | History | Annotate | Download | only in dmtracedump
      1 /*
      2  * Copyright 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  * Create a test file in the format required by dmtrace.
     19  */
     20 #include "profile.h"  // from VM header
     21 
     22 #include <assert.h>
     23 #include <ctype.h>
     24 #include <errno.h>
     25 #include <stdint.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <sys/time.h>
     30 #include <time.h>
     31 #include <unistd.h>
     32 
     33 /*
     34  * Values from the header of the data file.
     35  */
     36 typedef struct DataHeader {
     37   uint32_t magic;
     38   int16_t version;
     39   int16_t offsetToData;
     40   int64_t startWhen;
     41 } DataHeader;
     42 
     43 #define VERSION 2
     44 int32_t versionNumber = VERSION;
     45 int32_t verbose = 0;
     46 
     47 DataHeader header = {0x574f4c53, VERSION, sizeof(DataHeader), 0LL};
     48 
     49 const char* versionHeader = "*version\n";
     50 const char* clockDef = "clock=thread-cpu\n";
     51 
     52 const char* keyThreads =
     53     "*threads\n"
     54     "1      main\n"
     55     "2      foo\n"
     56     "3      bar\n"
     57     "4      blah\n";
     58 
     59 const char* keyEnd = "*end\n";
     60 
     61 typedef struct dataRecord {
     62   uint32_t time;
     63   int32_t threadId;
     64   uint32_t action; /* 0=entry, 1=exit, 2=exception exit */
     65   char* fullName;
     66   char* className;
     67   char* methodName;
     68   char* signature;
     69   uint32_t methodId;
     70 } dataRecord;
     71 
     72 dataRecord* records;
     73 
     74 #define BUF_SIZE 1024
     75 char buf[BUF_SIZE];
     76 
     77 typedef struct stack {
     78   dataRecord** frames;
     79   int32_t indentLevel;
     80 } stack;
     81 
     82 /* Mac OS doesn't have strndup(), so implement it here.
     83  */
     84 char* strndup(const char* src, size_t len) {
     85   char* dest = new char[len + 1];
     86   strncpy(dest, src, len);
     87   dest[len] = 0;
     88   return dest;
     89 }
     90 
     91 /*
     92  * Parse the input file.  It looks something like this:
     93  * # This is a comment line
     94  * 4  1 A
     95  * 6  1  B
     96  * 8  1  B
     97  * 10 1 A
     98  *
     99  * where the first column is the time, the second column is the thread id,
    100  * and the third column is the method (actually just the class name).  The
    101  * number of spaces between the 2nd and 3rd columns is the indentation and
    102  * determines the call stack.  Each called method must be indented by one
    103  * more space.  In the example above, A is called at time 4, A calls B at
    104  * time 6, B returns at time 8, and A returns at time 10.  Thread 1 is the
    105  * only thread that is running.
    106  *
    107  * An alternative file format leaves out the first two columns:
    108  * A
    109  *  B
    110  *  B
    111  * A
    112  *
    113  * In this file format, the thread id is always 1, and the time starts at
    114  * 2 and increments by 2 for each line.
    115  */
    116 void parseInputFile(const char* inputFileName) {
    117   FILE* inputFp = fopen(inputFileName, "r");
    118   if (inputFp == nullptr) {
    119     perror(inputFileName);
    120     exit(1);
    121   }
    122 
    123   /* Count the number of lines in the buffer */
    124   int32_t numRecords = 0;
    125   int32_t maxThreadId = 1;
    126   int32_t maxFrames = 0;
    127   char* indentEnd;
    128   while (fgets(buf, BUF_SIZE, inputFp)) {
    129     char* cp = buf;
    130     if (*cp == '#') continue;
    131     numRecords += 1;
    132     if (isdigit(*cp)) {
    133       while (isspace(*cp)) cp += 1;
    134       int32_t threadId = strtoul(cp, &cp, 0);
    135       if (maxThreadId < threadId) maxThreadId = threadId;
    136     }
    137     indentEnd = cp;
    138     while (isspace(*indentEnd)) indentEnd += 1;
    139     if (indentEnd - cp + 1 > maxFrames) maxFrames = indentEnd - cp + 1;
    140   }
    141   int32_t numThreads = maxThreadId + 1;
    142 
    143   /* Add space for a sentinel record at the end */
    144   numRecords += 1;
    145   records = new dataRecord[numRecords];
    146   stack* callStack = new stack[numThreads];
    147   for (int32_t ii = 0; ii < numThreads; ++ii) {
    148     callStack[ii].frames = nullptr;
    149     callStack[ii].indentLevel = 0;
    150   }
    151 
    152   rewind(inputFp);
    153 
    154   uint32_t time = 0;
    155   int32_t linenum = 0;
    156   int32_t nextRecord = 0;
    157   int32_t indentLevel = 0;
    158   while (fgets(buf, BUF_SIZE, inputFp)) {
    159     uint32_t threadId;
    160     int32_t len;
    161     int32_t indent;
    162     int32_t action;
    163     char* save_cp;
    164 
    165     linenum += 1;
    166     char* cp = buf;
    167 
    168     /* Skip lines that start with '#' */
    169     if (*cp == '#') continue;
    170 
    171     /* Get time and thread id */
    172     if (!isdigit(*cp)) {
    173       /* If the line does not begin with a digit, then fill in
    174        * default values for the time and threadId.
    175        */
    176       time += 2;
    177       threadId = 1;
    178     } else {
    179       time = strtoul(cp, &cp, 0);
    180       while (isspace(*cp)) cp += 1;
    181       threadId = strtoul(cp, &cp, 0);
    182       cp += 1;
    183     }
    184 
    185     // Allocate space for the thread stack, if necessary
    186     if (callStack[threadId].frames == nullptr) {
    187       dataRecord** stk = new dataRecord*[maxFrames];
    188       callStack[threadId].frames = stk;
    189     }
    190     indentLevel = callStack[threadId].indentLevel;
    191 
    192     save_cp = cp;
    193     while (isspace(*cp)) {
    194       cp += 1;
    195     }
    196     indent = cp - save_cp + 1;
    197     records[nextRecord].time = time;
    198     records[nextRecord].threadId = threadId;
    199 
    200     save_cp = cp;
    201     while (*cp != '\n') cp += 1;
    202 
    203     /* Remove trailing spaces */
    204     cp -= 1;
    205     while (isspace(*cp)) cp -= 1;
    206     cp += 1;
    207     len = cp - save_cp;
    208     records[nextRecord].fullName = strndup(save_cp, len);
    209 
    210     /* Parse the name to support "class.method signature" */
    211     records[nextRecord].className = nullptr;
    212     records[nextRecord].methodName = nullptr;
    213     records[nextRecord].signature = nullptr;
    214     cp = strchr(save_cp, '.');
    215     if (cp) {
    216       len = cp - save_cp;
    217       if (len > 0) records[nextRecord].className = strndup(save_cp, len);
    218       save_cp = cp + 1;
    219       cp = strchr(save_cp, ' ');
    220       if (cp == nullptr) cp = strchr(save_cp, '\n');
    221       if (cp && cp > save_cp) {
    222         len = cp - save_cp;
    223         records[nextRecord].methodName = strndup(save_cp, len);
    224         save_cp = cp + 1;
    225         cp = strchr(save_cp, ' ');
    226         if (cp == nullptr) cp = strchr(save_cp, '\n');
    227         if (cp && cp > save_cp) {
    228           len = cp - save_cp;
    229           records[nextRecord].signature = strndup(save_cp, len);
    230         }
    231       }
    232     }
    233 
    234     if (verbose) {
    235       printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
    236     }
    237 
    238     action = 0;
    239     if (indent == indentLevel + 1) {  // Entering a method
    240       if (verbose) printf("  Entering %s\n", records[nextRecord].fullName);
    241       callStack[threadId].frames[indentLevel] = &records[nextRecord];
    242     } else if (indent == indentLevel) {  // Exiting a method
    243       // Exiting method must be currently on top of stack (unless stack is
    244       // empty)
    245       if (callStack[threadId].frames[indentLevel - 1] == nullptr) {
    246         if (verbose)
    247           printf("  Exiting %s (past bottom of stack)\n",
    248                  records[nextRecord].fullName);
    249         callStack[threadId].frames[indentLevel - 1] = &records[nextRecord];
    250         action = 1;
    251       } else {
    252         if (indentLevel < 1) {
    253           fprintf(stderr, "Error: line %d: %s", linenum, buf);
    254           fprintf(stderr, "  expected positive (>0) indentation, found %d\n",
    255                   indent);
    256           exit(1);
    257         }
    258         char* name = callStack[threadId].frames[indentLevel - 1]->fullName;
    259         if (strcmp(name, records[nextRecord].fullName) == 0) {
    260           if (verbose) printf("  Exiting %s\n", name);
    261           action = 1;
    262         } else {  // exiting method doesn't match stack's top method
    263           fprintf(stderr, "Error: line %d: %s", linenum, buf);
    264           fprintf(stderr, "  expected exit from %s\n",
    265                   callStack[threadId].frames[indentLevel - 1]->fullName);
    266           exit(1);
    267         }
    268       }
    269     } else {
    270       if (nextRecord != 0) {
    271         fprintf(stderr, "Error: line %d: %s", linenum, buf);
    272         fprintf(stderr, "  expected indentation %d [+1], found %d\n",
    273                 indentLevel, indent);
    274         exit(1);
    275       }
    276 
    277       if (verbose) {
    278         printf("  Nonzero indent at first record\n");
    279         printf("  Entering %s\n", records[nextRecord].fullName);
    280       }
    281 
    282       // This is the first line of data, so we allow a larger
    283       // initial indent.  This allows us to test popping off more
    284       // frames than we entered.
    285       indentLevel = indent - 1;
    286       callStack[threadId].frames[indentLevel] = &records[nextRecord];
    287     }
    288 
    289     if (action == 0)
    290       indentLevel += 1;
    291     else
    292       indentLevel -= 1;
    293     records[nextRecord].action = action;
    294     callStack[threadId].indentLevel = indentLevel;
    295 
    296     nextRecord += 1;
    297   }
    298 
    299   /* Mark the last record with a sentinel */
    300   memset(&records[nextRecord], 0, sizeof(dataRecord));
    301 }
    302 
    303 /*
    304  * Write values to the binary data file.
    305  */
    306 void write2LE(FILE* fp, uint16_t val) {
    307   putc(val & 0xff, fp);
    308   putc(val >> 8, fp);
    309 }
    310 
    311 void write4LE(FILE* fp, uint32_t val) {
    312   putc(val & 0xff, fp);
    313   putc((val >> 8) & 0xff, fp);
    314   putc((val >> 16) & 0xff, fp);
    315   putc((val >> 24) & 0xff, fp);
    316 }
    317 
    318 void write8LE(FILE* fp, uint64_t val) {
    319   putc(val & 0xff, fp);
    320   putc((val >> 8) & 0xff, fp);
    321   putc((val >> 16) & 0xff, fp);
    322   putc((val >> 24) & 0xff, fp);
    323   putc((val >> 32) & 0xff, fp);
    324   putc((val >> 40) & 0xff, fp);
    325   putc((val >> 48) & 0xff, fp);
    326   putc((val >> 56) & 0xff, fp);
    327 }
    328 
    329 void writeDataRecord(FILE* dataFp, int32_t threadId, uint32_t methodVal, uint32_t elapsedTime) {
    330   if (versionNumber == 1)
    331     putc(threadId, dataFp);
    332   else
    333     write2LE(dataFp, threadId);
    334   write4LE(dataFp, methodVal);
    335   write4LE(dataFp, elapsedTime);
    336 }
    337 
    338 void writeDataHeader(FILE* dataFp) {
    339   struct timeval tv;
    340   struct timezone tz;
    341 
    342   gettimeofday(&tv, &tz);
    343   uint64_t startTime = tv.tv_sec;
    344   startTime = (startTime << 32) | tv.tv_usec;
    345   header.version = versionNumber;
    346   write4LE(dataFp, header.magic);
    347   write2LE(dataFp, header.version);
    348   write2LE(dataFp, header.offsetToData);
    349   write8LE(dataFp, startTime);
    350 }
    351 
    352 void writeKeyMethods(FILE* keyFp) {
    353   const char* methodStr = "*methods\n";
    354   fwrite(methodStr, strlen(methodStr), 1, keyFp);
    355 
    356   /* Assign method ids in multiples of 4 */
    357   uint32_t methodId = 0;
    358   for (dataRecord* pRecord = records; pRecord->fullName; ++pRecord) {
    359     if (pRecord->methodId) continue;
    360     uint32_t id = ++methodId << 2;
    361     pRecord->methodId = id;
    362 
    363     /* Assign this id to all the other records that have the
    364      * same name.
    365      */
    366     for (dataRecord* pNext = pRecord + 1; pNext->fullName; ++pNext) {
    367       if (pNext->methodId) continue;
    368       if (strcmp(pRecord->fullName, pNext->fullName) == 0) pNext->methodId = id;
    369     }
    370     if (pRecord->className == nullptr || pRecord->methodName == nullptr) {
    371       fprintf(keyFp, "%#x        %s      m       ()\n", pRecord->methodId,
    372               pRecord->fullName);
    373     } else if (pRecord->signature == nullptr) {
    374       fprintf(keyFp, "%#x        %s      %s      ()\n", pRecord->methodId,
    375               pRecord->className, pRecord->methodName);
    376     } else {
    377       fprintf(keyFp, "%#x        %s      %s      %s\n", pRecord->methodId,
    378               pRecord->className, pRecord->methodName, pRecord->signature);
    379     }
    380   }
    381 }
    382 
    383 void writeKeys(FILE* keyFp) {
    384   fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef);
    385   fwrite(keyThreads, strlen(keyThreads), 1, keyFp);
    386   writeKeyMethods(keyFp);
    387   fwrite(keyEnd, strlen(keyEnd), 1, keyFp);
    388 }
    389 
    390 void writeDataRecords(FILE* dataFp) {
    391   for (dataRecord* pRecord = records; pRecord->fullName; ++pRecord) {
    392     uint32_t val = METHOD_COMBINE(pRecord->methodId, pRecord->action);
    393     writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time);
    394   }
    395 }
    396 
    397 void writeTrace(const char* traceFileName) {
    398   FILE* fp = fopen(traceFileName, "w");
    399   if (fp == nullptr) {
    400     perror(traceFileName);
    401     exit(1);
    402   }
    403   writeKeys(fp);
    404   writeDataHeader(fp);
    405   writeDataRecords(fp);
    406   fclose(fp);
    407 }
    408 
    409 int32_t parseOptions(int32_t argc, char** argv) {
    410   int32_t err = 0;
    411   while (1) {
    412     int32_t opt = getopt(argc, argv, "v:d");
    413     if (opt == -1) break;
    414     switch (opt) {
    415       case 'v':
    416         versionNumber = strtoul(optarg, nullptr, 0);
    417         if (versionNumber != 1 && versionNumber != 2) {
    418           fprintf(stderr, "Error: version number (%d) must be 1 or 2\n", versionNumber);
    419           err = 1;
    420         }
    421         break;
    422       case 'd':
    423         verbose = 1;
    424         break;
    425       default:
    426         err = 1;
    427         break;
    428     }
    429   }
    430   return err;
    431 }
    432 
    433 int32_t main(int32_t argc, char** argv) {
    434   char* inputFile;
    435   char* traceFileName = nullptr;
    436 
    437   if (parseOptions(argc, argv) || argc - optind != 2) {
    438     fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n", argv[0]);
    439     exit(1);
    440   }
    441 
    442   inputFile = argv[optind++];
    443   parseInputFile(inputFile);
    444   traceFileName = argv[optind++];
    445 
    446   writeTrace(traceFileName);
    447 
    448   return 0;
    449 }
    450