OSDN Git Service

atrace: use creat instead of truncate
[android-x86/frameworks-native.git] / cmds / atrace / atrace.cpp
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <signal.h>
21 #include <stdarg.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/sendfile.h>
26 #include <time.h>
27 #include <zlib.h>
28
29 #include <binder/IBinder.h>
30 #include <binder/IServiceManager.h>
31 #include <binder/Parcel.h>
32
33 #include <cutils/properties.h>
34
35 #include <utils/String8.h>
36 #include <utils/Trace.h>
37
38 using namespace android;
39
40 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
41
42 enum { MAX_SYS_FILES = 8 };
43
44 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
45
46 typedef enum { OPT, REQ } requiredness  ;
47
48 struct TracingCategory {
49     // The name identifying the category.
50     const char* name;
51
52     // A longer description of the category.
53     const char* longname;
54
55     // The userland tracing tags that the category enables.
56     uint64_t tags;
57
58     // The fname==NULL terminated list of /sys/ files that the category
59     // enables.
60     struct {
61         // Whether the file must be writable in order to enable the tracing
62         // category.
63         requiredness required;
64
65         // The path to the enable file.
66         const char* path;
67     } sysfiles[MAX_SYS_FILES];
68 };
69
70 /* Tracing categories */
71 static const TracingCategory k_categories[] = {
72     { "gfx",        "Graphics",         ATRACE_TAG_GRAPHICS, { } },
73     { "input",      "Input",            ATRACE_TAG_INPUT, { } },
74     { "view",       "View System",      ATRACE_TAG_VIEW, { } },
75     { "webview",    "WebView",          ATRACE_TAG_WEBVIEW, { } },
76     { "wm",         "Window Manager",   ATRACE_TAG_WINDOW_MANAGER, { } },
77     { "am",         "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
78     { "audio",      "Audio",            ATRACE_TAG_AUDIO, { } },
79     { "video",      "Video",            ATRACE_TAG_VIDEO, { } },
80     { "camera",     "Camera",           ATRACE_TAG_CAMERA, { } },
81     { "hal",        "Hardware Modules", ATRACE_TAG_HAL, { } },
82     { "sched",      "CPU Scheduling",   0, {
83         { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
84         { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
85     } },
86     { "freq",       "CPU Frequency",    0, {
87         { REQ,      "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
88         { OPT,      "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
89     } },
90     { "membus",     "Memory Bus Utilization", 0, {
91         { REQ,      "/sys/kernel/debug/tracing/events/memory_bus/enable" },
92     } },
93     { "idle",       "CPU Idle",         0, {
94         { REQ,      "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
95     } },
96     { "disk",       "Disk I/O",         0, {
97         { REQ,      "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
98         { REQ,      "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
99         { REQ,      "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
100         { REQ,      "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
101     } },
102     { "load",       "CPU Load",         0, {
103         { REQ,      "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
104     } },
105     { "sync",       "Synchronization",  0, {
106         { REQ,      "/sys/kernel/debug/tracing/events/sync/enable" },
107     } },
108     { "workq",      "Kernel Workqueues", 0, {
109         { REQ,      "/sys/kernel/debug/tracing/events/workqueue/enable" },
110     } },
111 };
112
113 /* Command line options */
114 static int g_traceDurationSeconds = 5;
115 static bool g_traceOverwrite = false;
116 static int g_traceBufferSizeKB = 2048;
117 static bool g_compress = false;
118 static bool g_nohup = false;
119 static int g_initialSleepSecs = 0;
120 static const char* g_kernelTraceFuncs = NULL;
121
122 /* Global state */
123 static bool g_traceAborted = false;
124 static bool g_categoryEnables[NELEM(k_categories)] = {};
125
126 /* Sys file paths */
127 static const char* k_traceClockPath =
128     "/sys/kernel/debug/tracing/trace_clock";
129
130 static const char* k_traceBufferSizePath =
131     "/sys/kernel/debug/tracing/buffer_size_kb";
132
133 static const char* k_tracingOverwriteEnablePath =
134     "/sys/kernel/debug/tracing/options/overwrite";
135
136 static const char* k_currentTracerPath =
137     "/sys/kernel/debug/tracing/current_tracer";
138
139 static const char* k_printTgidPath =
140     "/sys/kernel/debug/tracing/options/print-tgid";
141
142 static const char* k_funcgraphAbsTimePath =
143     "/sys/kernel/debug/tracing/options/funcgraph-abstime";
144
145 static const char* k_funcgraphCpuPath =
146     "/sys/kernel/debug/tracing/options/funcgraph-cpu";
147
148 static const char* k_funcgraphProcPath =
149     "/sys/kernel/debug/tracing/options/funcgraph-proc";
150
151 static const char* k_funcgraphFlatPath =
152     "/sys/kernel/debug/tracing/options/funcgraph-flat";
153
154 static const char* k_funcgraphDurationPath =
155     "/sys/kernel/debug/tracing/options/funcgraph-duration";
156
157 static const char* k_ftraceFilterPath =
158     "/sys/kernel/debug/tracing/set_ftrace_filter";
159
160 static const char* k_tracingOnPath =
161     "/sys/kernel/debug/tracing/tracing_on";
162
163 static const char* k_tracePath =
164     "/sys/kernel/debug/tracing/trace";
165
166 // Check whether a file exists.
167 static bool fileExists(const char* filename) {
168     return access(filename, F_OK) != -1;
169 }
170
171 // Check whether a file is writable.
172 static bool fileIsWritable(const char* filename) {
173     return access(filename, W_OK) != -1;
174 }
175
176 // Truncate a file.
177 static bool truncateFile(const char* path)
178 {
179     // This uses creat rather than truncate because some of the debug kernel
180     // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
181     // calls to truncate, but they are cleared by calls to creat.
182     int traceFD = creat(path, 0);
183     if (traceFD == -1) {
184         fprintf(stderr, "error truncating %s: %s (%d)\n", path,
185             strerror(errno), errno);
186         return false;
187     }
188
189     close(traceFD);
190
191     return true;
192 }
193
194 static bool _writeStr(const char* filename, const char* str, int flags)
195 {
196     int fd = open(filename, flags);
197     if (fd == -1) {
198         fprintf(stderr, "error opening %s: %s (%d)\n", filename,
199                 strerror(errno), errno);
200         return false;
201     }
202
203     bool ok = true;
204     ssize_t len = strlen(str);
205     if (write(fd, str, len) != len) {
206         fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
207                 strerror(errno), errno);
208         ok = false;
209     }
210
211     close(fd);
212
213     return ok;
214 }
215
216 // Write a string to a file, returning true if the write was successful.
217 static bool writeStr(const char* filename, const char* str)
218 {
219     return _writeStr(filename, str, O_WRONLY);
220 }
221
222 // Append a string to a file, returning true if the write was successful.
223 static bool appendStr(const char* filename, const char* str)
224 {
225     return _writeStr(filename, str, O_APPEND|O_WRONLY);
226 }
227
228 // Enable or disable a kernel option by writing a "1" or a "0" into a /sys
229 // file.
230 static bool setKernelOptionEnable(const char* filename, bool enable)
231 {
232     return writeStr(filename, enable ? "1" : "0");
233 }
234
235 // Check whether the category is supported on the device with the current
236 // rootness.  A category is supported only if all its required /sys/ files are
237 // writable and if enabling the category will enable one or more tracing tags
238 // or /sys/ files.
239 static bool isCategorySupported(const TracingCategory& category)
240 {
241     bool ok = category.tags != 0;
242     for (int i = 0; i < MAX_SYS_FILES; i++) {
243         const char* path = category.sysfiles[i].path;
244         bool req = category.sysfiles[i].required == REQ;
245         if (path != NULL) {
246             if (req) {
247                 if (!fileIsWritable(path)) {
248                     return false;
249                 } else {
250                     ok = true;
251                 }
252             } else {
253                 ok |= fileIsWritable(path);
254             }
255         }
256     }
257     return ok;
258 }
259
260 // Check whether the category would be supported on the device if the user
261 // were root.  This function assumes that root is able to write to any file
262 // that exists.  It performs the same logic as isCategorySupported, but it
263 // uses file existance rather than writability in the /sys/ file checks.
264 static bool isCategorySupportedForRoot(const TracingCategory& category)
265 {
266     bool ok = category.tags != 0;
267     for (int i = 0; i < MAX_SYS_FILES; i++) {
268         const char* path = category.sysfiles[i].path;
269         bool req = category.sysfiles[i].required == REQ;
270         if (path != NULL) {
271             if (req) {
272                 if (!fileExists(path)) {
273                     return false;
274                 } else {
275                     ok = true;
276                 }
277             } else {
278                 ok |= fileExists(path);
279             }
280         }
281     }
282     return ok;
283 }
284
285 // Enable or disable overwriting of the kernel trace buffers.  Disabling this
286 // will cause tracing to stop once the trace buffers have filled up.
287 static bool setTraceOverwriteEnable(bool enable)
288 {
289     return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
290 }
291
292 // Enable or disable kernel tracing.
293 static bool setTracingEnabled(bool enable)
294 {
295     return setKernelOptionEnable(k_tracingOnPath, enable);
296 }
297
298 // Clear the contents of the kernel trace.
299 static bool clearTrace()
300 {
301     return truncateFile(k_tracePath);
302 }
303
304 // Set the size of the kernel's trace buffer in kilobytes.
305 static bool setTraceBufferSizeKB(int size)
306 {
307     char str[32] = "1";
308     int len;
309     if (size < 1) {
310         size = 1;
311     }
312     snprintf(str, 32, "%d", size);
313     return writeStr(k_traceBufferSizePath, str);
314 }
315
316 // Enable or disable the kernel's use of the global clock.  Disabling the global
317 // clock will result in the kernel using a per-CPU local clock.
318 static bool setGlobalClockEnable(bool enable)
319 {
320     return writeStr(k_traceClockPath, enable ? "global" : "local");
321 }
322
323 static bool setPrintTgidEnableIfPresent(bool enable)
324 {
325     if (fileExists(k_printTgidPath)) {
326         return setKernelOptionEnable(k_printTgidPath, enable);
327     }
328     return true;
329 }
330
331 // Poke all the binder-enabled processes in the system to get them to re-read
332 // their system properties.
333 static bool pokeBinderServices()
334 {
335     sp<IServiceManager> sm = defaultServiceManager();
336     Vector<String16> services = sm->listServices();
337     for (size_t i = 0; i < services.size(); i++) {
338         sp<IBinder> obj = sm->checkService(services[i]);
339         if (obj != NULL) {
340             Parcel data;
341             if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
342                     NULL, 0) != OK) {
343                 if (false) {
344                     // XXX: For some reason this fails on tablets trying to
345                     // poke the "phone" service.  It's not clear whether some
346                     // are expected to fail.
347                     String8 svc(services[i]);
348                     fprintf(stderr, "error poking binder service %s\n",
349                         svc.string());
350                     return false;
351                 }
352             }
353         }
354     }
355     return true;
356 }
357
358 // Set the trace tags that userland tracing uses, and poke the running
359 // processes to pick up the new value.
360 static bool setTagsProperty(uint64_t tags)
361 {
362     char buf[64];
363     snprintf(buf, 64, "%#llx", tags);
364     if (property_set(k_traceTagsProperty, buf) < 0) {
365         fprintf(stderr, "error setting trace tags system property\n");
366         return false;
367     }
368     return pokeBinderServices();
369 }
370
371 // Disable all /sys/ enable files.
372 static bool disableKernelTraceEvents() {
373     bool ok = true;
374     for (int i = 0; i < NELEM(k_categories); i++) {
375         const TracingCategory &c = k_categories[i];
376         for (int j = 0; j < MAX_SYS_FILES; j++) {
377             const char* path = c.sysfiles[j].path;
378             if (path != NULL && fileIsWritable(path)) {
379                 ok &= setKernelOptionEnable(path, false);
380             }
381         }
382     }
383     return ok;
384 }
385
386 // Verify that the comma separated list of functions are being traced by the
387 // kernel.
388 static bool verifyKernelTraceFuncs(const char* funcs)
389 {
390     int fd = open(k_ftraceFilterPath, O_RDONLY);
391     if (fd == -1) {
392         fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
393             strerror(errno), errno);
394         return false;
395     }
396
397     char buf[4097];
398     ssize_t n = read(fd, buf, 4096);
399     close(fd);
400     if (n == -1) {
401         fprintf(stderr, "error reading %s: %s (%d)\n", k_ftraceFilterPath,
402             strerror(errno), errno);
403         return false;
404     }
405
406     buf[n] = '\0';
407     String8 funcList = String8::format("\n%s", buf);
408
409     // Make sure that every function listed in funcs is in the list we just
410     // read from the kernel.
411     bool ok = true;
412     char* myFuncs = strdup(funcs);
413     char* func = strtok(myFuncs, ",");
414     while (func) {
415         String8 fancyFunc = String8::format("\n%s\n", func);
416         bool found = funcList.find(fancyFunc.string(), 0) >= 0;
417         if (!found || func[0] == '\0') {
418             fprintf(stderr, "error: \"%s\" is not a valid kernel function "
419                 "to trace.\n", func);
420             ok = false;
421         }
422         func = strtok(NULL, ",");
423     }
424     free(myFuncs);
425
426     return ok;
427 }
428
429 // Set the comma separated list of functions that the kernel is to trace.
430 static bool setKernelTraceFuncs(const char* funcs)
431 {
432     bool ok = true;
433
434     if (funcs == NULL || funcs[0] == '\0') {
435         // Disable kernel function tracing.
436         ok &= writeStr(k_currentTracerPath, "nop");
437         if (fileExists(k_ftraceFilterPath)) {
438             ok &= truncateFile(k_ftraceFilterPath);
439         }
440     } else {
441         // Enable kernel function tracing.
442         ok &= writeStr(k_currentTracerPath, "function_graph");
443         ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
444         ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
445         ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
446         ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
447
448         // Set the requested filter functions.
449         ok &= truncateFile(k_ftraceFilterPath);
450         char* myFuncs = strdup(funcs);
451         char* func = strtok(myFuncs, ",");
452         while (func) {
453             ok &= appendStr(k_ftraceFilterPath, func);
454             func = strtok(NULL, ",");
455         }
456         free(myFuncs);
457
458         // Verify that the set functions are being traced.
459         if (ok) {
460             ok &= verifyKernelTraceFuncs(funcs);
461         }
462     }
463
464     return ok;
465 }
466
467 // Set all the kernel tracing settings to the desired state for this trace
468 // capture.
469 static bool setUpTrace()
470 {
471     bool ok = true;
472
473     // Set up the tracing options.
474     ok &= setTraceOverwriteEnable(g_traceOverwrite);
475     ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
476     ok &= setGlobalClockEnable(true);
477     ok &= setPrintTgidEnableIfPresent(true);
478     ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
479
480     // Set up the tags property.
481     uint64_t tags = 0;
482     for (int i = 0; i < NELEM(k_categories); i++) {
483         if (g_categoryEnables[i]) {
484             const TracingCategory &c = k_categories[i];
485             tags |= c.tags;
486         }
487     }
488     ok &= setTagsProperty(tags);
489
490     // Disable all the sysfs enables.  This is done as a separate loop from
491     // the enables to allow the same enable to exist in multiple categories.
492     ok &= disableKernelTraceEvents();
493
494     // Enable all the sysfs enables that are in an enabled category.
495     for (int i = 0; i < NELEM(k_categories); i++) {
496         if (g_categoryEnables[i]) {
497             const TracingCategory &c = k_categories[i];
498             for (int j = 0; j < MAX_SYS_FILES; j++) {
499                 const char* path = c.sysfiles[j].path;
500                 bool required = c.sysfiles[j].required == REQ;
501                 if (path != NULL) {
502                     if (fileIsWritable(path)) {
503                         ok &= setKernelOptionEnable(path, true);
504                     } else if (required) {
505                         fprintf(stderr, "error writing file %s\n", path);
506                         ok = false;
507                     }
508                 }
509             }
510         }
511     }
512
513     return ok;
514 }
515
516 // Reset all the kernel tracing settings to their default state.
517 static void cleanUpTrace()
518 {
519     // Disable all tracing that we're able to.
520     disableKernelTraceEvents();
521
522     // Disable all the trace tags.
523     setTagsProperty(0);
524
525     // Set the options back to their defaults.
526     setTraceOverwriteEnable(true);
527     setTraceBufferSizeKB(1);
528     setGlobalClockEnable(false);
529     setPrintTgidEnableIfPresent(false);
530     setKernelTraceFuncs(NULL);
531 }
532
533
534 // Enable tracing in the kernel.
535 static bool startTrace()
536 {
537     return setTracingEnabled(true);
538 }
539
540 // Disable tracing in the kernel.
541 static void stopTrace()
542 {
543     setTracingEnabled(false);
544 }
545
546 // Read the current kernel trace and write it to stdout.
547 static void dumpTrace()
548 {
549     int traceFD = open(k_tracePath, O_RDWR);
550     if (traceFD == -1) {
551         fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
552                 strerror(errno), errno);
553         return;
554     }
555
556     if (g_compress) {
557         z_stream zs;
558         uint8_t *in, *out;
559         int result, flush;
560
561         bzero(&zs, sizeof(zs));
562         result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
563         if (result != Z_OK) {
564             fprintf(stderr, "error initializing zlib: %d\n", result);
565             close(traceFD);
566             return;
567         }
568
569         const size_t bufSize = 64*1024;
570         in = (uint8_t*)malloc(bufSize);
571         out = (uint8_t*)malloc(bufSize);
572         flush = Z_NO_FLUSH;
573
574         zs.next_out = out;
575         zs.avail_out = bufSize;
576
577         do {
578
579             if (zs.avail_in == 0) {
580                 // More input is needed.
581                 result = read(traceFD, in, bufSize);
582                 if (result < 0) {
583                     fprintf(stderr, "error reading trace: %s (%d)\n",
584                             strerror(errno), errno);
585                     result = Z_STREAM_END;
586                     break;
587                 } else if (result == 0) {
588                     flush = Z_FINISH;
589                 } else {
590                     zs.next_in = in;
591                     zs.avail_in = result;
592                 }
593             }
594
595             if (zs.avail_out == 0) {
596                 // Need to write the output.
597                 result = write(STDOUT_FILENO, out, bufSize);
598                 if ((size_t)result < bufSize) {
599                     fprintf(stderr, "error writing deflated trace: %s (%d)\n",
600                             strerror(errno), errno);
601                     result = Z_STREAM_END; // skip deflate error message
602                     zs.avail_out = bufSize; // skip the final write
603                     break;
604                 }
605                 zs.next_out = out;
606                 zs.avail_out = bufSize;
607             }
608
609         } while ((result = deflate(&zs, flush)) == Z_OK);
610
611         if (result != Z_STREAM_END) {
612             fprintf(stderr, "error deflating trace: %s\n", zs.msg);
613         }
614
615         if (zs.avail_out < bufSize) {
616             size_t bytes = bufSize - zs.avail_out;
617             result = write(STDOUT_FILENO, out, bytes);
618             if ((size_t)result < bytes) {
619                 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
620                         strerror(errno), errno);
621             }
622         }
623
624         result = deflateEnd(&zs);
625         if (result != Z_OK) {
626             fprintf(stderr, "error cleaning up zlib: %d\n", result);
627         }
628
629         free(in);
630         free(out);
631     } else {
632         ssize_t sent = 0;
633         while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0);
634         if (sent == -1) {
635             fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno),
636                     errno);
637         }
638     }
639
640     close(traceFD);
641 }
642
643 static void handleSignal(int signo)
644 {
645     if (!g_nohup) {
646         g_traceAborted = true;
647     }
648 }
649
650 static void registerSigHandler()
651 {
652     struct sigaction sa;
653     sigemptyset(&sa.sa_mask);
654     sa.sa_flags = 0;
655     sa.sa_handler = handleSignal;
656     sigaction(SIGHUP, &sa, NULL);
657     sigaction(SIGINT, &sa, NULL);
658     sigaction(SIGQUIT, &sa, NULL);
659     sigaction(SIGTERM, &sa, NULL);
660 }
661
662 static bool setCategoryEnable(const char* name, bool enable)
663 {
664     for (int i = 0; i < NELEM(k_categories); i++) {
665         const TracingCategory& c = k_categories[i];
666         if (strcmp(name, c.name) == 0) {
667             if (isCategorySupported(c)) {
668                 g_categoryEnables[i] = enable;
669                 return true;
670             } else {
671                 if (isCategorySupportedForRoot(c)) {
672                     fprintf(stderr, "error: category \"%s\" requires root "
673                             "privileges.\n", name);
674                 } else {
675                     fprintf(stderr, "error: category \"%s\" is not supported "
676                             "on this device.\n", name);
677                 }
678                 return false;
679             }
680         }
681     }
682     fprintf(stderr, "error: unknown tracing category \"%s\"\n", name);
683     return false;
684 }
685
686 static void listSupportedCategories()
687 {
688     for (int i = 0; i < NELEM(k_categories); i++) {
689         const TracingCategory& c = k_categories[i];
690         if (isCategorySupported(c)) {
691             printf("  %10s - %s\n", c.name, c.longname);
692         }
693     }
694 }
695
696 // Print the command usage help to stderr.
697 static void showHelp(const char *cmd)
698 {
699     fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
700     fprintf(stderr, "options include:\n"
701                     "  -b N            use a trace buffer size of N KB\n"
702                     "  -c              trace into a circular buffer\n"
703                     "  -k fname,...    trace the listed kernel functions\n"
704                     "  -n              ignore signals\n"
705                     "  -s N            sleep for N seconds before tracing [default 0]\n"
706                     "  -t N            trace for N seconds [defualt 5]\n"
707                     "  -z              compress the trace dump\n"
708                     "  --async_start   start circular trace and return immediatly\n"
709                     "  --async_dump    dump the current contents of circular trace buffer\n"
710                     "  --async_stop    stop tracing and dump the current contents of circular\n"
711                     "                    trace buffer\n"
712                     "  --list_categories\n"
713                     "                  list the available tracing categories\n"
714             );
715 }
716
717 int main(int argc, char **argv)
718 {
719     bool async = false;
720     bool traceStart = true;
721     bool traceStop = true;
722     bool traceDump = true;
723
724     if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
725         showHelp(argv[0]);
726         exit(0);
727     }
728
729     for (;;) {
730         int ret;
731         int option_index = 0;
732         static struct option long_options[] = {
733             {"async_start",     no_argument, 0,  0 },
734             {"async_stop",      no_argument, 0,  0 },
735             {"async_dump",      no_argument, 0,  0 },
736             {"list_categories", no_argument, 0,  0 },
737             {           0,                0, 0,  0 }
738         };
739
740         ret = getopt_long(argc, argv, "b:ck:ns:t:z",
741                           long_options, &option_index);
742
743         if (ret < 0) {
744             for (int i = optind; i < argc; i++) {
745                 if (!setCategoryEnable(argv[i], true)) {
746                     fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
747                     exit(1);
748                 }
749             }
750             break;
751         }
752
753         switch(ret) {
754             case 'b':
755                 g_traceBufferSizeKB = atoi(optarg);
756             break;
757
758             case 'c':
759                 g_traceOverwrite = true;
760             break;
761
762             case 'k':
763                 g_kernelTraceFuncs = optarg;
764                 break;
765
766             case 'n':
767                 g_nohup = true;
768                 break;
769
770             case 's':
771                 g_initialSleepSecs = atoi(optarg);
772             break;
773
774             case 't':
775                 g_traceDurationSeconds = atoi(optarg);
776             break;
777
778             case 'z':
779                 g_compress = true;
780             break;
781
782             case 0:
783                 if (!strcmp(long_options[option_index].name, "async_start")) {
784                     async = true;
785                     traceStop = false;
786                     traceDump = false;
787                     g_traceOverwrite = true;
788                 } else if (!strcmp(long_options[option_index].name, "async_stop")) {
789                     async = true;
790                     traceStop = false;
791                 } else if (!strcmp(long_options[option_index].name, "async_dump")) {
792                     async = true;
793                     traceStart = false;
794                     traceStop = false;
795                 } else if (!strcmp(long_options[option_index].name, "list_categories")) {
796                     listSupportedCategories();
797                     exit(0);
798                 }
799                 break;
800
801             default:
802                 fprintf(stderr, "\n");
803                 showHelp(argv[0]);
804                 exit(-1);
805             break;
806         }
807     }
808
809     registerSigHandler();
810
811     if (g_initialSleepSecs > 0) {
812         sleep(g_initialSleepSecs);
813     }
814
815     bool ok = true;
816     ok &= setUpTrace();
817     ok &= startTrace();
818
819     if (ok && traceStart) {
820         printf("capturing trace...");
821         fflush(stdout);
822
823         // We clear the trace after starting it because tracing gets enabled for
824         // each CPU individually in the kernel. Having the beginning of the trace
825         // contain entries from only one CPU can cause "begin" entries without a
826         // matching "end" entry to show up if a task gets migrated from one CPU to
827         // another.
828         ok = clearTrace();
829
830         if (ok && !async) {
831             // Sleep to allow the trace to be captured.
832             struct timespec timeLeft;
833             timeLeft.tv_sec = g_traceDurationSeconds;
834             timeLeft.tv_nsec = 0;
835             do {
836                 if (g_traceAborted) {
837                     break;
838                 }
839             } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
840         }
841     }
842
843     // Stop the trace and restore the default settings.
844     if (traceStop)
845         stopTrace();
846
847     if (ok && traceDump) {
848         if (!g_traceAborted) {
849             printf(" done\nTRACE:\n");
850             fflush(stdout);
851             dumpTrace();
852         } else {
853             printf("\ntrace aborted.\n");
854             fflush(stdout);
855         }
856         clearTrace();
857     } else if (!ok) {
858         fprintf(stderr, "unable to start tracing\n");
859     }
860
861     // Reset the trace buffer size to 1.
862     if (traceStop)
863         cleanUpTrace();
864
865     return g_traceAborted ? 1 : 0;
866 }