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