blob: a0d42502c0cb5194b360a6f02cf2da0501991791 [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
Jeff Brownac9453d2012-05-22 18:58:46 -070028#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
29
Jamie Gennisfb31ba62012-02-23 14:16:05 -080030/* Command line options */
31static int g_traceDurationSeconds = 5;
32static bool g_traceSchedSwitch = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080033static bool g_traceCpuFrequency = false;
Jamie Gennis31695332012-05-07 17:58:44 -070034static bool g_traceCpuIdle = false;
Jeff Brownac9453d2012-05-22 18:58:46 -070035static bool g_traceDisk = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080036static bool g_traceGovernorLoad = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080037static bool g_traceWorkqueue = false;
38static bool g_traceOverwrite = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080039static int g_traceBufferSizeKB = 2048;
Jamie Gennis7b5170b2012-04-28 19:06:49 -070040static bool g_compress = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080041
42/* Global state */
43static bool g_traceAborted = false;
44
45/* Sys file paths */
46static const char* k_traceClockPath =
47 "/sys/kernel/debug/tracing/trace_clock";
48
Jamie Genniscc24c8e2012-03-05 19:10:37 -080049static const char* k_traceBufferSizePath =
50 "/sys/kernel/debug/tracing/buffer_size_kb";
51
Jamie Gennisfb31ba62012-02-23 14:16:05 -080052static const char* k_tracingOverwriteEnablePath =
53 "/sys/kernel/debug/tracing/options/overwrite";
54
55static const char* k_schedSwitchEnablePath =
56 "/sys/kernel/debug/tracing/events/sched/sched_switch/enable";
57
Jamie Genniscc24c8e2012-03-05 19:10:37 -080058static const char* k_cpuFreqEnablePath =
59 "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable";
60
Jamie Gennis31695332012-05-07 17:58:44 -070061static const char* k_cpuIdleEnablePath =
62 "/sys/kernel/debug/tracing/events/power/cpu_idle/enable";
63
Jamie Genniscc24c8e2012-03-05 19:10:37 -080064static const char* k_governorLoadEnablePath =
65 "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable";
66
Jamie Gennisfb31ba62012-02-23 14:16:05 -080067static const char* k_workqueueEnablePath =
68 "/sys/kernel/debug/tracing/events/workqueue/enable";
69
Jeff Brownac9453d2012-05-22 18:58:46 -070070static const char* k_diskEnablePaths[] = {
71 "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable",
72 "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable",
73 "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable",
74 "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable",
75};
76
Jamie Gennisfb31ba62012-02-23 14:16:05 -080077static const char* k_tracingOnPath =
78 "/sys/kernel/debug/tracing/tracing_on";
79
80static const char* k_tracePath =
81 "/sys/kernel/debug/tracing/trace";
82
83static const char* k_traceMarkerPath =
84 "/sys/kernel/debug/tracing/trace_marker";
85
86// Write a string to a file, returning true if the write was successful.
87bool writeStr(const char* filename, const char* str)
88{
89 int fd = open(filename, O_WRONLY);
90 if (fd == -1) {
91 fprintf(stderr, "error opening %s: %s (%d)\n", filename,
92 strerror(errno), errno);
93 return false;
94 }
95
96 bool ok = true;
97 ssize_t len = strlen(str);
98 if (write(fd, str, len) != len) {
99 fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
100 strerror(errno), errno);
101 ok = false;
102 }
103
104 close(fd);
105
106 return ok;
107}
108
109// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file.
110static bool setKernelOptionEnable(const char* filename, bool enable)
111{
112 return writeStr(filename, enable ? "1" : "0");
113}
114
Jeff Brownac9453d2012-05-22 18:58:46 -0700115// Enable or disable a collection of kernel options by writing a "1" or a "0" into each /sys file.
116static bool setMultipleKernelOptionsEnable(const char** filenames, size_t count, bool enable)
117{
118 bool result = true;
119 for (size_t i = 0; i < count; i++) {
120 result &= setKernelOptionEnable(filenames[i], enable);
121 }
122 return result;
123}
124
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800125// Enable or disable overwriting of the kernel trace buffers. Disabling this
126// will cause tracing to stop once the trace buffers have filled up.
127static bool setTraceOverwriteEnable(bool enable)
128{
129 return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
130}
131
132// Enable or disable tracing of the kernel scheduler switching.
133static bool setSchedSwitchTracingEnable(bool enable)
134{
135 return setKernelOptionEnable(k_schedSwitchEnablePath, enable);
136}
137
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800138// Enable or disable tracing of the CPU clock frequency.
139static bool setCpuFrequencyTracingEnable(bool enable)
140{
141 return setKernelOptionEnable(k_cpuFreqEnablePath, enable);
142}
143
Jamie Gennis31695332012-05-07 17:58:44 -0700144// Enable or disable tracing of CPU idle events.
145static bool setCpuIdleTracingEnable(bool enable)
146{
147 return setKernelOptionEnable(k_cpuIdleEnablePath, enable);
148}
149
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800150// Enable or disable tracing of the interactive CPU frequency governor's idea of
151// the CPU load.
152static bool setGovernorLoadTracingEnable(bool enable)
153{
154 return setKernelOptionEnable(k_governorLoadEnablePath, enable);
155}
156
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800157// Enable or disable tracing of the kernel workqueues.
158static bool setWorkqueueTracingEnabled(bool enable)
159{
160 return setKernelOptionEnable(k_workqueueEnablePath, enable);
161}
162
Jeff Brownac9453d2012-05-22 18:58:46 -0700163// Enable or disable tracing of disk I/O.
164static bool setDiskTracingEnabled(bool enable)
165{
166 return setMultipleKernelOptionsEnable(k_diskEnablePaths, NELEM(k_diskEnablePaths), enable);
167}
168
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800169// Enable or disable kernel tracing.
170static bool setTracingEnabled(bool enable)
171{
172 return setKernelOptionEnable(k_tracingOnPath, enable);
173}
174
175// Clear the contents of the kernel trace.
176static bool clearTrace()
177{
178 int traceFD = creat(k_tracePath, 0);
179 if (traceFD == -1) {
180 fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath,
181 strerror(errno), errno);
182 return false;
183 }
184
185 close(traceFD);
186
187 return true;
188}
189
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800190// Set the size of the kernel's trace buffer in kilobytes.
191static bool setTraceBufferSizeKB(int size)
192{
193 char str[32] = "1";
194 int len;
195 if (size < 1) {
196 size = 1;
197 }
198 snprintf(str, 32, "%d", size);
199 return writeStr(k_traceBufferSizePath, str);
200}
201
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800202// Enable or disable the kernel's use of the global clock. Disabling the global
203// clock will result in the kernel using a per-CPU local clock.
204static bool setGlobalClockEnable(bool enable)
205{
206 return writeStr(k_traceClockPath, enable ? "global" : "local");
207}
208
Jamie Gennisfe312b92012-04-09 17:33:45 -0700209// Check whether a file exists.
210static bool fileExists(const char* filename) {
211 return access(filename, F_OK) != -1;
212}
213
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800214// Enable tracing in the kernel.
215static bool startTrace()
216{
217 bool ok = true;
218
219 // Set up the tracing options.
220 ok &= setTraceOverwriteEnable(g_traceOverwrite);
221 ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800222 ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency);
Jamie Gennis31695332012-05-07 17:58:44 -0700223 ok &= setCpuIdleTracingEnable(g_traceCpuIdle);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700224 if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) {
225 ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad);
226 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800227 ok &= setWorkqueueTracingEnabled(g_traceWorkqueue);
Jeff Brownac9453d2012-05-22 18:58:46 -0700228 ok &= setDiskTracingEnabled(g_traceDisk);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800229 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800230 ok &= setGlobalClockEnable(true);
231
232 // Enable tracing.
233 ok &= setTracingEnabled(true);
234
235 if (!ok) {
236 fprintf(stderr, "error: unable to start trace\n");
237 }
238
239 return ok;
240}
241
242// Disable tracing in the kernel.
243static void stopTrace()
244{
245 // Disable tracing.
246 setTracingEnabled(false);
247
248 // Set the options back to their defaults.
249 setTraceOverwriteEnable(true);
250 setSchedSwitchTracingEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800251 setCpuFrequencyTracingEnable(false);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700252 if (fileExists(k_governorLoadEnablePath)) {
253 setGovernorLoadTracingEnable(false);
254 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800255 setWorkqueueTracingEnabled(false);
256 setGlobalClockEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800257
258 // Note that we can't reset the trace buffer size here because that would
259 // clear the trace before we've read it.
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800260}
261
262// Read the current kernel trace and write it to stdout.
263static void dumpTrace()
264{
265 int traceFD = open(k_tracePath, O_RDWR);
266 if (traceFD == -1) {
267 fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
268 strerror(errno), errno);
269 return;
270 }
271
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700272 if (g_compress) {
273 z_stream zs;
274 uint8_t *in, *out;
275 int result, flush;
276
277 bzero(&zs, sizeof(zs));
278 result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
279 if (result != Z_OK) {
280 fprintf(stderr, "error initializing zlib: %d\n", result);
281 close(traceFD);
282 return;
283 }
284
285 const size_t bufSize = 64*1024;
286 in = (uint8_t*)malloc(bufSize);
287 out = (uint8_t*)malloc(bufSize);
288 flush = Z_NO_FLUSH;
289
290 zs.next_out = out;
291 zs.avail_out = bufSize;
292
293 do {
294
295 if (zs.avail_in == 0) {
296 // More input is needed.
297 result = read(traceFD, in, bufSize);
298 if (result < 0) {
299 fprintf(stderr, "error reading trace: %s (%d)\n",
300 strerror(errno), errno);
301 result = Z_STREAM_END;
302 break;
303 } else if (result == 0) {
304 flush = Z_FINISH;
305 } else {
306 zs.next_in = in;
307 zs.avail_in = result;
308 }
309 }
310
311 if (zs.avail_out == 0) {
312 // Need to write the output.
313 result = write(STDOUT_FILENO, out, bufSize);
314 if ((size_t)result < bufSize) {
315 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
316 strerror(errno), errno);
317 result = Z_STREAM_END; // skip deflate error message
318 zs.avail_out = bufSize; // skip the final write
319 break;
320 }
321 zs.next_out = out;
322 zs.avail_out = bufSize;
323 }
324
325 } while ((result = deflate(&zs, flush)) == Z_OK);
326
327 if (result != Z_STREAM_END) {
328 fprintf(stderr, "error deflating trace: %s\n", zs.msg);
329 }
330
331 if (zs.avail_out < bufSize) {
332 size_t bytes = bufSize - zs.avail_out;
333 result = write(STDOUT_FILENO, out, bytes);
334 if ((size_t)result < bytes) {
335 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
336 strerror(errno), errno);
337 }
338 }
339
340 result = deflateEnd(&zs);
341 if (result != Z_OK) {
342 fprintf(stderr, "error cleaning up zlib: %d\n", result);
343 }
344
345 free(in);
346 free(out);
347 } else {
348 ssize_t sent = 0;
349 while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0);
350 if (sent == -1) {
351 fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno),
352 errno);
353 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800354 }
355
356 close(traceFD);
357}
358
359// Print the command usage help to stderr.
360static void showHelp(const char *cmd)
361{
362 fprintf(stderr, "usage: %s [options]\n", cmd);
363 fprintf(stderr, "options include:\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800364 " -b N use a trace buffer size of N KB\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800365 " -c trace into a circular buffer\n"
Jeff Brownac9453d2012-05-22 18:58:46 -0700366 " -d trace disk I/O\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800367 " -f trace CPU frequency changes\n"
368 " -l trace CPU frequency governor load\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800369 " -s trace the kernel scheduler switches\n"
370 " -t N trace for N seconds [defualt 5]\n"
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700371 " -w trace the kernel workqueue\n"
372 " -z compress the trace dump\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800373}
374
375static void handleSignal(int signo) {
376 g_traceAborted = true;
377}
378
379static void registerSigHandler() {
380 struct sigaction sa;
381 sigemptyset(&sa.sa_mask);
382 sa.sa_flags = 0;
383 sa.sa_handler = handleSignal;
384 sigaction(SIGHUP, &sa, NULL);
385 sigaction(SIGINT, &sa, NULL);
386 sigaction(SIGQUIT, &sa, NULL);
387 sigaction(SIGTERM, &sa, NULL);
388}
389
390int main(int argc, char **argv)
391{
392 if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
393 showHelp(argv[0]);
394 exit(0);
395 }
396
397 if (getuid() != 0) {
398 fprintf(stderr, "error: %s must be run as root.", argv[0]);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800399 exit(1);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800400 }
401
402 for (;;) {
403 int ret;
404
Jeff Brownac9453d2012-05-22 18:58:46 -0700405 ret = getopt(argc, argv, "b:cidflst:wz");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800406
407 if (ret < 0) {
408 break;
409 }
410
411 switch(ret) {
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800412 case 'b':
413 g_traceBufferSizeKB = atoi(optarg);
414 break;
415
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800416 case 'c':
417 g_traceOverwrite = true;
418 break;
419
Jamie Gennis31695332012-05-07 17:58:44 -0700420 case 'i':
421 g_traceCpuIdle = true;
422 break;
423
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800424 case 'l':
425 g_traceGovernorLoad = true;
426 break;
427
Jeff Brownac9453d2012-05-22 18:58:46 -0700428 case 'd':
429 g_traceDisk = true;
430 break;
431
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800432 case 'f':
433 g_traceCpuFrequency = true;
434 break;
435
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800436 case 's':
437 g_traceSchedSwitch = true;
438 break;
439
440 case 't':
441 g_traceDurationSeconds = atoi(optarg);
442 break;
443
444 case 'w':
445 g_traceWorkqueue = true;
446 break;
447
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700448 case 'z':
449 g_compress = true;
450 break;
451
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800452 default:
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800453 fprintf(stderr, "\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800454 showHelp(argv[0]);
455 exit(-1);
456 break;
457 }
458 }
459
460 registerSigHandler();
461
462 bool ok = startTrace();
463
464 if (ok) {
465 printf("capturing trace...");
466 fflush(stdout);
467
468 // We clear the trace after starting it because tracing gets enabled for
469 // each CPU individually in the kernel. Having the beginning of the trace
470 // contain entries from only one CPU can cause "begin" entries without a
471 // matching "end" entry to show up if a task gets migrated from one CPU to
472 // another.
473 ok = clearTrace();
474
475 if (ok) {
476 // Sleep to allow the trace to be captured.
477 struct timespec timeLeft;
478 timeLeft.tv_sec = g_traceDurationSeconds;
479 timeLeft.tv_nsec = 0;
480 do {
481 if (g_traceAborted) {
482 break;
483 }
484 } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
485 }
486 }
487
488 // Stop the trace and restore the default settings.
489 stopTrace();
490
491 if (ok) {
492 if (!g_traceAborted) {
493 printf(" done\nTRACE:\n");
494 fflush(stdout);
495 dumpTrace();
496 } else {
497 printf("\ntrace aborted.\n");
498 fflush(stdout);
499 }
500 clearTrace();
501 } else {
502 fprintf(stderr, "unable to start tracing\n");
503 }
504
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800505 // Reset the trace buffer size to 1.
506 setTraceBufferSizeKB(1);
507
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800508 return g_traceAborted ? 1 : 0;
509}