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