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