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