blob: c6fda97721138bfc027f120bb057852c41101599 [file] [log] [blame]
Jamie Gennis6eea6fb2012-12-07 14:03:07 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <getopt.h>
20#include <signal.h>
21#include <stdarg.h>
22#include <stdbool.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/sendfile.h>
26#include <time.h>
27#include <zlib.h>
28
29#include <binder/IBinder.h>
30#include <binder/IServiceManager.h>
31#include <binder/Parcel.h>
32
33#include <cutils/properties.h>
34
35#include <utils/String8.h>
36#include <utils/Trace.h>
37
38using namespace android;
39
40#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
41
42enum { MAX_SYS_FILES = 8 };
43
44const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
45
46typedef enum { OPT, REQ } requiredness ;
47
48struct TracingCategory {
49 // The name identifying the category.
50 const char* name;
51
52 // A longer description of the category.
53 const char* longname;
54
55 // The userland tracing tags that the category enables.
56 uint64_t tags;
57
58 // The fname==NULL terminated list of /sys/ files that the category
59 // enables.
60 struct {
61 // Whether the file must be writable in order to enable the tracing
62 // category.
63 requiredness required;
64
65 // The path to the enable file.
66 const char* path;
67 } sysfiles[MAX_SYS_FILES];
68};
69
70/* Tracing categories */
71static const TracingCategory k_categories[] = {
72 { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
73 { "input", "Input", ATRACE_TAG_INPUT, { } },
74 { "view", "View System", ATRACE_TAG_VIEW, { } },
75 { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } },
76 { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
77 { "audio", "Audio", ATRACE_TAG_AUDIO, { } },
78 { "video", "Video", ATRACE_TAG_VIDEO, { } },
79 { "camera", "Camera", ATRACE_TAG_CAMERA, { } },
Alex Ray36ebf512013-02-13 15:29:59 -080080 { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } },
Jamie Gennis6eea6fb2012-12-07 14:03:07 -080081 { "sched", "CPU Scheduling", 0, {
82 { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
83 { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
84 } },
85 { "freq", "CPU Frequency", 0, {
86 { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
87 { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
88 } },
89 { "membus", "Memory Bus Utilization", 0, {
90 { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" },
91 } },
92 { "idle", "CPU Idle", 0, {
93 { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
94 } },
95 { "disk", "Disk I/O", 0, {
96 { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
97 { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
98 { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
99 { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
100 } },
101 { "load", "CPU Load", 0, {
102 { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
103 } },
104 { "sync", "Synchronization", 0, {
105 { REQ, "/sys/kernel/debug/tracing/events/sync/enable" },
106 } },
107 { "workq", "Kernel Workqueues", 0, {
108 { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" },
109 } },
110};
111
112/* Command line options */
113static int g_traceDurationSeconds = 5;
114static bool g_traceOverwrite = false;
115static int g_traceBufferSizeKB = 2048;
116static bool g_compress = false;
117static bool g_nohup = false;
118static int g_initialSleepSecs = 0;
119
120/* Global state */
121static bool g_traceAborted = false;
122static bool g_categoryEnables[NELEM(k_categories)] = {};
123
124/* Sys file paths */
125static const char* k_traceClockPath =
126 "/sys/kernel/debug/tracing/trace_clock";
127
128static const char* k_traceBufferSizePath =
129 "/sys/kernel/debug/tracing/buffer_size_kb";
130
131static const char* k_tracingOverwriteEnablePath =
132 "/sys/kernel/debug/tracing/options/overwrite";
133
134static const char* k_tracingOnPath =
135 "/sys/kernel/debug/tracing/tracing_on";
136
137static const char* k_tracePath =
138 "/sys/kernel/debug/tracing/trace";
139
140// Check whether a file exists.
141static bool fileExists(const char* filename) {
142 return access(filename, F_OK) != -1;
143}
144
145// Check whether a file is writable.
146static bool fileIsWritable(const char* filename) {
147 return access(filename, W_OK) != -1;
148}
149
150// Write a string to a file, returning true if the write was successful.
151static bool writeStr(const char* filename, const char* str)
152{
153 int fd = open(filename, O_WRONLY);
154 if (fd == -1) {
155 fprintf(stderr, "error opening %s: %s (%d)\n", filename,
156 strerror(errno), errno);
157 return false;
158 }
159
160 bool ok = true;
161 ssize_t len = strlen(str);
162 if (write(fd, str, len) != len) {
163 fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
164 strerror(errno), errno);
165 ok = false;
166 }
167
168 close(fd);
169
170 return ok;
171}
172
173// Enable or disable a kernel option by writing a "1" or a "0" into a /sys
174// file.
175static bool setKernelOptionEnable(const char* filename, bool enable)
176{
177 return writeStr(filename, enable ? "1" : "0");
178}
179
180// Check whether the category is supported on the device with the current
181// rootness. A category is supported only if all its required /sys/ files are
182// writable and if enabling the category will enable one or more tracing tags
183// or /sys/ files.
184static bool isCategorySupported(const TracingCategory& category)
185{
186 bool ok = category.tags != 0;
187 for (int i = 0; i < MAX_SYS_FILES; i++) {
188 const char* path = category.sysfiles[i].path;
189 bool req = category.sysfiles[i].required == REQ;
190 if (path != NULL) {
191 if (req) {
192 if (!fileIsWritable(path)) {
193 return false;
194 } else {
195 ok = true;
196 }
197 } else {
198 ok |= fileIsWritable(path);
199 }
200 }
201 }
202 return ok;
203}
204
205// Check whether the category would be supported on the device if the user
206// were root. This function assumes that root is able to write to any file
207// that exists. It performs the same logic as isCategorySupported, but it
208// uses file existance rather than writability in the /sys/ file checks.
209static bool isCategorySupportedForRoot(const TracingCategory& category)
210{
211 bool ok = category.tags != 0;
212 for (int i = 0; i < MAX_SYS_FILES; i++) {
213 const char* path = category.sysfiles[i].path;
214 bool req = category.sysfiles[i].required == REQ;
215 if (path != NULL) {
216 if (req) {
217 if (!fileExists(path)) {
218 return false;
219 } else {
220 ok = true;
221 }
222 } else {
223 ok |= fileExists(path);
224 }
225 }
226 }
227 return ok;
228}
229
230// Enable or disable overwriting of the kernel trace buffers. Disabling this
231// will cause tracing to stop once the trace buffers have filled up.
232static bool setTraceOverwriteEnable(bool enable)
233{
234 return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
235}
236
237// Enable or disable kernel tracing.
238static bool setTracingEnabled(bool enable)
239{
240 return setKernelOptionEnable(k_tracingOnPath, enable);
241}
242
243// Clear the contents of the kernel trace.
244static bool clearTrace()
245{
246 int traceFD = creat(k_tracePath, 0);
247 if (traceFD == -1) {
248 fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath,
249 strerror(errno), errno);
250 return false;
251 }
252
253 close(traceFD);
254
255 return true;
256}
257
258// Set the size of the kernel's trace buffer in kilobytes.
259static bool setTraceBufferSizeKB(int size)
260{
261 char str[32] = "1";
262 int len;
263 if (size < 1) {
264 size = 1;
265 }
266 snprintf(str, 32, "%d", size);
267 return writeStr(k_traceBufferSizePath, str);
268}
269
270// Enable or disable the kernel's use of the global clock. Disabling the global
271// clock will result in the kernel using a per-CPU local clock.
272static bool setGlobalClockEnable(bool enable)
273{
274 return writeStr(k_traceClockPath, enable ? "global" : "local");
275}
276
277// Poke all the binder-enabled processes in the system to get them to re-read
278// their system properties.
279static bool pokeBinderServices()
280{
281 sp<IServiceManager> sm = defaultServiceManager();
282 Vector<String16> services = sm->listServices();
283 for (size_t i = 0; i < services.size(); i++) {
284 sp<IBinder> obj = sm->checkService(services[i]);
285 if (obj != NULL) {
286 Parcel data;
287 if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
288 NULL, 0) != OK) {
289 if (false) {
290 // XXX: For some reason this fails on tablets trying to
291 // poke the "phone" service. It's not clear whether some
292 // are expected to fail.
293 String8 svc(services[i]);
294 fprintf(stderr, "error poking binder service %s\n",
295 svc.string());
296 return false;
297 }
298 }
299 }
300 }
301 return true;
302}
303
304// Set the trace tags that userland tracing uses, and poke the running
305// processes to pick up the new value.
306static bool setTagsProperty(uint64_t tags)
307{
308 char buf[64];
309 snprintf(buf, 64, "%#llx", tags);
310 if (property_set(k_traceTagsProperty, buf) < 0) {
311 fprintf(stderr, "error setting trace tags system property\n");
312 return false;
313 }
314 return pokeBinderServices();
315}
316
317// Disable all /sys/ enable files.
318static bool disableKernelTraceEvents() {
319 bool ok = true;
320 for (int i = 0; i < NELEM(k_categories); i++) {
321 const TracingCategory &c = k_categories[i];
322 for (int j = 0; j < MAX_SYS_FILES; j++) {
323 const char* path = c.sysfiles[j].path;
324 if (path != NULL && fileIsWritable(path)) {
325 ok &= setKernelOptionEnable(path, false);
326 }
327 }
328 }
329 return ok;
330}
331
332// Enable tracing in the kernel.
333static bool startTrace()
334{
335 bool ok = true;
336
337 // Set up the tracing options.
338 ok &= setTraceOverwriteEnable(g_traceOverwrite);
339 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
340 ok &= setGlobalClockEnable(true);
341
342 // Set up the tags property.
343 uint64_t tags = 0;
344 for (int i = 0; i < NELEM(k_categories); i++) {
345 if (g_categoryEnables[i]) {
346 const TracingCategory &c = k_categories[i];
347 tags |= c.tags;
348 }
349 }
350 ok &= setTagsProperty(tags);
351
352 // Disable all the sysfs enables. This is done as a separate loop from
353 // the enables to allow the same enable to exist in multiple categories.
354 ok &= disableKernelTraceEvents();
355
356 // Enable all the sysfs enables that are in an enabled category.
357 for (int i = 0; i < NELEM(k_categories); i++) {
358 if (g_categoryEnables[i]) {
359 const TracingCategory &c = k_categories[i];
360 for (int j = 0; j < MAX_SYS_FILES; j++) {
361 const char* path = c.sysfiles[j].path;
362 bool required = c.sysfiles[j].required == REQ;
363 if (path != NULL) {
364 if (fileIsWritable(path)) {
365 ok &= setKernelOptionEnable(path, true);
366 } else if (required) {
367 fprintf(stderr, "error writing file %s\n", path);
368 ok = false;
369 }
370 }
371 }
372 }
373 }
374
375 // Enable tracing.
376 ok &= setTracingEnabled(true);
377
378 return ok;
379}
380
381// Disable tracing in the kernel.
382static void stopTrace()
383{
384 // Disable tracing.
385 setTracingEnabled(false);
386
387 // Disable all tracing that we're able to.
388 disableKernelTraceEvents();
389
390 // Disable all the trace tags.
391 setTagsProperty(0);
392
393 // Set the options back to their defaults.
394 setTraceOverwriteEnable(true);
395 setGlobalClockEnable(false);
396
397 // Note that we can't reset the trace buffer size here because that would
398 // clear the trace before we've read it.
399}
400
401// Read the current kernel trace and write it to stdout.
402static void dumpTrace()
403{
404 int traceFD = open(k_tracePath, O_RDWR);
405 if (traceFD == -1) {
406 fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
407 strerror(errno), errno);
408 return;
409 }
410
411 if (g_compress) {
412 z_stream zs;
413 uint8_t *in, *out;
414 int result, flush;
415
416 bzero(&zs, sizeof(zs));
417 result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
418 if (result != Z_OK) {
419 fprintf(stderr, "error initializing zlib: %d\n", result);
420 close(traceFD);
421 return;
422 }
423
424 const size_t bufSize = 64*1024;
425 in = (uint8_t*)malloc(bufSize);
426 out = (uint8_t*)malloc(bufSize);
427 flush = Z_NO_FLUSH;
428
429 zs.next_out = out;
430 zs.avail_out = bufSize;
431
432 do {
433
434 if (zs.avail_in == 0) {
435 // More input is needed.
436 result = read(traceFD, in, bufSize);
437 if (result < 0) {
438 fprintf(stderr, "error reading trace: %s (%d)\n",
439 strerror(errno), errno);
440 result = Z_STREAM_END;
441 break;
442 } else if (result == 0) {
443 flush = Z_FINISH;
444 } else {
445 zs.next_in = in;
446 zs.avail_in = result;
447 }
448 }
449
450 if (zs.avail_out == 0) {
451 // Need to write the output.
452 result = write(STDOUT_FILENO, out, bufSize);
453 if ((size_t)result < bufSize) {
454 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
455 strerror(errno), errno);
456 result = Z_STREAM_END; // skip deflate error message
457 zs.avail_out = bufSize; // skip the final write
458 break;
459 }
460 zs.next_out = out;
461 zs.avail_out = bufSize;
462 }
463
464 } while ((result = deflate(&zs, flush)) == Z_OK);
465
466 if (result != Z_STREAM_END) {
467 fprintf(stderr, "error deflating trace: %s\n", zs.msg);
468 }
469
470 if (zs.avail_out < bufSize) {
471 size_t bytes = bufSize - zs.avail_out;
472 result = write(STDOUT_FILENO, out, bytes);
473 if ((size_t)result < bytes) {
474 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
475 strerror(errno), errno);
476 }
477 }
478
479 result = deflateEnd(&zs);
480 if (result != Z_OK) {
481 fprintf(stderr, "error cleaning up zlib: %d\n", result);
482 }
483
484 free(in);
485 free(out);
486 } else {
487 ssize_t sent = 0;
488 while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0);
489 if (sent == -1) {
490 fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno),
491 errno);
492 }
493 }
494
495 close(traceFD);
496}
497
498static void handleSignal(int signo)
499{
500 if (!g_nohup) {
501 g_traceAborted = true;
502 }
503}
504
505static void registerSigHandler()
506{
507 struct sigaction sa;
508 sigemptyset(&sa.sa_mask);
509 sa.sa_flags = 0;
510 sa.sa_handler = handleSignal;
511 sigaction(SIGHUP, &sa, NULL);
512 sigaction(SIGINT, &sa, NULL);
513 sigaction(SIGQUIT, &sa, NULL);
514 sigaction(SIGTERM, &sa, NULL);
515}
516
517static bool setCategoryEnable(const char* name, bool enable)
518{
519 for (int i = 0; i < NELEM(k_categories); i++) {
520 const TracingCategory& c = k_categories[i];
521 if (strcmp(name, c.name) == 0) {
522 if (isCategorySupported(c)) {
523 g_categoryEnables[i] = enable;
524 return true;
525 } else {
526 if (isCategorySupportedForRoot(c)) {
527 fprintf(stderr, "error: category \"%s\" requires root "
528 "privileges.\n", name);
529 } else {
530 fprintf(stderr, "error: category \"%s\" is not supported "
531 "on this device.\n", name);
532 }
533 return false;
534 }
535 }
536 }
537 fprintf(stderr, "error: unknown tracing category \"%s\"\n", name);
538 return false;
539}
540
541static void listSupportedCategories()
542{
543 for (int i = 0; i < NELEM(k_categories); i++) {
544 const TracingCategory& c = k_categories[i];
545 if (isCategorySupported(c)) {
546 printf(" %10s - %s\n", c.name, c.longname);
547 }
548 }
549}
550
551// Print the command usage help to stderr.
552static void showHelp(const char *cmd)
553{
554 fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
555 fprintf(stderr, "options include:\n"
556 " -b N use a trace buffer size of N KB\n"
557 " -c trace into a circular buffer\n"
558 " -n ignore signals\n"
559 " -s N sleep for N seconds before tracing [default 0]\n"
560 " -t N trace for N seconds [defualt 5]\n"
561 " -z compress the trace dump\n"
562 " --async_start start circular trace and return immediatly\n"
563 " --async_dump dump the current contents of circular trace buffer\n"
564 " --async_stop stop tracing and dump the current contents of circular\n"
565 " trace buffer\n"
Jamie Gennis92573f12012-12-07 16:29:03 -0800566 " --list_categories\n"
567 " list the available tracing categories\n"
Jamie Gennis6eea6fb2012-12-07 14:03:07 -0800568 );
569}
570
571int main(int argc, char **argv)
572{
573 bool async = false;
574 bool traceStart = true;
575 bool traceStop = true;
576 bool traceDump = true;
577
578 if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
579 showHelp(argv[0]);
580 exit(0);
581 }
582
583 for (;;) {
584 int ret;
585 int option_index = 0;
586 static struct option long_options[] = {
587 {"async_start", no_argument, 0, 0 },
588 {"async_stop", no_argument, 0, 0 },
589 {"async_dump", no_argument, 0, 0 },
590 {"list_categories", no_argument, 0, 0 },
591 { 0, 0, 0, 0 }
592 };
593
594 ret = getopt_long(argc, argv, "b:cns:t:z",
595 long_options, &option_index);
596
597 if (ret < 0) {
598 for (int i = optind; i < argc; i++) {
599 if (!setCategoryEnable(argv[i], true)) {
600 fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
601 exit(1);
602 }
603 }
604 break;
605 }
606
607 switch(ret) {
608 case 'b':
609 g_traceBufferSizeKB = atoi(optarg);
610 break;
611
612 case 'c':
613 g_traceOverwrite = true;
614 break;
615
616 case 'n':
617 g_nohup = true;
618 break;
619
620 case 's':
621 g_initialSleepSecs = atoi(optarg);
622 break;
623
624 case 't':
625 g_traceDurationSeconds = atoi(optarg);
626 break;
627
628 case 'z':
629 g_compress = true;
630 break;
631
632 case 0:
633 if (!strcmp(long_options[option_index].name, "async_start")) {
634 async = true;
635 traceStop = false;
636 traceDump = false;
637 g_traceOverwrite = true;
638 } else if (!strcmp(long_options[option_index].name, "async_stop")) {
639 async = true;
640 traceStop = false;
641 } else if (!strcmp(long_options[option_index].name, "async_dump")) {
642 async = true;
643 traceStart = false;
644 traceStop = false;
645 } else if (!strcmp(long_options[option_index].name, "list_categories")) {
646 listSupportedCategories();
647 exit(0);
648 }
649 break;
650
651 default:
652 fprintf(stderr, "\n");
653 showHelp(argv[0]);
654 exit(-1);
655 break;
656 }
657 }
658
659 registerSigHandler();
660
661 if (g_initialSleepSecs > 0) {
662 sleep(g_initialSleepSecs);
663 }
664
665 bool ok = startTrace();
666
667 if (ok && traceStart) {
668 printf("capturing trace...");
669 fflush(stdout);
670
671 // We clear the trace after starting it because tracing gets enabled for
672 // each CPU individually in the kernel. Having the beginning of the trace
673 // contain entries from only one CPU can cause "begin" entries without a
674 // matching "end" entry to show up if a task gets migrated from one CPU to
675 // another.
676 ok = clearTrace();
677
678 if (ok && !async) {
679 // Sleep to allow the trace to be captured.
680 struct timespec timeLeft;
681 timeLeft.tv_sec = g_traceDurationSeconds;
682 timeLeft.tv_nsec = 0;
683 do {
684 if (g_traceAborted) {
685 break;
686 }
687 } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
688 }
689 }
690
691 // Stop the trace and restore the default settings.
692 if (traceStop)
693 stopTrace();
694
695 if (ok && traceDump) {
696 if (!g_traceAborted) {
697 printf(" done\nTRACE:\n");
698 fflush(stdout);
699 dumpTrace();
700 } else {
701 printf("\ntrace aborted.\n");
702 fflush(stdout);
703 }
704 clearTrace();
705 } else if (!ok) {
706 fprintf(stderr, "unable to start tracing\n");
707 }
708
709 // Reset the trace buffer size to 1.
710 if (traceStop)
711 setTraceBufferSizeKB(1);
712
713 return g_traceAborted ? 1 : 0;
714}