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