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