atrace.c revision 416fd36c9f7097a11ea610522ef8297d2b82d27b
1/*
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;
30static bool g_traceCpuFrequency = false;
31static bool g_traceGovernorLoad = false;
32static bool g_traceWorkqueue = false;
33static bool g_traceOverwrite = false;
34static int g_traceBufferSizeKB = 2048;
35
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
43static const char* k_traceBufferSizePath =
44    "/sys/kernel/debug/tracing/buffer_size_kb";
45
46static 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
52static 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
58static 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
112// 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
125// 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
152// 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
164// 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
171// Check whether a file exists.
172static bool fileExists(const char* filename) {
173    return access(filename, F_OK) != -1;
174}
175
176// 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);
184    ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency);
185    if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) {
186        ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad);
187    }
188    ok &= setWorkqueueTracingEnabled(g_traceWorkqueue);
189    ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
190    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);
211    setCpuFrequencyTracingEnable(false);
212    if (fileExists(k_governorLoadEnablePath)) {
213        setGovernorLoadTracingEnable(false);
214    }
215    setWorkqueueTracingEnabled(false);
216    setGlobalClockEnable(false);
217
218    // Note that we can't reset the trace buffer size here because that would
219    // clear the trace before we've read it.
220}
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"
247                    "  -b N            use a trace buffer size of N KB\n"
248                    "  -c              trace into a circular buffer\n"
249                    "  -f              trace CPU frequency changes\n"
250                    "  -l              trace CPU frequency governor load\n"
251                    "  -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]);
280        exit(1);
281    }
282
283    for (;;) {
284        int ret;
285
286        ret = getopt(argc, argv, "b:cflst:w");
287
288        if (ret < 0) {
289            break;
290        }
291
292        switch(ret) {
293            case 'b':
294                g_traceBufferSizeKB = atoi(optarg);
295            break;
296
297            case 'c':
298                g_traceOverwrite = true;
299            break;
300
301            case 'l':
302                g_traceGovernorLoad = true;
303            break;
304
305            case 'f':
306                g_traceCpuFrequency = true;
307            break;
308
309            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:
322                fprintf(stderr, "\n");
323                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
374    // Reset the trace buffer size to 1.
375    setTraceBufferSizeKB(1);
376
377    return g_traceAborted ? 1 : 0;
378}
379