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