diff options
Diffstat (limited to 'cmds/atrace')
-rw-r--r-- | cmds/atrace/Android.mk | 20 | ||||
-rw-r--r-- | cmds/atrace/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | cmds/atrace/NOTICE | 190 | ||||
-rw-r--r-- | cmds/atrace/atrace.cpp | 893 |
4 files changed, 1103 insertions, 0 deletions
diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk new file mode 100644 index 0000000..028ca8f --- /dev/null +++ b/cmds/atrace/Android.mk @@ -0,0 +1,20 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= atrace.cpp + +LOCAL_C_INCLUDES += external/zlib + +LOCAL_MODULE:= atrace + +LOCAL_MODULE_TAGS:= optional + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libcutils \ + libutils \ + libz \ + +include $(BUILD_EXECUTABLE) diff --git a/cmds/atrace/MODULE_LICENSE_APACHE2 b/cmds/atrace/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cmds/atrace/MODULE_LICENSE_APACHE2 diff --git a/cmds/atrace/NOTICE b/cmds/atrace/NOTICE new file mode 100644 index 0000000..c77f135 --- /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.cpp b/cmds/atrace/atrace.cpp new file mode 100644 index 0000000..1911839 --- /dev/null +++ b/cmds/atrace/atrace.cpp @@ -0,0 +1,893 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/sendfile.h> +#include <time.h> +#include <zlib.h> + +#include <binder/IBinder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> + +#include <cutils/properties.h> + +#include <utils/String8.h> +#include <utils/Trace.h> + +using namespace android; + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +enum { MAX_SYS_FILES = 8 }; + +const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; +const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines"; + +typedef enum { OPT, REQ } requiredness ; + +struct TracingCategory { + // The name identifying the category. + const char* name; + + // A longer description of the category. + const char* longname; + + // The userland tracing tags that the category enables. + uint64_t tags; + + // The fname==NULL terminated list of /sys/ files that the category + // enables. + struct { + // Whether the file must be writable in order to enable the tracing + // category. + requiredness required; + + // The path to the enable file. + const char* path; + } sysfiles[MAX_SYS_FILES]; +}; + +/* Tracing categories */ +static const TracingCategory k_categories[] = { + { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } }, + { "input", "Input", ATRACE_TAG_INPUT, { } }, + { "view", "View System", ATRACE_TAG_VIEW, { } }, + { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, + { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } }, + { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } }, + { "audio", "Audio", ATRACE_TAG_AUDIO, { } }, + { "video", "Video", ATRACE_TAG_VIDEO, { } }, + { "camera", "Camera", ATRACE_TAG_CAMERA, { } }, + { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, + { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, + { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, + { "sched", "CPU Scheduling", 0, { + { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" }, + } }, + { "freq", "CPU Frequency", 0, { + { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" }, + { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" }, + } }, + { "membus", "Memory Bus Utilization", 0, { + { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" }, + } }, + { "idle", "CPU Idle", 0, { + { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" }, + } }, + { "disk", "Disk I/O", 0, { + { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" }, + } }, + { "load", "CPU Load", 0, { + { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" }, + } }, + { "sync", "Synchronization", 0, { + { REQ, "/sys/kernel/debug/tracing/events/sync/enable" }, + } }, + { "workq", "Kernel Workqueues", 0, { + { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" }, + } }, +}; + +/* Command line options */ +static int g_traceDurationSeconds = 5; +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; +static const char* g_kernelTraceFuncs = NULL; +static const char* g_debugAppCmdLine = ""; + +/* Global state */ +static bool g_traceAborted = false; +static bool g_categoryEnables[NELEM(k_categories)] = {}; + +/* Sys file paths */ +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_currentTracerPath = + "/sys/kernel/debug/tracing/current_tracer"; + +static const char* k_printTgidPath = + "/sys/kernel/debug/tracing/options/print-tgid"; + +static const char* k_funcgraphAbsTimePath = + "/sys/kernel/debug/tracing/options/funcgraph-abstime"; + +static const char* k_funcgraphCpuPath = + "/sys/kernel/debug/tracing/options/funcgraph-cpu"; + +static const char* k_funcgraphProcPath = + "/sys/kernel/debug/tracing/options/funcgraph-proc"; + +static const char* k_funcgraphFlatPath = + "/sys/kernel/debug/tracing/options/funcgraph-flat"; + +static const char* k_funcgraphDurationPath = + "/sys/kernel/debug/tracing/options/funcgraph-duration"; + +static const char* k_ftraceFilterPath = + "/sys/kernel/debug/tracing/set_ftrace_filter"; + +static const char* k_tracingOnPath = + "/sys/kernel/debug/tracing/tracing_on"; + +static const char* k_tracePath = + "/sys/kernel/debug/tracing/trace"; + +// Check whether a file exists. +static bool fileExists(const char* filename) { + return access(filename, F_OK) != -1; +} + +// Check whether a file is writable. +static bool fileIsWritable(const char* filename) { + return access(filename, W_OK) != -1; +} + +// Truncate a file. +static bool truncateFile(const char* path) +{ + // This uses creat rather than truncate because some of the debug kernel + // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by + // calls to truncate, but they are cleared by calls to creat. + int traceFD = creat(path, 0); + if (traceFD == -1) { + fprintf(stderr, "error truncating %s: %s (%d)\n", path, + strerror(errno), errno); + return false; + } + + close(traceFD); + + return true; +} + +static bool _writeStr(const char* filename, const char* str, int flags) +{ + int fd = open(filename, flags); + 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; +} + +// Write a string to a file, returning true if the write was successful. +static bool writeStr(const char* filename, const char* str) +{ + return _writeStr(filename, str, O_WRONLY); +} + +// Append a string to a file, returning true if the write was successful. +static bool appendStr(const char* filename, const char* str) +{ + return _writeStr(filename, str, O_APPEND|O_WRONLY); +} + +// 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"); +} + +// Check whether the category is supported on the device with the current +// rootness. A category is supported only if all its required /sys/ files are +// writable and if enabling the category will enable one or more tracing tags +// or /sys/ files. +static bool isCategorySupported(const TracingCategory& category) +{ + bool ok = category.tags != 0; + for (int i = 0; i < MAX_SYS_FILES; i++) { + const char* path = category.sysfiles[i].path; + bool req = category.sysfiles[i].required == REQ; + if (path != NULL) { + if (req) { + if (!fileIsWritable(path)) { + return false; + } else { + ok = true; + } + } else { + ok |= fileIsWritable(path); + } + } + } + return ok; +} + +// Check whether the category would be supported on the device if the user +// were root. This function assumes that root is able to write to any file +// that exists. It performs the same logic as isCategorySupported, but it +// uses file existance rather than writability in the /sys/ file checks. +static bool isCategorySupportedForRoot(const TracingCategory& category) +{ + bool ok = category.tags != 0; + for (int i = 0; i < MAX_SYS_FILES; i++) { + const char* path = category.sysfiles[i].path; + bool req = category.sysfiles[i].required == REQ; + if (path != NULL) { + if (req) { + if (!fileExists(path)) { + return false; + } else { + ok = true; + } + } else { + ok |= fileExists(path); + } + } + } + return ok; +} + +// 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 kernel tracing. +static bool setTracingEnabled(bool enable) +{ + return setKernelOptionEnable(k_tracingOnPath, enable); +} + +// Clear the contents of the kernel trace. +static bool clearTrace() +{ + return truncateFile(k_tracePath); +} + +// 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) +{ + return writeStr(k_traceClockPath, enable ? "global" : "local"); +} + +static bool setPrintTgidEnableIfPresent(bool enable) +{ + if (fileExists(k_printTgidPath)) { + return setKernelOptionEnable(k_printTgidPath, enable); + } + return true; +} + +// Poke all the binder-enabled processes in the system to get them to re-read +// their system properties. +static bool pokeBinderServices() +{ + sp<IServiceManager> sm = defaultServiceManager(); + Vector<String16> services = sm->listServices(); + for (size_t i = 0; i < services.size(); i++) { + sp<IBinder> obj = sm->checkService(services[i]); + if (obj != NULL) { + Parcel data; + if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data, + NULL, 0) != OK) { + if (false) { + // XXX: For some reason this fails on tablets trying to + // poke the "phone" service. It's not clear whether some + // are expected to fail. + String8 svc(services[i]); + fprintf(stderr, "error poking binder service %s\n", + svc.string()); + return false; + } + } + } + } + return true; +} + +// Set the trace tags that userland tracing uses, and poke the running +// processes to pick up the new value. +static bool setTagsProperty(uint64_t tags) +{ + char buf[64]; + snprintf(buf, 64, "%#llx", tags); + if (property_set(k_traceTagsProperty, buf) < 0) { + fprintf(stderr, "error setting trace tags system property\n"); + return false; + } + return true; +} + +// Set the system property that indicates which apps should perform +// application-level tracing. +static bool setAppCmdlineProperty(const char* cmdline) +{ + if (property_set(k_traceAppCmdlineProperty, cmdline) < 0) { + fprintf(stderr, "error setting trace app system property\n"); + return false; + } + return true; +} + +// Disable all /sys/ enable files. +static bool disableKernelTraceEvents() { + bool ok = true; + for (int i = 0; i < NELEM(k_categories); i++) { + const TracingCategory &c = k_categories[i]; + for (int j = 0; j < MAX_SYS_FILES; j++) { + const char* path = c.sysfiles[j].path; + if (path != NULL && fileIsWritable(path)) { + ok &= setKernelOptionEnable(path, false); + } + } + } + return ok; +} + +// Verify that the comma separated list of functions are being traced by the +// kernel. +static bool verifyKernelTraceFuncs(const char* funcs) +{ + int fd = open(k_ftraceFilterPath, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath, + strerror(errno), errno); + return false; + } + + char buf[4097]; + ssize_t n = read(fd, buf, 4096); + close(fd); + if (n == -1) { + fprintf(stderr, "error reading %s: %s (%d)\n", k_ftraceFilterPath, + strerror(errno), errno); + return false; + } + + buf[n] = '\0'; + String8 funcList = String8::format("\n%s", buf); + + // Make sure that every function listed in funcs is in the list we just + // read from the kernel. + bool ok = true; + char* myFuncs = strdup(funcs); + char* func = strtok(myFuncs, ","); + while (func) { + String8 fancyFunc = String8::format("\n%s\n", func); + bool found = funcList.find(fancyFunc.string(), 0) >= 0; + if (!found || func[0] == '\0') { + fprintf(stderr, "error: \"%s\" is not a valid kernel function " + "to trace.\n", func); + ok = false; + } + func = strtok(NULL, ","); + } + free(myFuncs); + + return ok; +} + +// Set the comma separated list of functions that the kernel is to trace. +static bool setKernelTraceFuncs(const char* funcs) +{ + bool ok = true; + + if (funcs == NULL || funcs[0] == '\0') { + // Disable kernel function tracing. + if (fileIsWritable(k_currentTracerPath)) { + ok &= writeStr(k_currentTracerPath, "nop"); + } + if (fileIsWritable(k_ftraceFilterPath)) { + ok &= truncateFile(k_ftraceFilterPath); + } + } else { + // Enable kernel function tracing. + ok &= writeStr(k_currentTracerPath, "function_graph"); + ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true); + ok &= setKernelOptionEnable(k_funcgraphCpuPath, true); + ok &= setKernelOptionEnable(k_funcgraphProcPath, true); + ok &= setKernelOptionEnable(k_funcgraphFlatPath, true); + + // Set the requested filter functions. + ok &= truncateFile(k_ftraceFilterPath); + char* myFuncs = strdup(funcs); + char* func = strtok(myFuncs, ","); + while (func) { + ok &= appendStr(k_ftraceFilterPath, func); + func = strtok(NULL, ","); + } + free(myFuncs); + + // Verify that the set functions are being traced. + if (ok) { + ok &= verifyKernelTraceFuncs(funcs); + } + } + + return ok; +} + +// Set all the kernel tracing settings to the desired state for this trace +// capture. +static bool setUpTrace() +{ + bool ok = true; + + // Set up the tracing options. + ok &= setTraceOverwriteEnable(g_traceOverwrite); + ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); + ok &= setGlobalClockEnable(true); + ok &= setPrintTgidEnableIfPresent(true); + ok &= setKernelTraceFuncs(g_kernelTraceFuncs); + + // Set up the tags property. + uint64_t tags = 0; + for (int i = 0; i < NELEM(k_categories); i++) { + if (g_categoryEnables[i]) { + const TracingCategory &c = k_categories[i]; + tags |= c.tags; + } + } + ok &= setTagsProperty(tags); + ok &= setAppCmdlineProperty(g_debugAppCmdLine); + ok &= pokeBinderServices(); + + // Disable all the sysfs enables. This is done as a separate loop from + // the enables to allow the same enable to exist in multiple categories. + ok &= disableKernelTraceEvents(); + + // Enable all the sysfs enables that are in an enabled category. + for (int i = 0; i < NELEM(k_categories); i++) { + if (g_categoryEnables[i]) { + const TracingCategory &c = k_categories[i]; + for (int j = 0; j < MAX_SYS_FILES; j++) { + const char* path = c.sysfiles[j].path; + bool required = c.sysfiles[j].required == REQ; + if (path != NULL) { + if (fileIsWritable(path)) { + ok &= setKernelOptionEnable(path, true); + } else if (required) { + fprintf(stderr, "error writing file %s\n", path); + ok = false; + } + } + } + } + } + + return ok; +} + +// Reset all the kernel tracing settings to their default state. +static void cleanUpTrace() +{ + // Disable all tracing that we're able to. + disableKernelTraceEvents(); + + // Reset the system properties. + setTagsProperty(0); + setAppCmdlineProperty(""); + pokeBinderServices(); + + // Set the options back to their defaults. + setTraceOverwriteEnable(true); + setTraceBufferSizeKB(1); + setGlobalClockEnable(false); + setPrintTgidEnableIfPresent(false); + setKernelTraceFuncs(NULL); +} + + +// Enable tracing in the kernel. +static bool startTrace() +{ + return setTracingEnabled(true); +} + +// Disable tracing in the kernel. +static void stopTrace() +{ + setTracingEnabled(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; + } + + 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); +} + +static void handleSignal(int signo) +{ + if (!g_nohup) { + 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); +} + +static bool setCategoryEnable(const char* name, bool enable) +{ + for (int i = 0; i < NELEM(k_categories); i++) { + const TracingCategory& c = k_categories[i]; + if (strcmp(name, c.name) == 0) { + if (isCategorySupported(c)) { + g_categoryEnables[i] = enable; + return true; + } else { + if (isCategorySupportedForRoot(c)) { + fprintf(stderr, "error: category \"%s\" requires root " + "privileges.\n", name); + } else { + fprintf(stderr, "error: category \"%s\" is not supported " + "on this device.\n", name); + } + return false; + } + } + } + fprintf(stderr, "error: unknown tracing category \"%s\"\n", name); + return false; +} + +static void listSupportedCategories() +{ + for (int i = 0; i < NELEM(k_categories); i++) { + const TracingCategory& c = k_categories[i]; + if (isCategorySupported(c)) { + printf(" %10s - %s\n", c.name, c.longname); + } + } +} + +// Print the command usage help to stderr. +static void showHelp(const char *cmd) +{ + fprintf(stderr, "usage: %s [options] [categories...]\n", cmd); + fprintf(stderr, "options include:\n" + " -a appname enable app-level tracing for a comma " + "separated list of cmdlines\n" + " -b N use a trace buffer size of N KB\n" + " -c trace into a circular buffer\n" + " -k fname,... trace the listed kernel functions\n" + " -n ignore signals\n" + " -s N sleep for N seconds before tracing [default 0]\n" + " -t N trace for N seconds [defualt 5]\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" + " --list_categories\n" + " list the available tracing categories\n" + ); +} + +int main(int argc, char **argv) +{ + bool async = false; + bool traceStart = true; + bool traceStop = true; + bool traceDump = true; + + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { + showHelp(argv[0]); + exit(0); + } + + 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 }, + {"list_categories", no_argument, 0, 0 }, + { 0, 0, 0, 0 } + }; + + ret = getopt_long(argc, argv, "a:b:ck:ns:t:z", + long_options, &option_index); + + if (ret < 0) { + for (int i = optind; i < argc; i++) { + if (!setCategoryEnable(argv[i], true)) { + fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); + exit(1); + } + } + break; + } + + switch(ret) { + case 'a': + g_debugAppCmdLine = optarg; + break; + + case 'b': + g_traceBufferSizeKB = atoi(optarg); + break; + + case 'c': + g_traceOverwrite = true; + break; + + case 'k': + g_kernelTraceFuncs = optarg; + break; + + case 'n': + g_nohup = true; + break; + + case 's': + g_initialSleepSecs = atoi(optarg); + break; + + case 't': + g_traceDurationSeconds = atoi(optarg); + break; + + case 'z': + 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; + } else if (!strcmp(long_options[option_index].name, "list_categories")) { + listSupportedCategories(); + exit(0); + } + break; + + default: + fprintf(stderr, "\n"); + showHelp(argv[0]); + exit(-1); + break; + } + } + + registerSigHandler(); + + if (g_initialSleepSecs > 0) { + sleep(g_initialSleepSecs); + } + + bool ok = true; + ok &= setUpTrace(); + ok &= startTrace(); + + if (ok && traceStart) { + 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 && !async) { + // 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. + if (traceStop) + stopTrace(); + + if (ok && traceDump) { + if (!g_traceAborted) { + printf(" done\nTRACE:\n"); + fflush(stdout); + dumpTrace(); + } else { + printf("\ntrace aborted.\n"); + fflush(stdout); + } + clearTrace(); + } else if (!ok) { + fprintf(stderr, "unable to start tracing\n"); + } + + // Reset the trace buffer size to 1. + if (traceStop) + cleanUpTrace(); + + return g_traceAborted ? 1 : 0; +} |