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