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