atrace.c revision a404a7e9bb05f4c1b0c9312d10a4c96c6cbdfcc9
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 <signal.h> 20#include <stdarg.h> 21#include <stdbool.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <sys/sendfile.h> 25#include <time.h> 26#include <zlib.h> 27 28#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 29 30/* Command line options */ 31static int g_traceDurationSeconds = 5; 32static bool g_traceSchedSwitch = false; 33static bool g_traceFrequency = false; 34static bool g_traceBusUtilization = false; 35static bool g_traceCpuIdle = false; 36static bool g_traceDisk = false; 37static bool g_traceGovernorLoad = false; 38static bool g_traceWorkqueue = false; 39static bool g_traceOverwrite = false; 40static int g_traceBufferSizeKB = 2048; 41static bool g_compress = false; 42 43/* Global state */ 44static bool g_traceAborted = false; 45 46/* Sys file paths */ 47static const char* k_traceClockPath = 48 "/sys/kernel/debug/tracing/trace_clock"; 49 50static const char* k_traceBufferSizePath = 51 "/sys/kernel/debug/tracing/buffer_size_kb"; 52 53static const char* k_tracingOverwriteEnablePath = 54 "/sys/kernel/debug/tracing/options/overwrite"; 55 56static const char* k_schedSwitchEnablePath = 57 "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; 58 59static const char* k_schedWakeupEnablePath = 60 "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable"; 61 62static const char* k_memoryBusEnablePath = 63 "/sys/kernel/debug/tracing/events/memory_bus/enable"; 64 65static const char* k_cpuFreqEnablePath = 66 "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable"; 67 68static const char *k_clockSetRateEnablePath = 69 "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable"; 70 71static const char* k_cpuIdleEnablePath = 72 "/sys/kernel/debug/tracing/events/power/cpu_idle/enable"; 73 74static const char* k_governorLoadEnablePath = 75 "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable"; 76 77static const char* k_workqueueEnablePath = 78 "/sys/kernel/debug/tracing/events/workqueue/enable"; 79 80static const char* k_diskEnablePaths[] = { 81 "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable", 82 "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable", 83 "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable", 84 "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable", 85}; 86 87static const char* k_tracingOnPath = 88 "/sys/kernel/debug/tracing/tracing_on"; 89 90static const char* k_tracePath = 91 "/sys/kernel/debug/tracing/trace"; 92 93static const char* k_traceMarkerPath = 94 "/sys/kernel/debug/tracing/trace_marker"; 95 96// Write a string to a file, returning true if the write was successful. 97bool writeStr(const char* filename, const char* str) 98{ 99 int fd = open(filename, O_WRONLY); 100 if (fd == -1) { 101 fprintf(stderr, "error opening %s: %s (%d)\n", filename, 102 strerror(errno), errno); 103 return false; 104 } 105 106 bool ok = true; 107 ssize_t len = strlen(str); 108 if (write(fd, str, len) != len) { 109 fprintf(stderr, "error writing to %s: %s (%d)\n", filename, 110 strerror(errno), errno); 111 ok = false; 112 } 113 114 close(fd); 115 116 return ok; 117} 118 119// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file. 120static bool setKernelOptionEnable(const char* filename, bool enable) 121{ 122 return writeStr(filename, enable ? "1" : "0"); 123} 124 125// Enable or disable a collection of kernel options by writing a "1" or a "0" into each /sys file. 126static bool setMultipleKernelOptionsEnable(const char** filenames, size_t count, bool enable) 127{ 128 bool result = true; 129 for (size_t i = 0; i < count; i++) { 130 result &= setKernelOptionEnable(filenames[i], enable); 131 } 132 return result; 133} 134 135// Enable or disable overwriting of the kernel trace buffers. Disabling this 136// will cause tracing to stop once the trace buffers have filled up. 137static bool setTraceOverwriteEnable(bool enable) 138{ 139 return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable); 140} 141 142// Enable or disable tracing of the kernel scheduler switching. 143static bool setSchedSwitchTracingEnable(bool enable) 144{ 145 bool ok = true; 146 ok &= setKernelOptionEnable(k_schedSwitchEnablePath, enable); 147 ok &= setKernelOptionEnable(k_schedWakeupEnablePath, enable); 148 return ok; 149} 150 151// Enable or disable tracing of the Bus utilization. 152static bool setBusUtilizationTracingEnable(bool enable) 153{ 154 bool ok = false; 155 // these can be platform specific so make sure that at least 156 // one succeeds. 157 ok |= setKernelOptionEnable(k_memoryBusEnablePath, enable); 158 return ok; 159} 160 161// Enable or disable tracing of the CPU clock frequency. 162static bool setFrequencyTracingEnable(bool enable) 163{ 164 bool ok = true; 165 ok &= setKernelOptionEnable(k_cpuFreqEnablePath, enable); 166 ok &= setKernelOptionEnable(k_clockSetRateEnablePath, enable); 167 return ok; 168} 169 170// Enable or disable tracing of CPU idle events. 171static bool setCpuIdleTracingEnable(bool enable) 172{ 173 return setKernelOptionEnable(k_cpuIdleEnablePath, enable); 174} 175 176// Enable or disable tracing of the interactive CPU frequency governor's idea of 177// the CPU load. 178static bool setGovernorLoadTracingEnable(bool enable) 179{ 180 return setKernelOptionEnable(k_governorLoadEnablePath, enable); 181} 182 183// Enable or disable tracing of the kernel workqueues. 184static bool setWorkqueueTracingEnabled(bool enable) 185{ 186 return setKernelOptionEnable(k_workqueueEnablePath, enable); 187} 188 189// Enable or disable tracing of disk I/O. 190static bool setDiskTracingEnabled(bool enable) 191{ 192 return setMultipleKernelOptionsEnable(k_diskEnablePaths, NELEM(k_diskEnablePaths), enable); 193} 194 195// Enable or disable kernel tracing. 196static bool setTracingEnabled(bool enable) 197{ 198 return setKernelOptionEnable(k_tracingOnPath, enable); 199} 200 201// Clear the contents of the kernel trace. 202static bool clearTrace() 203{ 204 int traceFD = creat(k_tracePath, 0); 205 if (traceFD == -1) { 206 fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath, 207 strerror(errno), errno); 208 return false; 209 } 210 211 close(traceFD); 212 213 return true; 214} 215 216// Set the size of the kernel's trace buffer in kilobytes. 217static bool setTraceBufferSizeKB(int size) 218{ 219 char str[32] = "1"; 220 int len; 221 if (size < 1) { 222 size = 1; 223 } 224 snprintf(str, 32, "%d", size); 225 return writeStr(k_traceBufferSizePath, str); 226} 227 228// Enable or disable the kernel's use of the global clock. Disabling the global 229// clock will result in the kernel using a per-CPU local clock. 230static bool setGlobalClockEnable(bool enable) 231{ 232 return writeStr(k_traceClockPath, enable ? "global" : "local"); 233} 234 235// Check whether a file exists. 236static bool fileExists(const char* filename) { 237 return access(filename, F_OK) != -1; 238} 239 240// Enable tracing in the kernel. 241static bool startTrace(bool isRoot) 242{ 243 bool ok = true; 244 245 // Set up the tracing options that don't require root. 246 ok &= setTraceOverwriteEnable(g_traceOverwrite); 247 ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); 248 ok &= setBusUtilizationTracingEnable(g_traceBusUtilization); 249 ok &= setFrequencyTracingEnable(g_traceFrequency); 250 ok &= setCpuIdleTracingEnable(g_traceCpuIdle); 251 if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { 252 ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); 253 } 254 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); 255 ok &= setGlobalClockEnable(true); 256 257 // Set up the tracing options that do require root. The options that 258 // require root should have errored out earlier if we're not running as 259 // root. 260 if (isRoot) { 261 ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); 262 ok &= setDiskTracingEnabled(g_traceDisk); 263 } 264 265 // Enable tracing. 266 ok &= setTracingEnabled(true); 267 268 if (!ok) { 269 fprintf(stderr, "error: unable to start trace\n"); 270 } 271 272 return ok; 273} 274 275// Disable tracing in the kernel. 276static void stopTrace(bool isRoot) 277{ 278 // Disable tracing. 279 setTracingEnabled(false); 280 281 // Set the options back to their defaults. 282 setTraceOverwriteEnable(true); 283 setSchedSwitchTracingEnable(false); 284 setBusUtilizationTracingEnable(false); 285 setFrequencyTracingEnable(false); 286 if (fileExists(k_governorLoadEnablePath)) { 287 setGovernorLoadTracingEnable(false); 288 } 289 setGlobalClockEnable(false); 290 291 if (isRoot) { 292 setWorkqueueTracingEnabled(false); 293 setDiskTracingEnabled(false); 294 } 295 296 // Note that we can't reset the trace buffer size here because that would 297 // clear the trace before we've read it. 298} 299 300// Read the current kernel trace and write it to stdout. 301static void dumpTrace() 302{ 303 int traceFD = open(k_tracePath, O_RDWR); 304 if (traceFD == -1) { 305 fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, 306 strerror(errno), errno); 307 return; 308 } 309 310 if (g_compress) { 311 z_stream zs; 312 uint8_t *in, *out; 313 int result, flush; 314 315 bzero(&zs, sizeof(zs)); 316 result = deflateInit(&zs, Z_DEFAULT_COMPRESSION); 317 if (result != Z_OK) { 318 fprintf(stderr, "error initializing zlib: %d\n", result); 319 close(traceFD); 320 return; 321 } 322 323 const size_t bufSize = 64*1024; 324 in = (uint8_t*)malloc(bufSize); 325 out = (uint8_t*)malloc(bufSize); 326 flush = Z_NO_FLUSH; 327 328 zs.next_out = out; 329 zs.avail_out = bufSize; 330 331 do { 332 333 if (zs.avail_in == 0) { 334 // More input is needed. 335 result = read(traceFD, in, bufSize); 336 if (result < 0) { 337 fprintf(stderr, "error reading trace: %s (%d)\n", 338 strerror(errno), errno); 339 result = Z_STREAM_END; 340 break; 341 } else if (result == 0) { 342 flush = Z_FINISH; 343 } else { 344 zs.next_in = in; 345 zs.avail_in = result; 346 } 347 } 348 349 if (zs.avail_out == 0) { 350 // Need to write the output. 351 result = write(STDOUT_FILENO, out, bufSize); 352 if ((size_t)result < bufSize) { 353 fprintf(stderr, "error writing deflated trace: %s (%d)\n", 354 strerror(errno), errno); 355 result = Z_STREAM_END; // skip deflate error message 356 zs.avail_out = bufSize; // skip the final write 357 break; 358 } 359 zs.next_out = out; 360 zs.avail_out = bufSize; 361 } 362 363 } while ((result = deflate(&zs, flush)) == Z_OK); 364 365 if (result != Z_STREAM_END) { 366 fprintf(stderr, "error deflating trace: %s\n", zs.msg); 367 } 368 369 if (zs.avail_out < bufSize) { 370 size_t bytes = bufSize - zs.avail_out; 371 result = write(STDOUT_FILENO, out, bytes); 372 if ((size_t)result < bytes) { 373 fprintf(stderr, "error writing deflated trace: %s (%d)\n", 374 strerror(errno), errno); 375 } 376 } 377 378 result = deflateEnd(&zs); 379 if (result != Z_OK) { 380 fprintf(stderr, "error cleaning up zlib: %d\n", result); 381 } 382 383 free(in); 384 free(out); 385 } else { 386 ssize_t sent = 0; 387 while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); 388 if (sent == -1) { 389 fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), 390 errno); 391 } 392 } 393 394 close(traceFD); 395} 396 397// Print the command usage help to stderr. 398static void showHelp(const char *cmd) 399{ 400 fprintf(stderr, "usage: %s [options]\n", cmd); 401 fprintf(stderr, "options include:\n" 402 " -b N use a trace buffer size of N KB\n" 403 " -c trace into a circular buffer\n" 404 " -d trace disk I/O\n" 405 " -f trace clock frequency changes\n" 406 " -l trace CPU frequency governor load\n" 407 " -s trace the kernel scheduler switches\n" 408 " -t N trace for N seconds [defualt 5]\n" 409 " -u trace bus utilization\n" 410 " -w trace the kernel workqueue\n" 411 " -z compress the trace dump\n"); 412} 413 414static void handleSignal(int signo) { 415 g_traceAborted = true; 416} 417 418static void registerSigHandler() { 419 struct sigaction sa; 420 sigemptyset(&sa.sa_mask); 421 sa.sa_flags = 0; 422 sa.sa_handler = handleSignal; 423 sigaction(SIGHUP, &sa, NULL); 424 sigaction(SIGINT, &sa, NULL); 425 sigaction(SIGQUIT, &sa, NULL); 426 sigaction(SIGTERM, &sa, NULL); 427} 428 429int main(int argc, char **argv) 430{ 431 bool isRoot = (getuid() == 0); 432 433 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 434 showHelp(argv[0]); 435 exit(0); 436 } 437 438 for (;;) { 439 int ret; 440 441 ret = getopt(argc, argv, "b:cidflst:uwz"); 442 443 if (ret < 0) { 444 break; 445 } 446 447 switch(ret) { 448 case 'b': 449 g_traceBufferSizeKB = atoi(optarg); 450 break; 451 452 case 'c': 453 g_traceOverwrite = true; 454 break; 455 456 case 'i': 457 g_traceCpuIdle = true; 458 break; 459 460 case 'l': 461 g_traceGovernorLoad = true; 462 break; 463 464 case 'd': 465 if (!isRoot) { 466 fprintf(stderr, "error: tracing disk activity requires root privileges\n"); 467 exit(1); 468 } 469 g_traceDisk = true; 470 break; 471 472 case 'f': 473 g_traceFrequency = true; 474 break; 475 476 case 's': 477 g_traceSchedSwitch = true; 478 break; 479 480 case 't': 481 g_traceDurationSeconds = atoi(optarg); 482 break; 483 484 case 'u': 485 if (!isRoot) { 486 fprintf(stderr, "error: tracing bus utilization requires root privileges\n"); 487 exit(1); 488 } 489 g_traceBusUtilization = true; 490 break; 491 492 case 'w': 493 if (!isRoot) { 494 fprintf(stderr, "error: tracing kernel work queues requires root privileges\n"); 495 exit(1); 496 } 497 g_traceWorkqueue = true; 498 break; 499 500 case 'z': 501 g_compress = true; 502 break; 503 504 default: 505 fprintf(stderr, "\n"); 506 showHelp(argv[0]); 507 exit(-1); 508 break; 509 } 510 } 511 512 registerSigHandler(); 513 514 bool ok = startTrace(isRoot); 515 516 if (ok) { 517 printf("capturing trace..."); 518 fflush(stdout); 519 520 // We clear the trace after starting it because tracing gets enabled for 521 // each CPU individually in the kernel. Having the beginning of the trace 522 // contain entries from only one CPU can cause "begin" entries without a 523 // matching "end" entry to show up if a task gets migrated from one CPU to 524 // another. 525 ok = clearTrace(); 526 527 if (ok) { 528 // Sleep to allow the trace to be captured. 529 struct timespec timeLeft; 530 timeLeft.tv_sec = g_traceDurationSeconds; 531 timeLeft.tv_nsec = 0; 532 do { 533 if (g_traceAborted) { 534 break; 535 } 536 } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); 537 } 538 } 539 540 // Stop the trace and restore the default settings. 541 stopTrace(isRoot); 542 543 if (ok) { 544 if (!g_traceAborted) { 545 printf(" done\nTRACE:\n"); 546 fflush(stdout); 547 dumpTrace(); 548 } else { 549 printf("\ntrace aborted.\n"); 550 fflush(stdout); 551 } 552 clearTrace(); 553 } else { 554 fprintf(stderr, "unable to start tracing\n"); 555 } 556 557 // Reset the trace buffer size to 1. 558 setTraceBufferSizeKB(1); 559 560 return g_traceAborted ? 1 : 0; 561} 562