blob: a674295713f30e9b455bff5950db3a63cbfc285b [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;
Jamie Gennis31695332012-05-07 17:58:44 -070032static bool g_traceCpuIdle = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080033static bool g_traceGovernorLoad = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080034static bool g_traceWorkqueue = false;
35static bool g_traceOverwrite = false;
Jamie Genniscc24c8e2012-03-05 19:10:37 -080036static int g_traceBufferSizeKB = 2048;
Jamie Gennis7b5170b2012-04-28 19:06:49 -070037static bool g_compress = false;
Jamie Gennisfb31ba62012-02-23 14:16:05 -080038
39/* Global state */
40static bool g_traceAborted = false;
41
42/* Sys file paths */
43static const char* k_traceClockPath =
44 "/sys/kernel/debug/tracing/trace_clock";
45
Jamie Genniscc24c8e2012-03-05 19:10:37 -080046static const char* k_traceBufferSizePath =
47 "/sys/kernel/debug/tracing/buffer_size_kb";
48
Jamie Gennisfb31ba62012-02-23 14:16:05 -080049static const char* k_tracingOverwriteEnablePath =
50 "/sys/kernel/debug/tracing/options/overwrite";
51
52static const char* k_schedSwitchEnablePath =
53 "/sys/kernel/debug/tracing/events/sched/sched_switch/enable";
54
Jamie Genniscc24c8e2012-03-05 19:10:37 -080055static const char* k_cpuFreqEnablePath =
56 "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable";
57
Jamie Gennis31695332012-05-07 17:58:44 -070058static const char* k_cpuIdleEnablePath =
59 "/sys/kernel/debug/tracing/events/power/cpu_idle/enable";
60
Jamie Genniscc24c8e2012-03-05 19:10:37 -080061static const char* k_governorLoadEnablePath =
62 "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable";
63
Jamie Gennisfb31ba62012-02-23 14:16:05 -080064static const char* k_workqueueEnablePath =
65 "/sys/kernel/debug/tracing/events/workqueue/enable";
66
67static const char* k_tracingOnPath =
68 "/sys/kernel/debug/tracing/tracing_on";
69
70static const char* k_tracePath =
71 "/sys/kernel/debug/tracing/trace";
72
73static const char* k_traceMarkerPath =
74 "/sys/kernel/debug/tracing/trace_marker";
75
76// Write a string to a file, returning true if the write was successful.
77bool writeStr(const char* filename, const char* str)
78{
79 int fd = open(filename, O_WRONLY);
80 if (fd == -1) {
81 fprintf(stderr, "error opening %s: %s (%d)\n", filename,
82 strerror(errno), errno);
83 return false;
84 }
85
86 bool ok = true;
87 ssize_t len = strlen(str);
88 if (write(fd, str, len) != len) {
89 fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
90 strerror(errno), errno);
91 ok = false;
92 }
93
94 close(fd);
95
96 return ok;
97}
98
99// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file.
100static bool setKernelOptionEnable(const char* filename, bool enable)
101{
102 return writeStr(filename, enable ? "1" : "0");
103}
104
105// Enable or disable overwriting of the kernel trace buffers. Disabling this
106// will cause tracing to stop once the trace buffers have filled up.
107static bool setTraceOverwriteEnable(bool enable)
108{
109 return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
110}
111
112// Enable or disable tracing of the kernel scheduler switching.
113static bool setSchedSwitchTracingEnable(bool enable)
114{
115 return setKernelOptionEnable(k_schedSwitchEnablePath, enable);
116}
117
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800118// Enable or disable tracing of the CPU clock frequency.
119static bool setCpuFrequencyTracingEnable(bool enable)
120{
121 return setKernelOptionEnable(k_cpuFreqEnablePath, enable);
122}
123
Jamie Gennis31695332012-05-07 17:58:44 -0700124// Enable or disable tracing of CPU idle events.
125static bool setCpuIdleTracingEnable(bool enable)
126{
127 return setKernelOptionEnable(k_cpuIdleEnablePath, enable);
128}
129
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800130// Enable or disable tracing of the interactive CPU frequency governor's idea of
131// the CPU load.
132static bool setGovernorLoadTracingEnable(bool enable)
133{
134 return setKernelOptionEnable(k_governorLoadEnablePath, enable);
135}
136
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800137// Enable or disable tracing of the kernel workqueues.
138static bool setWorkqueueTracingEnabled(bool enable)
139{
140 return setKernelOptionEnable(k_workqueueEnablePath, enable);
141}
142
143// Enable or disable kernel tracing.
144static bool setTracingEnabled(bool enable)
145{
146 return setKernelOptionEnable(k_tracingOnPath, enable);
147}
148
149// Clear the contents of the kernel trace.
150static bool clearTrace()
151{
152 int traceFD = creat(k_tracePath, 0);
153 if (traceFD == -1) {
154 fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath,
155 strerror(errno), errno);
156 return false;
157 }
158
159 close(traceFD);
160
161 return true;
162}
163
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800164// Set the size of the kernel's trace buffer in kilobytes.
165static bool setTraceBufferSizeKB(int size)
166{
167 char str[32] = "1";
168 int len;
169 if (size < 1) {
170 size = 1;
171 }
172 snprintf(str, 32, "%d", size);
173 return writeStr(k_traceBufferSizePath, str);
174}
175
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800176// Enable or disable the kernel's use of the global clock. Disabling the global
177// clock will result in the kernel using a per-CPU local clock.
178static bool setGlobalClockEnable(bool enable)
179{
180 return writeStr(k_traceClockPath, enable ? "global" : "local");
181}
182
Jamie Gennisfe312b92012-04-09 17:33:45 -0700183// Check whether a file exists.
184static bool fileExists(const char* filename) {
185 return access(filename, F_OK) != -1;
186}
187
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800188// Enable tracing in the kernel.
189static bool startTrace()
190{
191 bool ok = true;
192
193 // Set up the tracing options.
194 ok &= setTraceOverwriteEnable(g_traceOverwrite);
195 ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800196 ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency);
Jamie Gennis31695332012-05-07 17:58:44 -0700197 ok &= setCpuIdleTracingEnable(g_traceCpuIdle);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700198 if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) {
199 ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad);
200 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800201 ok &= setWorkqueueTracingEnabled(g_traceWorkqueue);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800202 ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800203 ok &= setGlobalClockEnable(true);
204
205 // Enable tracing.
206 ok &= setTracingEnabled(true);
207
208 if (!ok) {
209 fprintf(stderr, "error: unable to start trace\n");
210 }
211
212 return ok;
213}
214
215// Disable tracing in the kernel.
216static void stopTrace()
217{
218 // Disable tracing.
219 setTracingEnabled(false);
220
221 // Set the options back to their defaults.
222 setTraceOverwriteEnable(true);
223 setSchedSwitchTracingEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800224 setCpuFrequencyTracingEnable(false);
Jamie Gennisfe312b92012-04-09 17:33:45 -0700225 if (fileExists(k_governorLoadEnablePath)) {
226 setGovernorLoadTracingEnable(false);
227 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800228 setWorkqueueTracingEnabled(false);
229 setGlobalClockEnable(false);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800230
231 // Note that we can't reset the trace buffer size here because that would
232 // clear the trace before we've read it.
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800233}
234
235// Read the current kernel trace and write it to stdout.
236static void dumpTrace()
237{
238 int traceFD = open(k_tracePath, O_RDWR);
239 if (traceFD == -1) {
240 fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
241 strerror(errno), errno);
242 return;
243 }
244
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700245 if (g_compress) {
246 z_stream zs;
247 uint8_t *in, *out;
248 int result, flush;
249
250 bzero(&zs, sizeof(zs));
251 result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
252 if (result != Z_OK) {
253 fprintf(stderr, "error initializing zlib: %d\n", result);
254 close(traceFD);
255 return;
256 }
257
258 const size_t bufSize = 64*1024;
259 in = (uint8_t*)malloc(bufSize);
260 out = (uint8_t*)malloc(bufSize);
261 flush = Z_NO_FLUSH;
262
263 zs.next_out = out;
264 zs.avail_out = bufSize;
265
266 do {
267
268 if (zs.avail_in == 0) {
269 // More input is needed.
270 result = read(traceFD, in, bufSize);
271 if (result < 0) {
272 fprintf(stderr, "error reading trace: %s (%d)\n",
273 strerror(errno), errno);
274 result = Z_STREAM_END;
275 break;
276 } else if (result == 0) {
277 flush = Z_FINISH;
278 } else {
279 zs.next_in = in;
280 zs.avail_in = result;
281 }
282 }
283
284 if (zs.avail_out == 0) {
285 // Need to write the output.
286 result = write(STDOUT_FILENO, out, bufSize);
287 if ((size_t)result < bufSize) {
288 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
289 strerror(errno), errno);
290 result = Z_STREAM_END; // skip deflate error message
291 zs.avail_out = bufSize; // skip the final write
292 break;
293 }
294 zs.next_out = out;
295 zs.avail_out = bufSize;
296 }
297
298 } while ((result = deflate(&zs, flush)) == Z_OK);
299
300 if (result != Z_STREAM_END) {
301 fprintf(stderr, "error deflating trace: %s\n", zs.msg);
302 }
303
304 if (zs.avail_out < bufSize) {
305 size_t bytes = bufSize - zs.avail_out;
306 result = write(STDOUT_FILENO, out, bytes);
307 if ((size_t)result < bytes) {
308 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
309 strerror(errno), errno);
310 }
311 }
312
313 result = deflateEnd(&zs);
314 if (result != Z_OK) {
315 fprintf(stderr, "error cleaning up zlib: %d\n", result);
316 }
317
318 free(in);
319 free(out);
320 } else {
321 ssize_t sent = 0;
322 while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0);
323 if (sent == -1) {
324 fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno),
325 errno);
326 }
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800327 }
328
329 close(traceFD);
330}
331
332// Print the command usage help to stderr.
333static void showHelp(const char *cmd)
334{
335 fprintf(stderr, "usage: %s [options]\n", cmd);
336 fprintf(stderr, "options include:\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800337 " -b N use a trace buffer size of N KB\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800338 " -c trace into a circular buffer\n"
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800339 " -f trace CPU frequency changes\n"
340 " -l trace CPU frequency governor load\n"
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800341 " -s trace the kernel scheduler switches\n"
342 " -t N trace for N seconds [defualt 5]\n"
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700343 " -w trace the kernel workqueue\n"
344 " -z compress the trace dump\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800345}
346
347static void handleSignal(int signo) {
348 g_traceAborted = true;
349}
350
351static void registerSigHandler() {
352 struct sigaction sa;
353 sigemptyset(&sa.sa_mask);
354 sa.sa_flags = 0;
355 sa.sa_handler = handleSignal;
356 sigaction(SIGHUP, &sa, NULL);
357 sigaction(SIGINT, &sa, NULL);
358 sigaction(SIGQUIT, &sa, NULL);
359 sigaction(SIGTERM, &sa, NULL);
360}
361
362int main(int argc, char **argv)
363{
364 if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
365 showHelp(argv[0]);
366 exit(0);
367 }
368
369 if (getuid() != 0) {
370 fprintf(stderr, "error: %s must be run as root.", argv[0]);
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800371 exit(1);
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800372 }
373
374 for (;;) {
375 int ret;
376
Jamie Gennis31695332012-05-07 17:58:44 -0700377 ret = getopt(argc, argv, "b:ciflst:wz");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800378
379 if (ret < 0) {
380 break;
381 }
382
383 switch(ret) {
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800384 case 'b':
385 g_traceBufferSizeKB = atoi(optarg);
386 break;
387
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800388 case 'c':
389 g_traceOverwrite = true;
390 break;
391
Jamie Gennis31695332012-05-07 17:58:44 -0700392 case 'i':
393 g_traceCpuIdle = true;
394 break;
395
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800396 case 'l':
397 g_traceGovernorLoad = true;
398 break;
399
400 case 'f':
401 g_traceCpuFrequency = true;
402 break;
403
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800404 case 's':
405 g_traceSchedSwitch = true;
406 break;
407
408 case 't':
409 g_traceDurationSeconds = atoi(optarg);
410 break;
411
412 case 'w':
413 g_traceWorkqueue = true;
414 break;
415
Jamie Gennis7b5170b2012-04-28 19:06:49 -0700416 case 'z':
417 g_compress = true;
418 break;
419
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800420 default:
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800421 fprintf(stderr, "\n");
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800422 showHelp(argv[0]);
423 exit(-1);
424 break;
425 }
426 }
427
428 registerSigHandler();
429
430 bool ok = startTrace();
431
432 if (ok) {
433 printf("capturing trace...");
434 fflush(stdout);
435
436 // We clear the trace after starting it because tracing gets enabled for
437 // each CPU individually in the kernel. Having the beginning of the trace
438 // contain entries from only one CPU can cause "begin" entries without a
439 // matching "end" entry to show up if a task gets migrated from one CPU to
440 // another.
441 ok = clearTrace();
442
443 if (ok) {
444 // Sleep to allow the trace to be captured.
445 struct timespec timeLeft;
446 timeLeft.tv_sec = g_traceDurationSeconds;
447 timeLeft.tv_nsec = 0;
448 do {
449 if (g_traceAborted) {
450 break;
451 }
452 } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
453 }
454 }
455
456 // Stop the trace and restore the default settings.
457 stopTrace();
458
459 if (ok) {
460 if (!g_traceAborted) {
461 printf(" done\nTRACE:\n");
462 fflush(stdout);
463 dumpTrace();
464 } else {
465 printf("\ntrace aborted.\n");
466 fflush(stdout);
467 }
468 clearTrace();
469 } else {
470 fprintf(stderr, "unable to start tracing\n");
471 }
472
Jamie Genniscc24c8e2012-03-05 19:10:37 -0800473 // Reset the trace buffer size to 1.
474 setTraceBufferSizeKB(1);
475
Jamie Gennisfb31ba62012-02-23 14:16:05 -0800476 return g_traceAborted ? 1 : 0;
477}