• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
strndup(const char * src,size_t len)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  */
parseInputFile(const char * inputFileName)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  */
write2LE(FILE * fp,unsigned short val)326 void write2LE(FILE* fp, unsigned short val)
327 {
328     putc(val & 0xff, fp);
329     putc(val >> 8, fp);
330 }
331 
write4LE(FILE * fp,unsigned int val)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 
write8LE(FILE * fp,unsigned long long val)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 
writeDataRecord(FILE * dataFp,int threadId,unsigned int methodVal,unsigned int elapsedTime)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 
writeDataHeader(FILE * dataFp)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 
writeKeyMethods(FILE * keyFp)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 
writeKeys(FILE * keyFp)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 
writeDataRecords(FILE * dataFp)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 
writeTrace(const char * traceFileName)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 
parseOptions(int argc,char ** argv)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 
main(int argc,char ** argv)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