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