blob: 7b4f55e5e78945cbae50589129bda14777c0f7c5 [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>
Jamie Gennis7b5170b2012-04-28 19:06:49 -070026#include <zlib.h>
Jamie Gennisfb31ba62012-02-23 14:16:05 -080027
28/* Command line options */
29static int g_traceDurationSeconds = 5;
30static bool g_traceSchedSwitch = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080031static bool g_traceCpuFrequency = false;
32static bool g_traceGovernorLoad = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080033static bool g_traceWorkqueue = false;
34static bool g_traceOverwrite = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080035static int g_traceBufferSizeKB = 2048;
Jamie Gennis7b5170b2012-04-28 19:06:49 -070036static bool g_compress = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080037
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
Jamie Genniscc24c8e2012-03-05 19:10:37 -080045static const char* k_traceBufferSizePath =
46 "/sys/kernel/debug/tracing/buffer_size_kb";
47
Jamie Gennisfb31ba62012-02-23 14:16:05 -080048static 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -080054static 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
Jamie Gennisfb31ba62012-02-23 14:16:05 -080060static 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800114// 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
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800127// 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800154// 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
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800166// 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
Jamie Gennisfe312b92012-04-09 17:33:45 -0700173// Check whether a file exists.
174static bool fileExists(const char* filename) {
175 return access(filename, F_OK) != -1;
176}
177
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800178// 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);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800186 ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700187 if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) {
188 ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad);
189 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800190 ok &= setWorkqueueTracingEnabled(g_traceWorkqueue);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800191 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800192 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);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800213 setCpuFrequencyTracingEnable(false);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700214 if (fileExists(k_governorLoadEnablePath)) {
215 setGovernorLoadTracingEnable(false);
216 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800217 setWorkqueueTracingEnabled(false);
218 setGlobalClockEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800219
220 // Note that we can't reset the trace buffer size here because that would
221 // clear the trace before we've read it.
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800222}
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
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700234 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 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800316 }
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"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800326 " -b N use a trace buffer size of N KB\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800327 " -c trace into a circular buffer\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800328 " -f trace CPU frequency changes\n"
329 " -l trace CPU frequency governor load\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800330 " -s trace the kernel scheduler switches\n"
331 " -t N trace for N seconds [defualt 5]\n"
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700332 " -w trace the kernel workqueue\n"
333 " -z compress the trace dump\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800334}
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]);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800360 exit(1);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800361 }
362
363 for (;;) {
364 int ret;
365
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700366 ret = getopt(argc, argv, "b:cflst:wz");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800367
368 if (ret < 0) {
369 break;
370 }
371
372 switch(ret) {
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800373 case 'b':
374 g_traceBufferSizeKB = atoi(optarg);
375 break;
376
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800377 case 'c':
378 g_traceOverwrite = true;
379 break;
380
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800381 case 'l':
382 g_traceGovernorLoad = true;
383 break;
384
385 case 'f':
386 g_traceCpuFrequency = true;
387 break;
388
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800389 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
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700401 case 'z':
402 g_compress = true;
403 break;
404
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800405 default:
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800406 fprintf(stderr, "\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800407 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
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800458 // Reset the trace buffer size to 1.
459 setTraceBufferSizeKB(1);
460
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800461 return g_traceAborted ? 1 : 0;
462}