From fb31ba69282e34df62005ec63afda2b8ec69533e Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 23 Feb 2012 14:16:05 -0800 Subject: [PATCH 01/14] Add the atrace utility. This change adds a new system binary to help with capturing and dumping kernel traces. Change-Id: If2fc074480f822588a4c171312dc4c04fd305356 --- cmds/atrace/Android.mk | 12 ++ cmds/atrace/MODULE_LICENSE_APACHE2 | 0 cmds/atrace/NOTICE | 190 ++++++++++++++++++ cmds/atrace/atrace.c | 304 +++++++++++++++++++++++++++++ 4 files changed, 506 insertions(+) create mode 100644 cmds/atrace/Android.mk create mode 100644 cmds/atrace/MODULE_LICENSE_APACHE2 create mode 100644 cmds/atrace/NOTICE create mode 100644 cmds/atrace/atrace.c diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk new file mode 100644 index 000000000..b6c55d4fb --- /dev/null +++ b/cmds/atrace/Android.mk @@ -0,0 +1,12 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= atrace.c + +LOCAL_MODULE:= atrace + +LOCAL_MODULE_TAGS:= optional + +include $(BUILD_EXECUTABLE) diff --git a/cmds/atrace/MODULE_LICENSE_APACHE2 b/cmds/atrace/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/cmds/atrace/NOTICE b/cmds/atrace/NOTICE new file mode 100644 index 000000000..c77f135e7 --- /dev/null +++ b/cmds/atrace/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2012, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c new file mode 100644 index 000000000..3d2a52e55 --- /dev/null +++ b/cmds/atrace/atrace.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Command line options */ +static int g_traceDurationSeconds = 5; +static bool g_traceSchedSwitch = false; +static bool g_traceWorkqueue = false; +static bool g_traceOverwrite = false; + +/* Global state */ +static bool g_traceAborted = false; + +/* Sys file paths */ +static const char* k_traceClockPath = + "/sys/kernel/debug/tracing/trace_clock"; + +static const char* k_tracingOverwriteEnablePath = + "/sys/kernel/debug/tracing/options/overwrite"; + +static const char* k_schedSwitchEnablePath = + "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; + +static const char* k_workqueueEnablePath = + "/sys/kernel/debug/tracing/events/workqueue/enable"; + +static const char* k_tracingOnPath = + "/sys/kernel/debug/tracing/tracing_on"; + +static const char* k_tracePath = + "/sys/kernel/debug/tracing/trace"; + +static const char* k_traceMarkerPath = + "/sys/kernel/debug/tracing/trace_marker"; + +// Write a string to a file, returning true if the write was successful. +bool writeStr(const char* filename, const char* str) +{ + int fd = open(filename, O_WRONLY); + if (fd == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", filename, + strerror(errno), errno); + return false; + } + + bool ok = true; + ssize_t len = strlen(str); + if (write(fd, str, len) != len) { + fprintf(stderr, "error writing to %s: %s (%d)\n", filename, + strerror(errno), errno); + ok = false; + } + + close(fd); + + return ok; +} + +// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file. +static bool setKernelOptionEnable(const char* filename, bool enable) +{ + return writeStr(filename, enable ? "1" : "0"); +} + +// Enable or disable overwriting of the kernel trace buffers. Disabling this +// will cause tracing to stop once the trace buffers have filled up. +static bool setTraceOverwriteEnable(bool enable) +{ + return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable); +} + +// Enable or disable tracing of the kernel scheduler switching. +static bool setSchedSwitchTracingEnable(bool enable) +{ + return setKernelOptionEnable(k_schedSwitchEnablePath, enable); +} + +// Enable or disable tracing of the kernel workqueues. +static bool setWorkqueueTracingEnabled(bool enable) +{ + return setKernelOptionEnable(k_workqueueEnablePath, enable); +} + +// Enable or disable kernel tracing. +static bool setTracingEnabled(bool enable) +{ + return setKernelOptionEnable(k_tracingOnPath, enable); +} + +// Clear the contents of the kernel trace. +static bool clearTrace() +{ + int traceFD = creat(k_tracePath, 0); + if (traceFD == -1) { + fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath, + strerror(errno), errno); + return false; + } + + close(traceFD); + + return true; +} + +// Enable or disable the kernel's use of the global clock. Disabling the global +// clock will result in the kernel using a per-CPU local clock. +static bool setGlobalClockEnable(bool enable) +{ + return writeStr(k_traceClockPath, enable ? "global" : "local"); +} + +// Enable tracing in the kernel. +static bool startTrace() +{ + bool ok = true; + + // Set up the tracing options. + ok &= setTraceOverwriteEnable(g_traceOverwrite); + ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); + ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); + ok &= setGlobalClockEnable(true); + + // Enable tracing. + ok &= setTracingEnabled(true); + + if (!ok) { + fprintf(stderr, "error: unable to start trace\n"); + } + + return ok; +} + +// Disable tracing in the kernel. +static void stopTrace() +{ + // Disable tracing. + setTracingEnabled(false); + + // Set the options back to their defaults. + setTraceOverwriteEnable(true); + setSchedSwitchTracingEnable(false); + setWorkqueueTracingEnabled(false); + setGlobalClockEnable(false); +} + +// Read the current kernel trace and write it to stdout. +static void dumpTrace() +{ + int traceFD = open(k_tracePath, O_RDWR); + if (traceFD == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, + strerror(errno), errno); + return; + } + + ssize_t sent = 0; + while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); + if (sent == -1) { + fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), + errno); + } + + close(traceFD); +} + +// Print the command usage help to stderr. +static void showHelp(const char *cmd) +{ + fprintf(stderr, "usage: %s [options]\n", cmd); + fprintf(stderr, "options include:\n" + " -c trace into a circular buffer\n" + " -s trace the kernel scheduler switches\n" + " -t N trace for N seconds [defualt 5]\n" + " -w trace the kernel workqueue\n"); +} + +static void handleSignal(int signo) { + g_traceAborted = true; +} + +static void registerSigHandler() { + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = handleSignal; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); +} + +int main(int argc, char **argv) +{ + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { + showHelp(argv[0]); + exit(0); + } + + if (getuid() != 0) { + fprintf(stderr, "error: %s must be run as root.", argv[0]); + } + + for (;;) { + int ret; + + ret = getopt(argc, argv, "cst:w"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 'c': + g_traceOverwrite = true; + break; + + case 's': + g_traceSchedSwitch = true; + break; + + case 't': + g_traceDurationSeconds = atoi(optarg); + break; + + case 'w': + g_traceWorkqueue = true; + break; + + default: + showHelp(argv[0]); + exit(-1); + break; + } + } + + registerSigHandler(); + + bool ok = startTrace(); + + if (ok) { + printf("capturing trace..."); + fflush(stdout); + + // We clear the trace after starting it because tracing gets enabled for + // each CPU individually in the kernel. Having the beginning of the trace + // contain entries from only one CPU can cause "begin" entries without a + // matching "end" entry to show up if a task gets migrated from one CPU to + // another. + ok = clearTrace(); + + if (ok) { + // Sleep to allow the trace to be captured. + struct timespec timeLeft; + timeLeft.tv_sec = g_traceDurationSeconds; + timeLeft.tv_nsec = 0; + do { + if (g_traceAborted) { + break; + } + } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); + } + } + + // Stop the trace and restore the default settings. + stopTrace(); + + if (ok) { + if (!g_traceAborted) { + printf(" done\nTRACE:\n"); + fflush(stdout); + dumpTrace(); + } else { + printf("\ntrace aborted.\n"); + fflush(stdout); + } + clearTrace(); + } else { + fprintf(stderr, "unable to start tracing\n"); + } + + return g_traceAborted ? 1 : 0; +} From dd1ddbbdd7dc965d906129a4cda04dc86ecb2553 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sun, 26 Feb 2012 15:50:35 -0800 Subject: [PATCH 02/14] atrace: include the binary in debug builds This change makes the atrace module tag 'debug' so that the binary is included in all debug builds. Change-Id: I1fdb87be0461daf686e52ded04b37b9a122edea3 --- cmds/atrace/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk index b6c55d4fb..c1a0d87fa 100644 --- a/cmds/atrace/Android.mk +++ b/cmds/atrace/Android.mk @@ -7,6 +7,6 @@ LOCAL_SRC_FILES:= atrace.c LOCAL_MODULE:= atrace -LOCAL_MODULE_TAGS:= optional +LOCAL_MODULE_TAGS:= debug include $(BUILD_EXECUTABLE) From cc24c8e6b48c2eb204bf3d7f974dbff6dc0cc862 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 5 Mar 2012 19:10:37 -0800 Subject: [PATCH 03/14] atrace: add some more command line options This change adds two command line optiosn: -b for setting the trace buffer size -f for enabling tracing of the CPU frequency -l for enabling tracing of the CPU load Change-Id: Id99851a4c4eba5cfed218f29081e072bbe492133 --- cmds/atrace/atrace.c | 68 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 3d2a52e55..a617432f7 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -27,8 +27,11 @@ /* Command line options */ static int g_traceDurationSeconds = 5; static bool g_traceSchedSwitch = false; +static bool g_traceCpuFrequency = false; +static bool g_traceGovernorLoad = false; static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; +static int g_traceBufferSizeKB = 2048; /* Global state */ static bool g_traceAborted = false; @@ -37,12 +40,22 @@ static bool g_traceAborted = false; static const char* k_traceClockPath = "/sys/kernel/debug/tracing/trace_clock"; +static const char* k_traceBufferSizePath = + "/sys/kernel/debug/tracing/buffer_size_kb"; + static const char* k_tracingOverwriteEnablePath = "/sys/kernel/debug/tracing/options/overwrite"; static const char* k_schedSwitchEnablePath = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; +static const char* k_cpuFreqEnablePath = + "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable"; + +static const char* k_governorLoadEnablePath = + "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable"; + + static const char* k_workqueueEnablePath = "/sys/kernel/debug/tracing/events/workqueue/enable"; @@ -97,6 +110,19 @@ static bool setSchedSwitchTracingEnable(bool enable) return setKernelOptionEnable(k_schedSwitchEnablePath, enable); } +// Enable or disable tracing of the CPU clock frequency. +static bool setCpuFrequencyTracingEnable(bool enable) +{ + return setKernelOptionEnable(k_cpuFreqEnablePath, enable); +} + +// Enable or disable tracing of the interactive CPU frequency governor's idea of +// the CPU load. +static bool setGovernorLoadTracingEnable(bool enable) +{ + return setKernelOptionEnable(k_governorLoadEnablePath, enable); +} + // Enable or disable tracing of the kernel workqueues. static bool setWorkqueueTracingEnabled(bool enable) { @@ -124,6 +150,18 @@ static bool clearTrace() return true; } +// Set the size of the kernel's trace buffer in kilobytes. +static bool setTraceBufferSizeKB(int size) +{ + char str[32] = "1"; + int len; + if (size < 1) { + size = 1; + } + snprintf(str, 32, "%d", size); + return writeStr(k_traceBufferSizePath, str); +} + // Enable or disable the kernel's use of the global clock. Disabling the global // clock will result in the kernel using a per-CPU local clock. static bool setGlobalClockEnable(bool enable) @@ -139,7 +177,10 @@ static bool startTrace() // Set up the tracing options. ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); + ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency); + ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); + ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); ok &= setGlobalClockEnable(true); // Enable tracing. @@ -161,8 +202,13 @@ static void stopTrace() // Set the options back to their defaults. setTraceOverwriteEnable(true); setSchedSwitchTracingEnable(false); + setCpuFrequencyTracingEnable(false); + setGovernorLoadTracingEnable(false); setWorkqueueTracingEnabled(false); setGlobalClockEnable(false); + + // Note that we can't reset the trace buffer size here because that would + // clear the trace before we've read it. } // Read the current kernel trace and write it to stdout. @@ -190,7 +236,10 @@ static void showHelp(const char *cmd) { fprintf(stderr, "usage: %s [options]\n", cmd); fprintf(stderr, "options include:\n" + " -b N use a trace buffer size of N KB\n" " -c trace into a circular buffer\n" + " -f trace CPU frequency changes\n" + " -l trace CPU frequency governor load\n" " -s trace the kernel scheduler switches\n" " -t N trace for N seconds [defualt 5]\n" " -w trace the kernel workqueue\n"); @@ -220,22 +269,35 @@ int main(int argc, char **argv) if (getuid() != 0) { fprintf(stderr, "error: %s must be run as root.", argv[0]); + exit(1); } for (;;) { int ret; - ret = getopt(argc, argv, "cst:w"); + ret = getopt(argc, argv, "b:cflst:w"); if (ret < 0) { break; } switch(ret) { + case 'b': + g_traceBufferSizeKB = atoi(optarg); + break; + case 'c': g_traceOverwrite = true; break; + case 'l': + g_traceGovernorLoad = true; + break; + + case 'f': + g_traceCpuFrequency = true; + break; + case 's': g_traceSchedSwitch = true; break; @@ -249,6 +311,7 @@ int main(int argc, char **argv) break; default: + fprintf(stderr, "\n"); showHelp(argv[0]); exit(-1); break; @@ -300,5 +363,8 @@ int main(int argc, char **argv) fprintf(stderr, "unable to start tracing\n"); } + // Reset the trace buffer size to 1. + setTraceBufferSizeKB(1); + return g_traceAborted ? 1 : 0; } From fe312b98f747d7818ce865fb5b12b805f2ce9a9b Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 9 Apr 2012 17:33:45 -0700 Subject: [PATCH 04/14] atrace: make cpu freq governor tracing optional This change allows atrace to run on kernels that do not have CPU frequency interactive governor tracing enabled in the kernel. Change-Id: Ibfde751da7b6cc9a55794355093a37feb28f5e52 --- cmds/atrace/atrace.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index a617432f7..92fe5d10c 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -55,7 +55,6 @@ static const char* k_cpuFreqEnablePath = static const char* k_governorLoadEnablePath = "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable"; - static const char* k_workqueueEnablePath = "/sys/kernel/debug/tracing/events/workqueue/enable"; @@ -169,6 +168,11 @@ static bool setGlobalClockEnable(bool enable) return writeStr(k_traceClockPath, enable ? "global" : "local"); } +// Check whether a file exists. +static bool fileExists(const char* filename) { + return access(filename, F_OK) != -1; +} + // Enable tracing in the kernel. static bool startTrace() { @@ -178,7 +182,9 @@ static bool startTrace() ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency); - ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); + if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { + ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); + } ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); ok &= setGlobalClockEnable(true); @@ -203,7 +209,9 @@ static void stopTrace() setTraceOverwriteEnable(true); setSchedSwitchTracingEnable(false); setCpuFrequencyTracingEnable(false); - setGovernorLoadTracingEnable(false); + if (fileExists(k_governorLoadEnablePath)) { + setGovernorLoadTracingEnable(false); + } setWorkqueueTracingEnabled(false); setGlobalClockEnable(false); From 7b5170b249c1d312cfe3b5658b6c140b2a48226f Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sat, 28 Apr 2012 19:06:49 -0700 Subject: [PATCH 05/14] atrace: add support for zlib compression This change adds the -z command line flag to atrace to enable support for compressing the trace with zlib as it's printed to stdout. Change-Id: I45301c63a4d1d388152244fec3c9e05e554598e8 --- cmds/atrace/Android.mk | 4 ++ cmds/atrace/atrace.c | 98 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk index c1a0d87fa..df79e82c5 100644 --- a/cmds/atrace/Android.mk +++ b/cmds/atrace/Android.mk @@ -5,8 +5,12 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= atrace.c +LOCAL_C_INCLUDES += external/zlib + LOCAL_MODULE:= atrace LOCAL_MODULE_TAGS:= debug +LOCAL_STATIC_LIBRARIES := libz + include $(BUILD_EXECUTABLE) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 92fe5d10c..7b4f55e5e 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -23,6 +23,7 @@ #include #include #include +#include /* Command line options */ static int g_traceDurationSeconds = 5; @@ -32,6 +33,7 @@ static bool g_traceGovernorLoad = false; static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; static int g_traceBufferSizeKB = 2048; +static bool g_compress = false; /* Global state */ static bool g_traceAborted = false; @@ -229,11 +231,88 @@ static void dumpTrace() return; } - ssize_t sent = 0; - while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); - if (sent == -1) { - fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), - errno); + if (g_compress) { + z_stream zs; + uint8_t *in, *out; + int result, flush; + + bzero(&zs, sizeof(zs)); + result = deflateInit(&zs, Z_DEFAULT_COMPRESSION); + if (result != Z_OK) { + fprintf(stderr, "error initializing zlib: %d\n", result); + close(traceFD); + return; + } + + const size_t bufSize = 64*1024; + in = (uint8_t*)malloc(bufSize); + out = (uint8_t*)malloc(bufSize); + flush = Z_NO_FLUSH; + + zs.next_out = out; + zs.avail_out = bufSize; + + do { + + if (zs.avail_in == 0) { + // More input is needed. + result = read(traceFD, in, bufSize); + if (result < 0) { + fprintf(stderr, "error reading trace: %s (%d)\n", + strerror(errno), errno); + result = Z_STREAM_END; + break; + } else if (result == 0) { + flush = Z_FINISH; + } else { + zs.next_in = in; + zs.avail_in = result; + } + } + + if (zs.avail_out == 0) { + // Need to write the output. + result = write(STDOUT_FILENO, out, bufSize); + if ((size_t)result < bufSize) { + fprintf(stderr, "error writing deflated trace: %s (%d)\n", + strerror(errno), errno); + result = Z_STREAM_END; // skip deflate error message + zs.avail_out = bufSize; // skip the final write + break; + } + zs.next_out = out; + zs.avail_out = bufSize; + } + + } while ((result = deflate(&zs, flush)) == Z_OK); + + if (result != Z_STREAM_END) { + fprintf(stderr, "error deflating trace: %s\n", zs.msg); + } + + if (zs.avail_out < bufSize) { + size_t bytes = bufSize - zs.avail_out; + result = write(STDOUT_FILENO, out, bytes); + if ((size_t)result < bytes) { + fprintf(stderr, "error writing deflated trace: %s (%d)\n", + strerror(errno), errno); + } + } + + result = deflateEnd(&zs); + if (result != Z_OK) { + fprintf(stderr, "error cleaning up zlib: %d\n", result); + } + + free(in); + free(out); + } else { + ssize_t sent = 0; + while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); + if (sent == -1) { + fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), + errno); + } } close(traceFD); @@ -250,7 +329,8 @@ static void showHelp(const char *cmd) " -l trace CPU frequency governor load\n" " -s trace the kernel scheduler switches\n" " -t N trace for N seconds [defualt 5]\n" - " -w trace the kernel workqueue\n"); + " -w trace the kernel workqueue\n" + " -z compress the trace dump\n"); } static void handleSignal(int signo) { @@ -283,7 +363,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:cflst:w"); + ret = getopt(argc, argv, "b:cflst:wz"); if (ret < 0) { break; @@ -318,6 +398,10 @@ int main(int argc, char **argv) g_traceWorkqueue = true; break; + case 'z': + g_compress = true; + break; + default: fprintf(stderr, "\n"); showHelp(argv[0]); From 3169533f1d11c4a7aa0cd6fa2aa04fc810db0de6 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 7 May 2012 17:58:44 -0700 Subject: [PATCH 06/14] atrace: add support for tracing CPU idle events This change adds the -i command line flag to atrace to enable tracing CPU idle kernel events. Change-Id: I4a2c8619224c0f50c7fd7c61c7b24fa2211f656c --- cmds/atrace/atrace.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 7b4f55e5e..a67429571 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -29,6 +29,7 @@ static int g_traceDurationSeconds = 5; static bool g_traceSchedSwitch = false; static bool g_traceCpuFrequency = false; +static bool g_traceCpuIdle = false; static bool g_traceGovernorLoad = false; static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; @@ -54,6 +55,9 @@ static const char* k_schedSwitchEnablePath = static const char* k_cpuFreqEnablePath = "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable"; +static const char* k_cpuIdleEnablePath = + "/sys/kernel/debug/tracing/events/power/cpu_idle/enable"; + static const char* k_governorLoadEnablePath = "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable"; @@ -117,6 +121,12 @@ static bool setCpuFrequencyTracingEnable(bool enable) return setKernelOptionEnable(k_cpuFreqEnablePath, enable); } +// Enable or disable tracing of CPU idle events. +static bool setCpuIdleTracingEnable(bool enable) +{ + return setKernelOptionEnable(k_cpuIdleEnablePath, enable); +} + // Enable or disable tracing of the interactive CPU frequency governor's idea of // the CPU load. static bool setGovernorLoadTracingEnable(bool enable) @@ -184,6 +194,7 @@ static bool startTrace() ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency); + ok &= setCpuIdleTracingEnable(g_traceCpuIdle); if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); } @@ -363,7 +374,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:cflst:wz"); + ret = getopt(argc, argv, "b:ciflst:wz"); if (ret < 0) { break; @@ -378,6 +389,10 @@ int main(int argc, char **argv) g_traceOverwrite = true; break; + case 'i': + g_traceCpuIdle = true; + break; + case 'l': g_traceGovernorLoad = true; break; From ac9453d82e9077a8b02afa4e26ef9ff1f47b4ee9 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 22 May 2012 18:58:46 -0700 Subject: [PATCH 07/14] Add disk I/O tracing. Bug: 6488845 Change-Id: I93acb726f35b114fb1ea7297da4456826f805b7b --- cmds/atrace/Android.mk | 1 + cmds/atrace/atrace.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk index df79e82c5..1de8e95c4 100644 --- a/cmds/atrace/Android.mk +++ b/cmds/atrace/Android.mk @@ -6,6 +6,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= atrace.c LOCAL_C_INCLUDES += external/zlib +LOCAL_CFLAGS += -std=c99 LOCAL_MODULE:= atrace diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index a67429571..a0d42502c 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -25,11 +25,14 @@ #include #include +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + /* Command line options */ static int g_traceDurationSeconds = 5; static bool g_traceSchedSwitch = false; static bool g_traceCpuFrequency = false; static bool g_traceCpuIdle = false; +static bool g_traceDisk = false; static bool g_traceGovernorLoad = false; static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; @@ -64,6 +67,13 @@ static const char* k_governorLoadEnablePath = static const char* k_workqueueEnablePath = "/sys/kernel/debug/tracing/events/workqueue/enable"; +static const char* k_diskEnablePaths[] = { + "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable", + "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable", + "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable", + "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable", +}; + static const char* k_tracingOnPath = "/sys/kernel/debug/tracing/tracing_on"; @@ -102,6 +112,16 @@ static bool setKernelOptionEnable(const char* filename, bool enable) return writeStr(filename, enable ? "1" : "0"); } +// Enable or disable a collection of kernel options by writing a "1" or a "0" into each /sys file. +static bool setMultipleKernelOptionsEnable(const char** filenames, size_t count, bool enable) +{ + bool result = true; + for (size_t i = 0; i < count; i++) { + result &= setKernelOptionEnable(filenames[i], enable); + } + return result; +} + // Enable or disable overwriting of the kernel trace buffers. Disabling this // will cause tracing to stop once the trace buffers have filled up. static bool setTraceOverwriteEnable(bool enable) @@ -140,6 +160,12 @@ static bool setWorkqueueTracingEnabled(bool enable) return setKernelOptionEnable(k_workqueueEnablePath, enable); } +// Enable or disable tracing of disk I/O. +static bool setDiskTracingEnabled(bool enable) +{ + return setMultipleKernelOptionsEnable(k_diskEnablePaths, NELEM(k_diskEnablePaths), enable); +} + // Enable or disable kernel tracing. static bool setTracingEnabled(bool enable) { @@ -199,6 +225,7 @@ static bool startTrace() ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); } ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); + ok &= setDiskTracingEnabled(g_traceDisk); ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); ok &= setGlobalClockEnable(true); @@ -336,6 +363,7 @@ static void showHelp(const char *cmd) fprintf(stderr, "options include:\n" " -b N use a trace buffer size of N KB\n" " -c trace into a circular buffer\n" + " -d trace disk I/O\n" " -f trace CPU frequency changes\n" " -l trace CPU frequency governor load\n" " -s trace the kernel scheduler switches\n" @@ -374,7 +402,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:ciflst:wz"); + ret = getopt(argc, argv, "b:cidflst:wz"); if (ret < 0) { break; @@ -397,6 +425,10 @@ int main(int argc, char **argv) g_traceGovernorLoad = true; break; + case 'd': + g_traceDisk = true; + break; + case 'f': g_traceCpuFrequency = true; break; From 4b23eefd72501b358c61fb1a7096a2a94e5ea351 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 7 Jun 2012 16:27:03 -0700 Subject: [PATCH 08/14] atrace: enable running on user builds This change allows atrace to be run on a user build, where it is not run as root. Some command line options still require root, and atrace will refuse to run if those options are specified when run as non-root. Bug: 6513400 Change-Id: I29984cb8a7c3ad80ba5fa6b031ed8ff81acabacf --- cmds/atrace/Android.mk | 4 ++-- cmds/atrace/atrace.c | 49 ++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk index 1de8e95c4..12526d023 100644 --- a/cmds/atrace/Android.mk +++ b/cmds/atrace/Android.mk @@ -10,8 +10,8 @@ LOCAL_CFLAGS += -std=c99 LOCAL_MODULE:= atrace -LOCAL_MODULE_TAGS:= debug +LOCAL_MODULE_TAGS:= optional -LOCAL_STATIC_LIBRARIES := libz +LOCAL_SHARED_LIBRARIES := libz include $(BUILD_EXECUTABLE) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index a0d42502c..d81cb5c0c 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -55,6 +55,9 @@ static const char* k_tracingOverwriteEnablePath = static const char* k_schedSwitchEnablePath = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; +static const char* k_schedWakeupEnablePath = + "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable"; + static const char* k_cpuFreqEnablePath = "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable"; @@ -132,7 +135,10 @@ static bool setTraceOverwriteEnable(bool enable) // Enable or disable tracing of the kernel scheduler switching. static bool setSchedSwitchTracingEnable(bool enable) { - return setKernelOptionEnable(k_schedSwitchEnablePath, enable); + bool ok = true; + ok &= setKernelOptionEnable(k_schedSwitchEnablePath, enable); + ok &= setKernelOptionEnable(k_schedWakeupEnablePath, enable); + return ok; } // Enable or disable tracing of the CPU clock frequency. @@ -212,11 +218,11 @@ static bool fileExists(const char* filename) { } // Enable tracing in the kernel. -static bool startTrace() +static bool startTrace(bool isRoot) { bool ok = true; - // Set up the tracing options. + // Set up the tracing options that don't require root. ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency); @@ -224,11 +230,17 @@ static bool startTrace() if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); } - ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); - ok &= setDiskTracingEnabled(g_traceDisk); ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); ok &= setGlobalClockEnable(true); + // Set up the tracing options that do require root. The options that + // require root should have errored out earlier if we're not running as + // root. + if (isRoot) { + ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); + ok &= setDiskTracingEnabled(g_traceDisk); + } + // Enable tracing. ok &= setTracingEnabled(true); @@ -240,7 +252,7 @@ static bool startTrace() } // Disable tracing in the kernel. -static void stopTrace() +static void stopTrace(bool isRoot) { // Disable tracing. setTracingEnabled(false); @@ -252,9 +264,13 @@ static void stopTrace() if (fileExists(k_governorLoadEnablePath)) { setGovernorLoadTracingEnable(false); } - setWorkqueueTracingEnabled(false); setGlobalClockEnable(false); + if (isRoot) { + setWorkqueueTracingEnabled(false); + setDiskTracingEnabled(false); + } + // Note that we can't reset the trace buffer size here because that would // clear the trace before we've read it. } @@ -389,16 +405,13 @@ static void registerSigHandler() { int main(int argc, char **argv) { + bool isRoot = (getuid() == 0); + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { showHelp(argv[0]); exit(0); } - if (getuid() != 0) { - fprintf(stderr, "error: %s must be run as root.", argv[0]); - exit(1); - } - for (;;) { int ret; @@ -426,6 +439,10 @@ int main(int argc, char **argv) break; case 'd': + if (!isRoot) { + fprintf(stderr, "error: tracing disk activity requires root privileges\n"); + exit(1); + } g_traceDisk = true; break; @@ -442,6 +459,10 @@ int main(int argc, char **argv) break; case 'w': + if (!isRoot) { + fprintf(stderr, "error: tracing kernel work queues requires root privileges\n"); + exit(1); + } g_traceWorkqueue = true; break; @@ -459,7 +480,7 @@ int main(int argc, char **argv) registerSigHandler(); - bool ok = startTrace(); + bool ok = startTrace(isRoot); if (ok) { printf("capturing trace..."); @@ -486,7 +507,7 @@ int main(int argc, char **argv) } // Stop the trace and restore the default settings. - stopTrace(); + stopTrace(isRoot); if (ok) { if (!g_traceAborted) { From 99be77cf00d69a07ad5698b31c56a39f4add0bdf Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 20 Sep 2012 17:55:10 -0700 Subject: [PATCH 09/14] atrace: add clock rate and bus utilization Change-Id: I4e1764b51225436fc2b3d61454ee822f511a1a7d --- cmds/atrace/atrace.c | 49 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index d81cb5c0c..381a4ed9d 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -30,7 +30,8 @@ /* Command line options */ static int g_traceDurationSeconds = 5; static bool g_traceSchedSwitch = false; -static bool g_traceCpuFrequency = false; +static bool g_traceFrequency = false; +static bool g_traceBusUtilization = false; static bool g_traceCpuIdle = false; static bool g_traceDisk = false; static bool g_traceGovernorLoad = false; @@ -58,9 +59,15 @@ static const char* k_schedSwitchEnablePath = static const char* k_schedWakeupEnablePath = "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable"; +static const char* k_memoryBusEnablePath = + "/sys/kernel/debug/tracing/events/memory_bus/enable"; + static const char* k_cpuFreqEnablePath = "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable"; +static const char *k_clockSetRateEnablePath = + "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable"; + static const char* k_cpuIdleEnablePath = "/sys/kernel/debug/tracing/events/power/cpu_idle/enable"; @@ -141,10 +148,23 @@ static bool setSchedSwitchTracingEnable(bool enable) return ok; } -// Enable or disable tracing of the CPU clock frequency. -static bool setCpuFrequencyTracingEnable(bool enable) +// Enable or disable tracing of the Bus utilization. +static bool setBusUtilizationTracingEnable(bool enable) { - return setKernelOptionEnable(k_cpuFreqEnablePath, enable); + bool ok = false; + // these can be platform specific so make sure that at least + // one succeeds. + ok |= setKernelOptionEnable(k_memoryBusEnablePath, enable); + return ok; +} + +// Enable or disable tracing of the CPU clock frequency. +static bool setFrequencyTracingEnable(bool enable) +{ + bool ok = true; + ok &= setKernelOptionEnable(k_cpuFreqEnablePath, enable); + ok &= setKernelOptionEnable(k_clockSetRateEnablePath, enable); + return ok; } // Enable or disable tracing of CPU idle events. @@ -225,7 +245,8 @@ static bool startTrace(bool isRoot) // Set up the tracing options that don't require root. ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); - ok &= setCpuFrequencyTracingEnable(g_traceCpuFrequency); + ok &= setBusUtilizationTracingEnable(g_traceBusUtilization); + ok &= setFrequencyTracingEnable(g_traceFrequency); ok &= setCpuIdleTracingEnable(g_traceCpuIdle); if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); @@ -260,7 +281,8 @@ static void stopTrace(bool isRoot) // Set the options back to their defaults. setTraceOverwriteEnable(true); setSchedSwitchTracingEnable(false); - setCpuFrequencyTracingEnable(false); + setBusUtilizationTracingEnable(false); + setFrequencyTracingEnable(false); if (fileExists(k_governorLoadEnablePath)) { setGovernorLoadTracingEnable(false); } @@ -380,10 +402,11 @@ static void showHelp(const char *cmd) " -b N use a trace buffer size of N KB\n" " -c trace into a circular buffer\n" " -d trace disk I/O\n" - " -f trace CPU frequency changes\n" + " -f trace clock frequency changes\n" " -l trace CPU frequency governor load\n" " -s trace the kernel scheduler switches\n" " -t N trace for N seconds [defualt 5]\n" + " -u trace bus utilization\n" " -w trace the kernel workqueue\n" " -z compress the trace dump\n"); } @@ -415,7 +438,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:cidflst:wz"); + ret = getopt(argc, argv, "b:cidflst:uwz"); if (ret < 0) { break; @@ -447,7 +470,7 @@ int main(int argc, char **argv) break; case 'f': - g_traceCpuFrequency = true; + g_traceFrequency = true; break; case 's': @@ -458,6 +481,14 @@ int main(int argc, char **argv) g_traceDurationSeconds = atoi(optarg); break; + case 'u': + if (!isRoot) { + fprintf(stderr, "error: tracing bus utilization requires root privileges\n"); + exit(1); + } + g_traceBusUtilization = true; + break; + case 'w': if (!isRoot) { fprintf(stderr, "error: tracing kernel work queues requires root privileges\n"); From e8744fd4dce2881c83d69c084b6937d0397ace05 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Fri, 5 Oct 2012 16:04:12 -0700 Subject: [PATCH 10/14] atrace: make clock & bus bw tracing optional This change makes enabling clock tracing and bus bandwidth tracing depend on whether the device's kernel supports these features. If it's not supported it will be skipped. Change-Id: I51d57299693635b0ecebb75de5dccd5b62d47be3 --- cmds/atrace/atrace.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 381a4ed9d..5edc47b38 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -93,6 +93,11 @@ static const char* k_tracePath = static const char* k_traceMarkerPath = "/sys/kernel/debug/tracing/trace_marker"; +// Check whether a file exists. +static bool fileExists(const char* filename) { + return access(filename, F_OK) != -1; +} + // Write a string to a file, returning true if the write was successful. bool writeStr(const char* filename, const char* str) { @@ -151,11 +156,14 @@ static bool setSchedSwitchTracingEnable(bool enable) // Enable or disable tracing of the Bus utilization. static bool setBusUtilizationTracingEnable(bool enable) { - bool ok = false; + bool ok = true, oneSet = false; // these can be platform specific so make sure that at least // one succeeds. - ok |= setKernelOptionEnable(k_memoryBusEnablePath, enable); - return ok; + if (fileExists(k_memoryBusEnablePath)) { + ok &= setKernelOptionEnable(k_memoryBusEnablePath, enable); + oneSet |= ok; + } + return ok && (oneSet || !enable); } // Enable or disable tracing of the CPU clock frequency. @@ -163,7 +171,9 @@ static bool setFrequencyTracingEnable(bool enable) { bool ok = true; ok &= setKernelOptionEnable(k_cpuFreqEnablePath, enable); - ok &= setKernelOptionEnable(k_clockSetRateEnablePath, enable); + if (fileExists(k_clockSetRateEnablePath)) { + ok &= setKernelOptionEnable(k_clockSetRateEnablePath, enable); + } return ok; } @@ -177,7 +187,11 @@ static bool setCpuIdleTracingEnable(bool enable) // the CPU load. static bool setGovernorLoadTracingEnable(bool enable) { - return setKernelOptionEnable(k_governorLoadEnablePath, enable); + bool ok = true; + if (fileExists(k_governorLoadEnablePath) || enable) { + ok &= setKernelOptionEnable(k_governorLoadEnablePath, enable); + } + return ok; } // Enable or disable tracing of the kernel workqueues. @@ -232,11 +246,6 @@ static bool setGlobalClockEnable(bool enable) return writeStr(k_traceClockPath, enable ? "global" : "local"); } -// Check whether a file exists. -static bool fileExists(const char* filename) { - return access(filename, F_OK) != -1; -} - // Enable tracing in the kernel. static bool startTrace(bool isRoot) { @@ -245,12 +254,9 @@ static bool startTrace(bool isRoot) // Set up the tracing options that don't require root. ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); - ok &= setBusUtilizationTracingEnable(g_traceBusUtilization); ok &= setFrequencyTracingEnable(g_traceFrequency); ok &= setCpuIdleTracingEnable(g_traceCpuIdle); - if (fileExists(k_governorLoadEnablePath) || g_traceGovernorLoad) { - ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); - } + ok &= setGovernorLoadTracingEnable(g_traceGovernorLoad); ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); ok &= setGlobalClockEnable(true); @@ -258,6 +264,7 @@ static bool startTrace(bool isRoot) // require root should have errored out earlier if we're not running as // root. if (isRoot) { + ok &= setBusUtilizationTracingEnable(g_traceBusUtilization); ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); ok &= setDiskTracingEnabled(g_traceDisk); } @@ -281,14 +288,12 @@ static void stopTrace(bool isRoot) // Set the options back to their defaults. setTraceOverwriteEnable(true); setSchedSwitchTracingEnable(false); - setBusUtilizationTracingEnable(false); setFrequencyTracingEnable(false); - if (fileExists(k_governorLoadEnablePath)) { - setGovernorLoadTracingEnable(false); - } + setGovernorLoadTracingEnable(false); setGlobalClockEnable(false); if (isRoot) { + setBusUtilizationTracingEnable(false); setWorkqueueTracingEnabled(false); setDiskTracingEnabled(false); } From 31be80f02cddda55e75614884038fa4645b694cd Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 5 Jun 2012 16:42:22 -0700 Subject: [PATCH 11/14] Add -n and -S options for 'nohup', offline traces Change-Id: Ieef12b36b8519ff689ab5a81447b1b9bf4806171 --- cmds/atrace/atrace.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 5edc47b38..890165af8 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -39,6 +39,8 @@ static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; static int g_traceBufferSizeKB = 2048; static bool g_compress = false; +static bool g_nohup = false; +static int g_initialSleepSecs = 0; /* Global state */ static bool g_traceAborted = false; @@ -417,7 +419,9 @@ static void showHelp(const char *cmd) } static void handleSignal(int signo) { - g_traceAborted = true; + if (!g_nohup) { + g_traceAborted = true; + } } static void registerSigHandler() { @@ -443,7 +447,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:cidflst:uwz"); + ret = getopt(argc, argv, "b:cidflst:uwznS:"); if (ret < 0) { break; @@ -478,10 +482,18 @@ int main(int argc, char **argv) g_traceFrequency = true; break; + case 'n': + g_nohup = true; + break; + case 's': g_traceSchedSwitch = true; break; + case 'S': + g_initialSleepSecs = atoi(optarg); + break; + case 't': g_traceDurationSeconds = atoi(optarg); break; @@ -516,6 +528,10 @@ int main(int argc, char **argv) registerSigHandler(); + if (g_initialSleepSecs > 0) { + sleep(g_initialSleepSecs); + } + bool ok = startTrace(isRoot); if (ok) { From 9ba4baf178bb9dad3912403bfd9aee07c14da33a Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 17 Oct 2012 12:16:01 -0700 Subject: [PATCH 12/14] atrace: add sync trace support Change-Id: I2cd2f1a1e51f6e3d1ebbde4d5442be68a218b33d --- cmds/atrace/atrace.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 890165af8..2d9b4f84f 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -35,6 +35,7 @@ static bool g_traceBusUtilization = false; static bool g_traceCpuIdle = false; static bool g_traceDisk = false; static bool g_traceGovernorLoad = false; +static bool g_traceSync = false; static bool g_traceWorkqueue = false; static bool g_traceOverwrite = false; static int g_traceBufferSizeKB = 2048; @@ -76,6 +77,9 @@ static const char* k_cpuIdleEnablePath = static const char* k_governorLoadEnablePath = "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable"; +static const char* k_syncEnablePath = + "/sys/kernel/debug/tracing/events/sync/enable"; + static const char* k_workqueueEnablePath = "/sys/kernel/debug/tracing/events/workqueue/enable"; @@ -196,6 +200,16 @@ static bool setGovernorLoadTracingEnable(bool enable) return ok; } +// Enable or disable tracing of sync timelines and waits. +static bool setSyncTracingEnabled(bool enable) +{ + bool ok = true; + if (fileExists(k_syncEnablePath) || enable) { + ok &= setKernelOptionEnable(k_syncEnablePath, enable); + } + return ok; +} + // Enable or disable tracing of the kernel workqueues. static bool setWorkqueueTracingEnabled(bool enable) { @@ -267,6 +281,7 @@ static bool startTrace(bool isRoot) // root. if (isRoot) { ok &= setBusUtilizationTracingEnable(g_traceBusUtilization); + ok &= setSyncTracingEnabled(g_traceSync); ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); ok &= setDiskTracingEnabled(g_traceDisk); } @@ -296,6 +311,7 @@ static void stopTrace(bool isRoot) if (isRoot) { setBusUtilizationTracingEnable(false); + setSyncTracingEnabled(false); setWorkqueueTracingEnabled(false); setDiskTracingEnabled(false); } @@ -415,6 +431,7 @@ static void showHelp(const char *cmd) " -t N trace for N seconds [defualt 5]\n" " -u trace bus utilization\n" " -w trace the kernel workqueue\n" + " -y trace sync timelines and waits\n" " -z compress the trace dump\n"); } @@ -447,7 +464,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "b:cidflst:uwznS:"); + ret = getopt(argc, argv, "b:cidflst:uwyznS:"); if (ret < 0) { break; @@ -514,6 +531,14 @@ int main(int argc, char **argv) g_traceWorkqueue = true; break; + case 'y': + if (!isRoot) { + fprintf(stderr, "error: tracing sync requires root privileges\n"); + exit(1); + } + g_traceSync = true; + break; + case 'z': g_compress = true; break; From 4edbd078cddcc1cbe59156a0e7ece01de75156e0 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 14 Nov 2012 15:11:21 -0800 Subject: [PATCH 13/14] add asynchronous tracing Change-Id: I5b8660cd4d2af2ad7c6b2548c9572d3527882d6d --- cmds/atrace/atrace.c | 52 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 2d9b4f84f..331a95e69 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -432,7 +433,12 @@ static void showHelp(const char *cmd) " -u trace bus utilization\n" " -w trace the kernel workqueue\n" " -y trace sync timelines and waits\n" - " -z compress the trace dump\n"); + " -z compress the trace dump\n" + " --async_start start circular trace and return immediatly\n", + " --async_dump dump the current contents of circular trace buffer\n", + " --async_stop stop tracing and dump the current contents of circular\n", + " trace buffer\n", + ); } static void handleSignal(int signo) { @@ -455,6 +461,10 @@ static void registerSigHandler() { int main(int argc, char **argv) { bool isRoot = (getuid() == 0); + bool async = false; + bool traceStart = true; + bool traceStop = true; + bool traceDump = true; if (argc == 2 && 0 == strcmp(argv[1], "--help")) { showHelp(argv[0]); @@ -463,8 +473,16 @@ int main(int argc, char **argv) for (;;) { int ret; + int option_index = 0; + static struct option long_options[] = { + {"async_start", no_argument, 0, 0 }, + {"async_stop", no_argument, 0, 0 }, + {"async_dump", no_argument, 0, 0 }, + {0, 0, 0, 0 } + }; - ret = getopt(argc, argv, "b:cidflst:uwyznS:"); + ret = getopt_long(argc, argv, "b:cidflst:uwyznS:", + long_options, &option_index); if (ret < 0) { break; @@ -543,6 +561,22 @@ int main(int argc, char **argv) g_compress = true; break; + case 0: + if (!strcmp(long_options[option_index].name, "async_start")) { + async = true; + traceStop = false; + traceDump = false; + g_traceOverwrite = true; + } else if (!strcmp(long_options[option_index].name, "async_stop")) { + async = true; + traceStop = false; + } else if (!strcmp(long_options[option_index].name, "async_dump")) { + async = true; + traceStart = false; + traceStop = false; + } + break; + default: fprintf(stderr, "\n"); showHelp(argv[0]); @@ -559,7 +593,7 @@ int main(int argc, char **argv) bool ok = startTrace(isRoot); - if (ok) { + if (ok && traceStart) { printf("capturing trace..."); fflush(stdout); @@ -570,7 +604,7 @@ int main(int argc, char **argv) // another. ok = clearTrace(); - if (ok) { + if (ok && !async) { // Sleep to allow the trace to be captured. struct timespec timeLeft; timeLeft.tv_sec = g_traceDurationSeconds; @@ -584,9 +618,10 @@ int main(int argc, char **argv) } // Stop the trace and restore the default settings. - stopTrace(isRoot); + if (traceStop) + stopTrace(isRoot); - if (ok) { + if (ok && traceDump) { if (!g_traceAborted) { printf(" done\nTRACE:\n"); fflush(stdout); @@ -596,12 +631,13 @@ int main(int argc, char **argv) fflush(stdout); } clearTrace(); - } else { + } else if (!ok) { fprintf(stderr, "unable to start tracing\n"); } // Reset the trace buffer size to 1. - setTraceBufferSizeKB(1); + if (traceStop) + setTraceBufferSizeKB(1); return g_traceAborted ? 1 : 0; } From 9284fddfc6329d5ea5ae183d4cb0646ffb470561 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 16:24:27 -0800 Subject: [PATCH 14/14] Fix build Change-Id: Ibb398f36a7569af3480e24145da4317bf5b86998 --- cmds/atrace/atrace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmds/atrace/atrace.c b/cmds/atrace/atrace.c index 331a95e69..64f13e4eb 100644 --- a/cmds/atrace/atrace.c +++ b/cmds/atrace/atrace.c @@ -434,10 +434,10 @@ static void showHelp(const char *cmd) " -w trace the kernel workqueue\n" " -y trace sync timelines and waits\n" " -z compress the trace dump\n" - " --async_start start circular trace and return immediatly\n", - " --async_dump dump the current contents of circular trace buffer\n", - " --async_stop stop tracing and dump the current contents of circular\n", - " trace buffer\n", + " --async_start start circular trace and return immediatly\n" + " --async_dump dump the current contents of circular trace buffer\n" + " --async_stop stop tracing and dump the current contents of circular\n" + " trace buffer\n" ); }