blob: 92fe5d10cedbac1984dae51ba4e41c3c1ed3140d [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
Jamie Gennisfb31ba62012-02-23 14:16:05 -080058static 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800112// 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
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800125// 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800152// 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
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800164// 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
Jamie Gennisfe312b92012-04-09 17:33:45 -0700171// Check whether a file exists.
172static bool fileExists(const char* filename) {
173 return access(filename, F_OK) != -1;
174}
175
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800176// 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);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800184 ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700185 if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) {
186 ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad);
187 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800188 ok &= setWorkqueueTracingEnabled(g_traceWorkqueue);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800189 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800190 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);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800211 setCpuFrequencyTracingEnable(false);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700212 if (fileExists(k_governorLoadEnablePath)) {
213 setGovernorLoadTracingEnable(false);
214 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800215 setWorkqueueTracingEnabled(false);
216 setGlobalClockEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800217
218 // Note that we can't reset the trace buffer size here because that would
219 // clear the trace before we've read it.
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800220}
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"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800247 " -b N use a trace buffer size of N KB\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800248 " -c trace into a circular buffer\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800249 " -f trace CPU frequency changes\n"
250 " -l trace CPU frequency governor load\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800251 " -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]);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800280 exit(1);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800281 }
282
283 for (;;) {
284 int ret;
285
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800286 ret = getopt(argc, argv, "b:cflst:w");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800287
288 if (ret < 0) {
289 break;
290 }
291
292 switch(ret) {
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800293 case 'b':
294 g_traceBufferSizeKB = atoi(optarg);
295 break;
296
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800297 case 'c':
298 g_traceOverwrite = true;
299 break;
300
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800301 case 'l':
302 g_traceGovernorLoad = true;
303 break;
304
305 case 'f':
306 g_traceCpuFrequency = true;
307 break;
308
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800309 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:
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800322 fprintf(stderr, "\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800323 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800374 // Reset the trace buffer size to 1.
375 setTraceBufferSizeKB(1);
376
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800377 return g_traceAborted ? 1 : 0;
378}