blob: a4ae02e7bf300234aa9c3b27dddd9638b7f3c171 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/* //device/tools/dmtracedump/CreateTrace.c
2**
3** Copyright 2006, The Android Open Source Project
4**
Carl Shapirode750892010-06-08 16:37:12 -07005** 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08008**
Carl Shapirode750892010-06-08 16:37:12 -07009** http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080010**
Carl Shapirode750892010-06-08 16:37:12 -070011** 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080015** 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 */
38typedef struct DataHeader {
39 unsigned int magic;
40 short version;
41 short offsetToData;
42 long long startWhen;
43} DataHeader;
44
45#define VERSION 2
46int versionNumber = VERSION;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -070047int verbose = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080048
49DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
50
51char *versionHeader = "*version\n";
52char *clockDef = "clock=thread-cpu\n";
53
54char *keyThreads =
55"*threads\n"
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -070056"1 main\n"
57"2 foo\n"
58"3 bar\n"
59"4 blah\n";
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080060
61char *keyEnd = "*end\n";
62
63typedef struct dataRecord {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -070064 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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080072} dataRecord;
73
74dataRecord *records;
75
76#define BUF_SIZE 1024
77char buf[BUF_SIZE];
78
79typedef struct stack {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -070080 dataRecord **frames;
81 int indentLevel;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080082} stack;
83
84/* Mac OS doesn't have strndup(), so implement it here.
85 */
86char *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 */
119void 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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800127
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 */
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700135 int numRecords = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800136 int maxThreadId = 1;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700137 int maxFrames = 0;
138 char *indentEnd;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800139 while (fgets(buf, BUF_SIZE, inputFp)) {
140 char *cp = buf;
141 if (*cp == '#')
142 continue;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700143 numRecords += 1;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800144 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 }
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700152 indentEnd = cp;
153 while (isspace(*indentEnd))
154 indentEnd += 1;
155 if (indentEnd - cp + 1 > maxFrames)
156 maxFrames = indentEnd - cp + 1;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800157 }
158 int numThreads = maxThreadId + 1;
159
160 /* Add space for a sentinel record at the end */
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700161 numRecords += 1;
162 records = (dataRecord *) malloc(sizeof(dataRecord) * numRecords);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800163 callStack = (stack *) malloc(sizeof(stack) * numThreads);
164 int ii;
165 for (ii = 0; ii < numThreads; ++ii) {
166 callStack[ii].frames = NULL;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700167 callStack[ii].indentLevel = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168 }
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;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700178
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800179 /* Skip lines that start with '#' */
180 if (*cp == '#')
181 continue;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700182
183 /* Get time and thread id */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800184 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;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700201 stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800202 callStack[threadId].frames = stk;
203 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800204 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;
Carl Shapirode750892010-06-08 16:37:12 -0700217
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800218 /* 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
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700253 if (verbose) {
254 printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
255 }
256
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800257 action = 0;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700258 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];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800268 action = 1;
269 } else {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700270 if (indentLevel < 1) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800271 fprintf(stderr, "Error: line %d: %s", linenum, buf);
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700272 fprintf(stderr, " expected positive (>0) indentation, found %d\n",
273 indent);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800274 exit(1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800275 }
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700276 char *name = callStack[threadId].frames[indentLevel - 1]->fullName;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800277 if (strcmp(name, records[nextRecord].fullName) == 0) {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700278 if (verbose)
279 printf(" Exiting %s\n", name);
280 action = 1;
281 } else { // exiting method doesn't match stack's top method
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800282 fprintf(stderr, "Error: line %d: %s", linenum, buf);
283 fprintf(stderr, " expected exit from %s\n",
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700284 callStack[threadId].frames[indentLevel - 1]->fullName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800285 exit(1);
286 }
287 }
288 } else {
289 if (nextRecord != 0) {
290 fprintf(stderr, "Error: line %d: %s", linenum, buf);
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700291 fprintf(stderr, " expected indentation %d [+1], found %d\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800292 indentLevel, indent);
293 exit(1);
294 }
295
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700296 if (verbose) {
297 printf(" Nonzero indent at first record\n");
298 printf(" Entering %s\n", records[nextRecord].fullName);
299 }
300
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800301 // 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.
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700304 indentLevel = indent - 1;
305 callStack[threadId].frames[indentLevel] = &records[nextRecord];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800306 }
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700307
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800308 if (action == 0)
309 indentLevel += 1;
310 else
311 indentLevel -= 1;
312 records[nextRecord].action = action;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800313 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 */
326void write2LE(FILE* fp, unsigned short val)
327{
328 putc(val & 0xff, fp);
329 putc(val >> 8, fp);
330}
331
332void 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
340void 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
352void 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
363void 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
378void 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) {
Dan Bornstein291c84f2011-05-26 10:46:25 -0700402 fprintf(keyFp, "%#x %s m ()\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800403 pRecord->methodId, pRecord->fullName);
404 } else if (pRecord->signature == NULL) {
Dan Bornstein291c84f2011-05-26 10:46:25 -0700405 fprintf(keyFp, "%#x %s %s ()\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800406 pRecord->methodId, pRecord->className,
407 pRecord->methodName);
408 } else {
Dan Bornstein291c84f2011-05-26 10:46:25 -0700409 fprintf(keyFp, "%#x %s %s %s\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800410 pRecord->methodId, pRecord->className,
411 pRecord->methodName, pRecord->signature);
412 }
413 }
414}
415
416void 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
424void 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
434void 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
447int parseOptions(int argc, char **argv)
448{
449 int err = 0;
450 while (1) {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700451 int opt = getopt(argc, argv, "v:d");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800452 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;
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700463 case 'd':
464 verbose = 1;
465 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800466 default:
467 err = 1;
468 break;
469 }
470 }
471 return err;
472}
473
474int 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) {
Rodrigo Ipince4d9b6012009-10-22 13:21:02 -0700481 fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800482 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}