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