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