atrace.cpp revision 6f6f3f710b4dec2952298ae65d5f1674535c63f0
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 if (fileIsWritable(k_currentTracerPath)) { 437 ok &= writeStr(k_currentTracerPath, "nop"); 438 } 439 if (fileIsWritable(k_ftraceFilterPath)) { 440 ok &= truncateFile(k_ftraceFilterPath); 441 } 442 } else { 443 // Enable kernel function tracing. 444 ok &= writeStr(k_currentTracerPath, "function_graph"); 445 ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true); 446 ok &= setKernelOptionEnable(k_funcgraphCpuPath, true); 447 ok &= setKernelOptionEnable(k_funcgraphProcPath, true); 448 ok &= setKernelOptionEnable(k_funcgraphFlatPath, true); 449 450 // Set the requested filter functions. 451 ok &= truncateFile(k_ftraceFilterPath); 452 char* myFuncs = strdup(funcs); 453 char* func = strtok(myFuncs, ","); 454 while (func) { 455 ok &= appendStr(k_ftraceFilterPath, func); 456 func = strtok(NULL, ","); 457 } 458 free(myFuncs); 459 460 // Verify that the set functions are being traced. 461 if (ok) { 462 ok &= verifyKernelTraceFuncs(funcs); 463 } 464 } 465 466 return ok; 467} 468 469// Set all the kernel tracing settings to the desired state for this trace 470// capture. 471static bool setUpTrace() 472{ 473 bool ok = true; 474 475 // Set up the tracing options. 476 ok &= setTraceOverwriteEnable(g_traceOverwrite); 477 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); 478 ok &= setGlobalClockEnable(true); 479 ok &= setPrintTgidEnableIfPresent(true); 480 ok &= setKernelTraceFuncs(g_kernelTraceFuncs); 481 482 // Set up the tags property. 483 uint64_t tags = 0; 484 for (int i = 0; i < NELEM(k_categories); i++) { 485 if (g_categoryEnables[i]) { 486 const TracingCategory &c = k_categories[i]; 487 tags |= c.tags; 488 } 489 } 490 ok &= setTagsProperty(tags); 491 492 // Disable all the sysfs enables. This is done as a separate loop from 493 // the enables to allow the same enable to exist in multiple categories. 494 ok &= disableKernelTraceEvents(); 495 496 // Enable all the sysfs enables that are in an enabled category. 497 for (int i = 0; i < NELEM(k_categories); i++) { 498 if (g_categoryEnables[i]) { 499 const TracingCategory &c = k_categories[i]; 500 for (int j = 0; j < MAX_SYS_FILES; j++) { 501 const char* path = c.sysfiles[j].path; 502 bool required = c.sysfiles[j].required == REQ; 503 if (path != NULL) { 504 if (fileIsWritable(path)) { 505 ok &= setKernelOptionEnable(path, true); 506 } else if (required) { 507 fprintf(stderr, "error writing file %s\n", path); 508 ok = false; 509 } 510 } 511 } 512 } 513 } 514 515 return ok; 516} 517 518// Reset all the kernel tracing settings to their default state. 519static void cleanUpTrace() 520{ 521 // Disable all tracing that we're able to. 522 disableKernelTraceEvents(); 523 524 // Disable all the trace tags. 525 setTagsProperty(0); 526 527 // Set the options back to their defaults. 528 setTraceOverwriteEnable(true); 529 setTraceBufferSizeKB(1); 530 setGlobalClockEnable(false); 531 setPrintTgidEnableIfPresent(false); 532 setKernelTraceFuncs(NULL); 533} 534 535 536// Enable tracing in the kernel. 537static bool startTrace() 538{ 539 return setTracingEnabled(true); 540} 541 542// Disable tracing in the kernel. 543static void stopTrace() 544{ 545 setTracingEnabled(false); 546} 547 548// Read the current kernel trace and write it to stdout. 549static void dumpTrace() 550{ 551 int traceFD = open(k_tracePath, O_RDWR); 552 if (traceFD == -1) { 553 fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, 554 strerror(errno), errno); 555 return; 556 } 557 558 if (g_compress) { 559 z_stream zs; 560 uint8_t *in, *out; 561 int result, flush; 562 563 bzero(&zs, sizeof(zs)); 564 result = deflateInit(&zs, Z_DEFAULT_COMPRESSION); 565 if (result != Z_OK) { 566 fprintf(stderr, "error initializing zlib: %d\n", result); 567 close(traceFD); 568 return; 569 } 570 571 const size_t bufSize = 64*1024; 572 in = (uint8_t*)malloc(bufSize); 573 out = (uint8_t*)malloc(bufSize); 574 flush = Z_NO_FLUSH; 575 576 zs.next_out = out; 577 zs.avail_out = bufSize; 578 579 do { 580 581 if (zs.avail_in == 0) { 582 // More input is needed. 583 result = read(traceFD, in, bufSize); 584 if (result < 0) { 585 fprintf(stderr, "error reading trace: %s (%d)\n", 586 strerror(errno), errno); 587 result = Z_STREAM_END; 588 break; 589 } else if (result == 0) { 590 flush = Z_FINISH; 591 } else { 592 zs.next_in = in; 593 zs.avail_in = result; 594 } 595 } 596 597 if (zs.avail_out == 0) { 598 // Need to write the output. 599 result = write(STDOUT_FILENO, out, bufSize); 600 if ((size_t)result < bufSize) { 601 fprintf(stderr, "error writing deflated trace: %s (%d)\n", 602 strerror(errno), errno); 603 result = Z_STREAM_END; // skip deflate error message 604 zs.avail_out = bufSize; // skip the final write 605 break; 606 } 607 zs.next_out = out; 608 zs.avail_out = bufSize; 609 } 610 611 } while ((result = deflate(&zs, flush)) == Z_OK); 612 613 if (result != Z_STREAM_END) { 614 fprintf(stderr, "error deflating trace: %s\n", zs.msg); 615 } 616 617 if (zs.avail_out < bufSize) { 618 size_t bytes = bufSize - zs.avail_out; 619 result = write(STDOUT_FILENO, out, bytes); 620 if ((size_t)result < bytes) { 621 fprintf(stderr, "error writing deflated trace: %s (%d)\n", 622 strerror(errno), errno); 623 } 624 } 625 626 result = deflateEnd(&zs); 627 if (result != Z_OK) { 628 fprintf(stderr, "error cleaning up zlib: %d\n", result); 629 } 630 631 free(in); 632 free(out); 633 } else { 634 ssize_t sent = 0; 635 while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); 636 if (sent == -1) { 637 fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), 638 errno); 639 } 640 } 641 642 close(traceFD); 643} 644 645static void handleSignal(int signo) 646{ 647 if (!g_nohup) { 648 g_traceAborted = true; 649 } 650} 651 652static void registerSigHandler() 653{ 654 struct sigaction sa; 655 sigemptyset(&sa.sa_mask); 656 sa.sa_flags = 0; 657 sa.sa_handler = handleSignal; 658 sigaction(SIGHUP, &sa, NULL); 659 sigaction(SIGINT, &sa, NULL); 660 sigaction(SIGQUIT, &sa, NULL); 661 sigaction(SIGTERM, &sa, NULL); 662} 663 664static bool setCategoryEnable(const char* name, bool enable) 665{ 666 for (int i = 0; i < NELEM(k_categories); i++) { 667 const TracingCategory& c = k_categories[i]; 668 if (strcmp(name, c.name) == 0) { 669 if (isCategorySupported(c)) { 670 g_categoryEnables[i] = enable; 671 return true; 672 } else { 673 if (isCategorySupportedForRoot(c)) { 674 fprintf(stderr, "error: category \"%s\" requires root " 675 "privileges.\n", name); 676 } else { 677 fprintf(stderr, "error: category \"%s\" is not supported " 678 "on this device.\n", name); 679 } 680 return false; 681 } 682 } 683 } 684 fprintf(stderr, "error: unknown tracing category \"%s\"\n", name); 685 return false; 686} 687 688static void listSupportedCategories() 689{ 690 for (int i = 0; i < NELEM(k_categories); i++) { 691 const TracingCategory& c = k_categories[i]; 692 if (isCategorySupported(c)) { 693 printf(" %10s - %s\n", c.name, c.longname); 694 } 695 } 696} 697 698// Print the command usage help to stderr. 699static void showHelp(const char *cmd) 700{ 701 fprintf(stderr, "usage: %s [options] [categories...]\n", cmd); 702 fprintf(stderr, "options include:\n" 703 " -b N use a trace buffer size of N KB\n" 704 " -c trace into a circular buffer\n" 705 " -k fname,... trace the listed kernel functions\n" 706 " -n ignore signals\n" 707 " -s N sleep for N seconds before tracing [default 0]\n" 708 " -t N trace for N seconds [defualt 5]\n" 709 " -z compress the trace dump\n" 710 " --async_start start circular trace and return immediatly\n" 711 " --async_dump dump the current contents of circular trace buffer\n" 712 " --async_stop stop tracing and dump the current contents of circular\n" 713 " trace buffer\n" 714 " --list_categories\n" 715 " list the available tracing categories\n" 716 ); 717} 718 719int main(int argc, char **argv) 720{ 721 bool async = false; 722 bool traceStart = true; 723 bool traceStop = true; 724 bool traceDump = true; 725 726 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 727 showHelp(argv[0]); 728 exit(0); 729 } 730 731 for (;;) { 732 int ret; 733 int option_index = 0; 734 static struct option long_options[] = { 735 {"async_start", no_argument, 0, 0 }, 736 {"async_stop", no_argument, 0, 0 }, 737 {"async_dump", no_argument, 0, 0 }, 738 {"list_categories", no_argument, 0, 0 }, 739 { 0, 0, 0, 0 } 740 }; 741 742 ret = getopt_long(argc, argv, "b:ck:ns:t:z", 743 long_options, &option_index); 744 745 if (ret < 0) { 746 for (int i = optind; i < argc; i++) { 747 if (!setCategoryEnable(argv[i], true)) { 748 fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); 749 exit(1); 750 } 751 } 752 break; 753 } 754 755 switch(ret) { 756 case 'b': 757 g_traceBufferSizeKB = atoi(optarg); 758 break; 759 760 case 'c': 761 g_traceOverwrite = true; 762 break; 763 764 case 'k': 765 g_kernelTraceFuncs = optarg; 766 break; 767 768 case 'n': 769 g_nohup = true; 770 break; 771 772 case 's': 773 g_initialSleepSecs = atoi(optarg); 774 break; 775 776 case 't': 777 g_traceDurationSeconds = atoi(optarg); 778 break; 779 780 case 'z': 781 g_compress = true; 782 break; 783 784 case 0: 785 if (!strcmp(long_options[option_index].name, "async_start")) { 786 async = true; 787 traceStop = false; 788 traceDump = false; 789 g_traceOverwrite = true; 790 } else if (!strcmp(long_options[option_index].name, "async_stop")) { 791 async = true; 792 traceStop = false; 793 } else if (!strcmp(long_options[option_index].name, "async_dump")) { 794 async = true; 795 traceStart = false; 796 traceStop = false; 797 } else if (!strcmp(long_options[option_index].name, "list_categories")) { 798 listSupportedCategories(); 799 exit(0); 800 } 801 break; 802 803 default: 804 fprintf(stderr, "\n"); 805 showHelp(argv[0]); 806 exit(-1); 807 break; 808 } 809 } 810 811 registerSigHandler(); 812 813 if (g_initialSleepSecs > 0) { 814 sleep(g_initialSleepSecs); 815 } 816 817 bool ok = true; 818 ok &= setUpTrace(); 819 ok &= startTrace(); 820 821 if (ok && traceStart) { 822 printf("capturing trace..."); 823 fflush(stdout); 824 825 // We clear the trace after starting it because tracing gets enabled for 826 // each CPU individually in the kernel. Having the beginning of the trace 827 // contain entries from only one CPU can cause "begin" entries without a 828 // matching "end" entry to show up if a task gets migrated from one CPU to 829 // another. 830 ok = clearTrace(); 831 832 if (ok && !async) { 833 // Sleep to allow the trace to be captured. 834 struct timespec timeLeft; 835 timeLeft.tv_sec = g_traceDurationSeconds; 836 timeLeft.tv_nsec = 0; 837 do { 838 if (g_traceAborted) { 839 break; 840 } 841 } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); 842 } 843 } 844 845 // Stop the trace and restore the default settings. 846 if (traceStop) 847 stopTrace(); 848 849 if (ok && traceDump) { 850 if (!g_traceAborted) { 851 printf(" done\nTRACE:\n"); 852 fflush(stdout); 853 dumpTrace(); 854 } else { 855 printf("\ntrace aborted.\n"); 856 fflush(stdout); 857 } 858 clearTrace(); 859 } else if (!ok) { 860 fprintf(stderr, "unable to start tracing\n"); 861 } 862 863 // Reset the trace buffer size to 1. 864 if (traceStop) 865 cleanUpTrace(); 866 867 return g_traceAborted ? 1 : 0; 868} 869