• 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 
48 DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
49 
50 char *versionHeader = "*version\n";
51 char *clockDef = "clock=thread-cpu\n";
52 
53 char *keyThreads =
54 "*threads\n"
55 "1	main\n"
56 "2	foo\n"
57 "3	bar\n"
58 "4	blah\n";
59 
60 char *keyEnd = "*end\n";
61 
62 typedef struct dataRecord {
63     unsigned int	time;
64     int			threadId;
65     unsigned int	action;		/* 0=entry, 1=exit, 2=exception exit */
66     char		*fullName;
67     char		*className;
68     char		*methodName;
69     char		*signature;
70     unsigned int	methodId;
71 } dataRecord;
72 
73 dataRecord *records;
74 
75 #define BUF_SIZE 1024
76 char buf[BUF_SIZE];
77 
78 typedef struct stack {
79     dataRecord	**frames;
80     int		nextFrame;
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     int nextFrame = 0;
128 
129     FILE *inputFp = fopen(inputFileName, "r");
130     if (inputFp == NULL) {
131         perror(inputFileName);
132         exit(1);
133     }
134 
135     /* Count the number of lines in the buffer */
136     int numLines = 0;
137     int maxThreadId = 1;
138     while (fgets(buf, BUF_SIZE, inputFp)) {
139         char *cp = buf;
140         if (*cp == '#')
141             continue;
142         numLines += 1;
143         if (isdigit(*cp)) {
144             int time = strtoul(cp, &cp, 0);
145             while (isspace(*cp))
146                 cp += 1;
147             int threadId = strtoul(cp, &cp, 0);
148             if (maxThreadId < threadId)
149                 maxThreadId = threadId;
150         }
151     }
152     int numThreads = maxThreadId + 1;
153 
154     /* Add space for a sentinel record at the end */
155     numLines += 1;
156     records = (dataRecord *) malloc(sizeof(dataRecord) * numLines);
157     callStack = (stack *) malloc(sizeof(stack) * numThreads);
158     int ii;
159     for (ii = 0; ii < numThreads; ++ii) {
160         callStack[ii].frames = NULL;
161         callStack[ii].nextFrame = 0;
162     }
163 
164     rewind(inputFp);
165     while (fgets(buf, BUF_SIZE, inputFp)) {
166         int indent;
167         int action;
168         char *save_cp;
169 
170         linenum += 1;
171         char *cp = buf;
172         /* Skip lines that start with '#' */
173         if (*cp == '#')
174             continue;
175         if (!isdigit(*cp)) {
176             /* If the line does not begin with a digit, then fill in
177              * default values for the time and threadId.
178              */
179             time += 2;
180             threadId = 1;
181         } else {
182             time = strtoul(cp, &cp, 0);
183             while (isspace(*cp))
184                 cp += 1;
185             threadId = strtoul(cp, &cp, 0);
186             cp += 1;
187         }
188 
189         // Allocate space for the thread stack, if necessary
190         if (callStack[threadId].frames == NULL) {
191             dataRecord **stk;
192             stk = (dataRecord **) malloc(sizeof(dataRecord *) * numLines);
193             callStack[threadId].frames = stk;
194         }
195         nextFrame = callStack[threadId].nextFrame;
196         indentLevel = callStack[threadId].indentLevel;
197 
198         save_cp = cp;
199         while (isspace(*cp)) {
200             cp += 1;
201         }
202         indent = cp - save_cp + 1;
203         records[nextRecord].time = time;
204         records[nextRecord].threadId = threadId;
205 
206         save_cp = cp;
207         while (*cp != '\n')
208             cp += 1;
209 
210         /* Remove trailing spaces */
211         cp -= 1;
212         while (isspace(*cp))
213             cp -= 1;
214         cp += 1;
215         len = cp - save_cp;
216         records[nextRecord].fullName = strndup(save_cp, len);
217 
218         /* Parse the name to support "class.method signature" */
219         records[nextRecord].className = NULL;
220         records[nextRecord].methodName = NULL;
221         records[nextRecord].signature = NULL;
222         cp = strchr(save_cp, '.');
223         if (cp) {
224             len = cp - save_cp;
225             if (len > 0)
226                 records[nextRecord].className = strndup(save_cp, len);
227             save_cp = cp + 1;
228             cp = strchr(save_cp, ' ');
229             if (cp == NULL)
230                 cp = strchr(save_cp, '\n');
231             if (cp && cp > save_cp) {
232                 len = cp - save_cp;
233                 records[nextRecord].methodName = strndup(save_cp, len);
234                 save_cp = cp + 1;
235                 cp = strchr(save_cp, ' ');
236                 if (cp == NULL)
237                     cp = strchr(save_cp, '\n');
238                 if (cp && cp > save_cp) {
239                     len = cp - save_cp;
240                     records[nextRecord].signature = strndup(save_cp, len);
241                 }
242             }
243         }
244 
245         action = 0;
246         if (indent == indentLevel + 1) {
247             callStack[threadId].frames[nextFrame++] = &records[nextRecord];
248         } else if (indent == indentLevel) {
249             char *name = callStack[threadId].frames[nextFrame - 1]->fullName;
250             if (strcmp(name, records[nextRecord].fullName) == 0) {
251                 nextFrame -= 1;
252                 action = 1;
253             } else {
254                 if (nextFrame == indentLevel) {
255                     fprintf(stderr, "Error: line %d: %s", linenum, buf);
256                     fprintf(stderr, "  expected exit from %s\n",
257                             callStack[threadId].frames[nextFrame - 1]->fullName);
258                     exit(1);
259                 } else {
260                     callStack[threadId].frames[nextFrame++] = &records[nextRecord];
261                 }
262             }
263         } else if (indent == indentLevel - 1) {
264             action = 1;
265             // Allow popping frames past the bottom of the stack.
266             if (nextFrame > 0) {
267                 char *name = callStack[threadId].frames[nextFrame - 1]->fullName;
268                 if (strcmp(name, records[nextRecord].fullName) == 0) {
269                     nextFrame -= 1;
270                 } else {
271                     fprintf(stderr, "Error: line %d: %s", linenum, buf);
272                     fprintf(stderr, "  expected exit from %s\n",
273                             callStack[threadId].frames[nextFrame - 1]->fullName);
274                     exit(1);
275                 }
276             }
277         } else {
278             if (nextRecord != 0) {
279                 fprintf(stderr, "Error: line %d: %s", linenum, buf);
280                 fprintf(stderr, "  expected indentation %d +/- 1, found %d\n",
281                         indentLevel, indent);
282                 exit(1);
283             }
284 
285             // This is the first line of data, so we allow a larger
286             // initial indent.  This allows us to test popping off more
287             // frames than we entered.
288             callStack[threadId].frames[nextFrame++] = &records[nextRecord];
289             indentLevel = indent;
290         }
291         if (action == 0)
292             indentLevel += 1;
293         else
294             indentLevel -= 1;
295         records[nextRecord].action = action;
296         callStack[threadId].nextFrame = nextFrame;
297         callStack[threadId].indentLevel = indentLevel;
298 
299         nextRecord += 1;
300     }
301 
302     /* Mark the last record with a sentinel */
303     memset(&records[nextRecord], 0, sizeof(dataRecord));
304 }
305 
306 
307 /*
308  * Write values to the binary data file.
309  */
write2LE(FILE * fp,unsigned short val)310 void write2LE(FILE* fp, unsigned short val)
311 {
312     putc(val & 0xff, fp);
313     putc(val >> 8, fp);
314 }
315 
write4LE(FILE * fp,unsigned int val)316 void write4LE(FILE* fp, unsigned int val)
317 {
318     putc(val & 0xff, fp);
319     putc((val >> 8) & 0xff, fp);
320     putc((val >> 16) & 0xff, fp);
321     putc((val >> 24) & 0xff, fp);
322 }
323 
write8LE(FILE * fp,unsigned long long val)324 void write8LE(FILE* fp, unsigned long long val)
325 {
326     putc(val & 0xff, fp);
327     putc((val >> 8) & 0xff, fp);
328     putc((val >> 16) & 0xff, fp);
329     putc((val >> 24) & 0xff, fp);
330     putc((val >> 32) & 0xff, fp);
331     putc((val >> 40) & 0xff, fp);
332     putc((val >> 48) & 0xff, fp);
333     putc((val >> 56) & 0xff, fp);
334 }
335 
writeDataRecord(FILE * dataFp,int threadId,unsigned int methodVal,unsigned int elapsedTime)336 void writeDataRecord(FILE *dataFp, int threadId, unsigned int methodVal,
337                    unsigned int elapsedTime)
338 {
339     if (versionNumber == 1)
340         putc(threadId, dataFp);
341     else
342         write2LE(dataFp, threadId);
343     write4LE(dataFp, methodVal);
344     write4LE(dataFp, elapsedTime);
345 }
346 
writeDataHeader(FILE * dataFp)347 void writeDataHeader(FILE *dataFp)
348 {
349     struct timeval tv;
350     struct timezone tz;
351 
352     gettimeofday(&tv, &tz);
353     unsigned long long startTime = tv.tv_sec;
354     startTime = (startTime << 32) | tv.tv_usec;
355     header.version = versionNumber;
356     write4LE(dataFp, header.magic);
357     write2LE(dataFp, header.version);
358     write2LE(dataFp, header.offsetToData);
359     write8LE(dataFp, startTime);
360 }
361 
writeKeyMethods(FILE * keyFp)362 void writeKeyMethods(FILE *keyFp)
363 {
364     dataRecord *pRecord, *pNext;
365     char *methodStr = "*methods\n";
366     fwrite(methodStr, strlen(methodStr), 1, keyFp);
367 
368     /* Assign method ids in multiples of 4 */
369     unsigned int methodId = 0;
370     for (pRecord = records; pRecord->fullName; ++pRecord) {
371         if (pRecord->methodId)
372             continue;
373         unsigned int id = ++methodId << 2;
374         pRecord->methodId = id;
375 
376         /* Assign this id to all the other records that have the
377          * same name.
378          */
379         for (pNext = pRecord + 1; pNext->fullName; ++pNext) {
380             if (pNext->methodId)
381                 continue;
382             if (strcmp(pRecord->fullName, pNext->fullName) == 0)
383                 pNext->methodId = id;
384         }
385         if (pRecord->className == NULL || pRecord->methodName == NULL) {
386             fprintf(keyFp, "0x%x	%s	m	()\n",
387                     pRecord->methodId, pRecord->fullName);
388         } else if (pRecord->signature == NULL) {
389             fprintf(keyFp, "0x%x	%s	%s	()\n",
390                     pRecord->methodId, pRecord->className,
391                     pRecord->methodName);
392         } else {
393             fprintf(keyFp, "0x%x	%s	%s	%s\n",
394                     pRecord->methodId, pRecord->className,
395                     pRecord->methodName, pRecord->signature);
396         }
397     }
398 }
399 
writeKeys(FILE * keyFp)400 void writeKeys(FILE *keyFp)
401 {
402     fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef);
403     fwrite(keyThreads, strlen(keyThreads), 1, keyFp);
404     writeKeyMethods(keyFp);
405     fwrite(keyEnd, strlen(keyEnd), 1, keyFp);
406 }
407 
writeDataRecords(FILE * dataFp)408 void writeDataRecords(FILE *dataFp)
409 {
410     dataRecord *pRecord;
411 
412     for (pRecord = records; pRecord->fullName; ++pRecord) {
413         unsigned int val = METHOD_COMBINE(pRecord->methodId, pRecord->action);
414         writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time);
415     }
416 }
417 
writeTrace(const char * traceFileName)418 void writeTrace(const char* traceFileName)
419 {
420     FILE *fp = fopen(traceFileName, "w");
421     if (fp == NULL) {
422         perror(traceFileName);
423         exit(1);
424     }
425     writeKeys(fp);
426     writeDataHeader(fp);
427     writeDataRecords(fp);
428     fclose(fp);
429 }
430 
parseOptions(int argc,char ** argv)431 int parseOptions(int argc, char **argv)
432 {
433     int err = 0;
434     while (1) {
435         int opt = getopt(argc, argv, "v:");
436         if (opt == -1)
437             break;
438         switch (opt) {
439             case 'v':
440                 versionNumber = strtoul(optarg, NULL, 0);
441                 if (versionNumber != 1 && versionNumber != 2) {
442                     fprintf(stderr, "Error: version number (%d) must be 1 or 2\n",
443                             versionNumber);
444                     err = 1;
445                 }
446                 break;
447             default:
448                 err = 1;
449                 break;
450         }
451     }
452     return err;
453 }
454 
main(int argc,char ** argv)455 int main(int argc, char** argv)
456 {
457     char *inputFile;
458     char *traceFileName = NULL;
459     int len;
460 
461     if (parseOptions(argc, argv) || argc - optind != 2) {
462         fprintf(stderr, "Usage: %s [-v version] input_file trace_prefix\n",
463                 argv[0]);
464         exit(1);
465     }
466 
467     inputFile = argv[optind++];
468     parseInputFile(inputFile);
469     traceFileName = argv[optind++];
470 
471     writeTrace(traceFileName);
472 
473     return 0;
474 }
475