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