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