diff options
author | Elliott Hughes <enh@google.com> | 2019-07-10 12:35:15 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2019-07-10 12:35:15 -0700 |
commit | f895345280d53dfcde37bd61e9643977c519f6e9 (patch) | |
tree | cbb2630439af97b9743398ebccb02b61ef8b2343 | |
parent | 52aa89d133ac62468fe96bcaa242210510cddc05 (diff) | |
download | extras-f895345280d53dfcde37bd61e9643977c519f6e9.tar.gz |
Remove perfprofd.
This isn't finished, and no-one's working on finishing it.
Test: N/A
Change-Id: If52aa7f7dba8991f53bf432c0c3bc59c7a3cd2ca
53 files changed, 0 insertions, 8868 deletions
diff --git a/perfprofd/Android.bp b/perfprofd/Android.bp deleted file mode 100644 index 334590c1..00000000 --- a/perfprofd/Android.bp +++ /dev/null @@ -1,267 +0,0 @@ -// -// Copyright (C) 2016 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. -// - -cc_defaults { - name: "perfprofd_defaults", - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - - // Try some more extreme warnings. - "-Wpedantic", - "-Wunreachable-code-aggressive", - // And disable some dumb things. - "-Wno-zero-length-array", - "-Wno-c99-extensions", - "-Wno-language-extension-token", - "-Wno-gnu-zero-variadic-macro-arguments", - "-Wno-nested-anon-types", - "-Wno-gnu-statement-expression", - "-Wno-vla-extension", - ], - cppflags: [ - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - - target: { - darwin: { - enabled: false, - }, - }, -} - -cc_defaults { - name: "perfprofd_debug_defaults", - - cflags: [ - "-O0", - "-g", - "-UNDEBUG", - ], - - // Add sanitizers that work w/o extra libraries. This is important - // for atest etc to work. - sanitize: { - integer_overflow: true, - undefined: true, - }, - -// TODO: Re-enable when ART's ASAN flags are correctly propagated. -// target: { -// // On the host add ASAN. -// host: { -// sanitize: { -// address: true, -// }, -// }, -// } -} - -filegroup { - name: "perfprofd_record_proto", - srcs: [ - "perfprofd_record.proto", - ], -} - -// Static library for the record proto and its I/O. - -cc_library_static { - name: "libperfprofd_record_proto", - defaults: [ - "perfprofd_defaults", - ], - host_supported: true, - - static_libs: [ - "libbase", - "libprotobuf-cpp-lite", - "libquipper", - "libz", - ], - srcs: [ - "perfprofd_io.cc", - ":perfprofd_record_proto", - ], - - proto: { - export_proto_headers: true, - include_dirs: ["external/perf_data_converter/src/quipper"], - type: "lite", - }, - - export_include_dirs: ["."], // Really only the -fwd.h. - export_static_lib_headers: ["libquipper"], -} - -filegroup { - name: "perfprofd_config_proto", - srcs: [ - "perfprofd_config.proto", - ], -} - -cc_library_static { - name: "libperfprofd_proto_config", - defaults: [ - "perfprofd_defaults", - ], - host_supported: true, - - static_libs: [ - "libprotobuf-cpp-lite", - ], - srcs: [ - ":perfprofd_config_proto", - ], - - proto: { - export_proto_headers: true, - type: "lite", - }, - - export_include_dirs: ["."], // Really only the -fwd.h. -} - -// -// Static library containing guts of AWP daemon. -// - -cc_defaults { - name: "libperfprofdcore_defaults", - defaults: [ - "perfprofd_defaults", - ], - host_supported: true, - - static_libs: [ - "libbase", - "libperfprofd_proto_config", - "libprotobuf-cpp-lite", - "libsimpleperf_dex_read", - "libsimpleperf_elf_read", - ], - whole_static_libs: [ - "libperfprofd_dropbox", - "libperfprofd_record_proto", - "libquipper", - ], - srcs: [ - "perf_data_converter.cc", - "configreader.cc", - "cpuconfig.cc", - "perfprofdcore.cc", - "perfprofd_cmdline.cc", - "perfprofd_perf.cc", - "symbolizer.cc" - ], - - cflags: [ - "-Wno-gnu-anonymous-struct", - ], - - export_include_dirs: ["."], - - target: { - android: { - static_libs: [ - "libhealthhalutils", - ], - shared_libs: [ - "android.hardware.health@2.0", - "libhidlbase", - ], - } - } -} - -cc_library_static { - name: "libperfprofdcore", - defaults: [ - "libart_static_defaults", - "libperfprofdcore_defaults", - ], -} - -// Debug version. -cc_library_static { - name: "libperfprofdcored", - defaults: [ - "libartd_static_defaults", - "libperfprofdcore_defaults", - "perfprofd_debug_defaults", - ], -} - - -// -// Main daemon -// -cc_binary { - name: "perfprofd", - defaults: [ - "libart_static_defaults", - "perfprofd_defaults", - "libsimpleperf_dex_read_static_reqs_defaults", - "libsimpleperf_elf_read_static_reqs_defaults", - ], - - srcs: [ - "perfprofdmain.cc", - ], - - static_libs: [ - "libhealthhalutils", - "libperfprofdcore", - "libperfprofd_binder", - "libperfprofd_proto_config", - "libsimpleperf_dex_read", - "libsimpleperf_elf_read", - ], - group_static_libs: true, - - shared_libs: [ - "android.hardware.health@2.0", - "liblog", - "libprotobuf-cpp-lite", - "libbase", - "libbinder", - "libhidlbase", - "libservices", - "libutils", - ], - - init_rc: ["perfprofd.rc"], - - product_variables: { - pdk: { - enabled: false, - }, - }, - - // We're technically independent, but ensure simpleperf is there. - required: [ - "simpleperf", - ], -} - -subdirs = [ - "binder_interface", - "tests", -] diff --git a/perfprofd/NOTICE b/perfprofd/NOTICE deleted file mode 100644 index 8530865d..00000000 --- a/perfprofd/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2015, 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/perfprofd/OWNERS b/perfprofd/OWNERS deleted file mode 100644 index 2c69ac95..00000000 --- a/perfprofd/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -agampe@google.com -yabinc@google.com diff --git a/perfprofd/TEST_MAPPING b/perfprofd/TEST_MAPPING deleted file mode 100644 index 30101c2b..00000000 --- a/perfprofd/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit": [ - { - "name": "perfprofd_test" - } - ] -}
\ No newline at end of file diff --git a/perfprofd/binder_interface/Android.bp b/perfprofd/binder_interface/Android.bp deleted file mode 100644 index 1e287a20..00000000 --- a/perfprofd/binder_interface/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (C) 2017 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. -// - -// -// Static library with binder service. -// -cc_library_static { - name: "libperfprofd_binder", - defaults: [ - "perfprofd_defaults", - ], - - export_include_dirs: ["."], - shared_libs: [ - "libbinder", - ], - static_libs: [ - "libbase", - "libperfprofdcore", - "libperfprofd_proto_config", - "libprotobuf-cpp-lite", - ], - srcs: [ - "perfprofd_binder.cc", - ":perfprofd_aidl", - ], -} - -filegroup { - name: "perfprofd_aidl", - srcs: [ - "aidl/android/os/IPerfProfd.aidl", - ], - path: "aidl", -} diff --git a/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl b/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl deleted file mode 100644 index e2628c71..00000000 --- a/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -package android.os; - -/** {@hide} */ -interface IPerfProfd { - /** - * Start continuous profiling with the given parameters. - */ - void startProfiling(int collectionInterval, int iterations, - int process, int samplingPeriod, int samplingFrequency, - int sampleDuration, boolean stackProfile, - boolean useElfSymbolizer, boolean sendToDropbox); - - /** - * Start continuous profiling with the given encoded parameters. - * Parameters should be encoded in the ConfigReader syntax, - * separated by colons. - */ - void startProfilingString(String config); - - /** - * Start profiling with the parameters in the given protobuf. - */ - void startProfilingProtobuf(in byte[] config_proto); - - /** - * Stop an active profiling session. - */ - void stopProfiling(); -} diff --git a/perfprofd/binder_interface/perfprofd_binder.cc b/perfprofd/binder_interface/perfprofd_binder.cc deleted file mode 100644 index a1c77e24..00000000 --- a/perfprofd/binder_interface/perfprofd_binder.cc +++ /dev/null @@ -1,377 +0,0 @@ -/* -** -** Copyright 2017, 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 "perfprofd_binder.h" - -#include <cstdio> -#include <cstdlib> -#include <fstream> -#include <memory> -#include <mutex> -#include <string> -#include <thread> - -#include <inttypes.h> -#include <unistd.h> - -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <binder/BinderService.h> -#include <binder/IResultReceiver.h> -#include <binder/Status.h> -#include <google/protobuf/io/zero_copy_stream_impl_lite.h> -#include <utils/String16.h> -#include <utils/String8.h> -#include <utils/Vector.h> - -#include "android/os/BnPerfProfd.h" -#include "perfprofd_config.pb.h" -#include "perfprofd_record.pb.h" - -#include "config.h" -#include "configreader.h" -#include "perfprofdcore.h" -#include "perfprofd_threaded_handler.h" - -namespace android { -namespace perfprofd { -namespace binder { - -namespace { - -using Status = ::android::binder::Status; - -class PerfProfdNativeService : public BinderService<PerfProfdNativeService>, - public ::android::os::BnPerfProfd, - public ThreadedHandler { - public: - static status_t start(); - static int Main(); - - static char const* getServiceName() { return "perfprofd"; } - - status_t dump(int fd, const Vector<String16> &args) override; - - Status startProfiling(int32_t collectionInterval, - int32_t iterations, - int32_t process, - int32_t samplingPeriod, - int32_t samplingFrequency, - int32_t sampleDuration, - bool stackProfile, - bool useElfSymbolizer, - bool sendToDropbox) override; - Status startProfilingString(const String16& config) override; - Status startProfilingProtobuf(const std::vector<uint8_t>& config_proto) override; - - Status stopProfiling() override; - - // Override onTransact so we can handle shellCommand. - status_t onTransact(uint32_t _aidl_code, - const Parcel& _aidl_data, - Parcel* _aidl_reply, - uint32_t _aidl_flags = 0) override; - - private: - status_t shellCommand(int /*in*/, int out, int err, Vector<String16>& args); - - template <typename ProtoLoaderFn> Status StartProfilingProtobuf(ProtoLoaderFn fn); - Status StartProfilingProtobufFd(int fd); -}; - -status_t PerfProfdNativeService::start() { - IPCThreadState::self()->disableBackgroundScheduling(true); - status_t ret = BinderService<PerfProfdNativeService>::publish(); - if (ret != android::OK) { - return ret; - } - sp<ProcessState> ps(ProcessState::self()); - ps->startThreadPool(); - ps->giveThreadPoolName(); - return android::OK; -} - -status_t PerfProfdNativeService::dump(int fd, const Vector<String16> &args) { - auto out = std::fstream(base::StringPrintf("/proc/self/fd/%d", fd)); - auto print_config = [&out](bool is_profiling, const Config* config) { - if (is_profiling) { - out << "Profiling with config: " << ConfigReader::ConfigToString(*config); - } else { - out << "Not actively profiling."; - } - }; - RunOnConfig(print_config); - out << std::endl; - - return NO_ERROR; -} - -Status PerfProfdNativeService::startProfiling(int32_t collectionInterval, - int32_t iterations, - int32_t process, - int32_t samplingPeriod, - int32_t samplingFrequency, - int32_t sampleDuration, - bool stackProfile, - bool useElfSymbolizer, - bool sendToDropbox) { - auto config_fn = [&](ThreadedConfig& config) { - config = ThreadedConfig(); // Reset to a default config. - - if (collectionInterval >= 0) { - config.collection_interval_in_s = collectionInterval; - } - if (iterations >= 0) { - config.main_loop_iterations = iterations; - } - if (process >= 0) { - config.process = process; - } - if (samplingPeriod > 0) { - config.sampling_period = samplingPeriod; - } - if (samplingFrequency > 0) { - config.sampling_frequency = samplingFrequency; - } - if (sampleDuration > 0) { - config.sample_duration_in_s = sampleDuration; - } - config.stack_profile = stackProfile; - config.use_elf_symbolizer = useElfSymbolizer; - config.send_to_dropbox = sendToDropbox; - }; - std::string error_msg; - if (!StartProfiling(config_fn, &error_msg)) { - return Status::fromExceptionCode(1, error_msg.c_str()); - } - return Status::ok(); -} -Status PerfProfdNativeService::startProfilingString(const String16& config) { - ConfigReader reader; - std::string error_msg; - // Split configuration along colon. - std::vector<std::string> args = base::Split(String8(config).string(), ":"); - for (auto& arg : args) { - if (!reader.Read(arg, /* fail_on_error */ true, &error_msg)) { - std::string tmp = base::StringPrintf("Could not parse %s: %s", - arg.c_str(), - error_msg.c_str()); - return Status::fromExceptionCode(1, tmp.c_str()); - } - } - auto config_fn = [&](ThreadedConfig& config) { - config = ThreadedConfig(); // Reset to a default config. - reader.FillConfig(&config); - }; - if (!StartProfiling(config_fn, &error_msg)) { - return Status::fromExceptionCode(1, error_msg.c_str()); - } - return Status::ok(); -} -Status PerfProfdNativeService::startProfilingProtobuf(const std::vector<uint8_t>& config_proto) { - auto proto_loader_fn = [&config_proto](ProfilingConfig& proto_config) { - return proto_config.ParseFromArray(config_proto.data(), config_proto.size()); - }; - return StartProfilingProtobuf(proto_loader_fn); -} - -template <typename ProtoLoaderFn> -Status PerfProfdNativeService::StartProfilingProtobuf(ProtoLoaderFn fn) { - ProfilingConfig proto_config; - if (!fn(proto_config)) { - return binder::Status::fromExceptionCode(2, "Could not read protobuf"); - } - auto config_fn = [&proto_config](ThreadedConfig& config) { - config = ThreadedConfig(); // Reset to a default config. - ConfigReader::ProtoToConfig(proto_config, &config); - }; - std::string error_msg; - if (!StartProfiling(config_fn, &error_msg)) { - return Status::fromExceptionCode(1, error_msg.c_str()); - } - return Status::ok(); -} - -Status PerfProfdNativeService::StartProfilingProtobufFd(int fd) { - auto proto_loader_fn = [fd](ProfilingConfig& proto_config) { - struct IstreamCopyingInputStream : public google::protobuf::io::CopyingInputStream { - IstreamCopyingInputStream(int fd_in) - : stream(base::StringPrintf("/proc/self/fd/%d", fd_in), - std::ios::binary | std::ios::in) { - } - - int Read(void* buffer, int size) override { - stream.read(reinterpret_cast<char*>(buffer), size); - size_t count = stream.gcount(); - if (count > 0) { - return count; - } - return -1; - } - - std::ifstream stream; - }; - std::unique_ptr<IstreamCopyingInputStream> is(new IstreamCopyingInputStream(fd)); - std::unique_ptr<google::protobuf::io::CopyingInputStreamAdaptor> is_adaptor( - new google::protobuf::io::CopyingInputStreamAdaptor(is.get())); - return proto_config.ParseFromZeroCopyStream(is_adaptor.get()); - }; - return StartProfilingProtobuf(proto_loader_fn); -} - -Status PerfProfdNativeService::stopProfiling() { - std::string error_msg; - if (!StopProfiling(&error_msg)) { - Status::fromExceptionCode(1, error_msg.c_str()); - } - return Status::ok(); -} - -status_t PerfProfdNativeService::shellCommand(int in, - int out, - int err_fd, - Vector<String16>& args) { - if (android::base::kEnableDChecks) { - LOG(VERBOSE) << "Perfprofd::shellCommand"; - - for (size_t i = 0, n = args.size(); i < n; i++) { - LOG(VERBOSE) << " arg[" << i << "]: '" << String8(args[i]).string() << "'"; - } - } - - auto err_str = std::fstream(base::StringPrintf("/proc/self/fd/%d", err_fd)); - - if (args.size() >= 1) { - if (args[0] == String16("dump")) { - dump(out, args); - return OK; - } else if (args[0] == String16("startProfiling")) { - ConfigReader reader; - for (size_t i = 1; i < args.size(); ++i) { - std::string error_msg; - if (!reader.Read(String8(args[i]).string(), /* fail_on_error */ true, &error_msg)) { - err_str << "Could not parse '" << String8(args[i]).string() << "': " << error_msg - << std::endl; - return BAD_VALUE; - } - } - auto config_fn = [&](ThreadedConfig& config) { - config = ThreadedConfig(); // Reset to a default config. - reader.FillConfig(&config); - }; - std::string error_msg; - if (!StartProfiling(config_fn, &error_msg)) { - err_str << error_msg << std::endl; - return UNKNOWN_ERROR; - } - return OK; - } else if (args[0] == String16("startProfilingProto")) { - if (args.size() < 2) { - return BAD_VALUE; - } - int fd = -1; - if (args[1] == String16("-")) { - fd = in; - } else { - // TODO: Implement reading from disk? - } - if (fd < 0) { - err_str << "Bad file descriptor " << args[1] << std::endl; - return BAD_VALUE; - } - binder::Status status = StartProfilingProtobufFd(fd); - if (status.isOk()) { - return OK; - } else { - err_str << status.toString8() << std::endl; - return UNKNOWN_ERROR; - } - } else if (args[0] == String16("stopProfiling")) { - Status status = stopProfiling(); - if (status.isOk()) { - return OK; - } else { - err_str << status.toString8() << std::endl; - return UNKNOWN_ERROR; - } - } - } - return BAD_VALUE; -} - -status_t PerfProfdNativeService::onTransact(uint32_t _aidl_code, - const Parcel& _aidl_data, - Parcel* _aidl_reply, - uint32_t _aidl_flags) { - switch (_aidl_code) { - case IBinder::SHELL_COMMAND_TRANSACTION: { - int in = _aidl_data.readFileDescriptor(); - int out = _aidl_data.readFileDescriptor(); - int err = _aidl_data.readFileDescriptor(); - int argc = _aidl_data.readInt32(); - Vector<String16> args; - for (int i = 0; i < argc && _aidl_data.dataAvail() > 0; i++) { - args.add(_aidl_data.readString16()); - } - sp<IBinder> unusedCallback; - sp<IResultReceiver> resultReceiver; - status_t status; - if ((status = _aidl_data.readNullableStrongBinder(&unusedCallback)) != OK) - return status; - if ((status = _aidl_data.readNullableStrongBinder(&resultReceiver)) != OK) - return status; - status = shellCommand(in, out, err, args); - if (resultReceiver != nullptr) { - resultReceiver->send(status); - } - return OK; - } - - default: - return ::android::os::BnPerfProfd::onTransact( - _aidl_code, _aidl_data, _aidl_reply, _aidl_flags); - } -} - -} // namespace - -int Main() { - { - struct DummyConfig : public Config { - void Sleep(size_t seconds) override {} - bool IsProfilingEnabled() const override { return false; } - }; - DummyConfig config; - GlobalInit(config.perf_path); - } - - android::status_t ret; - if ((ret = PerfProfdNativeService::start()) != android::OK) { - LOG(ERROR) << "Unable to start InstalldNativeService: %d" << ret; - exit(1); - } - - android::IPCThreadState::self()->joinThreadPool(); - - LOG(INFO) << "Exiting perfprofd"; - return 0; -} - -} // namespace binder -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/binder_interface/perfprofd_binder.h b/perfprofd/binder_interface/perfprofd_binder.h deleted file mode 100644 index 8ab6d091..00000000 --- a/perfprofd/binder_interface/perfprofd_binder.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -** -** Copyright 2017, 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. -*/ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_BINDER_INTERFACE_PERFPROFD_BINDER_H_ -#define SYSTEM_EXTRAS_PERFPROFD_BINDER_INTERFACE_PERFPROFD_BINDER_H_ - -namespace android { -namespace perfprofd { -namespace binder { - -int Main(); - -} // namespace binder -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_BINDER_INTERFACE_PERFPROFD_BINDER_H_ diff --git a/perfprofd/config.h b/perfprofd/config.h deleted file mode 100644 index b5e5d5d4..00000000 --- a/perfprofd/config.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * - * Copyright 2015, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_CONFIG_H_ -#define SYSTEM_EXTRAS_PERFPROFD_CONFIG_H_ - -#include <cstdint> -#include <string> -#include <vector> - -struct Config { - virtual ~Config() {} - - // Average number of seconds between perf profile collections (if - // set to 100, then over time we want to see a perf profile - // collected every 100 seconds). The actual time within the interval - // for the collection is chosen randomly. - uint32_t collection_interval_in_s = 14400; - // Use the specified fixed seed for random number generation (unit - // testing) - uint32_t use_fixed_seed = 0; - // For testing purposes, number of times to iterate through main - // loop. Value of zero indicates that we should loop forever. - uint32_t main_loop_iterations = 0; - - // The pid of the process to profile. May be negative, in which case - // the whole system will be profiled. - int32_t process = -1; - - // Destination directory (where to write profiles). - std::string destination_directory = "/data/misc/perfprofd"; - // Config directory (where to read configs). - std::string config_directory = "/data/data/com.google.android.gms/files"; - // Full path to 'perf' executable. - std::string perf_path = "/system/bin/simpleperf"; - - // Desired sampling period (passed to perf -c option). Small - // sampling periods can perturb the collected profiles, so enforce - // min/max. A value of 0 means perf default. sampling_frequency - // takes priority. - uint32_t sampling_period = 0; - // Desired sampling frequency (passed to perf -f option). A value of 0 - // means using sampling_period or default. - uint32_t sampling_frequency = 0; - // Length of time to collect samples (number of seconds for 'perf - // record -a' run). - uint32_t sample_duration_in_s = 2; - - // If this parameter is non-zero it will cause perfprofd to - // exit immediately if the build type is not userdebug or eng. - // Currently defaults to 1 (true). - bool only_debug_build = true; - - // If the "mpdecision" service is running at the point we are ready - // to kick off a profiling run, then temporarily disable the service - // and hard-wire all cores on prior to the collection run, provided - // that the duration of the recording is less than or equal to the value of - // 'hardwire_cpus_max_duration'. - bool hardwire_cpus = true; - uint32_t hardwire_cpus_max_duration_in_s = 5; - - // Maximum number of unprocessed profiles we can accumulate in the - // destination directory. Once we reach this limit, we continue - // to collect, but we just overwrite the most recent profile. - uint32_t max_unprocessed_profiles = 10; - - // If set to 1, pass the -g option when invoking 'perf' (requests - // stack traces as opposed to flat profile). - bool stack_profile = false; - - // For unit testing only: if set to 1, emit info messages on config - // file parsing. - bool trace_config_read = false; - - // Control collection of various additional profile tags - bool collect_cpu_utilization = true; - bool collect_charging_state = true; - bool collect_booting = true; - bool collect_camera_active = false; - - // If true, use an ELF symbolizer to on-device symbolize. - bool use_elf_symbolizer = true; - // Whether to symbolize everything. If false, objects with build ID will be skipped. - bool symbolize_everything = false; - - // If true, use libz to compress the output proto. - bool compress = true; - - // If true, send the proto to dropbox instead to a file. - bool send_to_dropbox = false; - - // Whether to fail or strip unsupported events. - bool fail_on_unsupported_events = false; - - struct PerfCounterConfigElem { - std::vector<std::string> events; - bool group; - uint32_t sampling_period; - }; - std::vector<PerfCounterConfigElem> event_config; - - // Sleep for the given number of seconds. - virtual void Sleep(size_t seconds) = 0; - - // Should the profiling be stopped immediately? - virtual bool ShouldStopProfiling() { - return false; - } - - // Is profiling enabled? - virtual bool IsProfilingEnabled() const = 0; -}; - -#endif // SYSTEM_EXTRAS_PERFPROFD_CONFIG_H_ diff --git a/perfprofd/configreader.cc b/perfprofd/configreader.cc deleted file mode 100644 index 1ad573bd..00000000 --- a/perfprofd/configreader.cc +++ /dev/null @@ -1,620 +0,0 @@ -/* -** -** Copyright 2015, 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 "configreader.h" - -#include <inttypes.h> - -#include <algorithm> -#include <climits> -#include <cstdlib> -#include <cstring> -#include <map> -#include <sstream> -#include <vector> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/parseint.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> - -#include "perfprofd_config.pb.h" - -using android::base::StringPrintf; - -// -// Config file path -// -static const char *config_file_path = - "/data/data/com.google.android.gms/files/perfprofd.conf"; - -struct ConfigReader::Data { - struct values { - unsigned minv; - unsigned maxv; - }; - std::map<std::string, values> u_info; - std::map<std::string, unsigned> u_entries; - std::map<std::string, std::string> s_entries; - - struct events { - std::vector<std::string> names; - unsigned period; - bool group; - }; - std::vector<events> e_entries; - bool trace_config_read; -}; - -ConfigReader::ConfigReader() : data_(new ConfigReader::Data()) -{ - data_->trace_config_read = false; - addDefaultEntries(); -} - -ConfigReader::~ConfigReader() -{ -} - -const char *ConfigReader::getConfigFilePath() -{ - return config_file_path; -} - -void ConfigReader::setConfigFilePath(const char *path) -{ - config_file_path = strdup(path); - LOG(INFO) << "config file path set to " << config_file_path; -} - -// -// Populate the reader with the set of allowable entries -// -void ConfigReader::addDefaultEntries() -{ - struct DummyConfig : public Config { - void Sleep(size_t seconds) override {} - bool IsProfilingEnabled() const override { return false; } - }; - DummyConfig config; - - // Average number of seconds between perf profile collections (if - // set to 100, then over time we want to see a perf profile - // collected every 100 seconds). The actual time within the interval - // for the collection is chosen randomly. - addUnsignedEntry("collection_interval", config.collection_interval_in_s, 0, UINT32_MAX); - - // Use the specified fixed seed for random number generation (unit - // testing) - addUnsignedEntry("use_fixed_seed", config.use_fixed_seed, 0, UINT32_MAX); - - // For testing purposes, number of times to iterate through main - // loop. Value of zero indicates that we should loop forever. - addUnsignedEntry("main_loop_iterations", config.main_loop_iterations, 0, UINT32_MAX); - - // Destination directory (where to write profiles). - addStringEntry("destination_directory", config.destination_directory.c_str()); - - // Config directory (where to read configs). - addStringEntry("config_directory", config.config_directory.c_str()); - - // Full path to 'perf' executable. - addStringEntry("perf_path", config.perf_path.c_str()); - - // Desired sampling period (passed to perf -c option). - addUnsignedEntry("sampling_period", config.sampling_period, 0, UINT32_MAX); - // Desired sampling frequency (passed to perf -f option). - addUnsignedEntry("sampling_frequency", config.sampling_frequency, 0, UINT32_MAX); - - // Length of time to collect samples (number of seconds for 'perf - // record -a' run). - addUnsignedEntry("sample_duration", config.sample_duration_in_s, 1, 600); - - // If this parameter is non-zero it will cause perfprofd to - // exit immediately if the build type is not userdebug or eng. - // Currently defaults to 1 (true). - addUnsignedEntry("only_debug_build", config.only_debug_build ? 1 : 0, 0, 1); - - // If the "mpdecision" service is running at the point we are ready - // to kick off a profiling run, then temporarily disable the service - // and hard-wire all cores on prior to the collection run, provided - // that the duration of the recording is less than or equal to the value of - // 'hardwire_cpus_max_duration'. - addUnsignedEntry("hardwire_cpus", config.hardwire_cpus, 0, 1); - addUnsignedEntry("hardwire_cpus_max_duration", - config.hardwire_cpus_max_duration_in_s, - 1, - UINT32_MAX); - - // Maximum number of unprocessed profiles we can accumulate in the - // destination directory. Once we reach this limit, we continue - // to collect, but we just overwrite the most recent profile. - addUnsignedEntry("max_unprocessed_profiles", config.max_unprocessed_profiles, 1, UINT32_MAX); - - // If set to 1, pass the -g option when invoking 'perf' (requests - // stack traces as opposed to flat profile). - addUnsignedEntry("stack_profile", config.stack_profile ? 1 : 0, 0, 1); - - // For unit testing only: if set to 1, emit info messages on config - // file parsing. - addUnsignedEntry("trace_config_read", config.trace_config_read ? 1 : 0, 0, 1); - - // Control collection of various additional profile tags - addUnsignedEntry("collect_cpu_utilization", config.collect_cpu_utilization ? 1 : 0, 0, 1); - addUnsignedEntry("collect_charging_state", config.collect_charging_state ? 1 : 0, 0, 1); - addUnsignedEntry("collect_booting", config.collect_booting ? 1 : 0, 0, 1); - addUnsignedEntry("collect_camera_active", config.collect_camera_active ? 1 : 0, 0, 1); - - // If true, use an ELF symbolizer to on-device symbolize. - addUnsignedEntry("use_elf_symbolizer", config.use_elf_symbolizer ? 1 : 0, 0, 1); - // Whether to symbolize everything. If false, objects with build ID will be skipped. - addUnsignedEntry("symbolize_everything", config.symbolize_everything ? 1 : 0, 0, 1); - - // If true, use libz to compress the output proto. - addUnsignedEntry("compress", config.compress ? 1 : 0, 0, 1); - - // If true, send the proto to dropbox instead of to a file. - addUnsignedEntry("dropbox", config.send_to_dropbox ? 1 : 0, 0, 1); - - // The pid of the process to profile. May be negative, in which case - // the whole system will be profiled. - addUnsignedEntry("process", static_cast<uint32_t>(-1), 0, UINT32_MAX); - - // Whether to fail or strip unsupported events. - addUnsignedEntry("fail_on_unsupported_events", config.fail_on_unsupported_events ? 1 : 0, 0, 1); -} - -void ConfigReader::addUnsignedEntry(const char *key, - unsigned default_value, - unsigned min_value, - unsigned max_value) -{ - std::string ks(key); - CHECK(data_->u_entries.find(ks) == data_->u_entries.end() && - data_->s_entries.find(ks) == data_->s_entries.end()) - << "internal error -- duplicate entry for key " << key; - Data::values vals; - vals.minv = min_value; - vals.maxv = max_value; - data_->u_info[ks] = vals; - data_->u_entries[ks] = default_value; -} - -void ConfigReader::addStringEntry(const char *key, const char *default_value) -{ - std::string ks(key); - CHECK(data_->u_entries.find(ks) == data_->u_entries.end() && - data_->s_entries.find(ks) == data_->s_entries.end()) - << "internal error -- duplicate entry for key " << key; - CHECK(default_value != nullptr) << "internal error -- bad default value for key " << key; - data_->s_entries[ks] = std::string(default_value); -} - -unsigned ConfigReader::getUnsignedValue(const char *key) const -{ - std::string ks(key); - auto it = data_->u_entries.find(ks); - CHECK(it != data_->u_entries.end()); - return it->second; -} - -bool ConfigReader::getBoolValue(const char *key) const -{ - std::string ks(key); - auto it = data_->u_entries.find(ks); - CHECK(it != data_->u_entries.end()); - return it->second != 0; -} - -std::string ConfigReader::getStringValue(const char *key) const -{ - std::string ks(key); - auto it = data_->s_entries.find(ks); - CHECK(it != data_->s_entries.end()); - return it->second; -} - -void ConfigReader::overrideUnsignedEntry(const char *key, unsigned new_value) -{ - std::string ks(key); - auto it = data_->u_entries.find(ks); - CHECK(it != data_->u_entries.end()); - Data::values vals; - auto iit = data_->u_info.find(key); - CHECK(iit != data_->u_info.end()); - vals = iit->second; - CHECK(new_value >= vals.minv && new_value <= vals.maxv); - it->second = new_value; - LOG(INFO) << "option " << key << " overridden to " << new_value; -} - - -// -// Parse a key=value pair read from the config file. This will issue -// warnings or errors to the system logs if the line can't be -// interpreted properly. -// -bool ConfigReader::parseLine(const std::string& key, - const std::string& value, - unsigned linecount, - std::string* error_msg) -{ - if (key.empty()) { - *error_msg = StringPrintf("line %u: Key is empty", linecount); - return false; - } - if (value.empty()) { - *error_msg = StringPrintf("line %u: Value for %s is empty", linecount, key.c_str()); - return false; - } - - auto uit = data_->u_entries.find(key); - if (uit != data_->u_entries.end()) { - uint64_t conv; - if (!android::base::ParseUint(value, &conv)) { - *error_msg = StringPrintf("line %u: value %s cannot be parsed", linecount, value.c_str()); - return false; - } - Data::values vals; - auto iit = data_->u_info.find(key); - DCHECK(iit != data_->u_info.end()); - vals = iit->second; - if (conv < vals.minv || conv > vals.maxv) { - *error_msg = StringPrintf("line %u: " - "specified value %" PRIu64 " for '%s' " - "outside permitted range [%u %u]", - linecount, - conv, - key.c_str(), - vals.minv, - vals.maxv); - return false; - } else { - if (data_->trace_config_read) { - LOG(INFO) << "option " << key << " set to " << conv; - } - uit->second = static_cast<unsigned>(conv); - } - data_->trace_config_read = (getUnsignedValue("trace_config_read") != 0); - return true; - } - - auto sit = data_->s_entries.find(key); - if (sit != data_->s_entries.end()) { - if (data_->trace_config_read) { - LOG(INFO) << "option " << key << " set to " << value; - } - sit->second = std::string(value); - return true; - } - - // Check whether this follows event syntax, and create an event entry, if necessary. - // -e_evtname(,evtname)*=period - // -g_evtname(,evtname)*=period - { - bool event_key = android::base::StartsWith(key, "-e_"); - bool group_key = android::base::StartsWith(key, "-g_"); - if (event_key || group_key) { - Data::events events; - events.group = group_key; - - uint64_t conv; - if (!android::base::ParseUint(value, &conv)) { - *error_msg = StringPrintf("line %u: key %s cannot be parsed", linecount, key.c_str()); - return false; - } - if (conv > std::numeric_limits<unsigned>::max()) { - *error_msg = StringPrintf("line %u: key %s: period too large", linecount, key.c_str()); - return false; - } - events.period = static_cast<unsigned>(conv); - - events.names = android::base::Split(key.substr(3), ","); - data_->e_entries.push_back(events); - return true; - } - } - - *error_msg = StringPrintf("line %u: unknown option '%s'", linecount, key.c_str()); - return false; -} - -static bool isblank(const std::string &line) -{ - auto non_space = [](char c) { return isspace(c) == 0; }; - return std::find_if(line.begin(), line.end(), non_space) == line.end(); -} - - - -bool ConfigReader::readFile() -{ - std::string contents; - if (! android::base::ReadFileToString(config_file_path, &contents)) { - return false; - } - std::string error_msg; - if (!Read(contents, /* fail_on_error */ false, &error_msg)) { - LOG(ERROR) << error_msg; - return false; - } - if (!error_msg.empty()) { - LOG(WARNING) << error_msg; - } - return true; -} - -bool ConfigReader::Read(const std::string& content, bool fail_on_error, std::string* error_msg) { - std::stringstream ss(content); - std::string line; - - auto append_error = [error_msg](const std::string& tmp) { - if (!error_msg->empty()) { - error_msg->append("\n"); - error_msg->append(tmp); - } else { - *error_msg = tmp; - } - }; - - for (unsigned linecount = 1; - std::getline(ss,line,'\n'); - linecount += 1) - { - - // comment line? - if (line[0] == '#') { - continue; - } - - // blank line? - if (isblank(line)) { - continue; - } - - // look for X=Y assignment - auto efound = line.find('='); - if (efound == std::string::npos) { - append_error(StringPrintf("line %u: line malformed (no '=' found)", linecount)); - if (fail_on_error) { - return false; - } - continue; - } - - std::string key(line.substr(0, efound)); - std::string value(line.substr(efound+1, std::string::npos)); - - std::string local_error_msg; - bool parse_success = parseLine(key, value, linecount, &local_error_msg); - if (!parse_success) { - append_error(local_error_msg); - if (fail_on_error) { - return false; - } - } - } - - return true; -} - -void ConfigReader::FillConfig(Config* config) { - config->collection_interval_in_s = getUnsignedValue("collection_interval"); - - config->use_fixed_seed = getUnsignedValue("use_fixed_seed"); - - config->main_loop_iterations = getUnsignedValue("main_loop_iterations"); - - config->destination_directory = getStringValue("destination_directory"); - - config->config_directory = getStringValue("config_directory"); - - config->perf_path = getStringValue("perf_path"); - - config->sampling_period = getUnsignedValue("sampling_period"); - config->sampling_frequency = getUnsignedValue("sampling_frequency"); - - config->sample_duration_in_s = getUnsignedValue("sample_duration"); - - config->only_debug_build = getBoolValue("only_debug_build"); - - config->hardwire_cpus = getBoolValue("hardwire_cpus"); - config->hardwire_cpus_max_duration_in_s = getUnsignedValue("hardwire_cpus_max_duration"); - - config->max_unprocessed_profiles = getUnsignedValue("max_unprocessed_profiles"); - - config->stack_profile = getBoolValue("stack_profile"); - - config->trace_config_read = getBoolValue("trace_config_read"); - - config->collect_cpu_utilization = getBoolValue("collect_cpu_utilization"); - config->collect_charging_state = getBoolValue("collect_charging_state"); - config->collect_booting = getBoolValue("collect_booting"); - config->collect_camera_active = getBoolValue("collect_camera_active"); - - config->process = static_cast<int32_t>(getUnsignedValue("process")); - config->use_elf_symbolizer = getBoolValue("use_elf_symbolizer"); - config->symbolize_everything = getBoolValue("symbolize_everything"); - config->compress = getBoolValue("compress"); - config->send_to_dropbox = getBoolValue("dropbox"); - config->fail_on_unsupported_events = getBoolValue("fail_on_unsupported_events"); - - config->event_config.clear(); - for (const auto& event : data_->e_entries) { - Config::PerfCounterConfigElem elem; - elem.events = event.names; - elem.group = event.group; - elem.sampling_period = event.period; - config->event_config.push_back(std::move(elem)); - } -} - -namespace { - -template <typename T> -struct OssFormatter { -}; - -template <> -struct OssFormatter<std::string> { - void Add(std::ostream& os, const std::string& val) { - os << val; - } -}; - -template <> -struct OssFormatter<uint32_t> { - void Add(std::ostream& os, const uint32_t& val) { - os << val; - } -}; - -template <> -struct OssFormatter<int32_t> { - void Add(std::ostream& os, const int32_t& val) { - os << val; - } -}; - -template <> -struct OssFormatter<bool> { - void Add(std::ostream& os, const bool& val) { - os << (val ? 1 : 0); - } -}; - - -} // namespace - -std::string ConfigReader::ConfigToString(const Config& config) { - std::ostringstream oss; - - auto add = [&oss](const char* str, auto val) { - if (oss.tellp() != 0) { - oss << ' '; - } - oss << str << '='; - OssFormatter<decltype(val)> fmt; - fmt.Add(oss, val); - }; - - add("collection_interval", config.collection_interval_in_s); - add("use_fixed_seed", config.use_fixed_seed); - add("main_loop_iterations", config.main_loop_iterations); - - add("destination_directory", config.destination_directory); // TODO: Escape. - add("config_directory", config.config_directory); // TODO: Escape. - add("perf_path", config.perf_path); // TODO: Escape. - - add("sampling_period", config.sampling_period); - add("sampling_frequency", config.sampling_frequency); - - add("sample_duration", config.sample_duration_in_s); - - add("only_debug_build", config.only_debug_build); - - add("hardwire_cpus", config.hardwire_cpus); - - add("hardwire_cpus_max_duration", config.hardwire_cpus_max_duration_in_s); - - add("max_unprocessed_profiles", config.max_unprocessed_profiles); - - add("stack_profile", config.stack_profile); - - add("trace_config_read", config.trace_config_read); - - add("collect_cpu_utilization", config.collect_cpu_utilization); - add("collect_charging_state", config.collect_charging_state); - add("collect_booting", config.collect_booting); - add("collect_camera_active", config.collect_camera_active); - - add("process", config.process); - add("use_elf_symbolizer", config.use_elf_symbolizer); - add("symbolize_everything", config.symbolize_everything); - add("compress", config.compress); - add("dropbox", config.send_to_dropbox); - add("fail_on_unsupported_events", config.fail_on_unsupported_events); - - for (const auto& elem : config.event_config) { - std::ostringstream oss_elem; - oss_elem << '-' << (elem.group ? 'g' : 'e') << '_'; - bool first = true; - for (const auto& event : elem.events) { - if (!first) { - oss_elem << ','; - } - oss_elem << event; - first = false; - } - add(oss_elem.str().c_str(), elem.sampling_period); - } - - return oss.str(); -} - -void ConfigReader::ProtoToConfig(const android::perfprofd::ProfilingConfig& in, Config* out) { - // Copy base proto values. -#define CHECK_AND_COPY_FROM_PROTO(name) \ - if (in.has_ ## name()) { \ - out->name = in.name(); \ - } - CHECK_AND_COPY_FROM_PROTO(collection_interval_in_s) - CHECK_AND_COPY_FROM_PROTO(use_fixed_seed) - CHECK_AND_COPY_FROM_PROTO(main_loop_iterations) - CHECK_AND_COPY_FROM_PROTO(destination_directory) - CHECK_AND_COPY_FROM_PROTO(config_directory) - CHECK_AND_COPY_FROM_PROTO(perf_path) - CHECK_AND_COPY_FROM_PROTO(sampling_period) - CHECK_AND_COPY_FROM_PROTO(sampling_frequency) - CHECK_AND_COPY_FROM_PROTO(sample_duration_in_s) - CHECK_AND_COPY_FROM_PROTO(only_debug_build) - CHECK_AND_COPY_FROM_PROTO(hardwire_cpus) - CHECK_AND_COPY_FROM_PROTO(hardwire_cpus_max_duration_in_s) - CHECK_AND_COPY_FROM_PROTO(max_unprocessed_profiles) - CHECK_AND_COPY_FROM_PROTO(stack_profile) - CHECK_AND_COPY_FROM_PROTO(collect_cpu_utilization) - CHECK_AND_COPY_FROM_PROTO(collect_charging_state) - CHECK_AND_COPY_FROM_PROTO(collect_booting) - CHECK_AND_COPY_FROM_PROTO(collect_camera_active) - CHECK_AND_COPY_FROM_PROTO(process) - CHECK_AND_COPY_FROM_PROTO(use_elf_symbolizer) - CHECK_AND_COPY_FROM_PROTO(symbolize_everything) - CHECK_AND_COPY_FROM_PROTO(send_to_dropbox) - CHECK_AND_COPY_FROM_PROTO(compress) - CHECK_AND_COPY_FROM_PROTO(fail_on_unsupported_events) -#undef CHECK_AND_COPY_FROM_PROTO - - // Convert counters. - for (const auto& event_config : in.event_config()) { - Config::PerfCounterConfigElem config_elem; - - if (event_config.counters_size() == 0) { - LOG(WARNING) << "Missing counters."; - continue; - } - config_elem.events.reserve(event_config.counters_size()); - for (const std::string& str : event_config.counters()) { - config_elem.events.push_back(str); - } - config_elem.group = event_config.has_as_group() ? event_config.as_group() : false; - config_elem.sampling_period = event_config.has_sampling_period() - ? event_config.sampling_period() - : 0; - out->event_config.push_back(std::move(config_elem)); - } -} diff --git a/perfprofd/configreader.h b/perfprofd/configreader.h deleted file mode 100644 index 3d2243c5..00000000 --- a/perfprofd/configreader.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * Copyright 2015, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_CONFIGREADER_H_ -#define SYSTEM_EXTRAS_PERFPROFD_CONFIGREADER_H_ - -#include <memory> -#include <string> - -#include "config.h" - -namespace android { -namespace perfprofd { -class ProfilingConfig; // Config proto. -} // namespace perfprofd -} // namespace android - -// -// This table describes the perfprofd config file syntax in terms of -// key/value pairs. Values come in two flavors: strings, or unsigned -// integers. In the latter case the reader sets allowable -// minimum/maximum for the setting. -// -class ConfigReader { - - public: - ConfigReader(); - ~ConfigReader(); - - // Ask for the current setting of a config item - unsigned getUnsignedValue(const char *key) const; - bool getBoolValue(const char *key) const; - std::string getStringValue(const char *key) const; - - // read the specified config file, applying any settings it contains - // returns true for successful read, false if conf file cannot be opened. - bool readFile(); - - bool Read(const std::string& data, bool fail_on_error, std::string* error_msg); - - // set/get path to config file - static void setConfigFilePath(const char *path); - static const char *getConfigFilePath(); - - // override a config item (for unit testing purposes) - void overrideUnsignedEntry(const char *key, unsigned new_value); - - void FillConfig(Config* config); - static std::string ConfigToString(const Config& config); - - static void ProtoToConfig(const android::perfprofd::ProfilingConfig& in, Config* out); - - private: - void addUnsignedEntry(const char *key, - unsigned default_value, - unsigned min_value, - unsigned max_value); - void addStringEntry(const char *key, const char *default_value); - void addDefaultEntries(); - bool parseLine(const std::string& key, - const std::string& value, - unsigned linecount, - std::string* error_msg); - - struct Data; - std::unique_ptr<Data> data_; -}; - -#endif diff --git a/perfprofd/cpuconfig.cc b/perfprofd/cpuconfig.cc deleted file mode 100644 index 0f23fd0f..00000000 --- a/perfprofd/cpuconfig.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* -** -** Copyright 2015, 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <string> -#include <sstream> -#include <sys/types.h> -#include <sys/wait.h> - -#include <android-base/logging.h> -#ifdef __BIONIC__ -#include <android-base/properties.h> -#endif - -#include "cpuconfig.h" - -#define SYSFSCPU "/sys/devices/system/cpu" - -HardwireCpuHelper::HardwireCpuHelper(bool perform) - : mpdecision_stopped_(false) -{ - if (perform && GetMpdecisionRunning()) { - mpdecision_stopped_ = true; - StopMpdecision(); - int ncores = GetNumCores(); - for (int i = 0; i < ncores; ++i) { - OnlineCore(i, 1); - } - } -} - -HardwireCpuHelper::~HardwireCpuHelper() -{ - if (mpdecision_stopped_) { - RestartMpdecision(); - } -} - -bool HardwireCpuHelper::GetMpdecisionRunning() -{ -#ifdef __BIONIC__ - return android::base::GetProperty("init.svc.mpdecision", "") == "running"; -#else - return false; -#endif -} - - -int HardwireCpuHelper::GetNumCores() -{ - int ncores = -1; - std::string possible(SYSFSCPU "/possible"); - FILE *fp = fopen(possible.c_str(), "re"); - if (fp) { - unsigned lo = 0, hi = 0; - if (fscanf(fp, "%u-%u", &lo, &hi) == 2) { - ncores = hi - lo + 1; - } - fclose(fp); - } - return ncores; -} - -void HardwireCpuHelper::OnlineCore(int i, int onoff) -{ - std::stringstream ss; - ss << SYSFSCPU "/cpu" << i << "/online"; - FILE *fp = fopen(ss.str().c_str(), "we"); - if (fp) { - fprintf(fp, onoff ? "1\n" : "0\n"); - fclose(fp); - } else { - PLOG(WARNING) << "open failed for " << ss.str(); - } -} - -void HardwireCpuHelper::StopMpdecision() -{ -#ifdef __BIONIC__ - if (!android::base::SetProperty("ctl.stop", "mpdecision")) { - LOG(ERROR) << "setprop ctl.stop mpdecision failed"; - } -#endif -} - -void HardwireCpuHelper::RestartMpdecision() -{ -#ifdef __BIONIC__ - // Don't try to offline the cores we previously onlined -- let - // mpdecision figure out what to do - - if (!android::base::SetProperty("ctl.start", "mpdecision")) { - LOG(ERROR) << "setprop ctl.start mpdecision failed"; - } -#endif -} diff --git a/perfprofd/cpuconfig.h b/perfprofd/cpuconfig.h deleted file mode 100644 index bc5b5cf9..00000000 --- a/perfprofd/cpuconfig.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -** -** Copyright 2015, 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. -*/ - -// -// Helper class to perform cpu setup (if needed) prior to a profile collection. -// -class HardwireCpuHelper { - public: - - // The constructor for this class checks to see if the 'mpdecision' - // service is running; if so (and if 'perform' is TRUE), then it - // disables the service and on-lines all of the available cores/cpus - // (anything listed in /sys/devices/system/cpu/possible). The - // destructor will re-enable the mpdecision service if it was - // previously disabled. - explicit HardwireCpuHelper(bool perform); - virtual ~HardwireCpuHelper(); - - private: - bool mpdecision_stopped_; - - // Collect the number of available cpus/cores from /sys/devices/system/cpu/possible - int GetNumCores(); - - // Returns TRUE if the system service 'mpdecision' is running - bool GetMpdecisionRunning(); - - // Online/offline the specified cpu - void OnlineCore(int whichCore, int onoff); - - // Enable/disable the mpdecision service via the equivalent of - // setprop ctl.start mpdecision - // setprop ctl.stop mpdecision - void StopMpdecision(); - void RestartMpdecision(); -}; diff --git a/perfprofd/dropbox/Android.bp b/perfprofd/dropbox/Android.bp deleted file mode 100644 index 4ab3a11c..00000000 --- a/perfprofd/dropbox/Android.bp +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (C) 2018 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. -// - -// -// Static library for dropbox submission. -// -cc_library_static { - name: "libperfprofd_dropbox", - defaults: [ - "perfprofd_defaults", - ], - host_supported: true, - - export_include_dirs: ["."], - static_libs: [ - "libbase", - "libperfprofd_record_proto", - "libprotobuf-cpp-lite", - ], - target: { - android: { - srcs: [ - "dropbox.cc", - ], - static_libs: [ - "libutils", - ], - shared_libs: [ - "libbinder", - "libservices", - ], - }, - host: { - srcs: [ - "dropbox_host.cc", - ], - }, - }, -} diff --git a/perfprofd/dropbox/dropbox.cc b/perfprofd/dropbox/dropbox.cc deleted file mode 100644 index 2b1dc2ef..00000000 --- a/perfprofd/dropbox/dropbox.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * Copyright 2017, 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 "dropbox.h" - -#include <cstdio> -#include <cstdlib> -#include <memory> - -#include <inttypes.h> -#include <unistd.h> - -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> -#include <android/os/DropBoxManager.h> -#include <binder/Status.h> -#include <utils/String8.h> - -#include "perfprofd_record.pb.h" - -#include "perfprofd_io.h" - -namespace android { -namespace perfprofd { -namespace dropbox { - -namespace { - -bool WriteDropboxFile(android::perfprofd::PerfprofdRecord* encodedProfile, - const std::string& temp_dir, - std::string* error_msg) { - android::base::unique_fd tmp_fd; - { - char path[PATH_MAX]; - snprintf(path, sizeof(path), "%s/dropboxtmp-XXXXXX", temp_dir.c_str()); - tmp_fd.reset(mkstemp(path)); - if (tmp_fd.get() == -1) { - *error_msg = android::base::StringPrintf("Could not create temp file %s: %s", - path, - strerror(errno)); - return false; - } - if (unlink(path) != 0) { - PLOG(WARNING) << "Could not unlink binder temp file"; - } - } - - // Dropbox takes ownership of the fd, and if it is not readonly, - // a selinux violation will occur. Get a read-only version. - android::base::unique_fd read_only; - { - char fdpath[64]; - snprintf(fdpath, arraysize(fdpath), "/proc/self/fd/%d", tmp_fd.get()); - read_only.reset(open(fdpath, O_RDONLY | O_CLOEXEC)); - if (read_only.get() < 0) { - *error_msg = android::base::StringPrintf("Could not create read-only fd: %s", - strerror(errno)); - return false; - } - } - - constexpr bool kCompress = true; // Ignore the config here. Dropbox will always end up - // compressing the data, might as well make the temp - // file smaller and help it out. - using DropBoxManager = android::os::DropBoxManager; - constexpr int kDropboxFlags = DropBoxManager::IS_GZIPPED; - - if (!SerializeProtobuf(encodedProfile, std::move(tmp_fd), kCompress)) { - *error_msg = "Could not serialize to temp file"; - return false; - } - - sp<DropBoxManager> dropbox(new DropBoxManager()); - android::binder::Status status = dropbox->addFile(String16("perfprofd"), - read_only.release(), - kDropboxFlags); - if (!status.isOk()) { - *error_msg = status.toString8(); - return false; - } - return true; -} - -} // namespace - -bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, - const std::string& temp_directory, - std::string* error_msg) { - size_t size = profile->ByteSize(); - if (size < 1024 * 1024) { - // For a small size, send as a byte buffer directly. - std::unique_ptr<uint8_t[]> data(new uint8_t[size]); - profile->SerializeWithCachedSizesToArray(data.get()); - - using DropBoxManager = android::os::DropBoxManager; - sp<DropBoxManager> dropbox(new DropBoxManager()); - android::binder::Status status = dropbox->addData(String16("perfprofd"), - data.get(), - size, - 0); - if (!status.isOk()) { - *error_msg = status.toString8(); - return false; - } - return true; - } else { - // For larger buffers, we need to go through the filesystem. - return WriteDropboxFile(profile, temp_directory, error_msg); - } -} - -} // namespace dropbox -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/dropbox/dropbox.h b/perfprofd/dropbox/dropbox.h deleted file mode 100644 index b25d2cc2..00000000 --- a/perfprofd/dropbox/dropbox.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright 2018, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ -#define SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ - -#include <string> - -#include "perfprofd_record-fwd.h" - -namespace android { -namespace perfprofd { -namespace dropbox { - -bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, - const std::string& temp_directory, - std::string* error_msg); - -} // namespace dropbox -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ diff --git a/perfprofd/dropbox/dropbox_host.cc b/perfprofd/dropbox/dropbox_host.cc deleted file mode 100644 index 5c08aa85..00000000 --- a/perfprofd/dropbox/dropbox_host.cc +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * Copyright 2018, 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 "dropbox.h" - -#include <android-base/macros.h> - -namespace android { -namespace perfprofd { -namespace dropbox { - -bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, - const std::string& temp_directory ATTRIBUTE_UNUSED, - std::string* error_msg) { - *error_msg = "Dropbox not supported on host"; - return false; -} - -} // namespace dropbox -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/map_utils.h b/perfprofd/map_utils.h deleted file mode 100644 index 2e3d97d9..00000000 --- a/perfprofd/map_utils.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef SYSTEM_EXTRAS_PERFPROFD_MAP_UTILS_H_ -#define SYSTEM_EXTRAS_PERFPROFD_MAP_UTILS_H_ - -#include <map> -#include <set> - -#include <android-base/logging.h> - -namespace android { -namespace perfprofd { - -template <typename T, typename U> -decltype(static_cast<T*>(nullptr)->begin()) GetLeqIterator(T& map, U key) { - if (map.empty()) { - return map.end(); - } - auto it = map.upper_bound(key); - if (it == map.begin()) { - return map.end(); - } - --it; - return it; -} - -template <typename SymType, typename ValType> -class RangeMap { - public: - struct AggregatedSymbol { - SymType symbol; - std::set<ValType> offsets; - AggregatedSymbol(const SymType& sym, const ValType& offset) : symbol(sym) { - offsets.insert(offset); - } - }; - - public: - void Insert(const SymType& sym, const ValType& val) { - auto aggr_it = GetLeqIterator(map_, val); - if (aggr_it == map_.end()) { - // Maybe we need to extend the first one. - if (!map_.empty()) { - AggregatedSymbol& first = map_.begin()->second; - CHECK_LT(val, map_.begin()->first); - if (first.symbol == sym) { - ExtendLeft(map_.begin(), val); - return; - } - } - // Nope, new entry needed. - map_.emplace(val, AggregatedSymbol(sym, val)); - return; - } - - AggregatedSymbol& maybe_match = aggr_it->second; - - if (maybe_match.symbol == sym) { - // Same symbol, just insert. This is true for overlap as well as extension. - maybe_match.offsets.insert(val); - return; - } - - // Is there overlap? - if (*maybe_match.offsets.rbegin() < val) { - // No. See if it can be merged with the next one. - ++aggr_it; - if (aggr_it != map_.end() && aggr_it->second.symbol == sym) { - ExtendLeft(aggr_it, val); - return; - } - - // Just add a new symbol entry. - map_.emplace(val, AggregatedSymbol(sym, val)); - return; - } - - // OK, we have an overlapping non-symbol-equal AggregatedSymbol. Need to break - // things up. - AggregatedSymbol left(maybe_match.symbol, *maybe_match.offsets.begin()); - auto offset_it = maybe_match.offsets.begin(); - for (; *offset_it < val; ++offset_it) { - left.offsets.insert(*offset_it); - } - - if (*offset_it == val) { - // This should not happen. - LOG(ERROR) << "Unexpected overlap!"; - return; - } - - AggregatedSymbol right(maybe_match.symbol, *offset_it); - for (; offset_it != maybe_match.offsets.end(); ++offset_it) { - right.offsets.insert(*offset_it); - } - - map_.erase(aggr_it); - map_.emplace(*left.offsets.begin(), std::move(left)); - map_.emplace(val, AggregatedSymbol(sym, val)); - map_.emplace(*right.offsets.begin(), std::move(right)); - } - - using RangeMapType = std::map<ValType, AggregatedSymbol>; - - typename RangeMapType::const_iterator begin() const { - return map_.begin(); - } - typename RangeMapType::const_iterator end() const { - return map_.end(); - } - - bool empty() const { - return map_.empty(); - } - - private: - void ExtendLeft(typename RangeMapType::iterator it, const ValType& val) { - CHECK(val < *it->second.offsets.begin()); - AggregatedSymbol copy = std::move(it->second); - map_.erase(it); - copy.offsets.insert(val); - map_.emplace(val, std::move(copy)); - } - - RangeMapType map_; -}; - -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_MAP_UTILS_H_ diff --git a/perfprofd/perf_data_converter.cc b/perfprofd/perf_data_converter.cc deleted file mode 100644 index ea560f11..00000000 --- a/perfprofd/perf_data_converter.cc +++ /dev/null @@ -1,197 +0,0 @@ - -#include "perf_data_converter.h" - -#include <algorithm> -#include <limits> -#include <map> -#include <memory> -#include <set> -#include <unordered_map> - -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/strings.h> -#include <perf_data_utils.h> -#include <perf_parser.h> -#include <perf_protobuf_io.h> - -#include "perfprofd_record.pb.h" -#include "perf_data.pb.h" - -#include "map_utils.h" -#include "quipper_helper.h" -#include "symbolizer.h" - -using std::map; - -namespace android { -namespace perfprofd { - -namespace { - -void AddSymbolInfo(PerfprofdRecord* record, - ::quipper::PerfParser& perf_parser, - ::perfprofd::Symbolizer* symbolizer, - bool symbolize_everything) { - std::unordered_set<std::string> filenames_w_build_id; - if (!symbolize_everything) { - for (auto& perf_build_id : record->build_ids()) { - filenames_w_build_id.insert(perf_build_id.filename()); - } - } - - std::unordered_set<std::string> files_wo_build_id; - { - quipper::MmapEventIterator it(*record); - for (; it != it.end(); ++it) { - const ::quipper::PerfDataProto_MMapEvent* mmap_event = &it->mmap_event(); - if (!mmap_event->has_filename() || !mmap_event->has_start() || !mmap_event->has_len()) { - // Don't care. - continue; - } - if (filenames_w_build_id.count(mmap_event->filename()) == 0) { - files_wo_build_id.insert(mmap_event->filename()); - } - } - } - if (files_wo_build_id.empty()) { - return; - } - - struct Dso { - uint64_t min_vaddr; - RangeMap<std::string, uint64_t> symbols; - explicit Dso(uint64_t min_vaddr_in) : min_vaddr(min_vaddr_in) { - } - }; - std::unordered_map<std::string, Dso> files; - - auto it = record->events().begin(); - auto end = record->events().end(); - auto parsed_it = perf_parser.parsed_events().begin(); - auto parsed_end = perf_parser.parsed_events().end(); - for (; it != end; ++it, ++parsed_it) { - CHECK(parsed_it != parsed_end); - if (!it->has_sample_event()) { - continue; - } - - const ::quipper::PerfDataProto_SampleEvent& sample_event = it->sample_event(); - - if (android::base::kEnableDChecks) { - // Check that the parsed_event and sample_event are consistent. - CHECK_EQ(parsed_it->callchain.size(), sample_event.callchain_size()); - } - - auto check_address = [&](const std::string& dso_name, uint64_t offset) { - if (files_wo_build_id.count(dso_name) == 0) { - return; - } - - // OK, that's a hit in the mmap segment (w/o build id). - - Dso* dso_data; - { - auto dso_it = files.find(dso_name); - constexpr uint64_t kNoMinAddr = std::numeric_limits<uint64_t>::max(); - if (dso_it == files.end()) { - uint64_t min_vaddr; - bool has_min_vaddr = symbolizer->GetMinExecutableVAddr(dso_name, &min_vaddr); - if (!has_min_vaddr) { - min_vaddr = kNoMinAddr; - } - auto it = files.emplace(dso_name, Dso(min_vaddr)); - dso_data = &it.first->second; - } else { - dso_data = &dso_it->second; - } - if (dso_data->min_vaddr == kNoMinAddr) { - return; - } - } - - // TODO: Is min_vaddr necessary here? - const uint64_t file_addr = offset; - - std::string symbol = symbolizer->Decode(dso_name, file_addr); - if (symbol.empty()) { - return; - } - - dso_data->symbols.Insert(symbol, file_addr); - }; - if (sample_event.has_ip() && parsed_it->dso_and_offset.dso_info_ != nullptr) { - check_address(parsed_it->dso_and_offset.dso_info_->name, parsed_it->dso_and_offset.offset_); - } - if (sample_event.callchain_size() > 0) { - for (auto& callchain_data: parsed_it->callchain) { - if (callchain_data.dso_info_ == nullptr) { - continue; - } - check_address(callchain_data.dso_info_->name, callchain_data.offset_); - } - } - } - - if (!files.empty()) { - // We have extra symbol info, create proto messages now. - size_t symbol_info_index = 0; - for (auto& file_data : files) { - const std::string& filename = file_data.first; - const Dso& dso = file_data.second; - if (dso.symbols.empty()) { - continue; - } - - auto* symbol_info = record->AddExtension(::quipper::symbol_info); - symbol_info->set_filename(filename); - symbol_info->set_filename_md5_prefix(::quipper::Md5Prefix(filename)); - symbol_info->set_min_vaddr(dso.min_vaddr); - for (auto& aggr_sym : dso.symbols) { - auto* symbol = symbol_info->add_symbols(); - symbol->set_addr(*aggr_sym.second.offsets.begin()); - symbol->set_size(*aggr_sym.second.offsets.rbegin() - *aggr_sym.second.offsets.begin() + 1); - symbol->set_name(aggr_sym.second.symbol); - symbol->set_name_md5_prefix(::quipper::Md5Prefix(aggr_sym.second.symbol)); - } - - ++symbol_info_index; - } - } -} - -} // namespace - -PerfprofdRecord* -RawPerfDataToAndroidPerfProfile(const string &perf_file, - ::perfprofd::Symbolizer* symbolizer, - bool symbolize_everything) { - std::unique_ptr<PerfprofdRecord> ret(new PerfprofdRecord()); - ret->SetExtension(::quipper::id, 0); // TODO. - - ::quipper::PerfParserOptions options = {}; - options.do_remap = true; - options.discard_unused_events = true; - options.read_missing_buildids = true; - - ::quipper::PerfReader reader; - if (!reader.ReadFile(perf_file)) return nullptr; - - ::quipper::PerfParser parser(&reader, options); - if (!parser.ParseRawEvents()) return nullptr; - - if (!reader.Serialize(ret.get())) return nullptr; - - // Append parser stats to protobuf. - ::quipper::PerfSerializer::SerializeParserStats(parser.stats(), ret.get()); - - // TODO: Symbolization. - if (symbolizer != nullptr) { - AddSymbolInfo(ret.get(), parser, symbolizer, symbolize_everything); - } - - return ret.release(); -} - -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/perf_data_converter.h b/perfprofd/perf_data_converter.h deleted file mode 100644 index 8b4ab9ff..00000000 --- a/perfprofd/perf_data_converter.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_ -#define WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_ - -#include <string> - -#include "perfprofd_record-fwd.h" - -namespace perfprofd { -struct Symbolizer; -} // namespace perfprofd - -namespace android { -namespace perfprofd { - -PerfprofdRecord* -RawPerfDataToAndroidPerfProfile(const std::string &perf_file, - ::perfprofd::Symbolizer* symbolizer, - bool symbolize_everything); - -} // namespace perfprofd -} // namespace android - -#endif // WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_ diff --git a/perfprofd/perf_profile.proto b/perfprofd/perf_profile.proto deleted file mode 100644 index 65c9c39a..00000000 --- a/perfprofd/perf_profile.proto +++ /dev/null @@ -1,131 +0,0 @@ - -syntax = "proto2"; - -option java_package = "com.google.common.logging"; - -option optimize_for = LITE_RUNTIME; - -package wireless_android_play_playlog; - -// An entry of the map from a stack of addresses to count. -// Address here is the offset of the instruction address to the load address -// of the load_module. -message AddressSample { - // List of addresses that represents a call stack. - // address[0] is the leaf of the call stack. - repeated uint64 address = 1; - - // List of load_module_ids that represents a call stack. - // load_module_id[0] is the leaf of the call stack. - // This field can be set as empty if all frame share the same load_module_id - // with LoadModuleSamples.load_module_id. - repeated int32 load_module_id = 2; - - // Total count that the address/address_range is sampled. - optional int64 count = 3; -}; - -// An entry of the map from address_range to count. -// [start, end] represents the range of addresses, end->to represents the -// taken branch that ends the range. -message RangeSample { - // Start instruction address of a range. - optional uint64 start = 1; - - // If "end" and "to" is not provided, "start" represents a single instruction. - optional uint64 end = 2; - optional uint64 to = 3; - - // Total count that the address/address_range is sampled. - optional int64 count = 4; -}; - -// A load module. -message LoadModule { - // Name of the load_module. - optional string name = 1; - - // LoadModule's linker build_id. - optional string build_id = 2; - - // On-device symbolized entries. - repeated string symbol = 3; -} - -// All samples for a load_module. -message LoadModuleSamples { - optional int32 load_module_id = 1; - - // Map from a stack of addresses to count. - repeated AddressSample address_samples = 2; - - // Map from a range triplet (start, end, to) to count. - repeated RangeSample range_samples = 3; -} - -// A table of program names. -message ProcessNames { - repeated string name = 1; -} - -// All samples for a program. -message ProgramSamples { - // Name of the program. - optional string name = 1; - - // Load module profiles. - repeated LoadModuleSamples modules = 2; - - // Index into ProcessNames for the name of the process. - optional uint32 process_name_id = 3; -} - -// A compressed representation of a perf profile, which contains samples from -// multiple binaries. -message AndroidPerfProfile { - - // Type of the hardware event. - enum EventType { - CYCLE = 0; - BRANCH = 1; - } - // Hardware event used in profiling. - optional EventType event = 1; - - // Total number of samples in this profile. - // This is the sum of counts of address_samples and range_samples in all - // load_module_samples. - optional int64 total_samples = 2; - - // Samples for all profiled programs. - repeated ProgramSamples programs = 3; - - // List of all load modules. - repeated LoadModule load_modules = 4; - - // Table of process names. - optional ProcessNames process_names = 11; - - // is device screen on at point when profile is collected? - optional bool display_on = 5; - - // system load at point when profile is collected; corresponds - // to first value from /proc/loadavg multiplied by 100 then - // converted to int32 - optional int32 sys_load_average = 6; - - // At the point when the profile was collected, was a camera active? - optional bool camera_active = 7; - - // At the point when the profile was collected, was the device still booting? - optional bool booting = 8; - - // At the point when the profile was collected, was the device plugged into - // a charger? - optional bool on_charger = 9; - - // CPU utilization measured prior to profile collection (expressed as - // 100 minus the idle percentage). - optional int32 cpu_utilization = 10; - -} diff --git a/perfprofd/perfprofd.conf b/perfprofd/perfprofd.conf deleted file mode 100644 index 696c3de5..00000000 --- a/perfprofd/perfprofd.conf +++ /dev/null @@ -1,24 +0,0 @@ -# -# Configuration file for perf profile collection daemon (perfprofd) -# -#------------------------------------------------------------------------ -# -# Destination directory for profiles -# -destination_directory=/data/misc/perfprofd -# -# Config directory for perfprofd -# -config_directory=/data/data/com.google.android.gms/files -# -# Sampling period (for perf -c option) -# -sampling_period=500000 -# -# Average interval to wait between profile collection attempts (seconds) -# -collection_interval=86400 -# -# Number of seconds of profile data to collect -# -sample_duration=3 diff --git a/perfprofd/perfprofd.rc b/perfprofd/perfprofd.rc deleted file mode 100644 index 937c407b..00000000 --- a/perfprofd/perfprofd.rc +++ /dev/null @@ -1,5 +0,0 @@ -service perfprofd /system/bin/perfprofd --binder - class late_start - user root - group root wakelock - writepid /dev/cpuset/system-background/tasks diff --git a/perfprofd/perfprofd_cmdline.cc b/perfprofd/perfprofd_cmdline.cc deleted file mode 100644 index 2c958c0a..00000000 --- a/perfprofd/perfprofd_cmdline.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* - * - * Copyright 2015, 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 "perfprofd_cmdline.h" - -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <set> -#include <string> - -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/stringprintf.h> - -#include "perfprofd_record.pb.h" - -#include "configreader.h" -#include "dropbox.h" -#include "perfprofdcore.h" -#include "perfprofd_io.h" - -// -// Perf profiling daemon -- collects system-wide profiles using -// -// simpleperf record -a -// -// and encodes them so that they can be uploaded by a separate service. -// - -// - -// -// Output file from 'perf record'. -// -#define PERF_OUTPUT "perf.data" - -// -// Path to the perf file to convert and exit? Empty value is the default, daemon mode. -// -static std::string perf_file_to_convert = ""; - -// -// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it -// out of a sleep() call so as to trigger a new collection (debugging) -// -static void sig_hup(int /* signum */) -{ - LOG(WARNING) << "SIGHUP received"; -} - -// -// Parse command line args. Currently supported flags: -// * "-c PATH" sets the path of the config file to PATH. -// * "-x PATH" reads PATH as a perf data file and saves it as a file in -// perf_profile.proto format. ".encoded" suffix is appended to PATH to form -// the output file path. -// -static void parse_args(int argc, char** argv) -{ - int ac; - - for (ac = 1; ac < argc; ++ac) { - if (!strcmp(argv[ac], "-c")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -c option requires argument)"; - continue; - } - ConfigReader::setConfigFilePath(argv[ac+1]); - ++ac; - } else if (!strcmp(argv[ac], "-x")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -x option requires argument)"; - continue; - } - perf_file_to_convert = argv[ac+1]; - ++ac; - } else { - LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; - continue; - } - } -} - -// -// Post-processes after profile is collected and converted to protobuf. -// * GMS core stores processed file sequence numbers in -// /data/data/com.google.android.gms/files/perfprofd_processed.txt -// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence -// numbers that have been processed and append the current seq number -// Returns true if the current_seq should increment. -// -static bool post_process(const Config& config, int current_seq) -{ - const std::string& dest_dir = config.destination_directory; - std::string processed_file_path = - config.config_directory + "/" + PROCESSED_FILENAME; - std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; - - - std::set<int> processed; - FILE *fp = fopen(processed_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (remove(android::base::StringPrintf( - "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { - processed.insert(seq); - } - } - fclose(fp); - } - - std::set<int> produced; - fp = fopen(produced_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (processed.find(seq) == processed.end()) { - produced.insert(seq); - } - } - fclose(fp); - } - - uint32_t maxLive = config.max_unprocessed_profiles; - if (produced.size() >= maxLive) { - return false; - } - - produced.insert(current_seq); - fp = fopen(produced_file_path.c_str(), "w"); - if (fp == NULL) { - PLOG(WARNING) << "Cannot write " << produced_file_path; - return false; - } - for (std::set<int>::const_iterator iter = produced.begin(); - iter != produced.end(); ++iter) { - fprintf(fp, "%d\n", *iter); - } - fclose(fp); - chmod(produced_file_path.c_str(), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - return true; -} - -// -// Initialization -// - -static void init(ConfigReader &config) -{ - if (!config.readFile()) { - LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); - } - - CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), - config.getStringValue("destination_directory").c_str()); - - signal(SIGHUP, sig_hup); -} - -// -// Main routine: -// 1. parse cmd line args -// 2. read config file -// 3. loop: { -// sleep for a while -// perform a profile collection -// } -// -int perfprofd_main(int argc, char** argv, Config* config) -{ - LOG(INFO) << "starting Android Wide Profiling daemon"; - - parse_args(argc, argv); - { - ConfigReader config_reader; - init(config_reader); - config_reader.FillConfig(config); - } - GlobalInit(config->perf_path); - - if (!perf_file_to_convert.empty()) { - std::string encoded_path = perf_file_to_convert + ".encoded"; - encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); - return 0; - } - - // Early exit if we're not supposed to run on this build flavor - if (!IsDebugBuild() && config->only_debug_build) { - LOG(INFO) << "early exit due to inappropriate build type"; - return 0; - } - - auto config_fn = [config]() { - return config; - }; - auto reread_config = [config]() { - // Reread config file -- the uploader may have rewritten it. - ConfigReader config_reader; - if (config_reader.readFile()) { - config_reader.FillConfig(config); - } - }; - int seq = 0; - auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { - if (proto == nullptr) { - return false; - } - if (handler_config->send_to_dropbox) { - std::string error_msg; - if (!android::perfprofd::dropbox::SendToDropbox(proto, - handler_config->destination_directory, - &error_msg)) { - LOG(ERROR) << "Failed dropbox submission: " << error_msg; - return false; - } - } else { - std::string data_file_path(handler_config->destination_directory); - data_file_path += "/"; - data_file_path += PERF_OUTPUT; - std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); - if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { - return false; - } - if (!post_process(*handler_config, seq)) { - return false; - } - } - seq++; - return true; - }; - ProfilingLoop(config_fn, reread_config, handler); - - LOG(INFO) << "finishing Android Wide Profiling daemon"; - return 0; -} diff --git a/perfprofd/perfprofd_cmdline.h b/perfprofd/perfprofd_cmdline.h deleted file mode 100644 index 5a6b766c..00000000 --- a/perfprofd/perfprofd_cmdline.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ - -// Semaphore file that indicates that the user is opting in -#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been processed/uploaded. Written by the GmsCore uploader, -// within the GmsCore files directory. -#define PROCESSED_FILENAME "perfprofd_processed.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been created by the perfprofd but not yet uploaded. Written -// by perfprofd within the destination directory; consumed by GmsCore. -#define PRODUCED_FILENAME "perfprofd_produced.txt" - -struct Config; - -// Main routine for perfprofd daemon -int perfprofd_main(int argc, char **argv, Config* config); - -#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ diff --git a/perfprofd/perfprofd_config.proto b/perfprofd/perfprofd_config.proto deleted file mode 100644 index 3702b8d8..00000000 --- a/perfprofd/perfprofd_config.proto +++ /dev/null @@ -1,96 +0,0 @@ - -syntax = "proto2"; - -option java_package = "android.perfprofd"; - -package android.perfprofd; - -message PerfConfigElement { - repeated string counters = 1; - optional bool as_group = 2 [ default = false ]; - optional uint32 sampling_period = 3; -}; - -// The configuration for a profiling session. -message ProfilingConfig { - // Average number of seconds between perf profile collections (if - // set to 100, then over time we want to see a perf profile - // collected every 100 seconds). The actual time within the interval - // for the collection is chosen randomly. - optional uint32 collection_interval_in_s = 1; - - // Use the specified fixed seed for random number generation (unit - // testing) - optional uint32 use_fixed_seed = 2; - - // Number of times to iterate through main - // loop. Value of zero indicates that we should loop forever. - optional uint32 main_loop_iterations = 3; - - // Destination directory (where to write profiles). - optional string destination_directory = 4; - // Config directory (where to read configs). - optional string config_directory = 5; - // Full path to 'perf' executable. - optional string perf_path = 6; - - // Desired sampling period (passed to perf -c option). Small - // sampling periods can perturb the collected profiles, so enforce - // min/max. A value of 0 means perf default. sampling_frequency - // takes priority. - optional uint32 sampling_period = 7; - // Desired sampling frequency (passed to perf -f option). A value of 0 - // means using sampling_period or default. - optional uint32 sampling_frequency = 22; - // Length of time to collect samples (number of seconds for 'perf - // record -a' run). - optional uint32 sample_duration_in_s = 8; - - // If this parameter is non-zero it will cause perfprofd to - // exit immediately if the build type is not userdebug or eng. - // Currently defaults to 1 (true). - optional bool only_debug_build = 9; - - // If the "mpdecision" service is running at the point we are ready - // to kick off a profiling run, then temporarily disable the service - // and hard-wire all cores on prior to the collection run, provided - // that the duration of the recording is less than or equal to the value of - // 'hardwire_cpus_max_duration'. - optional bool hardwire_cpus = 10; - optional uint32 hardwire_cpus_max_duration_in_s = 11; - - // Maximum number of unprocessed profiles we can accumulate in the - // destination directory. Once we reach this limit, we continue - // to collect, but we just overwrite the most recent profile. - optional uint32 max_unprocessed_profiles = 12; - - // If set to 1, pass the -g option when invoking 'perf' (requests - // stack traces as opposed to flat profile). - optional bool stack_profile = 13; - - // Control collection of various additional profile tags - optional bool collect_cpu_utilization = 14; - optional bool collect_charging_state = 15; - optional bool collect_booting = 16; - optional bool collect_camera_active = 17; - - // The pid of the process to profile. May be negative, in which case - // the whole system will be profiled. - optional int32 process = 18; - - // Whether to use a symbolizer on-device. - optional bool use_elf_symbolizer = 19; - // Whether to symbolize everything. If false, objects with build ID will be skipped. - optional bool symbolize_everything = 25; - - // Whether to send the result to dropbox. - optional bool send_to_dropbox = 20; - - // If true, use libz to compress the output proto. - optional bool compress = 21; - - // Whether to fail or strip unsupported events. - optional bool fail_on_unsupported_events = 24; - - repeated PerfConfigElement event_config = 23; -}; diff --git a/perfprofd/perfprofd_io.cc b/perfprofd/perfprofd_io.cc deleted file mode 100644 index d88cae4a..00000000 --- a/perfprofd/perfprofd_io.cc +++ /dev/null @@ -1,310 +0,0 @@ -/* -** -** Copyright 2015, 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 "perfprofd_io.h" - -#include <fcntl.h> -#include <unistd.h> - -#include <memory> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/stringprintf.h> -#include <google/protobuf/io/zero_copy_stream_impl_lite.h> -#include <zlib.h> - -#include "perfprofd_record.pb.h" - -namespace android { -namespace perfprofd { - -using android::base::StringPrintf; -using android::base::unique_fd; -using android::base::WriteFully; - -namespace { - -// Protobuf's file implementation is not available in protobuf-lite. :-( -class FileCopyingOutputStream : public ::google::protobuf::io::CopyingOutputStream { - public: - explicit FileCopyingOutputStream(android::base::unique_fd&& fd_in) : fd_(std::move(fd_in)) { - }; - bool Write(const void * buffer, int size) override { - return WriteFully(fd_.get(), buffer, size); - } - - private: - android::base::unique_fd fd_; -}; - -using google::protobuf::io::ZeroCopyOutputStream; - -// Protobuf's Gzip implementation is not available in protobuf-lite. :-( -class GzipOutputStream : public ZeroCopyOutputStream { - public: - ~GzipOutputStream(); - - static std::unique_ptr<GzipOutputStream> Create(ZeroCopyOutputStream* next, - std::string* error_msg); - - bool Next(void** data, int* size) override; - - void BackUp(int count) override; - - google::protobuf::int64 ByteCount() const override; - - bool WriteAliasedRaw(const void* data, int size) override; - bool AllowsAliasing() const override; - - bool Flush(); - bool Close(); - - private: - GzipOutputStream(ZeroCopyOutputStream* next, z_stream* stream); - - int Write(int flush_flags); - bool NextBuffer(); - - ZeroCopyOutputStream* next_; - void* next_data_; - int next_size_; - - z_stream* stream_; - std::unique_ptr<uint8_t[]> stream_buffer_; - bool had_error_; -}; - -constexpr size_t kStreamBufferSize = 16u * 1024u; - -GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* next, z_stream* stream) - : next_(next), - next_data_(nullptr), - next_size_(0), - stream_(stream), - stream_buffer_(nullptr), - had_error_(false) { -} - -GzipOutputStream::~GzipOutputStream() { - if (stream_ != nullptr) { - deflateEnd(stream_); - delete stream_; - stream_ = nullptr; - } -} - -bool GzipOutputStream::WriteAliasedRaw(const void* data ATTRIBUTE_UNUSED, - int size ATTRIBUTE_UNUSED) { - LOG(FATAL) << "Not supported"; - __builtin_unreachable(); -} -bool GzipOutputStream::AllowsAliasing() const { - return false; -} - -google::protobuf::int64 GzipOutputStream::ByteCount() const { - return stream_->total_in + stream_->avail_in; -} - -std::unique_ptr<GzipOutputStream> GzipOutputStream::Create(ZeroCopyOutputStream* next, - std::string* error_msg) { - std::unique_ptr<z_stream> stream(new z_stream); - - stream->zalloc = Z_NULL; - stream->zfree = Z_NULL; - stream->opaque = Z_NULL; - stream->msg = nullptr; - stream->avail_in = 0; - stream->total_in = 0; - stream->next_in = nullptr; - stream->total_out = 0; - - { - constexpr int kWindowBits = 15; - constexpr int kGzipEncoding = 16; - constexpr int kMemLevel = 8; // Default. - int init_result = deflateInit2(stream.get(), - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - kWindowBits | kGzipEncoding, - kMemLevel, - Z_DEFAULT_STRATEGY); - if (init_result != Z_OK) { - *error_msg = StringPrintf("Could not initialize compression: %d (%s)", - init_result, - stream->msg != nullptr ? stream->msg : "no message"); - return nullptr; - } - } - - return std::unique_ptr<GzipOutputStream>(new GzipOutputStream(next, stream.release())); -} - -bool GzipOutputStream::NextBuffer() { - for (;;) { - if (!next_->Next(&next_data_, &next_size_)) { - next_data_ = nullptr; - next_size_ = 0; - return false; - } - if (next_size_ == 0) { - continue; - } - stream_->next_out = static_cast<Bytef*>(next_data_); - stream_->avail_out = next_size_; - return true; - } -} - -int GzipOutputStream::Write(int flush_flags) { - CHECK(flush_flags == Z_NO_FLUSH || flush_flags == Z_FULL_FLUSH || flush_flags == Z_FINISH); - - int res; - do { - if ((next_data_ == nullptr || stream_->avail_out == 0) && !NextBuffer()) { - return Z_BUF_ERROR; - } - res = deflate(stream_, flush_flags); - } while (res == Z_OK && stream_->avail_out == 0); - - if (flush_flags == Z_FULL_FLUSH || flush_flags == Z_FINISH) { - next_->BackUp(stream_->avail_out); - next_data_ = nullptr; - next_size_ = 0; - } - - return res; -} - -bool GzipOutputStream::Next(void** data, int* size) { - if (had_error_) { - return false; - } - - // Write all pending data. - if (stream_->avail_in > 0) { - int write_error = Write(Z_NO_FLUSH); - if (write_error != Z_OK) { - had_error_ = true; - return false; - } - CHECK_EQ(stream_->avail_in, 0); - } - - if (stream_buffer_ == nullptr) { - stream_buffer_.reset(new uint8_t[kStreamBufferSize]); - } - - stream_->next_in = static_cast<Bytef*>(stream_buffer_.get()); - stream_->avail_in = kStreamBufferSize; - *data = stream_buffer_.get(); - *size = kStreamBufferSize; - return true; -} - -void GzipOutputStream::BackUp(int count) { - CHECK_GE(stream_->avail_in, count); - stream_->avail_in -= count; -} - -bool GzipOutputStream::Flush() { - if (had_error_) { - return false; - } - - int res = Write(Z_FULL_FLUSH); - had_error_ |= (res != Z_OK) - && !(res == Z_BUF_ERROR && stream_->avail_in == 0 && stream_->avail_out > 0); - return !had_error_; -} - -bool GzipOutputStream::Close() { - if (had_error_) { - return false; - } - - { - int res; - do { - res = Write(Z_FINISH); - } while (res == Z_OK); - } - - int res = deflateEnd(stream_); - delete stream_; - stream_ = nullptr; - - had_error_ = true; // Pretend an error so no other operations succeed. - - return res == Z_OK; -} - -} // namespace - -bool SerializeProtobuf(android::perfprofd::PerfprofdRecord* encodedProfile, - android::base::unique_fd&& fd, - bool compress) { - FileCopyingOutputStream fcos(std::move(fd)); - google::protobuf::io::CopyingOutputStreamAdaptor cosa(&fcos); - - ZeroCopyOutputStream* out; - - std::unique_ptr<GzipOutputStream> gzip; - if (compress) { - std::string error_msg; - gzip = GzipOutputStream::Create(&cosa, &error_msg); - if (gzip == nullptr) { - LOG(ERROR) << error_msg; - return false; - } - out = gzip.get(); - } else { - out = &cosa; - } - - bool serialized = encodedProfile->SerializeToZeroCopyStream(out); - if (!serialized) { - LOG(WARNING) << "SerializeToZeroCopyStream failed"; - return false; - } - - bool zip_ok = true; - if (gzip != nullptr) { - zip_ok = gzip->Flush(); - zip_ok = gzip->Close() && zip_ok; - } - cosa.Flush(); - return zip_ok; -} - -bool SerializeProtobuf(PerfprofdRecord* encodedProfile, - const char* encoded_file_path, - bool compress) { - unlink(encoded_file_path); // Attempt to unlink for a clean slate. - constexpr int kFlags = O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC; - unique_fd fd(open(encoded_file_path, kFlags, 0664)); - if (fd.get() == -1) { - PLOG(WARNING) << "Could not open " << encoded_file_path << " for serialization"; - return false; - } - return SerializeProtobuf(encodedProfile, std::move(fd), compress); -} - -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/perfprofd_io.h b/perfprofd/perfprofd_io.h deleted file mode 100644 index 3e754b54..00000000 --- a/perfprofd/perfprofd_io.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -** -** Copyright 2018, 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. -*/ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_IO_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_IO_H_ - -#include <android-base/unique_fd.h> - -#include "perfprofd_record-fwd.h" - -namespace android { -namespace perfprofd { - -bool SerializeProtobuf(android::perfprofd::PerfprofdRecord* encodedProfile, - const char* encoded_file_path, - bool compress = true); -bool SerializeProtobuf(android::perfprofd::PerfprofdRecord* encodedProfile, - android::base::unique_fd&& fd, - bool compress = true); - -} // namespace perfprofd -} // namespace android - -#endif diff --git a/perfprofd/perfprofd_perf.cc b/perfprofd/perfprofd_perf.cc deleted file mode 100644 index 15dde6fb..00000000 --- a/perfprofd/perfprofd_perf.cc +++ /dev/null @@ -1,332 +0,0 @@ -/* -** -** Copyright 2015, 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 "perfprofd_perf.h" - - -#include <inttypes.h> -#include <signal.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <algorithm> -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <memory> -#include <vector> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> - -#include "config.h" - -namespace android { -namespace perfprofd { - -namespace { - -std::unordered_set<std::string>& GetSupportedPerfCountersInternal() { - static std::unordered_set<std::string>& vec = *new std::unordered_set<std::string>(); - return vec; -} - -} // namespace - -// -// Invoke "perf record". Return value is OK_PROFILE_COLLECTION for -// success, or some other error code if something went wrong. -// -PerfResult InvokePerf(Config& config, - const std::string &perf_path, - const char *stack_profile_opt, - unsigned duration, - const std::string &data_file_path, - const std::string &perf_stderr_path) -{ - std::vector<std::string> argv_backing; - std::vector<const char*> argv_vector; - char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1"; - char* envp[2] = {paranoid_env, nullptr}; - - { - auto add = [&argv_backing](auto arg) { - argv_backing.push_back(arg); - }; - - add(perf_path); - add("record"); - - // -o perf.data - add("-o"); - add(data_file_path); - - // -c/f N - std::string p_str; - if (config.sampling_frequency > 0) { - add("-f"); - add(android::base::StringPrintf("%u", config.sampling_frequency)); - } else if (config.sampling_period > 0) { - add("-c"); - add(android::base::StringPrintf("%u", config.sampling_period)); - } - - if (!config.event_config.empty()) { - const std::unordered_set<std::string>& supported = GetSupportedPerfCountersInternal(); - for (const auto& event_set : config.event_config) { - if (event_set.events.empty()) { - LOG(WARNING) << "Unexpected empty event set"; - continue; - } - - std::ostringstream event_str; - bool added = false; - for (const std::string& event : event_set.events) { - if (supported.find(event) == supported.end()) { - LOG(WARNING) << "Event " << event << " is unsupported."; - if (config.fail_on_unsupported_events) { - return PerfResult::kUnsupportedEvent; - } - continue; - } - if (added) { - event_str << ','; - } - event_str << event; - added = true; - } - - if (!added) { - continue; - } - - if (event_set.sampling_period > 0) { - add("-c"); - add(std::to_string(event_set.sampling_period)); - } - add(event_set.group ? "--group" : "-e"); - add(event_str.str()); - } - } - - // -g if desired - if (stack_profile_opt != nullptr) { - add(stack_profile_opt); - add("-m"); - add("8192"); - } - - if (config.process < 0) { - // system wide profiling - add("-a"); - } else { - add("-p"); - add(std::to_string(config.process)); - } - - // no need for kernel or other symbols - add("--no-dump-kernel-symbols"); - add("--no-dump-symbols"); - - // sleep <duration> - add("--duration"); - add(android::base::StringPrintf("%u", duration)); - - - // Now create the char* buffer. - argv_vector.resize(argv_backing.size() + 1, nullptr); - std::transform(argv_backing.begin(), - argv_backing.end(), - argv_vector.begin(), - [](const std::string& in) { return in.c_str(); }); - } - - pid_t pid = fork(); - - if (pid == -1) { - PLOG(ERROR) << "Fork failed"; - return PerfResult::kForkFailed; - } - - if (pid == 0) { - // child - - // Open file to receive stderr/stdout from perf - FILE *efp = fopen(perf_stderr_path.c_str(), "w"); - if (efp) { - dup2(fileno(efp), STDERR_FILENO); - dup2(fileno(efp), STDOUT_FILENO); - } else { - PLOG(WARNING) << "unable to open " << perf_stderr_path << " for writing"; - } - - // record the final command line in the error output file for - // posterity/debugging purposes - fprintf(stderr, "perf invocation (pid=%d):\n", getpid()); - for (unsigned i = 0; argv_vector[i] != nullptr; ++i) { - fprintf(stderr, "%s%s", i ? " " : "", argv_vector[i]); - } - fprintf(stderr, "\n"); - - // exec - execvpe(argv_vector[0], const_cast<char* const*>(argv_vector.data()), envp); - fprintf(stderr, "exec failed: %s\n", strerror(errno)); - exit(1); - - } else { - // parent - - // Try to sleep. - config.Sleep(duration); - - // We may have been woken up to stop profiling. - if (config.ShouldStopProfiling()) { - // Send SIGHUP to simpleperf to make it stop. - kill(pid, SIGHUP); - } - - // Wait for the child, so it's reaped correctly. - int st = 0; - pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0)); - - auto print_perferr = [&perf_stderr_path]() { - std::string tmp; - if (android::base::ReadFileToString(perf_stderr_path, &tmp)) { - LOG(WARNING) << tmp; - } else { - PLOG(WARNING) << "Could not read " << perf_stderr_path; - } - }; - - if (reaped == -1) { - PLOG(WARNING) << "waitpid failed"; - } else if (WIFSIGNALED(st)) { - if (WTERMSIG(st) == SIGHUP && config.ShouldStopProfiling()) { - // That was us... - return PerfResult::kOK; - } - LOG(WARNING) << "perf killed by signal " << WTERMSIG(st); - print_perferr(); - } else if (WEXITSTATUS(st) != 0) { - LOG(WARNING) << "perf bad exit status " << WEXITSTATUS(st); - print_perferr(); - } else { - return PerfResult::kOK; - } - } - - return PerfResult::kRecordFailed; -} - -bool FindSupportedPerfCounters(const std::string& perf_path) { - const char* argv[] = { perf_path.c_str(), "list", nullptr }; - char paranoid_env[] = "PERFPROFD_DISABLE_PERF_EVENT_PARANOID_CHANGE=1"; - char* envp[2] = {paranoid_env, nullptr}; - - base::unique_fd link[2]; - { - int link_fd[2]; - - if (pipe(link_fd) == -1) { - PLOG(ERROR) << "Pipe failed"; - return false; - } - link[0].reset(link_fd[0]); - link[1].reset(link_fd[1]); - } - - pid_t pid = fork(); - - if (pid == -1) { - PLOG(ERROR) << "Fork failed"; - return PerfResult::kForkFailed; - } - - if (pid == 0) { - // Child - - // Redirect stdout and stderr. - dup2(link[1].get(), STDOUT_FILENO); - dup2(link[1].get(), STDERR_FILENO); - - link[0].reset(); - link[1].reset(); - - // exec - execvpe(argv[0], const_cast<char* const*>(argv), envp); - PLOG(WARNING) << "exec failed"; - exit(1); - __builtin_unreachable(); - } - - link[1].reset(); - - std::string result; - if (!android::base::ReadFdToString(link[0].get(), &result)) { - PLOG(WARNING) << perf_path << " list reading failed."; - } - - link[0].reset(); - - int status_code; - if (waitpid(pid, &status_code, 0) == -1) { - LOG(WARNING) << "Failed to wait for " << perf_path << " list"; - return false; - } - - if (!WIFEXITED(status_code) || WEXITSTATUS(status_code) != 0) { - LOG(WARNING) << perf_path << " list did not exit normally."; - return false; - } - - std::unordered_set<std::string>& supported = GetSupportedPerfCountersInternal(); - supported.clear(); - - // Could implement something with less memory requirements. But for now this is good - // enough. - std::vector<std::string> lines = base::Split(result, "\n"); - for (const std::string& line : lines) { - if (line.length() < 2 || line.compare(0, 2, " ") != 0) { - continue; - } - const size_t comment = line.find('#'); - const size_t space = line.find(' ', 2); - size_t end = std::min(space, comment); - if (end != std::string::npos) { - // Scan backwards. - --end; - while (end > 2 && isspace(line[end])) { - end--; - } - } - if (end > 2) { - supported.insert(line.substr(2, end - 2)); - } - } - - return true; -} - -const std::unordered_set<std::string>& GetSupportedPerfCounters() { - return GetSupportedPerfCountersInternal(); -} - -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/perfprofd_perf.h b/perfprofd/perfprofd_perf.h deleted file mode 100644 index 232ef829..00000000 --- a/perfprofd/perfprofd_perf.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -** -** Copyright 2018, 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. -*/ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_PERF_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_PERF_H_ - -#include <string> -#include <unordered_set> - -struct Config; - -namespace android { -namespace perfprofd { - - - -enum PerfResult { - kOK, - kForkFailed, - kRecordFailed, - kUnsupportedEvent, -}; - -// -// Invoke "perf record". Return value is PerfResult::kOK for -// success, or some other error code if something went wrong. -// -PerfResult InvokePerf(Config& config, - const std::string &perf_path, - const char *stack_profile_opt, - unsigned duration, - const std::string &data_file_path, - const std::string &perf_stderr_path); - -// Prepare the internal list of supported perf counters. -bool FindSupportedPerfCounters(const std::string& perf_path); -// Get the list of supported perf counters. -const std::unordered_set<std::string>& GetSupportedPerfCounters(); - -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_PERF_H_ diff --git a/perfprofd/perfprofd_record-fwd.h b/perfprofd/perfprofd_record-fwd.h deleted file mode 100644 index 69725826..00000000 --- a/perfprofd/perfprofd_record-fwd.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright 2018, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_RECORD_FWD_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_RECORD_FWD_H_ - -namespace quipper { -class PerfDataProto; -} // namespace quipper - -namespace android { -namespace perfprofd { -using PerfprofdRecord = ::quipper::PerfDataProto; -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_RECORD_FWD_H_ diff --git a/perfprofd/perfprofd_record.proto b/perfprofd/perfprofd_record.proto deleted file mode 100644 index 1660d5f1..00000000 --- a/perfprofd/perfprofd_record.proto +++ /dev/null @@ -1,58 +0,0 @@ - -syntax = "proto2"; - -import "perf_data.proto"; - -option java_package = "com.google.android.perfprofd"; - -package quipper; - -// Symbol info for a shared library without build id. -message SymbolInfo { - // A symbol, stretching the given range of the library. - message Symbol { - optional string name = 1; - optional uint64 name_md5_prefix = 2; - - optional uint64 addr = 3; - optional uint64 size = 4; - }; - - optional string filename = 1; - optional uint64 filename_md5_prefix = 2; - - optional uint64 min_vaddr = 3; - - repeated Symbol symbols = 4; -}; - -extend PerfDataProto { - optional int64 id = 32; - - // Extra symbol info. - repeated SymbolInfo symbol_info = 33; - - // Stats inherited from old perf_profile.proto. - - // is device screen on at point when profile is collected? - optional bool display_on = 34; - - // system load at point when profile is collected; corresponds - // to first value from /proc/loadavg multiplied by 100 then - // converted to int32 - optional int32 sys_load_average = 35; - - // At the point when the profile was collected, was a camera active? - optional bool camera_active = 36; - - // At the point when the profile was collected, was the device still booting? - optional bool booting = 37; - - // At the point when the profile was collected, was the device plugged into - // a charger? - optional bool on_charger = 38; - - // CPU utilization measured prior to profile collection (expressed as - // 100 minus the idle percentage). - optional int32 cpu_utilization = 39; -}; diff --git a/perfprofd/perfprofd_threaded_handler.h b/perfprofd/perfprofd_threaded_handler.h deleted file mode 100644 index 76cdd67a..00000000 --- a/perfprofd/perfprofd_threaded_handler.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * - * Copyright 2017, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ - -#include <chrono> -#include <condition_variable> -#include <cstdio> -#include <cstdlib> -#include <memory> -#include <mutex> -#include <string> -#include <thread> -#include <functional> - -#include <inttypes.h> -#include <unistd.h> - -#include <android-base/logging.h> -#include <android-base/stringprintf.h> - -#include "perfprofd_record.pb.h" - -#include "config.h" -#include "dropbox.h" -#include "perfprofdcore.h" -#include "perfprofd_io.h" - -namespace android { -namespace perfprofd { - -class ThreadedConfig : public Config { - public: - void Sleep(size_t seconds) override { - if (seconds == 0) { - return; - } - std::unique_lock<std::mutex> guard(mutex_); - using namespace std::chrono_literals; - cv_.wait_for(guard, seconds * 1s, [&]() { return interrupted_; }); - } - bool ShouldStopProfiling() override { - std::unique_lock<std::mutex> guard(mutex_); - return interrupted_; - } - - void ResetStopProfiling() { - std::unique_lock<std::mutex> guard(mutex_); - interrupted_ = false; - } - void StopProfiling() { - std::unique_lock<std::mutex> guard(mutex_); - interrupted_ = true; - cv_.notify_all(); - } - - bool IsProfilingEnabled() const override { - return true; - } - - // Operator= to simplify setting the config values. This will retain the - // original mutex, condition-variable etc. - ThreadedConfig& operator=(const ThreadedConfig& rhs) { - // Copy base fields. - *static_cast<Config*>(this) = static_cast<const Config&>(rhs); - - return *this; - } - - private: - bool is_profiling = false; - std::mutex mutex_; - std::condition_variable cv_; - bool interrupted_ = false; - - friend class ThreadedHandler; -}; - -class ThreadedHandler { - public: - ThreadedHandler() : cur_config_(new ThreadedConfig()) {} - explicit ThreadedHandler(ThreadedConfig* in) : cur_config_(in) { - CHECK(cur_config_ != nullptr); - } - - virtual ~ThreadedHandler() {} - - template <typename ConfigFn> bool StartProfiling(ConfigFn fn, std::string* error_msg) { - std::lock_guard<std::mutex> guard(lock_); - - if (cur_config_->is_profiling) { - *error_msg = "Already profiling"; - return false; - } - cur_config_->is_profiling = true; - cur_config_->ResetStopProfiling(); - - fn(*cur_config_); - - HandlerFn handler = GetResultHandler(); - auto profile_runner = [handler](ThreadedHandler* service) { - ProfilingLoop(*service->cur_config_, handler); - - // This thread is done. - std::lock_guard<std::mutex> unset_guard(service->lock_); - service->cur_config_->is_profiling = false; - }; - std::thread profiling_thread(profile_runner, this); - profiling_thread.detach(); // Let it go. - - return true; - } - - bool StopProfiling(std::string* error_msg) { - std::lock_guard<std::mutex> guard(lock_); - if (!cur_config_->is_profiling) { - *error_msg = "Not profiling"; - return false; - } - - cur_config_->StopProfiling(); - - return true; - } - - protected: - // Handler for ProfilingLoop. - virtual bool ResultHandler(android::perfprofd::PerfprofdRecord* encodedProfile, - Config* config) { - CHECK(config != nullptr); - if (encodedProfile == nullptr) { - return false; - } - - if (static_cast<ThreadedConfig*>(config)->send_to_dropbox) { - std::string error_msg; - if (!dropbox::SendToDropbox(encodedProfile, config->destination_directory, &error_msg)) { - LOG(WARNING) << "Failed dropbox submission: " << error_msg; - return false; - } - return true; - } - - if (encodedProfile == nullptr) { - return false; - } - std::string data_file_path(config->destination_directory); - data_file_path += "/perf.data"; - std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq_); - if (!SerializeProtobuf(encodedProfile, path.c_str(), config->compress)) { - return false; - } - - seq_++; - return true; - } - - template <typename Fn> - void RunOnConfig(Fn& fn) { - std::lock_guard<std::mutex> guard(lock_); - fn(cur_config_->is_profiling, cur_config_.get()); - } - - private: - // Helper for the handler. - HandlerFn GetResultHandler() { - return HandlerFn(std::bind(&ThreadedHandler::ResultHandler, - this, - std::placeholders::_1, - std::placeholders::_2)); - } - - std::mutex lock_; - - std::unique_ptr<ThreadedConfig> cur_config_; - - int seq_ = 0; -}; - -} // namespace perfprofd -} // namespace android - -#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_THREADED_HANDLER_H_ diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc deleted file mode 100644 index 00ec8a70..00000000 --- a/perfprofd/perfprofdcore.cc +++ /dev/null @@ -1,732 +0,0 @@ -/* -** -** Copyright 2015, 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 <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> - -#include <memory> -#include <sstream> -#include <string> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/scopeguard.h> -#include <android-base/stringprintf.h> - -#ifdef __BIONIC__ -#include <android-base/properties.h> -#endif - -#ifdef __ANDROID__ -#include <healthhalutils/HealthHalUtils.h> -#endif - -#include "perfprofd_record.pb.h" - -#include "config.h" -#include "cpuconfig.h" -#include "perf_data_converter.h" -#include "perfprofdcore.h" -#include "perfprofd_io.h" -#include "perfprofd_perf.h" -#include "symbolizer.h" - -// -// Perf profiling daemon -- collects system-wide profiles using -// -// simpleperf record -a -// -// and encodes them so that they can be uploaded by a separate service. -// - -//...................................................................... - -using ProtoUniquePtr = std::unique_ptr<android::perfprofd::PerfprofdRecord>; - -// -// Output file from 'perf record'. -// -#define PERF_OUTPUT "perf.data" - -// -// This enum holds the results of the "should we profile" configuration check. -// -typedef enum { - - // All systems go for profile collection. - DO_COLLECT_PROFILE, - - // The selected configuration directory doesn't exist. - DONT_PROFILE_MISSING_CONFIG_DIR, - - // Destination directory does not contain the semaphore file that - // the perf profile uploading service creates when it determines - // that the user has opted "in" for usage data collection. No - // semaphore -> no user approval -> no profiling. - DONT_PROFILE_MISSING_SEMAPHORE, - - // No perf executable present - DONT_PROFILE_MISSING_PERF_EXECUTABLE, - - // We're running in the emulator, perf won't be able to do much - DONT_PROFILE_RUNNING_IN_EMULATOR - -} CKPROFILE_RESULT; - -static bool common_initialized = false; - -// -// Are we running in the emulator? If so, stub out profile collection -// Starts as uninitialized (-1), then set to 1 or 0 at init time. -// -static int running_in_emulator = -1; - -// -// Is this a debug build ('userdebug' or 'eng')? -// -static bool is_debug_build = false; - -// -// Random number generator seed (set at startup time). -// -static unsigned short random_seed[3]; - -// -// Convert a CKPROFILE_RESULT to a string -// -static const char *ckprofile_result_to_string(CKPROFILE_RESULT result) -{ - switch (result) { - case DO_COLLECT_PROFILE: - return "DO_COLLECT_PROFILE"; - case DONT_PROFILE_MISSING_CONFIG_DIR: - return "missing config directory"; - case DONT_PROFILE_MISSING_SEMAPHORE: - return "missing semaphore file"; - case DONT_PROFILE_MISSING_PERF_EXECUTABLE: - return "missing 'perf' executable"; - case DONT_PROFILE_RUNNING_IN_EMULATOR: - return "running in emulator"; - default: - return "unknown"; - } -} - -// -// Check to see whether we should perform a profile collection -// -static CKPROFILE_RESULT check_profiling_enabled(const Config& config) -{ - // - // Profile collection in the emulator doesn't make sense - // - assert(running_in_emulator != -1); - if (running_in_emulator) { - return DONT_PROFILE_RUNNING_IN_EMULATOR; - } - - if (!config.IsProfilingEnabled()) { - return DONT_PROFILE_MISSING_CONFIG_DIR; - } - - // Check for existence of simpleperf/perf executable - std::string pp = config.perf_path; - if (access(pp.c_str(), R_OK|X_OK) == -1) { - LOG(WARNING) << "unable to access/execute " << pp; - return DONT_PROFILE_MISSING_PERF_EXECUTABLE; - } - - // - // We are good to go - // - return DO_COLLECT_PROFILE; -} - -bool get_booting() -{ -#ifdef __BIONIC__ - return android::base::GetBoolProperty("sys.boot_completed", false) != true; -#else - return false; -#endif -} - -// -// Constructor takes a timeout (in seconds) and a child pid; If an -// alarm set for the specified number of seconds triggers, then a -// SIGKILL is sent to the child. Destructor resets alarm. Example: -// -// pid_t child_pid = ...; -// { AlarmHelper h(10, child_pid); -// ... = read_from_child(child_pid, ...); -// } -// -// NB: this helper is not re-entrant-- avoid nested use or -// use by multiple threads -// -class AlarmHelper { - public: - AlarmHelper(unsigned num_seconds, pid_t child) - { - struct sigaction sigact; - assert(child); - assert(child_ == 0); - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = handler; - sigaction(SIGALRM, &sigact, &oldsigact_); - child_ = child; - alarm(num_seconds); - } - ~AlarmHelper() - { - alarm(0); - child_ = 0; - sigaction(SIGALRM, &oldsigact_, NULL); - } - static void handler(int, siginfo_t *, void *); - - private: - struct sigaction oldsigact_; - static pid_t child_; -}; - -pid_t AlarmHelper::child_; - -void AlarmHelper::handler(int, siginfo_t *, void *) -{ - LOG(WARNING) << "SIGALRM timeout"; - kill(child_, SIGKILL); -} - -// -// This implementation invokes "dumpsys media.camera" and inspects the -// output to determine if any camera clients are active. NB: this is -// currently disable (via config option) until the selinux issues can -// be sorted out. Another possible implementation (not yet attempted) -// would be to use the binder to call into the native camera service -// via "ICameraService". -// -bool get_camera_active() -{ - int pipefds[2]; - if (pipe2(pipefds, O_CLOEXEC) != 0) { - PLOG(ERROR) << "pipe2() failed"; - return false; - } - pid_t pid = fork(); - if (pid == -1) { - PLOG(ERROR) << "fork() failed"; - close(pipefds[0]); - close(pipefds[1]); - return false; - } else if (pid == 0) { - // child - close(pipefds[0]); - dup2(pipefds[1], fileno(stderr)); - dup2(pipefds[1], fileno(stdout)); - const char *argv[10]; - unsigned slot = 0; - argv[slot++] = "/system/bin/dumpsys"; - argv[slot++] = "media.camera"; - argv[slot++] = nullptr; - execvp(argv[0], (char * const *)argv); - PLOG(ERROR) << "execvp() failed"; - return false; - } - // parent - AlarmHelper helper(10, pid); - close(pipefds[1]); - - // read output - bool have_cam = false; - bool have_clients = true; - std::string dump_output; - bool result = android::base::ReadFdToString(pipefds[0], &dump_output); - close(pipefds[0]); - if (result) { - std::stringstream ss(dump_output); - std::string line; - while (std::getline(ss,line,'\n')) { - if (line.find("Camera module API version:") != - std::string::npos) { - have_cam = true; - } - if (line.find("No camera module available") != - std::string::npos || - line.find("No active camera clients yet") != - std::string::npos) { - have_clients = false; - } - } - } - - // reap child (no zombies please) - int st = 0; - TEMP_FAILURE_RETRY(waitpid(pid, &st, 0)); - return have_cam && have_clients; -} - -bool get_charging() -{ -#ifdef __ANDROID__ - using android::sp; - using android::hardware::Return; - using android::hardware::health::V2_0::get_health_service; - using android::hardware::health::V2_0::HealthInfo; - using android::hardware::health::V2_0::IHealth; - using android::hardware::health::V2_0::Result; - - sp<IHealth> service = get_health_service(); - if (service == nullptr) { - LOG(ERROR) << "Failed to get health HAL"; - return false; - } - Result res = Result::UNKNOWN; - HealthInfo val; - Return<void> ret = - service->getHealthInfo([&](Result out_res, HealthInfo out_val) { - res = out_res; - val = out_val; - }); - if (!ret.isOk()) { - LOG(ERROR) << "Failed to call getChargeStatus on health HAL: " << ret.description(); - return false; - } - if (res != Result::SUCCESS) { - LOG(ERROR) << "Failed to retrieve charge status from health HAL: result = " - << toString(res); - return false; - } - return val.legacy.chargerAcOnline || val.legacy.chargerUsbOnline || - val.legacy.chargerWirelessOnline; -#else - return false; -#endif -} - -static bool postprocess_proc_stat_contents(const std::string &pscontents, - long unsigned *idleticks, - long unsigned *remainingticks) -{ - long unsigned usertime, nicetime, systime, idletime, iowaittime; - long unsigned irqtime, softirqtime; - - int rc = sscanf(pscontents.c_str(), "cpu %lu %lu %lu %lu %lu %lu %lu", - &usertime, &nicetime, &systime, &idletime, - &iowaittime, &irqtime, &softirqtime); - if (rc != 7) { - return false; - } - *idleticks = idletime; - *remainingticks = usertime + nicetime + systime + iowaittime + irqtime + softirqtime; - return true; -} - -unsigned collect_cpu_utilization() -{ - std::string contents; - long unsigned idle[2]; - long unsigned busy[2]; - for (unsigned iter = 0; iter < 2; ++iter) { - if (!android::base::ReadFileToString("/proc/stat", &contents)) { - return 0; - } - if (!postprocess_proc_stat_contents(contents, &idle[iter], &busy[iter])) { - return 0; - } - if (iter == 0) { - sleep(1); - } - } - long unsigned total_delta = (idle[1] + busy[1]) - (idle[0] + busy[0]); - long unsigned busy_delta = busy[1] - busy[0]; - return busy_delta * 100 / total_delta; -} - -static void annotate_encoded_perf_profile(android::perfprofd::PerfprofdRecord* profile, - const Config& config, - unsigned cpu_utilization) -{ - // - // Incorporate cpu utilization (collected prior to perf run) - // - if (config.collect_cpu_utilization) { - profile->SetExtension(quipper::cpu_utilization, cpu_utilization); - } - - // - // Load average as reported by the kernel - // - std::string load; - double fload = 0.0; - if (android::base::ReadFileToString("/proc/loadavg", &load) && - sscanf(load.c_str(), "%lf", &fload) == 1) { - int iload = static_cast<int>(fload * 100.0); - profile->SetExtension(quipper::sys_load_average, iload); - } else { - PLOG(ERROR) << "Failed to read or scan /proc/loadavg"; - } - - // - // Device still booting? Camera in use? Plugged into charger? - // - bool is_booting = get_booting(); - if (config.collect_booting) { - profile->SetExtension(quipper::booting, is_booting); - } - if (config.collect_camera_active) { - profile->SetExtension(quipper::camera_active, is_booting ? false : get_camera_active()); - } - if (config.collect_charging_state) { - profile->SetExtension(quipper::on_charger, get_charging()); - } - - // - // Examine the contents of wake_unlock to determine whether the - // device display is on or off. NB: is this really the only way to - // determine this info? - // - std::string disp; - if (android::base::ReadFileToString("/sys/power/wake_unlock", &disp)) { - bool ison = (strstr(disp.c_str(), "PowerManagerService.Display") == 0); - profile->SetExtension(quipper::display_on, ison); - } else { - PLOG(ERROR) << "Failed to read /sys/power/wake_unlock"; - } -} - -static ProtoUniquePtr encode_to_proto(const std::string &data_file_path, - const Config& config, - unsigned cpu_utilization, - perfprofd::Symbolizer* symbolizer) { - // - // Open and read perf.data file - // - ProtoUniquePtr encodedProfile( - android::perfprofd::RawPerfDataToAndroidPerfProfile(data_file_path, - symbolizer, - config.symbolize_everything)); - if (encodedProfile == nullptr) { - return nullptr; - } - - // All of the info in 'encodedProfile' is derived from the perf.data file; - // here we tack display status, cpu utilization, system load, etc. - annotate_encoded_perf_profile(encodedProfile.get(), config, cpu_utilization); - - return encodedProfile; -} - -PROFILE_RESULT encode_to_proto(const std::string &data_file_path, - const char *encoded_file_path, - const Config& config, - unsigned cpu_utilization, - perfprofd::Symbolizer* symbolizer) -{ - ProtoUniquePtr encodedProfile = encode_to_proto(data_file_path, - config, - cpu_utilization, - symbolizer); - - // - // Issue error if no samples - // - if (encodedProfile == nullptr || encodedProfile->events_size() == 0) { - return ERR_PERF_ENCODE_FAILED; - } - - return android::perfprofd::SerializeProtobuf(encodedProfile.get(), - encoded_file_path, - config.compress) - ? OK_PROFILE_COLLECTION - : ERR_WRITE_ENCODED_FILE_FAILED; -} - -// -// Remove all files in the destination directory during initialization -// -static void cleanup_destination_dir(const std::string& dest_dir) -{ - DIR* dir = opendir(dest_dir.c_str()); - if (dir != NULL) { - struct dirent* e; - while ((e = readdir(dir)) != 0) { - if (e->d_name[0] != '.') { - std::string file_path = dest_dir + "/" + e->d_name; - remove(file_path.c_str()); - } - } - closedir(dir); - } else { - PLOG(WARNING) << "unable to open destination dir " << dest_dir << " for cleanup"; - } -} - -// -// Collect a perf profile. Steps for this operation are: -// - kick off 'perf record' -// - read perf.data, convert to protocol buf -// -static ProtoUniquePtr collect_profile(Config& config) -{ - // - // Collect cpu utilization if enabled - // - unsigned cpu_utilization = 0; - if (config.collect_cpu_utilization) { - cpu_utilization = collect_cpu_utilization(); - } - - // - // Form perf.data file name, perf error output file name - // - const std::string& destdir = config.destination_directory; - std::string data_file_path(destdir); - data_file_path += "/"; - data_file_path += PERF_OUTPUT; - std::string perf_stderr_path(destdir); - perf_stderr_path += "/perferr.txt"; - - // - // Remove any existing perf.data file -- if we don't do this, perf - // will rename the old file and we'll have extra cruft lying around. - // - struct stat statb; - if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists... - if (unlink(data_file_path.c_str())) { // then try to remove - PLOG(WARNING) << "unable to unlink previous perf.data file"; - } - } - - // - // The "mpdecision" daemon can cause problems for profile - // collection: if it decides to online a CPU partway through the - // 'perf record' run, the activity on that CPU will be invisible to - // perf, and if it offlines a CPU during the recording this can - // sometimes leave the PMU in an unusable state (dmesg errors of the - // form "perfevents: unable to request IRQXXX for ..."). To avoid - // these issues, if "mpdecision" is running the helper below will - // stop the service and then online all available CPUs. The object - // destructor (invoked when this routine terminates) will then - // restart the service again when needed. - // - uint32_t duration = config.sample_duration_in_s; - bool hardwire = config.hardwire_cpus; - uint32_t max_duration = config.hardwire_cpus_max_duration_in_s; - bool take_action = (hardwire && duration <= max_duration); - HardwireCpuHelper helper(take_action); - - auto scope_guard = android::base::make_scope_guard( - [&data_file_path]() { unlink(data_file_path.c_str()); }); - - // - // Invoke perf - // - const char *stack_profile_opt = - (config.stack_profile ? "-g" : nullptr); - const std::string& perf_path = config.perf_path; - - android::perfprofd::PerfResult invoke_res = - android::perfprofd::InvokePerf(config, - perf_path, - stack_profile_opt, - duration, - data_file_path, - perf_stderr_path); - if (invoke_res != android::perfprofd::PerfResult::kOK) { - return nullptr; - } - - // - // Read the resulting perf.data file, encode into protocol buffer, then write - // the result to the file perf.data.encoded - // - std::unique_ptr<perfprofd::Symbolizer> symbolizer; - if (config.use_elf_symbolizer) { - symbolizer = perfprofd::CreateELFSymbolizer(); - } - return encode_to_proto(data_file_path, config, cpu_utilization, symbolizer.get()); -} - -// -// Assuming that we want to collect a profile every N seconds, -// randomly partition N into two sub-intervals. -// -static void determine_before_after(unsigned &sleep_before_collect, - unsigned &sleep_after_collect, - unsigned collection_interval) -{ - double frac = erand48(random_seed); - sleep_before_collect = (unsigned) (((double)collection_interval) * frac); - assert(sleep_before_collect <= collection_interval); - sleep_after_collect = collection_interval - sleep_before_collect; -} - -// -// Set random number generator seed -// -static void set_seed(uint32_t use_fixed_seed) -{ - unsigned seed = 0; - if (use_fixed_seed) { - // - // Use fixed user-specified seed - // - seed = use_fixed_seed; - } else { - // - // Randomized seed - // -#ifdef __BIONIC__ - seed = arc4random(); -#else - seed = 12345678u; -#endif - } - LOG(INFO) << "random seed set to " << seed; - // Distribute the 32-bit seed into the three 16-bit array - // elements. The specific values being written do not especially - // matter as long as we are setting them to something based on the seed. - random_seed[0] = seed & 0xffff; - random_seed[1] = (seed >> 16); - random_seed[2] = (random_seed[0] ^ random_seed[1]); -} - -void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { - // Children of init inherit an artificially low OOM score -- this is not - // desirable for perfprofd (its OOM score should be on par with - // other user processes). - std::stringstream oomscore_path; - oomscore_path << "/proc/" << getpid() << "/oom_score_adj"; - if (!android::base::WriteStringToFile("0", oomscore_path.str())) { - LOG(ERROR) << "unable to write to " << oomscore_path.str(); - } - - set_seed(use_fixed_seed); - if (dest_dir != nullptr) { - cleanup_destination_dir(dest_dir); - } - -#ifdef __BIONIC__ - running_in_emulator = android::base::GetBoolProperty("ro.kernel.qemu", false); - is_debug_build = android::base::GetBoolProperty("ro.debuggable", false); -#else - running_in_emulator = false; - is_debug_build = true; -#endif - - common_initialized = true; -} - -void GlobalInit(const std::string& perf_path) { - if (!android::perfprofd::FindSupportedPerfCounters(perf_path)) { - LOG(WARNING) << "Could not read supported perf counters."; - } -} - -bool IsDebugBuild() { - CHECK(common_initialized); - return is_debug_build; -} - -template <typename ConfigFn, typename UpdateFn> -static void ProfilingLoopImpl(ConfigFn config, UpdateFn update, HandlerFn handler) { - unsigned iterations = 0; - while(config()->main_loop_iterations == 0 || - iterations < config()->main_loop_iterations) { - if (config()->ShouldStopProfiling()) { - return; - } - - // Figure out where in the collection interval we're going to actually - // run perf - unsigned sleep_before_collect = 0; - unsigned sleep_after_collect = 0; - determine_before_after(sleep_before_collect, - sleep_after_collect, - config()->collection_interval_in_s); - if (sleep_before_collect > 0) { - config()->Sleep(sleep_before_collect); - } - - if (config()->ShouldStopProfiling()) { - return; - } - - // Run any necessary updates. - update(); - - // Check for profiling enabled... - CKPROFILE_RESULT ckresult = check_profiling_enabled(*config()); - if (ckresult != DO_COLLECT_PROFILE) { - LOG(INFO) << "profile collection skipped (" << ckprofile_result_to_string(ckresult) << ")"; - } else { - // Kick off the profiling run... - LOG(INFO) << "initiating profile collection"; - ProtoUniquePtr proto = collect_profile(*config()); - if (proto == nullptr) { - LOG(WARNING) << "profile collection failed"; - } - - // Always report, even a null result. - bool handle_result = handler(proto.get(), config()); - if (handle_result) { - LOG(INFO) << "profile collection complete"; - } else if (proto != nullptr) { - LOG(WARNING) << "profile handling failed"; - } - } - - if (config()->ShouldStopProfiling()) { - return; - } - - if (sleep_after_collect > 0) { - config()->Sleep(sleep_after_collect); - } - iterations += 1; - } -} - -void ProfilingLoop(Config& config, HandlerFn handler) { - CommonInit(config.use_fixed_seed, nullptr); - - auto config_fn = [&config]() { - return &config;; - }; - auto do_nothing = []() { - }; - ProfilingLoopImpl(config_fn, do_nothing, handler); -} - -void ProfilingLoop(std::function<Config*()> config_fn, - std::function<void()> update_fn, - HandlerFn handler) { - ProfilingLoopImpl(config_fn, update_fn, handler); -} diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h deleted file mode 100644 index fdcc6f7a..00000000 --- a/perfprofd/perfprofdcore.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -** -** Copyright 2015, 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. -*/ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFDCORE_H_ -#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFDCORE_H_ - -#include <functional> -#include <memory> -#include <string> - -#include "perfprofd_record-fwd.h" - -struct Config; - -namespace perfprofd { -struct Symbolizer; -} - -void CommonInit(uint32_t use_fixed_seed, const char* dest_dir); -void GlobalInit(const std::string& perf_path); - -// -// This enumeration holds the results of what happened when on an -// attempted perf profiling run. -// -typedef enum { - - // Success - OK_PROFILE_COLLECTION, - - // Fork system call failed (lo mem?) - ERR_FORK_FAILED, - - // Perf ran but crashed or returned a bad exit status - ERR_PERF_RECORD_FAILED, - - // The perf.data encoding process failed somehow - ERR_PERF_ENCODE_FAILED, - - // We tried to open the output file perf.data.encoded but the open failed - ERR_OPEN_ENCODED_FILE_FAILED, - - // Error while writing perf.data.encoded - ERR_WRITE_ENCODED_FILE_FAILED -} PROFILE_RESULT; - -// -// Given a full path to a perf.data file specified by "data_file_path", -// read/summarize/encode the contents into a new file specified -// by "encoded_file_path". Return status indicates whether the operation -// was successful (either OK_PROFILE_COLLECTION or an error of some sort). -// -PROFILE_RESULT encode_to_proto(const std::string &data_file_path, - const char *encoded_file_path, - const Config& config, - unsigned cpu_utilization, - perfprofd::Symbolizer* symbolizer); - -using HandlerFn = std::function<bool(android::perfprofd::PerfprofdRecord* proto, - Config* config)>; - -void ProfilingLoop(Config& config, HandlerFn handler); -void ProfilingLoop(std::function<Config*()> config_fn, - std::function<void()> update_fn, - HandlerFn handler); - -// -// Exposed for unit testing -// -extern unsigned collect_cpu_utilization(); -extern bool get_booting(); -extern bool get_charging(); -extern bool get_camera_active(); - -bool IsDebugBuild(); - -#endif diff --git a/perfprofd/perfprofdmain.cc b/perfprofd/perfprofdmain.cc deleted file mode 100644 index 0f9f53e9..00000000 --- a/perfprofd/perfprofdmain.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* -** -** Copyright 2015, 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 <string.h> - -#include <android-base/logging.h> - -#include "config.h" -#include "perfprofd_binder.h" -#include "perfprofd_cmdline.h" -#include "perfprofdcore.h" - -extern int perfprofd_main(int argc, char** argv, Config* config); - -int main(int argc, char** argv) -{ - if (argc > 1 && strcmp(argv[1], "--binder") == 0) { - return android::perfprofd::binder::Main(); - } - - struct PosixSleepConfig : public Config { - void Sleep(size_t seconds) override { - sleep(seconds); - } - bool IsProfilingEnabled() const override { - // - // Check for existence of semaphore file in config directory - // - if (access(config_directory.c_str(), F_OK) == -1) { - PLOG(WARNING) << "unable to open config directory " << config_directory; - return false; - } - - // Check for existence of semaphore file - std::string semaphore_filepath = config_directory - + "/" + SEMAPHORE_FILENAME; - if (access(semaphore_filepath.c_str(), F_OK) == -1) { - return false; - } - - return true; - } - }; - PosixSleepConfig config; - return perfprofd_main(argc, argv, &config); -} diff --git a/perfprofd/quipper_helper.h b/perfprofd/quipper_helper.h deleted file mode 100644 index d72bd405..00000000 --- a/perfprofd/quipper_helper.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2018 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 <iterator> - -#include "perf_data.pb.h" - -namespace android { -namespace perfprofd { -namespace quipper { - -template<typename Iterator, typename Predicate> -class FilteredIterator { - public: - using value_type = typename std::iterator_traits<Iterator>::value_type; - using difference_type = typename std::iterator_traits<Iterator>::difference_type; - using reference = typename std::iterator_traits<Iterator>::reference; - using pointer = typename std::iterator_traits<Iterator>::pointer; - - FilteredIterator(const Iterator& begin, const Iterator& end, const Predicate& pred) - : iter_(begin), end_(end), pred_(pred) { - filter(); - } - - reference operator*() const { - return *iter_; - } - pointer operator->() const { - return std::addressof(*iter_); - } - - FilteredIterator& operator++() { - ++iter_; - filter(); - return *this; - } - - FilteredIterator end() { - return FilteredIterator(end_, end_, pred_); - } - - bool operator==(const FilteredIterator& rhs) const { - return iter_ == rhs.iter_; - } - bool operator!=(const FilteredIterator& rhs) const { - return !(operator==(rhs)); - } - -private: - void filter() { - while (iter_ != end_ && !pred_(*iter_)) { - ++iter_; - } - } - - Iterator iter_; - Iterator end_; - Predicate pred_; -}; - -template <typename Predicate> -using EventFilteredIterator = FilteredIterator< - decltype(static_cast<::quipper::PerfDataProto*>(nullptr)->events().begin()), - Predicate>; - -struct CommEventPredicate { - bool operator()(const ::quipper::PerfDataProto_PerfEvent& evt) { - return evt.has_comm_event(); - } -}; -struct CommEventIterator : public EventFilteredIterator<CommEventPredicate> { - explicit CommEventIterator(const ::quipper::PerfDataProto& proto) - : EventFilteredIterator<CommEventPredicate>(proto.events().begin(), - proto.events().end(), - CommEventPredicate()) { - } -}; - -struct MmapEventPredicate { - bool operator()(const ::quipper::PerfDataProto_PerfEvent& evt) { - return evt.has_mmap_event(); - } -}; -struct MmapEventIterator : public EventFilteredIterator<MmapEventPredicate> { - explicit MmapEventIterator(const ::quipper::PerfDataProto& proto) - : EventFilteredIterator<MmapEventPredicate>(proto.events().begin(), - proto.events().end(), - MmapEventPredicate()) { - } -}; - -struct SampleEventPredicate { - bool operator()(const ::quipper::PerfDataProto_PerfEvent& evt) { - return evt.has_sample_event(); - } -}; -struct SampleEventIterator : public EventFilteredIterator<SampleEventPredicate> { - explicit SampleEventIterator(const ::quipper::PerfDataProto& proto) - : EventFilteredIterator<SampleEventPredicate>(proto.events().begin(), - proto.events().end(), - SampleEventPredicate()) { - } -}; - -struct ForkEventPredicate { - bool operator()(const ::quipper::PerfDataProto_PerfEvent& evt) { - return evt.has_fork_event(); - } -}; -struct ForkEventIterator : public EventFilteredIterator<ForkEventPredicate> { - explicit ForkEventIterator(const ::quipper::PerfDataProto& proto) - : EventFilteredIterator<ForkEventPredicate>(proto.events().begin(), - proto.events().end(), - ForkEventPredicate()) { - } -}; - -struct ExitEventPredicate { - bool operator()(const ::quipper::PerfDataProto_PerfEvent& evt) { - return evt.has_exit_event(); - } -}; -struct ExitEventIterator : public EventFilteredIterator<ExitEventPredicate> { - explicit ExitEventIterator(const ::quipper::PerfDataProto& proto) - : EventFilteredIterator<ExitEventPredicate>(proto.events().begin(), - proto.events().end(), - ExitEventPredicate()) { - } -}; - -} // namespace quipper -} // namespace perfprofd -} // namespace android diff --git a/perfprofd/scripts/Android.bp b/perfprofd/scripts/Android.bp deleted file mode 100644 index 93aed1c8..00000000 --- a/perfprofd/scripts/Android.bp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2018 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. - -python_defaults { - name: "perfprofd_python_default", - version: { - py2: { - enabled: true, - embedded_launcher: true, - }, - py3: { - enabled: false, - embedded_launcher: false, - }, - }, -} - -python_binary_host { - name: "perf_proto_stack", - defaults: ["perfprofd_python_default"], - main: "perf_proto_stack.py", - srcs: [ - "perf_proto_stack.py", - "sorted_collection.py", - ":quipper_data_proto", - ":perfprofd_record_proto", - ], - libs: [ - "python-symbol", - // Soong won't add "libprotobuf-python" to the dependencies if - // filegroup contains .proto files. So add it here explicitly. - "libprotobuf-python", - ], - proto: { - canonical_path_from_root: false, - include_dirs: ["external/perf_data_converter/src/quipper"], - }, - required: [ "unwind_symbols" ], -} - -python_binary_host { - name: "perf_proto_json2sqlite", - defaults: ["perfprofd_python_default"], - main: "perf_proto_json2sqlite.py", - srcs: [ - "perf_proto_json2sqlite.py", - ], -} - -python_binary_host { - name: "perf_proto_flames", - defaults: ["perfprofd_python_default"], - main: "perf_proto_stack_sqlite_flame.py", - srcs: [ - "perf_proto_stack_sqlite_flame.py", - ], - libs: [ - "simpleperf-inferno", - ], -} - -python_binary_host { - name: "perf_config_proto", - defaults: ["perfprofd_python_default"], - main: "perf_config_proto.py", - srcs: [ - "perf_config_proto.py", - ":perfprofd_config_proto", - ], - libs: [ - // Soong won't add "libprotobuf-python" to the dependencies if - // filegroup contains .proto files. So add it here explicitly. - "libprotobuf-python", - ], - proto: { - canonical_path_from_root: false, - }, -} diff --git a/perfprofd/scripts/perf_config_proto.py b/perfprofd/scripts/perf_config_proto.py deleted file mode 100644 index 2b374a8c..00000000 --- a/perfprofd/scripts/perf_config_proto.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2017 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. - -# Test converter of a Config proto. - -# Generate with: -# aprotoc -I=system/extras/perfprofd --python_out=system/extras/perfprofd/scripts \ -# system/extras/perfprofd/binder_interface/perfprofd_config.proto -# -# Note: it is necessary to do a '*' import to not have to jump through hoops -# with reflective instantiation. -from perfprofd_config_pb2 import * - -# Necessary for introspection. -from google.protobuf.descriptor import FieldDescriptor - -import sys - -PROTO_FIELD_TYPE_NAMES = { - FieldDescriptor.TYPE_DOUBLE: "double", - FieldDescriptor.TYPE_FLOAT: "float", - FieldDescriptor.TYPE_INT64: "int64", - FieldDescriptor.TYPE_UINT64: "uint64", - FieldDescriptor.TYPE_INT32: "int32", - FieldDescriptor.TYPE_FIXED64: "fixed64", - FieldDescriptor.TYPE_FIXED32: "fixed32", - FieldDescriptor.TYPE_BOOL: "bool", - FieldDescriptor.TYPE_STRING: "string", - FieldDescriptor.TYPE_GROUP: "group", - FieldDescriptor.TYPE_MESSAGE: "message", - FieldDescriptor.TYPE_BYTES: "bytes", - FieldDescriptor.TYPE_UINT32: "uint32", - FieldDescriptor.TYPE_ENUM: "enum", - FieldDescriptor.TYPE_SFIXED32: "sfixed32", - FieldDescriptor.TYPE_SFIXED64: "sfixed64", - FieldDescriptor.TYPE_SINT32: "sint32", - FieldDescriptor.TYPE_SINT64: "sint64", -} -def get_type_string(proto_field_type): - if proto_field_type in PROTO_FIELD_TYPE_NAMES: - return PROTO_FIELD_TYPE_NAMES[proto_field_type] - return "unknown type" - - -def read_message(msg_descriptor, indent): - istr = ' ' * indent - print('%s%s' % (istr, msg_descriptor.name)) - # Create an instance - instance = globals()[msg_descriptor.name]() - - # Fill fields. - - primitive_fields = [None] - message_fields = [None] - for field in msg_descriptor.fields: - if field.type == FieldDescriptor.TYPE_MESSAGE: - message_fields.append(field) - else: - primitive_fields.append(field) - - def table_loop(fields, field_fn): - while True: - # Print selection table - maxlen = len(str(len(fields) - 1)) - def pad_index(key): - while len(key) < maxlen: - key = ' ' + key - return key - - for i in xrange(1, len(fields)): - print('%s%s: %s' % (istr, pad_index(str(i)), fields[i].name)) - print('%s%s: done' % (istr, pad_index('0'))) - print('%s%s: end' % (istr, pad_index('!'))) - - sel = raw_input('%s ? ' % (istr)) - if sel == '!': - # Special-case input, end argument collection. - return False - - try: - sel_int = int(sel) - if sel_int == 0: - return True - - if sel_int > 0 and sel_int < len(fields): - field = fields[sel_int] - if not field_fn(field): - return False - else: - print('Not a valid input (%d)!' % (sel_int)) - continue - except: - print('Not a valid input! (%s, %s)' % (sel, str(sys.exc_info()[0]))) - continue - -# # 1) Non-message-type fields. - if len(primitive_fields) > 1: - print('%s(Primitives)' % (istr)) - - def primitive_fn(field): - input = raw_input('%s -> %s (%s): ' % (istr, field.name, get_type_string(field.type))) - if input == '': - # Skip this field - return True - if input == '!': - # Special-case input, end argument collection. - return False - - # Simplification: assume ints or bools or strings, but not floats - if field.type == FieldDescriptor.TYPE_BOOL: - input = input.lower() - set_val = True if input == 'y' or input == 'true' or input == '1' else False - elif field.type == FieldDescriptor.TYPE_STRING: - set_val = input - else: - try: - set_val = int(input) - except: - print('Could not parse input as integer!') - return True - if field.label == FieldDescriptor.LABEL_REPEATED: - getattr(instance, field.name).extend([set_val]) - else: - setattr(instance, field.name, set_val) - return True - - if not table_loop(primitive_fields, primitive_fn): - return (instance, False) - - # 2) Message-type fields. - if len(message_fields) > 1: - print('%s(Nested messages)' % (istr)) - - def message_fn(field): - sub_msg, cont = read_message(field.message_type, indent + 4) - if sub_msg is not None: - if field.label == FieldDescriptor.LABEL_REPEATED: - # Repeated field, use extend. - getattr(instance, field.name).extend([sub_msg]) - else: - # Singular field, copy into. - getattr(instance, field.name).CopyFrom(sub_msg) - return cont - - if not table_loop(message_fields, message_fn): - return (instance, False) - - return (instance, True) - - -def collect_and_write(filename): - config, _ = read_message(ProfilingConfig.DESCRIPTOR, 0) - - if config is not None: - with open(filename, "wb") as f: - f.write(config.SerializeToString()) - - -def read_and_print(filename): - config = ProfilingConfig() - - with open(filename, "rb") as f: - config.ParseFromString(f.read()) - - print config - - -def print_usage(): - print('Usage: python perf_config_proto.py (read|write) filename') - - -if __name__ == "__main__": - if len(sys.argv) < 3: - print_usage() - elif sys.argv[1] == 'read': - read_and_print(sys.argv[2]) - elif sys.argv[1] == 'write': - collect_and_write(sys.argv[2]) - else: - print_usage() diff --git a/perfprofd/scripts/perf_proto_json2sqlite.py b/perfprofd/scripts/perf_proto_json2sqlite.py deleted file mode 100644 index b0d74c8b..00000000 --- a/perfprofd/scripts/perf_proto_json2sqlite.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2018 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. - -import argparse -import itertools -import json -import sqlite3 - - -class SqliteWriter(object): - def __init__(self): - self.sample_count = 0 - self.dso_map = {} - self.pid_map = {} - self.tid_map = {} - self.symbol_map = {} - - def open(self, out): - self._conn = sqlite3.connect(out) - self._c = self._conn.cursor() - # Ensure tables exist - # The sample replicates pid and tid. - try: - self._c.execute('''CREATE TABLE pids (id integer PRIMARY KEY AUTOINCREMENT, - name text)''') - self._c.execute('''CREATE TABLE tids (id integer PRIMARY KEY AUTOINCREMENT, - name text)''') - self._c.execute('''CREATE TABLE syms (id integer PRIMARY KEY AUTOINCREMENT, - name text)''') - self._c.execute('''CREATE TABLE dsos (id integer PRIMARY KEY AUTOINCREMENT, - name text)''') - self._c.execute('''CREATE TABLE samples (id integer PRIMARY KEY AUTOINCREMENT, - pid_id int not null, - tid_id int not null) - ''') - self._c.execute('''CREATE TABLE stacks (sample_id int not null, - depth int not null, - dso_id int not null, - sym_id int not null, - offset int not null, - primary key (sample_id, depth)) - ''') - except sqlite3.OperationalError: - pass # ignore - - def close(self): - self._conn.commit() - self._conn.close() - - def insert_into_tmp_or_get(self, name, table_dict, table_dict_tmp): - if name in table_dict: - return table_dict[name] - if name in table_dict_tmp: - return table_dict_tmp[name] - index = len(table_dict) + len(table_dict_tmp) - table_dict_tmp[name] = index - return index - - def prepare(self): - self.dso_tmp_map = {} - self.pid_tmp_map = {} - self.tid_tmp_map = {} - self.symbol_tmp_map = {} - self.samples_tmp_list = [] - self.stacks_tmp_list = [] - - def write_sqlite_index_table(self, table_dict, table_name): - for key, value in table_dict.iteritems(): - self._c.execute("insert into {tn} values (?,?)".format(tn=table_name), (value, key)) - - def flush(self): - self.write_sqlite_index_table(self.pid_tmp_map, 'pids') - self.write_sqlite_index_table(self.tid_tmp_map, 'tids') - self.write_sqlite_index_table(self.dso_tmp_map, 'dsos') - self.write_sqlite_index_table(self.symbol_tmp_map, 'syms') - - for sample in self.samples_tmp_list: - self._c.execute("insert into samples values (?,?,?)", sample) - for stack in self.stacks_tmp_list: - self._c.execute("insert into stacks values (?,?,?,?,?)", stack) - - self.pid_map.update(self.pid_tmp_map) - self.tid_map.update(self.tid_tmp_map) - self.dso_map.update(self.dso_tmp_map) - self.symbol_map.update(self.symbol_tmp_map) - - self.dso_tmp_map = {} - self.pid_tmp_map = {} - self.tid_tmp_map = {} - self.symbol_tmp_map = {} - self.samples_tmp_list = [] - self.stacks_tmp_list = [] - - def add_sample(self, sample, tid_name_map): - sample_id = self.sample_count - self.sample_count = self.sample_count + 1 - - def get_name(pid, name_map): - if pid in name_map: - return name_map[pid] - pid_str = str(pid) - if pid_str in name_map: - return name_map[pid_str] - if pid == 0: - return "[kernel]" - return "[unknown]" - - pid_name = get_name(sample[0], tid_name_map) - pid_id = self.insert_into_tmp_or_get(pid_name, self.pid_map, self.pid_tmp_map) - tid_name = get_name(sample[1], tid_name_map) - tid_id = self.insert_into_tmp_or_get(tid_name, self.tid_map, self.tid_tmp_map) - - self.samples_tmp_list.append((sample_id, pid_id, tid_id)) - - stack_depth = 0 - for entry in sample[2]: - sym_id = self.insert_into_tmp_or_get(entry[0], self.symbol_map, self.symbol_tmp_map) - dso = entry[2] - if dso is None: - dso = "None" - dso_id = self.insert_into_tmp_or_get(dso, self.dso_map, self.dso_tmp_map) - - self.stacks_tmp_list.append((sample_id, stack_depth, dso_id, sym_id, entry[1])) - - stack_depth = stack_depth + 1 - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='''Process a set of perfprofd JSON files produced - by perf_proto_stack.py into SQLite database''') - - parser.add_argument('file', help='JSON files to parse and combine', metavar='file', nargs='+') - - parser.add_argument('--sqlite-out', help='SQLite database output', type=str, - default='sqlite.db') - - args = parser.parse_args() - if args is not None: - sql_out = SqliteWriter() - sql_out.open(args.sqlite_out) - sql_out.prepare() - - for f in args.file: - print 'Processing %s' % (f) - fp = open(f, 'r') - data = json.load(fp) - fp.close() - - for sample in data['samples']: - sql_out.add_sample(sample, data['names']) - - sql_out.flush() - - sql_out.close() diff --git a/perfprofd/scripts/perf_proto_stack.py b/perfprofd/scripts/perf_proto_stack.py deleted file mode 100644 index 2b0c3107..00000000 --- a/perfprofd/scripts/perf_proto_stack.py +++ /dev/null @@ -1,576 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2017 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. - -# Super simplistic printer of a perfprofd output proto. Illustrates -# how to parse and traverse a perfprofd output proto in Python. - -# This relies on libunwindstack's unwind_symbol. Build with -# mmma system/core/libunwindstack - -import argparse -from datetime import datetime -import itertools -import json -import logging -from multiprocessing.dummy import Pool as ThreadPool -import os.path -from sorted_collection import SortedCollection -import subprocess -from threading import Timer - -# Generate with: -# aprotoc -I=external/perf_data_converter/src/quipper \ -# --python_out=system/extras/perfprofd/scripts \ -# external/perf_data_converter/src/quipper/perf_data.proto -# aprotoc -I=external/perf_data_converter/src/quipper -I=system/extras/perfprofd \ -# --python_out=system/extras/perfprofd/scripts \ -# system/extras/perfprofd/perfprofd_record.proto -import perfprofd_record_pb2 -import perf_data_pb2 - -# Make sure that symbol is on the PYTHONPATH, e.g., run as -# PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/development/scripts python ... -import symbol -from symbol import SymbolInformation - -logging.basicConfig(format='%(message)s') - -# This is wrong. But then the symbol module is a bad quagmire. -# TODO: Check build IDs. -symbol.SetAbi(["ABI: 'arm64'"]) - - -class MmapState(object): - - def __init__(self): - self._list = SortedCollection((), lambda x: x[0]) - - def add_map(self, start, length, pgoff, name): - map_tuple = (start, length, pgoff, name) - self._list.insert(map_tuple) - - def find(self, addr): - try: - map_tuple = self._list.find_le(addr) - if addr < map_tuple[0] + map_tuple[1]: - return map_tuple - return None - except ValueError: - return None - - def copy(self): - ret = MmapState() - ret._list = self._list.copy() - return ret - - def __str__(self): - return 'MmapState: ' + self._list.__str__() - - def __repr__(self): - return self.__str__() - - -class SymbolMap(object): - def __init__(self, min_v): - self._list = SortedCollection((), lambda x: x[0]) - self._min_vaddr = min_v - - def add_symbol(self, start, length, name): - tuple = (start, length, name) - self._list.insert(tuple) - - def find(self, addr): - try: - tuple = self._list.find_le(addr) - if addr < tuple[0] + tuple[1]: - return tuple[2] - return None - except ValueError: - return None - - def copy(self): - ret = SymbolMap() - ret._list = self._list.copy() - return ret - - def __str__(self): - return "SymbolMap: " + self._list.__str__() - - def __repr__(self): - return self.__str__() - - -def intern_uni(u): - return intern(u.encode('ascii', 'replace')) - - -def collect_tid_names(perf_data): - tid_name_map = {} - for event in perf_data.events: - if event.HasField('comm_event'): - tid_name_map[event.comm_event.tid] = intern_uni(event.comm_event.comm) - return tid_name_map - - -def create_symbol_maps(profile): - symbol_maps = {} - - for si in profile.Extensions[perfprofd_record_pb2.symbol_info]: - map = SymbolMap(si.min_vaddr) - symbol_maps[si.filename] = map - for sym in si.symbols: - map.add_symbol(sym.addr, sym.size, intern_uni(sym.name)) - return symbol_maps - - -def update_mmap_states(event, state_map): - if event.HasField('mmap_event'): - mmap_event = event.mmap_event - # Skip kernel stuff. - if mmap_event.tid == 0: - return - # Create new map, if necessary. - if mmap_event.pid not in state_map: - state_map[mmap_event.pid] = MmapState() - state_map[mmap_event.pid].add_map(mmap_event.start, mmap_event.len, mmap_event.pgoff, - intern_uni(mmap_event.filename)) - elif event.HasField('fork_event'): - fork_event = event.fork_event - # Skip threads - if fork_event.pid == fork_event.ppid: - return - - if fork_event.ppid not in state_map: - logging.warn("fork from %d without map", fork_event.ppid) - return - state_map[fork_event.pid] = state_map[fork_event.ppid].copy() - - -skip_dso = set() -vaddr = {} - - -def find_vaddr(vaddr_map, filename): - if filename in vaddr_map: - return vaddr_map[filename] - - path = "%s/%s" % (symbol.SYMBOLS_DIR, filename) - if not os.path.isfile(path): - logging.warn('Cannot find %s for min_vaddr', filename) - vaddr_map[filename] = 0 - return 0 - - try: - # Use "-W" to have single-line format. - res = subprocess.check_output(['readelf', '-lW', path]) - lines = res.split("\n") - reading_headers = False - min_vaddr = None - - def min_fn(x, y): - return y if x is None else min(x, y) - # Using counting loop for access to next line. - for i in range(0, len(lines) - 1): - line = lines[i].strip() - if reading_headers: - if line == "": - # Block is done, won't find anything else. - break - if line.startswith("LOAD"): - # Look at the current line to distinguish 32-bit from 64-bit - line_split = line.split() - if len(line_split) >= 8: - if " R E " in line: - # Found something expected. So parse VirtAddr. - try: - min_vaddr = min_fn(min_vaddr, int(line_split[2], 0)) - except ValueError: - pass - else: - logging.warn('Could not parse readelf line %s', line) - else: - if line.strip() == "Program Headers:": - reading_headers = True - - if min_vaddr is None: - min_vaddr = 0 - logging.debug("min_vaddr for %s is %d", filename, min_vaddr) - vaddr_map[filename] = min_vaddr - except subprocess.CalledProcessError: - logging.warn('Error finding min_vaddr for %s', filename) - - vaddr_map[filename] = 0 - return vaddr_map[filename] - - -unwind_symbols_cache = {} -unwind_symbols_warn_missing_cache = set() - - -def run_unwind_symbols(filename, offset_hex): - path = "%s/%s" % (symbol.SYMBOLS_DIR, filename) - if not os.path.isfile(path): - if path not in unwind_symbols_warn_missing_cache: - logging.warn('Cannot find %s for unwind_symbols', filename) - unwind_symbols_warn_missing_cache.add(path) - return None - - if (path, offset_hex) in unwind_symbols_cache: - pair = unwind_symbols_cache[(path, offset_hex)] - if pair is None: - return None - return [(pair[0], pair[1], filename)] - - try: - res = subprocess.check_output(['unwind_symbols', path, offset_hex]) - lines = res.split("\n") - for line in lines: - if line.startswith('<0x'): - parts = line.split(' ', 1) - if len(parts) == 2: - # Get offset, too. - offset = 0 - plus_index = parts[0].find('>+') - if plus_index > 0: - offset_str = parts[0][plus_index + 2:-1] - try: - offset = int(offset_str) - except ValueError: - logging.warn('error parsing offset from %s', parts[0]) - - # TODO C++ demangling necessary. - logging.debug('unwind_symbols: %s %s -> %s +%d', filename, offset_hex, parts[1], - offset) - sym = intern(parts[1]) - unwind_symbols_cache[(path, offset_hex)] = (sym, offset) - return [(sym, offset, filename)] - except subprocess.CalledProcessError: - logging.warn('Failed running unwind_symbols for %s', filename) - unwind_symbols_cache[(path, offset_hex)] = None - return None - - -def decode_with_symbol_lib(name, addr_rel_hex): - info = SymbolInformation(name, addr_rel_hex) - # As-is, only info[0] (inner-most inlined function) is recognized. - (source_symbol, source_location, object_symbol_with_offset) = info[0] - - def parse_symbol_lib_output(s): - i = s.rfind('+') - if i > 0: - try: - off = int(s[i + 1:]) - return (s[0:i], off) - except ValueError: - pass - return (s, 0) - - ret = [] - - if object_symbol_with_offset is not None: - pair = parse_symbol_lib_output(object_symbol_with_offset) - ret.append((intern(pair[0]), pair[1], name)) - - if source_symbol is not None: - iterinfo = iter(info) - next(iterinfo) - for (sym_inlined, loc_inlined, _) in iterinfo: - # TODO: Figure out what's going on here: - if sym_inlined is not None: - pair = parse_symbol_lib_output(sym_inlined) - ret.insert(0, (intern(pair[0]), pair[1], name)) - if len(ret) > 0: - return ret - return None - - -def decode_addr(addr, mmap_state, device_symbols): - """Try to decode the given address against the current mmap table and device symbols. - - First, look up the address in the mmap state. If none is found, use a simple address - heuristic to guess kernel frames on 64-bit devices. - - Next, check on-device symbolization for a hit. - - Last, try to symbolize against host information. First try the symbol module. However, - as it is based on addr2line, it will not work for pure-gnu_debugdata DSOs (e.g., ART - preopt artifacts). For that case, use libunwindstack's unwind_symbols. - """ - - map = mmap_state.find(addr) - if map is None: - # If it looks large enough, assume it's from - # the kernel. - if addr > 18000000000000000000: - return [("[kernel]", 0, "[kernel]")] - return [("%d (no mapped segment)" % addr, 0, None)] - name = map[3] - logging.debug('%d is %s (%d +%d)', addr, name, map[0], map[1]) - - # Once relocation packer is off, it would be: - # offset = addr - map.start + map.pgoff - # Right now it is - # offset = addr - map.start (+ min_vaddr) - # Note that on-device symbolization doesn't include min_vaddr but - # does include pgoff. - offset = addr - map[0] - - if name in device_symbols: - offset = offset + map[2] - symbol = device_symbols[name].find(offset) - if symbol is None: - return [("%s (missing on-device symbol)" % (name), offset, name)] - else: - # TODO: Should we change the format? - return [(symbol, 0, name)] - offset = offset + find_vaddr(vaddr, name) - if (name, offset) in skip_dso: - # We already failed, skip symbol finding. - return [(name, offset, name)] - else: - addr_rel_hex = intern("%x" % offset) - ret = decode_with_symbol_lib(name, addr_rel_hex) - if ret is not None and len(ret) != 0: - # Addr2line may report oatexec+xyz. Let unwind_symbols take care of that. - if len(ret) != 1 or ret[0][0] != 'oatexec': - logging.debug('Got result from symbol module: %s', str(ret)) - return ret - # Try unwind_symbols - ret = run_unwind_symbols(name, addr_rel_hex) - if ret is not None and len(ret) != 0: - return ret - logging.warn("Failed to find symbol for %s +%d (%d)", name, offset, addr) - # Remember the fail. - skip_dso.add((name, offset)) - return [(name, offset, name)] - - -def print_sample(sample, tid_name_map): - if sample[0] in tid_name_map: - pid_name = "%s (%d)" % (tid_name_map[sample[0]], sample[0]) - elif sample[0] == 0: - pid_name = "kernel (0)" - else: - pid_name = "unknown (%d)" % (sample[0]) - - if sample[1] in tid_name_map: - tid_name = "%s (%d)" % (tid_name_map[sample[1]], sample[1]) - elif sample[1] == 0: - tid_name = "kernel (0)" - else: - tid_name = "unknown (%d)" % (sample[1]) - print " %s - %s:" % (pid_name, tid_name) - - for sym in sample[2]: - print " %s +%d (%s)" % (sym[0], sym[1], sym[2]) - - -def print_samples(samples, tid_name_map): - for sample in samples: - print_sample(sample, tid_name_map) - - -def symbolize_events(perf_data, device_symbols, tid_name_map, printSamples=False, - removeKernelTop=False): - samples = [] - mmap_states = {} - for event in perf_data.events: - update_mmap_states(event, mmap_states) - if event.HasField('sample_event'): - sample_ev = event.sample_event - # Handle sample. - new_sample = None - if sample_ev.pid in mmap_states: - mmap_state = mmap_states[sample_ev.pid] - ip_sym = decode_addr(sample_ev.ip, mmap_state, device_symbols) - stack = ip_sym - for cc_ip in sample_ev.callchain: - cc_sym = decode_addr(cc_ip, mmap_state, device_symbols) - stack.extend(cc_sym) - if removeKernelTop: - while len(stack) > 1 and stack[0][0] == "[kernel]": - stack.pop(0) - new_sample = (sample_ev.pid, sample_ev.tid, stack) - else: - # Handle kernel symbols specially. - if sample_ev.pid == 0: - samples.append((0, sample_ev.tid, [("[kernel]", 0, "[kernel]")])) - elif sample_ev.pid in tid_name_map: - samples.append((sample_ev.pid, sample_ev.tid, [(tid_name_map[sample_ev.pid], 0, - None)])) - else: - samples.append((sample_ev.pid, sample_ev.tid, [("[unknown]", 0, None)])) - if new_sample is not None: - samples.append(new_sample) - if printSamples: - print_sample(new_sample, tid_name_map) - return samples - - -def count_key_reduce_function(x, y, key_fn): - key = key_fn(y) - if key not in x: - x[key] = 0 - x[key] += 1 - return x - - -def print_histogram(samples, reduce_key_fn, label_key_fn, size): - # Create a sorted list of top samples. - sorted_count_list = sorted( - reduce(lambda x, y: count_key_reduce_function(x, y, reduce_key_fn), samples, {}). - iteritems(), - cmp=lambda x, y: cmp(x[1], y[1]), - reverse=True) - sorted_count_topX = list(itertools.islice(sorted_count_list, size)) - - # Print top-size samples. - print 'Histogram top-%d:' % (size) - for i in xrange(0, len(sorted_count_topX)): - print ' %d: %s (%s)' % (i + 1, label_key_fn(sorted_count_topX[i][0]), - sorted_count_topX[i][1]) - - -def get_name(pid, tid_name_map): - if pid in tid_name_map: - return tid_name_map[pid] - if pid == 0: - return "[kernel]" - return "[unknown]" - - -def create_cmd(args, f): - ret = ['python', '-u', 'system/extras/perfprofd/scripts/perf_proto_stack.py'] - if args.syms is not None: - ret.extend(['--syms', args.syms[0]]) - if args.print_samples is not None: - ret.append('--print-samples') - if args.skip_kernel_syms is not None: - ret.append('--skip-kernel-syms') - if args.print_pid_histogram is not None: - ret.append('--print-pid-histogram') - if args.print_sym_histogram is not None: - ret.append('--print-sym-histogram') - if args.print_dso_histogram is not None: - - ret.append('--print-dso-histogram') - ret.extend(['--json-out', '%s.json' % (f)]) - ret.append(f) - return ret - - -def run_cmd(x): - - args = x[0] - f = x[1] - cmd = create_cmd(args, f) - logging.warn('Running on %s', f) - success = False - logging.debug('%r', cmd) - err_out = open('%s.err' % (f), 'w') - - def kill(process): - process.kill() - start = datetime.now() - - p = subprocess.Popen(cmd, stderr=err_out) - kill_timer = Timer(3600, kill, [p]) - try: - kill_timer.start() - p.communicate() - success = True - - finally: - kill_timer.cancel() - err_out.close() - end = datetime.now() - logging.warn('Ended %s (%s)', f, str(end - start)) - return '%s: %r' % (f, success) - - -def parallel_runner(args): - pool = ThreadPool(args.parallel) - map_args = map(lambda f: (args, f), args.file) - - result = pool.map(run_cmd, map_args) - pool.close() - pool.join() - print result - - -def run(args): - if args.syms is not None: - symbol.SYMBOLS_DIR = args.syms[0] - print_symbols = args.print_samples is not None - skip_kernel_syms = args.skip_kernel_syms is not None - - # TODO: accept argument for parsing. - file = open(args.file[0], 'rb') - data = file.read() - - file.close() - - profile = perf_data_pb2.PerfDataProto() - # PerfprofdRecord() - profile.ParseFromString(data) - - perf_data = profile - - print "Stats: ", perf_data.stats - - tid_name_map = collect_tid_names(perf_data) - symbol_maps = create_symbol_maps(profile) - - samples = symbolize_events(perf_data, symbol_maps, tid_name_map, printSamples=print_symbols, - removeKernelTop=skip_kernel_syms) - - if args.print_pid_histogram is not None: - print_histogram(samples, lambda x: x[0], lambda x: get_name(x, tid_name_map), 25) - if args.print_sym_histogram is not None: - print_histogram(samples, lambda x: x[2][0][0], lambda x: x, 100) - if args.print_dso_histogram is not None: - print_histogram(samples, lambda x: x[2][0][2], lambda x: x, 25) - - if args.json_out is not None: - json_file = open(args.json_out[0], 'w') - json_data = {'samples': samples, 'names': tid_name_map} - json.dump(json_data, json_file) - json_file.close() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Process a perfprofd record.') - - parser.add_argument('file', help='proto file to parse', metavar='file', nargs='+') - parser.add_argument('--syms', help='directory for symbols', nargs=1) - parser.add_argument('--json-out', help='output file for JSON', nargs=1) - parser.add_argument('--print-samples', help='print samples', action='store_const', const=True) - parser.add_argument('--skip-kernel-syms', help='skip kernel symbols at the top of stack', - action='store_const', const=True) - parser.add_argument('--print-pid-histogram', help='print a top-25 histogram of processes', - action='store_const', const=True) - parser.add_argument('--print-sym-histogram', help='print a top-100 histogram of symbols', - action='store_const', const=True) - parser.add_argument('--print-dso-histogram', help='print a top-25 histogram of maps', - action='store_const', const=True) - parser.add_argument('--parallel', help='run parallel jobs', type=int) - - args = parser.parse_args() - if args is not None: - if args.parallel is not None: - parallel_runner(args) - else: - run(args) diff --git a/perfprofd/scripts/perf_proto_stack_sqlite_flame.py b/perfprofd/scripts/perf_proto_stack_sqlite_flame.py deleted file mode 100644 index 3eb2379a..00000000 --- a/perfprofd/scripts/perf_proto_stack_sqlite_flame.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2018 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. - -# Make sure that simpleperf's inferno is on the PYTHONPATH, e.g., run as -# PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/system/extras/simpleperf/scripts/inferno python .. - -import argparse -import itertools -import sqlite3 - - -class Callsite(object): - def __init__(self, dso_id, sym_id): - self.dso_id = dso_id - self.sym_id = sym_id - self.count = 0 - self.child_map = {} - self.id = self._get_next_callsite_id() - - def add(self, dso_id, sym_id): - if (dso_id, sym_id) in self.child_map: - return self.child_map[(dso_id, sym_id)] - new_callsite = Callsite(dso_id, sym_id) - self.child_map[(dso_id, sym_id)] = new_callsite - return new_callsite - - def child_count_to_self(self): - self.count = reduce(lambda x, y: x + y[1].count, self.child_map.iteritems(), 0) - - def trim(self, local_threshold_in_percent, global_threshold): - local_threshold = local_threshold_in_percent * 0.01 * self.count - threshold = max(local_threshold, global_threshold) - for k, v in self.child_map.items(): - if v.count < threshold: - del self.child_map[k] - for _, v in self.child_map.iteritems(): - v.trim(local_threshold_in_percent, global_threshold) - - def _get_str(self, id, m): - if id in m: - return m[id] - return str(id) - - def print_callsite_ascii(self, depth, indent, dsos, syms): - - print ' ' * indent + "%s (%s) [%d]" % (self._get_str(self.sym_id, syms), - self._get_str(self.dso_id, dsos), - self.count) - if depth == 0: - return - for v in sorted(self.child_map.itervalues, key=lambda x: x.count, reverse=True): - v.print_callsite_ascii(depth - 1, indent + 1, dsos, syms) - - # Functions for flamegraph compatibility. - - callsite_counter = 0 - - @classmethod - def _get_next_callsite_id(cls): - cls.callsite_counter += 1 - return cls.callsite_counter - - def create_children_list(self): - self.children = sorted(self.child_map.itervalues(), key=lambda x: x.count, reverse=True) - - def generate_offset(self, start_offset): - self.offset = start_offset - child_offset = start_offset - for child in self.children: - child_offset = child.generate_offset(child_offset) - return self.offset + self.count - - def svgrenderer_compat(self, dsos, syms): - self.create_children_list() - self.method = self._get_str(self.sym_id, syms) - self.dso = self._get_str(self.dso_id, dsos) - self.offset = 0 - for c in self.children: - c.svgrenderer_compat(dsos, syms) - - def weight(self): - return float(self.count) - - def get_max_depth(self): - if self.child_map: - return max([c.get_max_depth() for c in self.child_map.itervalues()]) + 1 - return 1 - - -class SqliteReader(object): - def __init__(self): - self.root = Callsite("root", "root") - self.dsos = {} - self.syms = {} - - def open(self, f): - self._conn = sqlite3.connect(f) - self._c = self._conn.cursor() - - def close(self): - self._conn.close() - - def read(self, local_threshold_in_percent, global_threshold_in_percent, limit, - skip_simpleperf): - # Read aux tables first, as we need to find the kernel symbols. - def read_table(name, dest_table): - self._c.execute('select id, name from %s' % (name)) - while True: - rows = self._c.fetchmany(100) - if not rows: - break - for row in rows: - dest_table[row[0]] = row[1] - - print 'Reading DSOs' - read_table('dsos', self.dsos) - - print 'Reading symbol strings' - read_table('syms', self.syms) - - kernel_sym_id = None - for i, v in self.syms.iteritems(): - if v == '[kernel]': - kernel_sym_id = i - break - - skip_query_str = "" - if skip_simpleperf: - self._c.execute('select id from pids where name = "simpleperf"') - pid_row = self._c.fetchone() - if pid_row: - skip_query_join = "as st join samples sa on st.sample_id = sa.id " - skip_query_str = skip_query_join + "where sa.pid_id != %d" % (pid_row[0]) - - query_prefix = 'select sample_id, depth, dso_id, sym_id from stacks ' - query_suffix = ' order by sample_id asc, depth desc' - - print 'Reading samples' - self._c.execute(query_prefix + skip_query_str + query_suffix) - - last_sample_id = None - chain = None - count = 0 - while True: - rows = self._c.fetchmany(100) - - if not rows: - break - for row in rows: - if row[3] == kernel_sym_id and row[1] == 0: - # Skip kernel. - continue - if row[0] != last_sample_id: - last_sample_id = row[0] - chain = self.root - chain = chain.add(row[2], row[3]) - chain.count = chain.count + 1 - count = count + len(rows) - if limit is not None and count >= limit: - print 'Breaking as limit is reached' - break - - self.root.child_count_to_self() - global_threshold = global_threshold_in_percent * 0.01 * self.root.count - self.root.trim(local_threshold_in_percent, global_threshold) - - def print_data_ascii(self, depth): - self.root.print_callsite_ascii(depth, 0, self.dsos, self.syms) - - def get_script_js(self): - # Try to get the data directly (if packaged with embedded_loader). - import os.path - import sys - if '__loader__' in globals(): - try: - js = __loader__.get_data(os.path.join(os.path.dirname(__file__), "script.js")) - if js is not None and len(js) > 0: - return js - except: - pass - # See if we can find it another way. - rel_paths = [ - # Maybe we're being run packaged. - "script.js", - # Maybe we're being run directly. - "../../simpleperf/scripts/inferno/script.js", - ] - for rel_path in rel_paths: - script_js = os.path.join(os.path.dirname(__file__), rel_path) - if os.path.exists(script_js): - with open(script_js, 'r') as script_f: - return script_f.read() - return None - - def print_svg(self, filename, depth): - from svg_renderer import render_svg - self.root.svgrenderer_compat(self.dsos, self.syms) - self.root.generate_offset(0) - f = open(filename, 'w') - f.write(''' -<html> -<body> -<div id='flamegraph_id' style='font-family: Monospace;'> -<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;} </style> -<style type="text/css"> .t:hover { cursor:pointer; } </style> -''') - - class FakeProcess: - def __init__(self): - self.props = {'trace_offcpu': False} - fake_process = FakeProcess() - render_svg(fake_process, self.root, f, 'hot') - - f.write(''' -</div> -''') - - # Emit script.js, if we can find it. - script_data = self.get_script_js() - if script_data is not None: - f.write('<script>\n') - f.write(script_data) - f.write(''' -</script> -<br/><br/> -<div>Navigate with WASD, zoom in with SPACE, zoom out with BACKSPACE.</div> -<script>document.addEventListener('DOMContentLoaded', flamegraphInit);</script> -</body> -</html> -''') - f.close() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='''Translate a perfprofd database into a flame - representation''') - - parser.add_argument('file', help='the sqlite database to use', metavar='file', type=str) - - parser.add_argument('--html-out', help='output file for HTML flame graph', type=str) - parser.add_argument('--threshold', help='child threshold in percent', type=float, default=5) - parser.add_argument('--global-threshold', help='global threshold in percent', type=float, - default=.1) - parser.add_argument('--depth', help='depth to print to', type=int, default=10) - parser.add_argument('--limit', help='limit to given number of stack trace entries', type=int) - parser.add_argument('--skip-simpleperf', help='skip simpleperf samples', action='store_const', - const=True) - - args = parser.parse_args() - if args is not None: - sql_out = SqliteReader() - sql_out.open(args.file) - sql_out.read(args.threshold, args.global_threshold, args.limit, - args.skip_simpleperf is not None) - if args.html_out is None: - sql_out.print_data_ascii(args.depth) - else: - sql_out.print_svg(args.html_out, args.depth) - sql_out.close() diff --git a/perfprofd/scripts/sorted_collection.py b/perfprofd/scripts/sorted_collection.py deleted file mode 100644 index 46622e21..00000000 --- a/perfprofd/scripts/sorted_collection.py +++ /dev/null @@ -1,147 +0,0 @@ -# Note: Taken from https://code.activestate.com/recipes/577197-sortedcollection/. -# -# Copyright 2010 Raymond Hettinger -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in the -# Software without restriction, including without limitation the rights to use, copy, -# modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the -# following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies -# or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -from bisect import bisect_left, bisect_right - - -class SortedCollection(object): - def __init__(self, iterable=(), key=None): - self._given_key = key - key = (lambda x: x) if key is None else key - decorated = sorted((key(item), item) for item in iterable) - self._keys = [k for k, item in decorated] - self._items = [item for k, item in decorated] - self._key = key - - def _getkey(self): - return self._key - - def _setkey(self, key): - if key is not self._key: - self.__init__(self._items, key=key) - - def _delkey(self): - self._setkey(None) - - key = property(_getkey, _setkey, _delkey, 'key function') - - def clear(self): - self.__init__([], self._key) - - def copy(self): - return self.__class__(self, self._key) - - def __len__(self): - return len(self._items) - - def __getitem__(self, i): - return self._items[i] - - def __iter__(self): - return iter(self._items) - - def __reversed__(self): - return reversed(self._items) - - def __repr__(self): - return '%s(%r, key=%s)' % ( - self.__class__.__name__, - self._items, - getattr(self._given_key, '__name__', repr(self._given_key)) - ) - - def __reduce__(self): - return self.__class__, (self._items, self._given_key) - - def __contains__(self, item): - k = self._key(item) - i = bisect_left(self._keys, k) - j = bisect_right(self._keys, k) - return item in self._items[i:j] - - def index(self, item): - 'Find the position of an item. Raise ValueError if not found.' - k = self._key(item) - i = bisect_left(self._keys, k) - j = bisect_right(self._keys, k) - return self._items[i:j].index(item) + i - - def count(self, item): - 'Return number of occurrences of item' - k = self._key(item) - i = bisect_left(self._keys, k) - j = bisect_right(self._keys, k) - return self._items[i:j].count(item) - - def insert(self, item): - 'Insert a new item. If equal keys are found, add to the left' - k = self._key(item) - i = bisect_left(self._keys, k) - self._keys.insert(i, k) - self._items.insert(i, item) - - def insert_right(self, item): - 'Insert a new item. If equal keys are found, add to the right' - k = self._key(item) - i = bisect_right(self._keys, k) - self._keys.insert(i, k) - self._items.insert(i, item) - - def remove(self, item): - 'Remove first occurence of item. Raise ValueError if not found' - i = self.index(item) - del self._keys[i] - del self._items[i] - - def find(self, k): - 'Return first item with a key == k. Raise ValueError if not found.' - i = bisect_left(self._keys, k) - if i != len(self) and self._keys[i] == k: - return self._items[i] - raise ValueError('No item found with key equal to: %r' % (k,)) - - def find_le(self, k): - 'Return last item with a key <= k. Raise ValueError if not found.' - i = bisect_right(self._keys, k) - if i: - return self._items[i-1] - raise ValueError('No item found with key at or below: %r' % (k,)) - - def find_lt(self, k): - 'Return last item with a key < k. Raise ValueError if not found.' - i = bisect_left(self._keys, k) - if i: - return self._items[i-1] - raise ValueError('No item found with key below: %r' % (k,)) - - def find_ge(self, k): - 'Return first item with a key >= equal to k. Raise ValueError if not found' - i = bisect_left(self._keys, k) - if i != len(self): - return self._items[i] - raise ValueError('No item found with key at or above: %r' % (k,)) - - def find_gt(self, k): - 'Return first item with a key > k. Raise ValueError if not found' - i = bisect_right(self._keys, k) - if i != len(self): - return self._items[i] - raise ValueError('No item found with key above: %r' % (k,)) diff --git a/perfprofd/symbolizer.cc b/perfprofd/symbolizer.cc deleted file mode 100644 index 3b39f6f5..00000000 --- a/perfprofd/symbolizer.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * - * Copyright 2018, 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 "symbolizer.h" - -#include <map> -#include <memory> -#include <unordered_map> - -#include <android-base/logging.h> -#include <base/mem_map.h> -#include <build_id.h> -#include <read_dex_file.h> -#include <read_elf.h> -#include <vdex_file.h> - -namespace perfprofd { - -namespace { - -struct SimpleperfSymbolizer : public Symbolizer { - // For simplicity, we assume non-overlapping symbols. - struct Symbol { - Symbol(std::string_view n, uint64_t l) : name(n), length(l) {} - - std::string name; - uint64_t length; - }; - using SymbolMap = std::map<uint64_t, Symbol>; - - std::string Decode(const std::string& dso, uint64_t address) override { - auto it = dsos.find(dso); - if (it == dsos.end()) { - LoadDso(dso); - it = dsos.find(dso); - DCHECK(it != dsos.end()); - } - - const SymbolMap& map = it->second; - if (map.empty()) { - return ""; - } - auto upper_bound = map.upper_bound(address); - if (upper_bound == map.begin()) { - // Nope, not in the map. - return ""; - } - - upper_bound--; - if (upper_bound->first + upper_bound->second.length > address) { - // This element covers the given address, return its name. - return upper_bound->second.name; - } - - return ""; - } - - void LoadDso(const std::string& dso) { - // See whether it's an ELF file. - { - SymbolMap elf_data; - auto callback = [&elf_data](const ElfFileSymbol& sym) { - if (sym.is_func) { - if (sym.len == 0) { - LOG(ERROR) << "Symbol size is zero for " << sym.name; - } - elf_data.emplace(sym.vaddr, Symbol(sym.name, sym.len)); - } - }; - ElfStatus status = ParseSymbolsFromElfFile(dso, BuildId(), callback); - if (status == ElfStatus::NO_ERROR) { - dsos.emplace(dso, std::move(elf_data)); - return; - } - } - - // See whether it's a vdex file. - { - ::art::MemMap::Init(); - - using VdexFile = ::art::VdexFile; - std::string error_msg; - std::unique_ptr<VdexFile> vdex = VdexFile::Open(dso, - /* writable= */ false, - /* low_4gb= */ false, - /* unquicken= */ false, - &error_msg); - if (vdex != nullptr) { - const uint8_t* cur = nullptr; - std::vector<uint64_t> dex_file_offsets; - const uint8_t* base = vdex->Begin(); - for (;;) { - cur = vdex->GetNextDexFileData(cur); - if (cur == nullptr) { - break; - } - dex_file_offsets.push_back(cur - base); - } - - if (!dex_file_offsets.empty()) { - std::vector<DexFileSymbol> symbols; - if (ReadSymbolsFromDexFile(dso, dex_file_offsets, &symbols)) { - SymbolMap vdex_data; - for (const DexFileSymbol& symbol : symbols) { - vdex_data.emplace(symbol.offset, Symbol(symbol.name, symbol.len)); - } - dsos.emplace(dso, std::move(vdex_data)); - LOG(INFO) << "Found " << symbols.size() << " dex symbols in " << dso; - return; - } else { - LOG(WARNING) << "Could not read symbols from dex files in " << dso; - } - } else { - LOG(WARNING) << "Could not find dex files for vdex " << dso; - dsos.emplace(dso, SymbolMap()); - } - } else { - LOG(WARNING) << dso << " is not a vdex: " << error_msg; - } - } - - // TODO: See whether it's a dex file. - - // OK, give up. - LOG(WARNING) << "Could not symbolize " << dso; - dsos.emplace(dso, SymbolMap()); - } - - bool GetMinExecutableVAddr(const std::string& dso, uint64_t* addr) override { - uint64_t file_offset_of_min_vaddr; - ElfStatus status = ReadMinExecutableVirtualAddressFromElfFile(dso, BuildId(), addr, - &file_offset_of_min_vaddr); - if (status != ElfStatus::NO_ERROR) { - return true; - } - - { - ::art::MemMap::Init(); - - using VdexFile = ::art::VdexFile; - std::string error_msg; - std::unique_ptr<VdexFile> vdex = VdexFile::Open(dso, - /* writable= */ false, - /* low_4gb= */ false, - /* unquicken= */ false, - &error_msg); - if (vdex != nullptr) { - *addr = 0u; - return true; - } - } - - return false; - } - - std::unordered_map<std::string, SymbolMap> dsos; -}; - -} // namespace - -std::unique_ptr<Symbolizer> CreateELFSymbolizer() { - return std::unique_ptr<Symbolizer>(new SimpleperfSymbolizer()); -} - -} // namespace perfprofd diff --git a/perfprofd/symbolizer.h b/perfprofd/symbolizer.h deleted file mode 100644 index 10771595..00000000 --- a/perfprofd/symbolizer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * Copyright 2018, 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. - */ - -#ifndef SYSTEM_EXTRAS_PERFPROFD_SYMBOLIZER_H_ -#define SYSTEM_EXTRAS_PERFPROFD_SYMBOLIZER_H_ - -#include <memory> - -namespace perfprofd { - -struct Symbolizer { - virtual ~Symbolizer() {} - virtual std::string Decode(const std::string& dso, uint64_t address) = 0; - virtual bool GetMinExecutableVAddr(const std::string& dso, uint64_t* addr) = 0; -}; - -std::unique_ptr<Symbolizer> CreateELFSymbolizer(); - -} // namespace perfprofd - -#endif // SYSTEM_EXTRAS_PERFPROFD_SYMBOLIZER_H_ diff --git a/perfprofd/tests/Android.bp b/perfprofd/tests/Android.bp deleted file mode 100644 index 21649033..00000000 --- a/perfprofd/tests/Android.bp +++ /dev/null @@ -1,73 +0,0 @@ -// Build the unit tests. - -cc_defaults { - name: "perfprofd_test_defaults", - defaults: [ - "libartd_static_defaults", - "perfprofd_defaults", - "perfprofd_debug_defaults", - ], - - strip: { - keep_symbols: true, - }, -} - -// -// Unit test for perfprofd -// -cc_test { - name: "perfprofd_test", - defaults: [ - "perfprofd_test_defaults", - "libsimpleperf_dex_read_static_reqs_defaults", - "libsimpleperf_elf_read_static_reqs_defaults", - ], - test_suites: ["device-tests"], - host_supported: true, - - stl: "libc++", - static_libs: [ - "libperfprofdcored", - "libperfprofd_proto_config", - "libsimpleperf_dex_read", - "libsimpleperf_elf_read", - "libbase", - "libutils", - "libz", - "libprotobuf-cpp-lite", - "liblog", - ], - target: { - host: { - host_ldlibs: [ - "-lncurses", - ], - }, - - // The live tests require simpleperf. - android: { - required: [ - "simpleperf", - ], - static_libs: [ - "libhealthhalutils", - ], - shared_libs: [ - "android.hardware.health@2.0", - "libbinder", - "libhidlbase", - "libservices", - "libutils", - ], - }, - - }, - srcs: [ - "perfprofd_test.cc", - ], - data: [ - "canned.perf.data", - "callchain.canned.perf.data", - ], -} diff --git a/perfprofd/tests/AndroidTest.xml b/perfprofd/tests/AndroidTest.xml deleted file mode 100644 index 20b13731..00000000 --- a/perfprofd/tests/AndroidTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 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. ---> -<configuration description="Config for perfprofd_test"> - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="push" value="perfprofd_test->/data/local/tmp/perfprofd_test" /> - <option name="push" value="canned.perf.data->/data/local/tmp/canned.perf.data" /> - <option name="push" value="callchain.canned.perf.data->/data/local/tmp/callchain.canned.perf.data" /> - - </target_preparer> - <option name="test-suite-tag" value="apct" /> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp" /> - <option name="module-name" value="perfprofd_test" /> - </test> -</configuration> diff --git a/perfprofd/tests/README.txt b/perfprofd/tests/README.txt deleted file mode 100644 index 949e73f0..00000000 --- a/perfprofd/tests/README.txt +++ /dev/null @@ -1,64 +0,0 @@ -Native tests for 'perfprofd'. Please run with - - runtest --path=system/extras/perfprofd/tests - -(where runtest == $ANDROID_BUILD_TOP"/development/testrunner/runtest.py). - -Notes: - -1. Several of the testpoints in this unit tests perform a live 'simpleperf' -run on the device (if you are using a userdebug build, simpleperf should -already be available in /system/xbin/simpleperf). - -2. Part of the test is a system-wide profile, meaning that you will -need to run 'adb root' prior to test execution. - -3. The daemon under test, perfprofd, is broken into a main function, a -"core" library, and a "utils library. Picture: - - +-----------+ perfprofdmain.o - | perfprofd | - | main() | 1-liner; calls perfprofd_main() - +-----------+ - | - v - +-----------+ perfprofdcore.a - | perfprofd | - | core | most of the interesting code is here; - | | calls into utils library when for - +-----------+ operations such as sleep, log, etc - | - v - +-----------+ perfprofdutils.a - | perfprofd | - | utils | real implementations of perfprofd_sleep, - | | perfprofd_log_* etc - +-----------+ - -Because the daemon tends to spend a lot of time sleeping/waiting, -it is impractical to try to test it directly. Instead we insert a -mock utilities layer and then have a test driver that invokes the -daemon main function. Picture for perfprofd_test: - - +----------------+ perfprofd_test.cc - | perfprofd_test | - | | makes calls into perfprofd_main(), - +----------------+ then verifies behavior - | - v - +-----------+ perfprofdcore.a - | perfprofd | - | core | same as above - +-----------+ - | - v - +-----------+ perfprofdmockutils.a - | perfprofd | - | mockutils | mock implementations of perfprofd_sleep, - | | perfprofd_log_* etc - +-----------+ - -The mockup versions of perfprofd_sleep() and perfprofd_log_* do -simply log the fact that they are called; the test driver can -then examine the log to make sure that the daemon is doing -what it is supposed to be doing. diff --git a/perfprofd/tests/callchain.canned.perf.data b/perfprofd/tests/callchain.canned.perf.data Binary files differdeleted file mode 100644 index 8d843935..00000000 --- a/perfprofd/tests/callchain.canned.perf.data +++ /dev/null diff --git a/perfprofd/tests/canned.perf.data b/perfprofd/tests/canned.perf.data Binary files differdeleted file mode 100644 index e6510d2a..00000000 --- a/perfprofd/tests/canned.perf.data +++ /dev/null diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc deleted file mode 100644 index 311ab6a0..00000000 --- a/perfprofd/tests/perfprofd_test.cc +++ /dev/null @@ -1,1743 +0,0 @@ -/* - * Copyright (C) 2015 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 <algorithm> -#include <cctype> -#include <functional> -#include <iterator> -#include <memory> -#include <mutex> -#include <regex> -#include <string> -#include <unordered_set> - -#include <fcntl.h> -#include <stdio.h> -#include <libgen.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/thread_annotations.h> -#include <gtest/gtest.h> -#include <zlib.h> - -#include "config.h" -#include "configreader.h" -#include "map_utils.h" -#include "perfprofdcore.h" -#include "perfprofd_cmdline.h" -#include "perfprofd_perf.h" -#include "perfprofd_threaded_handler.h" -#include "quipper_helper.h" -#include "symbolizer.h" - -#include "perfprofd_record.pb.h" - -using namespace android::perfprofd::quipper; - -static_assert(android::base::kEnableDChecks, "Expected DCHECKs to be enabled"); - -// -// Set to argv[0] on startup -// -static std::string gExecutableRealpath; - -namespace { - -using android::base::LogId; -using android::base::LogSeverity; - -class TestLogHelper { - public: - void Install() { - using namespace std::placeholders; - android::base::SetLogger( - std::bind(&TestLogHelper::TestLogFunction, this, _1, _2, _3, _4, _5, _6)); - } - - std::string JoinTestLog(const char* delimiter) { - std::unique_lock<std::mutex> ul(lock_); - return android::base::Join(test_log_messages_, delimiter); - } - template <typename Predicate> - std::string JoinTestLog(const char* delimiter, Predicate pred) { - std::unique_lock<std::mutex> ul(lock_); - std::vector<std::string> tmp; - std::copy_if(test_log_messages_.begin(), - test_log_messages_.end(), - std::back_inserter(tmp), - pred); - return android::base::Join(tmp, delimiter); - } - - private: - void TestLogFunction(LogId log_id, - LogSeverity severity, - const char* tag, - const char* file, - unsigned int line, - const char* message) { - std::unique_lock<std::mutex> ul(lock_); - constexpr char log_characters[] = "VDIWEFF"; - char severity_char = log_characters[severity]; - test_log_messages_.push_back(android::base::StringPrintf("%c: %s", severity_char, message)); - - if (severity >= LogSeverity::FATAL_WITHOUT_ABORT) { - android::base::StderrLogger(log_id, severity, tag, file, line, message); - } - } - - private: - std::mutex lock_; - - std::vector<std::string> test_log_messages_; -}; - -} // namespace - -// Path to perf executable on device -#define PERFPATH "/system/bin/perf" - -// Temporary config file that we will emit for the daemon to read -#define CONFIGFILE "perfprofd.conf" - -static bool bothWhiteSpace(char lhs, char rhs) -{ - return (std::isspace(lhs) && std::isspace(rhs)); -} - -#ifdef __ANDROID__ - -static bool IsPerfSupported() { - auto check_perf_supported = []() { -#if defined(__i386__) || defined(__x86_64__) - // Cloud devices may suppress perf. Check for arch_perfmon. - std::string cpuinfo; - if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) { - // This is pretty unexpected. Return true to see if we can run tests anyways. - return true; - } - return cpuinfo.find("arch_perfmon") != std::string::npos; -#else - // Expect other architectures to have perf support. - return true; -#endif - }; - static bool perf_supported = check_perf_supported(); - return perf_supported; -} - -#endif - -// -// Squeeze out repeated whitespace from expected/actual logs. -// -static std::string squeezeWhite(const std::string &str, - const char *tag, - bool dump=false) -{ - if (dump) { fprintf(stderr, "raw %s is %s\n", tag, str.c_str()); } - std::string result(str); - std::replace(result.begin(), result.end(), '\n', ' '); - auto new_end = std::unique(result.begin(), result.end(), bothWhiteSpace); - result.erase(new_end, result.end()); - while (result.begin() != result.end() && std::isspace(*result.rbegin())) { - result.pop_back(); - } - if (dump) { fprintf(stderr, "squeezed %s is %s\n", tag, result.c_str()); } - return result; -} - -// -// Replace all occurrences of a string with another string. -// -static std::string replaceAll(const std::string &str, - const std::string &from, - const std::string &to) -{ - std::string ret = ""; - size_t pos = 0; - while (pos < str.size()) { - size_t found = str.find(from, pos); - if (found == std::string::npos) { - ret += str.substr(pos); - break; - } - ret += str.substr(pos, found - pos) + to; - pos = found + from.size(); - } - return ret; -} - -// -// Replace occurrences of special variables in the string. -// -#ifdef __ANDROID__ -static std::string expandVars(const std::string &str) { -#ifdef __LP64__ - return replaceAll(str, "$NATIVE_TESTS", "/data/nativetest64"); -#else - return replaceAll(str, "$NATIVE_TESTS", "/data/nativetest"); -#endif -} -#endif - -class PerfProfdTest : public testing::Test { - protected: - virtual void SetUp() { - test_logger.Install(); - create_dirs(); - } - - virtual void TearDown() { - android::base::SetLogger(android::base::StderrLogger); - - // TODO: proper management of test files. For now, use old system() code. - for (const auto dir : { &dest_dir, &conf_dir }) { - std::string cmd("rm -rf "); - cmd += *dir; - int ret = system(cmd.c_str()); - CHECK_EQ(0, ret); - } - } - - protected: - // - // Check to see if the log messages emitted by the daemon - // match the expected result. By default we use a partial - // match, e.g. if we see the expected excerpt anywhere in the - // result, it's a match (for exact match, set exact to true) - // - ::testing::AssertionResult CompareLogMessages(const std::string& expected, - bool exactMatch = false) { - std::string sqexp = squeezeWhite(expected, "expected"); - - // Strip out JIT errors. - std::regex jit_regex("E: Failed to open ELF file: [^ ]*dalvik-jit-code-cache.*"); - auto strip_jit = [&](const std::string& str) { - std::smatch jit_match; - return !std::regex_match(str, jit_match, jit_regex); - }; - std::string sqact = squeezeWhite(test_logger.JoinTestLog(" ", strip_jit), "actual"); - - if (exactMatch) { - if (sqexp == sqact) { - return ::testing::AssertionSuccess() << sqexp << " is equal to " << sqact; - } - return ::testing::AssertionFailure() << "Expected:" << std::endl << sqexp << std::endl - << "Received:" << std::endl << sqact; - } else { - if (sqact.find(sqexp) == std::string::npos) { - return ::testing::AssertionFailure() - << "Expected to find:" << std::endl << sqexp << std::endl - << "in:" << std::endl << sqact; - } - return ::testing::AssertionSuccess() << sqexp << " was found in " << sqact; - } - } - - // test_dir is the directory containing the test executable and - // any files associated with the test (will be created by the harness). - std::string test_dir; - - // dest_dir is a temporary directory that we're using as the destination directory. - // It is backed by temp_dir1. - std::string dest_dir; - - // conf_dir is a temporary directory that we're using as the configuration directory. - // It is backed by temp_dir2. - std::string conf_dir; - - TestLogHelper test_logger; - - private: - void create_dirs() { - temp_dir1.reset(new TemporaryDir()); - temp_dir2.reset(new TemporaryDir()); - dest_dir = temp_dir1->path; - conf_dir = temp_dir2->path; - test_dir = android::base::Dirname(gExecutableRealpath); - } - - std::unique_ptr<TemporaryDir> temp_dir1; - std::unique_ptr<TemporaryDir> temp_dir2; -}; - -/// -/// Helper class to kick off a run of the perfprofd daemon with a specific -/// config file. -/// -class PerfProfdRunner { - public: - explicit PerfProfdRunner(const std::string& config_dir) - : config_dir_(config_dir) - { - config_path_ = config_dir + "/" CONFIGFILE; - } - - ~PerfProfdRunner() - { - remove_processed_file(); - } - - void addToConfig(const std::string &line) - { - config_text_ += line; - config_text_ += "\n"; - } - - void remove_semaphore_file() - { - std::string semaphore(config_dir_); - semaphore += "/" SEMAPHORE_FILENAME; - unlink(semaphore.c_str()); - } - - void create_semaphore_file() - { - std::string semaphore(config_dir_); - semaphore += "/" SEMAPHORE_FILENAME; - close(open(semaphore.c_str(), O_WRONLY|O_CREAT, 0600)); - } - - void write_processed_file(int start_seq, int end_seq) - { - std::string processed = config_dir_ + "/" PROCESSED_FILENAME; - FILE *fp = fopen(processed.c_str(), "w"); - for (int i = start_seq; i < end_seq; i++) { - fprintf(fp, "%d\n", i); - } - fclose(fp); - } - - void remove_processed_file() - { - std::string processed = config_dir_ + "/" PROCESSED_FILENAME; - unlink(processed.c_str()); - } - - struct LoggingConfig : public Config { - void Sleep(size_t seconds) override { - // Log sleep calls but don't sleep. - LOG(INFO) << "sleep " << seconds << " seconds"; - } - - bool IsProfilingEnabled() const override { - // - // Check for existence of semaphore file in config directory - // - if (access(config_directory.c_str(), F_OK) == -1) { - PLOG(WARNING) << "unable to open config directory " << config_directory; - return false; - } - - // Check for existence of semaphore file - std::string semaphore_filepath = config_directory - + "/" + SEMAPHORE_FILENAME; - if (access(semaphore_filepath.c_str(), F_OK) == -1) { - return false; - } - - return true; - } - }; - - int invoke() - { - static const char *argv[3] = { "perfprofd", "-c", "" }; - argv[2] = config_path_.c_str(); - - writeConfigFile(config_path_, config_text_); - - // execute daemon main - LoggingConfig config; - return perfprofd_main(3, (char **) argv, &config); - } - - private: - std::string config_dir_; - std::string config_path_; - std::string config_text_; - - void writeConfigFile(const std::string &config_path, - const std::string &config_text) - { - FILE *fp = fopen(config_path.c_str(), "w"); - ASSERT_TRUE(fp != nullptr); - fprintf(fp, "%s\n", config_text.c_str()); - fclose(fp); - } -}; - -//...................................................................... - -static std::string encoded_file_path(const std::string& dest_dir, - int seq) { - return android::base::StringPrintf("%s/perf.data.encoded.%d", - dest_dir.c_str(), seq); -} - -static void readEncodedProfile(const std::string& dest_dir, - bool compressed, - android::perfprofd::PerfprofdRecord& encodedProfile) -{ - struct stat statb; - int perf_data_stat_result = stat(encoded_file_path(dest_dir, 0).c_str(), &statb); - ASSERT_NE(-1, perf_data_stat_result); - - // read - std::string encoded; - encoded.resize(statb.st_size); - FILE *ifp = fopen(encoded_file_path(dest_dir, 0).c_str(), "r"); - ASSERT_NE(nullptr, ifp); - size_t items_read = fread((void*) encoded.data(), statb.st_size, 1, ifp); - ASSERT_EQ(1, items_read); - fclose(ifp); - - // uncompress - if (compressed && !encoded.empty()) { - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - - { - constexpr int kWindowBits = 15; - constexpr int kGzipEncoding = 16; - int init_result = inflateInit2(&stream, kWindowBits | kGzipEncoding); - if (init_result != Z_OK) { - LOG(ERROR) << "Could not initialize libz stream " << init_result; - return; - } - } - - std::string buf; - buf.reserve(2 * encoded.size()); - stream.avail_in = encoded.size(); - stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(encoded.data())); - - int result; - do { - uint8_t chunk[1024]; - stream.next_out = static_cast<Bytef*>(chunk); - stream.avail_out = arraysize(chunk); - - result = inflate(&stream, 0); - const size_t amount = arraysize(chunk) - stream.avail_out; - if (amount > 0) { - if (buf.capacity() - buf.size() < amount) { - buf.reserve(buf.capacity() + 64u * 1024u); - CHECK_LE(amount, buf.capacity() - buf.size()); - } - size_t index = buf.size(); - buf.resize(buf.size() + amount); - memcpy(reinterpret_cast<uint8_t*>(const_cast<char*>(buf.data())) + index, chunk, amount); - } - } while (result == Z_OK); - inflateEnd(&stream); - if (result != Z_STREAM_END) { - LOG(ERROR) << "Finished with not-Z_STREAM_END " << result; - return; - } - encoded = buf; - } - - // decode - encodedProfile.ParseFromString(encoded); -} - -#define RAW_RESULT(x) #x - -TEST_F(PerfProfdTest, TestUtil) -{ - EXPECT_EQ("", replaceAll("", "", "")); - EXPECT_EQ("zzbc", replaceAll("abc", "a", "zz")); - EXPECT_EQ("azzc", replaceAll("abc", "b", "zz")); - EXPECT_EQ("abzz", replaceAll("abc", "c", "zz")); - EXPECT_EQ("xxyyzz", replaceAll("abc", "abc", "xxyyzz")); -} - -TEST_F(PerfProfdTest, MissingGMS) -{ - // - // AWP requires cooperation between the daemon and the GMS core - // piece. If we're running on a device that has an old or damaged - // version of GMS core, then the config directory we're interested in - // may not be there. This test insures that the daemon does the - // right thing in this case. - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - runner.addToConfig("trace_config_read=0"); - runner.addToConfig("config_directory=/does/not/exist"); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=1"); - runner.addToConfig("collection_interval=100"); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - EXPECT_EQ(0, daemon_main_return_code); - - // Verify log contents - const std::string expected = RAW_RESULT( - I: sleep 90 seconds - W: unable to open config directory /does/not/exist: No such file or directory - I: profile collection skipped (missing config directory) - ); - - // check to make sure entire log matches - EXPECT_TRUE(CompareLogMessages(expected)); -} - - -TEST_F(PerfProfdTest, MissingOptInSemaphoreFile) -{ - // - // Android device owners must opt in to "collect and report usage - // data" in order for us to be able to collect profiles. The opt-in - // check is performed in the GMS core component; if the check - // passes, then it creates a semaphore file for the daemon to pick - // up on. - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=1"); - runner.addToConfig("collection_interval=100"); - - runner.remove_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - EXPECT_EQ(0, daemon_main_return_code); - - // Verify log contents - const std::string expected = RAW_RESULT( - I: profile collection skipped (missing config directory) - ); - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expected)); -} - -TEST_F(PerfProfdTest, MissingPerfExecutable) -{ - // - // Perfprofd uses the 'simpleperf' tool to collect profiles - // (although this may conceivably change in the future). This test - // checks to make sure that if 'simpleperf' is not present we bail out - // from collecting profiles. - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - runner.addToConfig("trace_config_read=1"); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=1"); - runner.addToConfig("collection_interval=100"); - runner.addToConfig("perf_path=/does/not/exist"); - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - EXPECT_EQ(0, daemon_main_return_code); - - // expected log contents - const std::string expected = RAW_RESULT( - I: profile collection skipped (missing 'perf' executable) - ); - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expected)); -} - -TEST_F(PerfProfdTest, BadPerfRun) -{ - // - // Perf tools tend to be tightly coupled with a specific kernel - // version -- if things are out of sync perf could fail or - // crash. This test makes sure that we detect such a case and log - // the error. - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=1"); - runner.addToConfig("collection_interval=100"); -#ifdef __ANDROID__ - runner.addToConfig("perf_path=/system/bin/false"); -#else - runner.addToConfig("perf_path=/bin/false"); -#endif - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - EXPECT_EQ(0, daemon_main_return_code); - - // Verify log contents. Because of perferr logging containing pids and test paths, - // it is easier to have three expected parts. - const std::string expected1 = "W: perf bad exit status 1"; - const std::string expected2 = "bin/false record"; - const std::string expected3 = "W: profile collection failed"; - - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expected1)); - EXPECT_TRUE(CompareLogMessages(expected2)); - EXPECT_TRUE(CompareLogMessages(expected3)); -} - -TEST_F(PerfProfdTest, ConfigFileParsing) -{ - // - // Gracefully handly malformed items in the config file - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("collection_interval=100"); - runner.addToConfig("use_fixed_seed=1"); - runner.addToConfig("destination_directory=/does/not/exist"); - - // assorted bad syntax - runner.addToConfig("collection_interval=-1"); - runner.addToConfig("collection_interval=18446744073709551615"); - runner.addToConfig("nonexistent_key=something"); - runner.addToConfig("no_equals_stmt"); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - EXPECT_EQ(0, daemon_main_return_code); - - // Verify log contents - const std::string expected = RAW_RESULT( - W: line 6: value -1 cannot be parsed - W: line 7: specified value 18446744073709551615 for 'collection_interval' outside permitted range [0 4294967295] - W: line 8: unknown option 'nonexistent_key' - W: line 9: line malformed (no '=' found) - ); - - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expected)); -} - -TEST_F(PerfProfdTest, ConfigFileParsing_Events) { - auto check_event_config = [](const Config& config, - size_t index, - const std::vector<std::string>& names, - bool group, - uint32_t period) { - if (config.event_config.size() <= index) { - return ::testing::AssertionFailure() << "Not enough entries " << config.event_config.size() - << " " << index; - } - const auto& elem = config.event_config[index]; - - if (elem.group != group) { - return ::testing::AssertionFailure() << "Type wrong " << elem.group << " " << group; - } - - if (elem.sampling_period != period) { - return ::testing::AssertionFailure() << "Period wrong " << elem.sampling_period << " " - << period; - } - - auto strvec = [](const std::vector<std::string>& v) { - return "[" + android::base::Join(v, ',') + "]"; - }; - if (elem.events.size() != names.size()) { - return ::testing::AssertionFailure() << "Names wrong " << strvec(elem.events) << " " - << strvec(names); - } - for (size_t i = 0; i != elem.events.size(); ++i) { - if (elem.events[i] != names[i]) { - return ::testing::AssertionFailure() << "Names wrong at " << i << ": " - << strvec(elem.events) << " " - << strvec(names); - } - } - return ::testing::AssertionSuccess(); - }; - - { - std::string data = "-e_hello,world=1\n" - "-g_foo,bar=2\n" - "-e_abc,xyz=3\n" - "-g_ftrace:test,ftrace:test2=4"; - - ConfigReader reader; - std::string error_msg; - ASSERT_TRUE(reader.Read(data, true, &error_msg)) << error_msg; - - PerfProfdRunner::LoggingConfig config; - reader.FillConfig(&config); - - EXPECT_TRUE(check_event_config(config, 0, { "hello", "world" }, false, 1)); - EXPECT_TRUE(check_event_config(config, 1, { "foo", "bar" }, true, 2)); - EXPECT_TRUE(check_event_config(config, 2, { "abc", "xyz" }, false, 3)); - EXPECT_TRUE(check_event_config(config, 3, { "ftrace:test", "ftrace:test2" }, true, 4)); - } - - { - std::string data = "-e_hello,world=dummy"; - - ConfigReader reader; - std::string error_msg; - EXPECT_FALSE(reader.Read(data, true, &error_msg)); - } - - { - std::string data = "-g_hello,world=dummy"; - - ConfigReader reader; - std::string error_msg; - EXPECT_FALSE(reader.Read(data, true, &error_msg)); - } -} - - -TEST_F(PerfProfdTest, ConfigDump) { - constexpr const char* kConfigElems[] = { - "collection_interval=14400", - "use_fixed_seed=1", - "main_loop_iterations=2", - "destination_directory=/does/not/exist", - "config_directory=a", - "perf_path=/system/xbin/simpleperf2", - "sampling_period=3", - "sampling_frequency=4", - "sample_duration=5", - "only_debug_build=1", - "hardwire_cpus=1", - "hardwire_cpus_max_duration=6", - "max_unprocessed_profiles=7", - "stack_profile=1", - "trace_config_read=1", - "collect_cpu_utilization=1", - "collect_charging_state=1", - "collect_booting=1", - "collect_camera_active=1", - "process=8", - "use_elf_symbolizer=1", - "symbolize_everything=1", - "compress=1", - "dropbox=1", - "fail_on_unsupported_events=1", - "-e_hello,world=1", - "-g_foo,bar=2", - "-e_abc,xyz=3", - "-g_ftrace:test,ftrace:test2=4", - }; - - std::string input; - for (const char* elem : kConfigElems) { - input.append(elem); - input.append("\n"); - } - - ConfigReader reader; - std::string error_msg; - ASSERT_TRUE(reader.Read(input, false, &error_msg)) << error_msg; - - PerfProfdRunner::LoggingConfig config; - reader.FillConfig(&config); - - std::string output = ConfigReader::ConfigToString(config); - for (const char* elem : kConfigElems) { - EXPECT_TRUE(output.find(elem) != std::string::npos) << elem << " not in " << output; - } -} - -TEST_F(PerfProfdTest, ProfileCollectionAnnotations) -{ - unsigned util1 = collect_cpu_utilization(); - EXPECT_LE(util1, 100); - EXPECT_GE(util1, 0); - - // NB: expectation is that when we run this test, the device will be - // completed booted, will be on charger, and will not have the camera - // active. - EXPECT_FALSE(get_booting()); -#ifdef __ANDROID__ - EXPECT_TRUE(get_charging()); -#endif - EXPECT_FALSE(get_camera_active()); -} - -namespace { - -template <typename Iterator> -size_t CountEvents(const quipper::PerfDataProto& proto) { - size_t count = 0; - for (Iterator it(proto); it != it.end(); ++it) { - count++; - } - return count; -} - -size_t CountCommEvents(const quipper::PerfDataProto& proto) { - return CountEvents<CommEventIterator>(proto); -} -size_t CountMmapEvents(const quipper::PerfDataProto& proto) { - return CountEvents<MmapEventIterator>(proto); -} -size_t CountSampleEvents(const quipper::PerfDataProto& proto) { - return CountEvents<SampleEventIterator>(proto); -} -size_t CountForkEvents(const quipper::PerfDataProto& proto) { - return CountEvents<ForkEventIterator>(proto); -} -size_t CountExitEvents(const quipper::PerfDataProto& proto) { - return CountEvents<ExitEventIterator>(proto); -} - -std::string CreateStats(const quipper::PerfDataProto& proto) { - std::ostringstream oss; - oss << "Mmap events: " << CountMmapEvents(proto) << std::endl; - oss << "Sample events: " << CountSampleEvents(proto) << std::endl; - oss << "Comm events: " << CountCommEvents(proto) << std::endl; - oss << "Fork events: " << CountForkEvents(proto) << std::endl; - oss << "Exit events: " << CountExitEvents(proto) << std::endl; - return oss.str(); -} - -std::string FormatSampleEvent(const quipper::PerfDataProto_SampleEvent& sample) { - std::ostringstream oss; - if (sample.has_pid()) { - oss << "pid=" << sample.pid(); - } - if (sample.has_tid()) { - oss << " tid=" << sample.tid(); - } - if (sample.has_ip()) { - oss << " ip=" << sample.ip(); - } - if (sample.has_addr()) { - oss << " addr=" << sample.addr(); - } - if (sample.callchain_size() > 0) { - oss << " callchain="; - for (uint64_t cc : sample.callchain()) { - oss << "->" << cc; - } - } - return oss.str(); -} - -} - -struct BasicRunWithCannedPerf : PerfProfdTest { - void VerifyBasicCannedProfile(const android::perfprofd::PerfprofdRecord& encodedProfile) { - const quipper::PerfDataProto& perf_data = encodedProfile; - - // Expect 21108 events. - EXPECT_EQ(21108, perf_data.events_size()) << CreateStats(perf_data); - - EXPECT_EQ(48, CountMmapEvents(perf_data)) << CreateStats(perf_data); - EXPECT_EQ(19986, CountSampleEvents(perf_data)) << CreateStats(perf_data); - EXPECT_EQ(1033, CountCommEvents(perf_data)) << CreateStats(perf_data); - EXPECT_EQ(15, CountForkEvents(perf_data)) << CreateStats(perf_data); - EXPECT_EQ(26, CountExitEvents(perf_data)) << CreateStats(perf_data); - - if (HasNonfatalFailure()) { - FAIL(); - } - - { - MmapEventIterator mmap(perf_data); - constexpr std::pair<const char*, uint64_t> kMmapEvents[] = { - std::make_pair("[kernel.kallsyms]_text", 0), - std::make_pair("/system/lib/libc.so", 3067412480u), - std::make_pair("/system/vendor/lib/libdsutils.so", 3069911040u), - std::make_pair("/system/lib/libc.so", 3067191296u), - std::make_pair("/system/lib/libc++.so", 3069210624u), - std::make_pair("/data/dalvik-cache/arm/system@framework@boot.oat", 1900048384u), - std::make_pair("/system/lib/libjavacore.so", 2957135872u), - std::make_pair("/system/vendor/lib/libqmi_encdec.so", 3006644224u), - std::make_pair("/data/dalvik-cache/arm/system@framework@wifi-service.jar@classes.dex", - 3010351104u), - std::make_pair("/system/lib/libart.so", 3024150528u), - std::make_pair("/system/lib/libz.so", 3056410624u), - std::make_pair("/system/lib/libicui18n.so", 3057610752u), - }; - for (auto& pair : kMmapEvents) { - EXPECT_STREQ(pair.first, mmap->mmap_event().filename().c_str()); - EXPECT_EQ(pair.second, mmap->mmap_event().start()) << pair.first; - ++mmap; - } - } - - { - CommEventIterator comm(perf_data); - constexpr const char* kCommEvents[] = { - "init", "kthreadd", "ksoftirqd/0", "kworker/u:0H", "migration/0", "khelper", - "netns", "modem_notifier", "smd_channel_clo", "smsm_cb_wq", "rpm-smd", "kworker/u:1H", - }; - for (auto str : kCommEvents) { - EXPECT_STREQ(str, comm->comm_event().comm().c_str()); - ++comm; - } - } - - { - SampleEventIterator samples(perf_data); - constexpr const char* kSampleEvents[] = { - "pid=0 tid=0 ip=3222720196", - "pid=0 tid=0 ip=3222910876", - "pid=0 tid=0 ip=3222910876", - "pid=0 tid=0 ip=3222910876", - "pid=0 tid=0 ip=3222910876", - "pid=0 tid=0 ip=3222910876", - "pid=0 tid=0 ip=3222910876", - "pid=3 tid=3 ip=3231975108", - "pid=5926 tid=5926 ip=3231964952", - "pid=5926 tid=5926 ip=3225342428", - "pid=5926 tid=5926 ip=3223841448", - "pid=5926 tid=5926 ip=3069807920", - }; - for (auto str : kSampleEvents) { - EXPECT_STREQ(str, FormatSampleEvent(samples->sample_event()).c_str()); - ++samples; - } - - // Skip some samples. - for (size_t i = 0; i != 5000; ++i) { - ++samples; - } - constexpr const char* kSampleEvents2[] = { - "pid=5938 tid=5938 ip=3069630992", - "pid=5938 tid=5938 ip=3069626616", - "pid=5938 tid=5938 ip=3069626636", - "pid=5938 tid=5938 ip=3069637212", - "pid=5938 tid=5938 ip=3069637208", - "pid=5938 tid=5938 ip=3069637252", - "pid=5938 tid=5938 ip=3069346040", - "pid=5938 tid=5938 ip=3069637128", - "pid=5938 tid=5938 ip=3069626616", - }; - for (auto str : kSampleEvents2) { - EXPECT_STREQ(str, FormatSampleEvent(samples->sample_event()).c_str()); - ++samples; - } - - // Skip some samples. - for (size_t i = 0; i != 5000; ++i) { - ++samples; - } - constexpr const char* kSampleEvents3[] = { - "pid=5938 tid=5938 ip=3069912036", - "pid=5938 tid=5938 ip=3069637260", - "pid=5938 tid=5938 ip=3069631024", - "pid=5938 tid=5938 ip=3069346064", - "pid=5938 tid=5938 ip=3069637356", - "pid=5938 tid=5938 ip=3069637144", - "pid=5938 tid=5938 ip=3069912036", - "pid=5938 tid=5938 ip=3069912036", - "pid=5938 tid=5938 ip=3069631244", - }; - for (auto str : kSampleEvents3) { - EXPECT_STREQ(str, FormatSampleEvent(samples->sample_event()).c_str()); - ++samples; - } - } - } -}; - -TEST_F(BasicRunWithCannedPerf, Basic) -{ - // - // Verify the portion of the daemon that reads and encodes - // perf.data files. Here we run the encoder on a canned perf.data - // file and verify that the resulting protobuf contains what - // we think it should contain. - // - std::string input_perf_data(test_dir); - input_perf_data += "/canned.perf.data"; - - // Set up config to avoid these annotations (they are tested elsewhere) - ConfigReader config_reader; - config_reader.overrideUnsignedEntry("collect_cpu_utilization", 0); - config_reader.overrideUnsignedEntry("collect_charging_state", 0); - config_reader.overrideUnsignedEntry("collect_camera_active", 0); - - // Disable compression. - config_reader.overrideUnsignedEntry("compress", 0); - - PerfProfdRunner::LoggingConfig config; - config_reader.FillConfig(&config); - - // Kick off encoder and check return code - PROFILE_RESULT result = - encode_to_proto(input_perf_data, encoded_file_path(dest_dir, 0).c_str(), config, 0, nullptr); - ASSERT_EQ(OK_PROFILE_COLLECTION, result) << test_logger.JoinTestLog(" "); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - VerifyBasicCannedProfile(encodedProfile); -} - -TEST_F(BasicRunWithCannedPerf, Compressed) -{ - // - // Verify the portion of the daemon that reads and encodes - // perf.data files. Here we run the encoder on a canned perf.data - // file and verify that the resulting protobuf contains what - // we think it should contain. - // - std::string input_perf_data(test_dir); - input_perf_data += "/canned.perf.data"; - - // Set up config to avoid these annotations (they are tested elsewhere) - ConfigReader config_reader; - config_reader.overrideUnsignedEntry("collect_cpu_utilization", 0); - config_reader.overrideUnsignedEntry("collect_charging_state", 0); - config_reader.overrideUnsignedEntry("collect_camera_active", 0); - - // Enable compression. - config_reader.overrideUnsignedEntry("compress", 1); - - PerfProfdRunner::LoggingConfig config; - config_reader.FillConfig(&config); - - // Kick off encoder and check return code - PROFILE_RESULT result = - encode_to_proto(input_perf_data, encoded_file_path(dest_dir, 0).c_str(), config, 0, nullptr); - ASSERT_EQ(OK_PROFILE_COLLECTION, result) << test_logger.JoinTestLog(" "); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, true, encodedProfile); - - VerifyBasicCannedProfile(encodedProfile); -} - -class BasicRunWithCannedPerfWithSymbolizer : public BasicRunWithCannedPerf { - protected: - std::vector<::testing::AssertionResult> Run(bool symbolize_everything, size_t expected_count) { - // - // Verify the portion of the daemon that reads and encodes - // perf.data files. Here we run the encoder on a canned perf.data - // file and verify that the resulting protobuf contains what - // we think it should contain. - // - std::string input_perf_data(test_dir); - input_perf_data += "/canned.perf.data"; - - // Set up config to avoid these annotations (they are tested elsewhere) - ConfigReader config_reader; - config_reader.overrideUnsignedEntry("collect_cpu_utilization", 0); - config_reader.overrideUnsignedEntry("collect_charging_state", 0); - config_reader.overrideUnsignedEntry("collect_camera_active", 0); - - // Disable compression. - config_reader.overrideUnsignedEntry("compress", 0); - - if (symbolize_everything) { - config_reader.overrideUnsignedEntry("symbolize_everything", 1); - } - - PerfProfdRunner::LoggingConfig config; - config_reader.FillConfig(&config); - - // Kick off encoder and check return code - struct TestSymbolizer : public perfprofd::Symbolizer { - std::string Decode(const std::string& dso, uint64_t address) override { - return dso + "@" + std::to_string(address); - } - bool GetMinExecutableVAddr(const std::string& dso, uint64_t* addr) override { - *addr = 4096; - return true; - } - }; - TestSymbolizer test_symbolizer; - PROFILE_RESULT result = - encode_to_proto(input_perf_data, - encoded_file_path(dest_dir, 0).c_str(), - config, - 0, - &test_symbolizer); - if (result != OK_PROFILE_COLLECTION) { - return { ::testing::AssertionFailure() << "Profile collection failed: " << result }; - } - - std::vector<::testing::AssertionResult> ret; - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - VerifyBasicCannedProfile(encodedProfile); - - auto find_symbol = [&](const std::string& filename) -> const quipper::SymbolInfo* { - const size_t size = encodedProfile.ExtensionSize(quipper::symbol_info); - for (size_t i = 0; i != size; ++i) { - auto& symbol_info = encodedProfile.GetExtension(quipper::symbol_info, i); - if (symbol_info.filename() == filename) { - return &symbol_info; - } - } - return nullptr; - }; - auto all_filenames = [&]() { - std::ostringstream oss; - const size_t size = encodedProfile.ExtensionSize(quipper::symbol_info); - for (size_t i = 0; i != size; ++i) { - auto& symbol_info = encodedProfile.GetExtension(quipper::symbol_info, i); - oss << " " << symbol_info.filename(); - } - return oss.str(); - }; - - auto check_dsos = [&](const char* const* dsos, const size_t len) { - bool failed = false; - for (size_t i = 0; i != len; ++i) { - if (find_symbol(dsos[i]) == nullptr) { - failed = true; - ret.push_back(::testing::AssertionFailure() << "Did not find " << dsos[i]); - } - } - return failed; - }; - - bool failed = false; - - constexpr const char* kDSOs[] = { - "/data/app/com.google.android.apps.plus-1/lib/arm/libcronet.so", - "/data/dalvik-cache/arm/system@framework@wifi-service.jar@classes.dex", - "/data/dalvik-cache/arm/data@app@com.google.android.gms-2@base.apk@classes.dex", - "/data/dalvik-cache/arm/system@framework@boot.oat", - }; - failed |= check_dsos(kDSOs, arraysize(kDSOs)); - - if (symbolize_everything) { - constexpr const char* kDSOsWithBuildIDs[] = { - "/system/lib/libz.so", "/system/lib/libutils.so", - }; - failed |= check_dsos(kDSOsWithBuildIDs, arraysize(kDSOsWithBuildIDs)); - } - - if (failed) { - ret.push_back(::testing::AssertionFailure() << "Found: " << all_filenames()); - } - - if (encodedProfile.ExtensionSize(quipper::symbol_info) != expected_count) { - ret.push_back( - ::testing::AssertionFailure() << "Expected " << expected_count - << " symbolized libraries, found " - << encodedProfile.ExtensionSize(quipper::symbol_info)); - } - - return ret; - } -}; - -TEST_F(BasicRunWithCannedPerfWithSymbolizer, Default) { - auto result = Run(false, 5); - for (const auto& result_component : result) { - EXPECT_TRUE(result_component); - } -} - -TEST_F(BasicRunWithCannedPerfWithSymbolizer, Everything) { - auto result = Run(true, 26); - for (const auto& result_component : result) { - EXPECT_TRUE(result_component); - } -} - -TEST_F(PerfProfdTest, CallchainRunWithCannedPerf) -{ - // This test makes sure that the perf.data converter - // can handle call chains. - // - std::string input_perf_data(test_dir); - input_perf_data += "/callchain.canned.perf.data"; - - // Set up config to avoid these annotations (they are tested elsewhere) - ConfigReader config_reader; - config_reader.overrideUnsignedEntry("collect_cpu_utilization", 0); - config_reader.overrideUnsignedEntry("collect_charging_state", 0); - config_reader.overrideUnsignedEntry("collect_camera_active", 0); - - // Disable compression. - config_reader.overrideUnsignedEntry("compress", 0); - - PerfProfdRunner::LoggingConfig config; - config_reader.FillConfig(&config); - - // Kick off encoder and check return code - PROFILE_RESULT result = - encode_to_proto(input_perf_data, encoded_file_path(dest_dir, 0).c_str(), config, 0, nullptr); - ASSERT_EQ(OK_PROFILE_COLLECTION, result); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - const quipper::PerfDataProto& perf_data = encodedProfile; - - // Expect 21108 events. - EXPECT_EQ(2224, perf_data.events_size()) << CreateStats(perf_data); - - { - SampleEventIterator samples(perf_data); - constexpr const char* kSampleEvents[] = { - "0: pid=6225 tid=6225 ip=18446743798834668032 callchain=->18446744073709551488->" - "18446743798834668032->18446743798834782596->18446743798834784624->" - "18446743798835055136->18446743798834788016->18446743798834789192->" - "18446743798834789512->18446743798834790216->18446743798833756776", - "1: pid=6225 tid=6225 ip=18446743798835685700 callchain=->18446744073709551488->" - "18446743798835685700->18446743798835688704->18446743798835650964->" - "18446743798834612104->18446743798834612276->18446743798835055528->" - "18446743798834788016->18446743798834789192->18446743798834789512->" - "18446743798834790216->18446743798833756776", - "2: pid=6225 tid=6225 ip=18446743798835055804 callchain=->18446744073709551488->" - "18446743798835055804->18446743798834788016->18446743798834789192->" - "18446743798834789512->18446743798834790216->18446743798833756776", - "3: pid=6225 tid=6225 ip=18446743798835991212 callchain=->18446744073709551488->" - "18446743798835991212->18446743798834491060->18446743798834675572->" - "18446743798834676516->18446743798834612172->18446743798834612276->" - "18446743798835056664->18446743798834788016->18446743798834789192->" - "18446743798834789512->18446743798834790216->18446743798833756776", - "4: pid=6225 tid=6225 ip=18446743798844881108 callchain=->18446744073709551488->" - "18446743798844881108->18446743798834836140->18446743798834846384->" - "18446743798834491100->18446743798834675572->18446743798834676516->" - "18446743798834612172->18446743798834612276->18446743798835056784->" - "18446743798834788016->18446743798834789192->18446743798834789512->" - "18446743798834790216->18446743798833756776", - }; - size_t cmp_index = 0; - for (size_t index = 0; samples != samples.end(); ++samples, ++index) { - if (samples->sample_event().callchain_size() > 0) { - std::ostringstream oss; - oss << index << ": " << FormatSampleEvent(samples->sample_event()); - EXPECT_STREQ(kSampleEvents[cmp_index], oss.str().c_str()); - cmp_index++; - if (cmp_index == arraysize(kSampleEvents)) { - break; - } - } - } - } -} - -#ifdef __ANDROID__ - -TEST_F(PerfProfdTest, GetSupportedPerfCounters) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - // Check basic perf counters. - { - struct DummyConfig : public Config { - void Sleep(size_t seconds) override {} - bool IsProfilingEnabled() const override { return false; } - }; - DummyConfig config; - ASSERT_TRUE(android::perfprofd::FindSupportedPerfCounters(config.perf_path)); - } - const std::unordered_set<std::string>& counters = android::perfprofd::GetSupportedPerfCounters(); - EXPECT_TRUE(std::find(counters.begin(), counters.end(), std::string("cpu-cycles")) - != counters.end()) << android::base::Join(counters, ','); - EXPECT_TRUE(std::find(counters.begin(), counters.end(), std::string("page-faults")) - != counters.end()) << android::base::Join(counters, ','); -} - -TEST_F(PerfProfdTest, BasicRunWithLivePerf) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - // - // Basic test to exercise the main loop of the daemon. It includes - // a live 'perf' run - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=12345678"); - runner.addToConfig("max_unprocessed_profiles=100"); - runner.addToConfig("collection_interval=9999"); - runner.addToConfig("sample_duration=2"); - // Avoid the symbolizer for spurious messages. - runner.addToConfig("use_elf_symbolizer=0"); - - // Disable compression. - runner.addToConfig("compress=0"); - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - ASSERT_EQ(0, daemon_main_return_code); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - // Examine what we get back. Since it's a live profile, we can't - // really do much in terms of verifying the contents. - EXPECT_LT(0, encodedProfile.events_size()); - - // Verify log contents - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: finishing Android Wide Profiling daemon - ); - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expandVars(expected), true)); -} - -class PerfProfdLiveEventsTest : public PerfProfdTest { - protected: - ::testing::AssertionResult SetupAndInvoke( - const std::string& event_config, - const std::vector<std::string>& extra_config, - bool expect_success, - std::string expected_log, - bool log_match_exact) { - // - // Basic test to check that the event set functionality works. - // - // Note: this is brittle, as we do not really know which events the hardware - // supports. Use "cpu-cycles" and "page-faults" as something likely. - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=12345678"); - runner.addToConfig("max_unprocessed_profiles=100"); - runner.addToConfig("collection_interval=9999"); - runner.addToConfig("sample_duration=2"); - // Avoid the symbolizer for spurious messages. - runner.addToConfig("use_elf_symbolizer=0"); - - // Disable compression. - runner.addToConfig("compress=0"); - - // Set event set. - runner.addToConfig(event_config); - - for (const std::string& str : extra_config) { - runner.addToConfig(str); - } - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - if (0 != daemon_main_return_code) { - return ::testing::AssertionFailure() << "Daemon exited with " << daemon_main_return_code; - } - - if (expect_success) { - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - // Examine what we get back. Since it's a live profile, we can't - // really do much in terms of verifying the contents. - if (0 == encodedProfile.events_size()) { - return ::testing::AssertionFailure() << "Empty encoded profile."; - } - } - - // Verify log contents - return CompareLogMessages(expandVars(expected_log), log_match_exact); - } -}; - -TEST_F(PerfProfdLiveEventsTest, BasicRunWithLivePerf_Events) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: finishing Android Wide Profiling daemon - ); - ASSERT_TRUE(SetupAndInvoke("-e_cpu-cycles,page-faults=100000", {}, true, expected, true)); -} - -TEST_F(PerfProfdLiveEventsTest, BasicRunWithLivePerf_Events_Strip) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - W: Event does:not:exist is unsupported. - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: finishing Android Wide Profiling daemon - ); - ASSERT_TRUE(SetupAndInvoke("-e_cpu-cycles,page-faults,does:not:exist=100000", - { "fail_on_unsupported_events=0" }, - true, - expected, - true)); -} - -TEST_F(PerfProfdLiveEventsTest, BasicRunWithLivePerf_Events_NoStrip) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - const std::string expected = - RAW_RESULT( - W: Event does:not:exist is unsupported. - W: profile collection failed - ); - ASSERT_TRUE(SetupAndInvoke("-e_cpu-cycles,page-faults,does:not:exist=100000", - { "fail_on_unsupported_events=1" }, - false, - expected, - false)); -} - -TEST_F(PerfProfdLiveEventsTest, BasicRunWithLivePerf_EventsGroup) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: finishing Android Wide Profiling daemon - ); - ASSERT_TRUE(SetupAndInvoke("-g_cpu-cycles,page-faults=100000", {}, true, expected, true)); -} - -TEST_F(PerfProfdTest, MultipleRunWithLivePerf) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - // - // Basic test to exercise the main loop of the daemon. It includes - // a live 'perf' run - // - PerfProfdRunner runner(conf_dir); - runner.addToConfig("only_debug_build=0"); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - runner.addToConfig("main_loop_iterations=3"); - runner.addToConfig("use_fixed_seed=12345678"); - runner.addToConfig("collection_interval=9999"); - runner.addToConfig("sample_duration=2"); - // Avoid the symbolizer for spurious messages. - runner.addToConfig("use_elf_symbolizer=0"); - - // Disable compression. - runner.addToConfig("compress=0"); - - runner.write_processed_file(1, 2); - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - ASSERT_EQ(0, daemon_main_return_code); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - // Examine what we get back. Since it's a live profile, we can't - // really do much in terms of verifying the contents. - EXPECT_LT(0, encodedProfile.events_size()); - - // Examine that encoded.1 file is removed while encoded.{0|2} exists. - EXPECT_EQ(0, access(encoded_file_path(dest_dir, 0).c_str(), F_OK)); - EXPECT_NE(0, access(encoded_file_path(dest_dir, 1).c_str(), F_OK)); - EXPECT_EQ(0, access(encoded_file_path(dest_dir, 2).c_str(), F_OK)); - - // Verify log contents - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: sleep 4974 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 5025 seconds - I: sleep 501 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9498 seconds - I: finishing Android Wide Profiling daemon - ); - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expandVars(expected), true)); -} - -TEST_F(PerfProfdTest, CallChainRunWithLivePerf) -{ - if (!IsPerfSupported()) { - std::cerr << "Test not supported!" << std::endl; - return; - } - // - // Collect a callchain profile, so as to exercise the code in - // perf_data post-processing that digests callchains. - // - PerfProfdRunner runner(conf_dir); - std::string ddparam("destination_directory="); ddparam += dest_dir; - runner.addToConfig(ddparam); - std::string cfparam("config_directory="); cfparam += conf_dir; - runner.addToConfig(cfparam); - runner.addToConfig("main_loop_iterations=1"); - runner.addToConfig("use_fixed_seed=12345678"); - runner.addToConfig("max_unprocessed_profiles=100"); - runner.addToConfig("collection_interval=9999"); - runner.addToConfig("stack_profile=1"); - runner.addToConfig("sample_duration=2"); - // Avoid the symbolizer for spurious messages. - runner.addToConfig("use_elf_symbolizer=0"); - - // Disable compression. - runner.addToConfig("compress=0"); - - // Create semaphore file - runner.create_semaphore_file(); - - // Kick off daemon - int daemon_main_return_code = runner.invoke(); - - // Check return code from daemon - ASSERT_EQ(0, daemon_main_return_code); - - // Read and decode the resulting perf.data.encoded file - android::perfprofd::PerfprofdRecord encodedProfile; - readEncodedProfile(dest_dir, false, encodedProfile); - - // Examine what we get back. Since it's a live profile, we can't - // really do much in terms of verifying the contents. - EXPECT_LT(0, encodedProfile.events_size()); - - // Verify log contents - const std::string expected = std::string( - "I: starting Android Wide Profiling daemon ") + - "I: config file path set to " + conf_dir + "/perfprofd.conf " + - RAW_RESULT( - I: random seed set to 12345678 - I: sleep 674 seconds - I: initiating profile collection - I: sleep 2 seconds - I: profile collection complete - I: sleep 9325 seconds - I: finishing Android Wide Profiling daemon - ); - // check to make sure log excerpt matches - EXPECT_TRUE(CompareLogMessages(expandVars(expected), true)); - - // Check that we have at least one SampleEvent with a callchain. - SampleEventIterator samples(encodedProfile); - bool found_callchain = false; - while (!found_callchain && samples != samples.end()) { - found_callchain = samples->sample_event().callchain_size() > 0; - } - EXPECT_TRUE(found_callchain) << CreateStats(encodedProfile); -} - -#endif - -class RangeMapTest : public testing::Test { -}; - -TEST_F(RangeMapTest, TestRangeMap) { - using namespace android::perfprofd; - - RangeMap<std::string, uint64_t> map; - auto print = [&]() { - std::ostringstream oss; - for (auto& aggr_sym : map) { - oss << aggr_sym.first << "#" << aggr_sym.second.symbol; - oss << "["; - for (auto& x : aggr_sym.second.offsets) { - oss << x << ","; - } - oss << "]"; - } - return oss.str(); - }; - - EXPECT_STREQ("", print().c_str()); - - map.Insert("a", 10); - EXPECT_STREQ("10#a[10,]", print().c_str()); - map.Insert("a", 100); - EXPECT_STREQ("10#a[10,100,]", print().c_str()); - map.Insert("a", 1); - EXPECT_STREQ("1#a[1,10,100,]", print().c_str()); - map.Insert("a", 1); - EXPECT_STREQ("1#a[1,10,100,]", print().c_str()); - map.Insert("a", 2); - EXPECT_STREQ("1#a[1,2,10,100,]", print().c_str()); - - map.Insert("b", 200); - EXPECT_STREQ("1#a[1,2,10,100,]200#b[200,]", print().c_str()); - map.Insert("b", 199); - EXPECT_STREQ("1#a[1,2,10,100,]199#b[199,200,]", print().c_str()); - - map.Insert("c", 50); - EXPECT_STREQ("1#a[1,2,10,]50#c[50,]100#a[100,]199#b[199,200,]", print().c_str()); -} - -class ThreadedHandlerTest : public PerfProfdTest { - public: - void SetUp() override { - PerfProfdTest::SetUp(); - threaded_handler_.reset(new android::perfprofd::ThreadedHandler()); - } - - void TearDown() override { - threaded_handler_.reset(); - PerfProfdTest::TearDown(); - } - - protected: - std::unique_ptr<android::perfprofd::ThreadedHandler> threaded_handler_; -}; - -TEST_F(ThreadedHandlerTest, Basic) { - std::string error_msg; - EXPECT_FALSE(threaded_handler_->StopProfiling(&error_msg)); -} - -#ifdef __ANDROID__ -#define ThreadedHandlerTestName(x) x -#else -#define ThreadedHandlerTestName(x) DISABLED_ ## x -#endif - -TEST_F(ThreadedHandlerTest, ThreadedHandlerTestName(Live)) { - auto config_fn = [](android::perfprofd::ThreadedConfig& config) { - // Use some values that make it likely that things don't fail quickly. - config.main_loop_iterations = 0; - config.collection_interval_in_s = 1000000; - }; - std::string error_msg; - ASSERT_TRUE(threaded_handler_->StartProfiling(config_fn, &error_msg)) << error_msg; - EXPECT_TRUE(threaded_handler_->StopProfiling(&error_msg)) << error_msg; -} - -int main(int argc, char **argv) { - // Always log to cerr, so that device failures are visible. - android::base::SetLogger(android::base::StderrLogger); - - CHECK(android::base::Realpath(argv[0], &gExecutableRealpath)); - - // switch to / before starting testing (perfprofd - // should be location-independent) - chdir("/"); - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/perfprofd/tests/perfprofdmockutils.cc b/perfprofd/tests/perfprofdmockutils.cc deleted file mode 100644 index f8858090..00000000 --- a/perfprofd/tests/perfprofdmockutils.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* -** -** Copyright 2015, 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. -*/ - -#define LOG_TAG "perfprofd" - -#include <stdarg.h> -#include <unistd.h> -#include <vector> -#include <string> -#include <assert.h> - -#include <utils/Log.h> - -#include "perfprofdutils.h" - -static std::vector<std::string> *mock_log; - -static void append_to_log(const std::string &s) -{ - assert(mock_log); - mock_log->push_back(s); -} - -void mock_perfprofdutils_init() -{ - assert(!mock_log); - mock_log = new std::vector<std::string>; -} - -void mock_perfprofdutils_finish() -{ - assert(mock_log); - delete mock_log; -} - -std::string mock_perfprofdutils_getlogged() -{ - std::string result; - assert(mock_log); - for (const std::string &s : (*mock_log)) { - result += s; - } - mock_log->clear(); - return result; -} - -extern "C" { - -#define LMAX 8192 - -void perfprofd_mocklog(const char *tag, const char *fmt, va_list ap) -{ - char buffer[LMAX]; - strcpy(buffer, tag); - vsnprintf(buffer+strlen(tag), LMAX, fmt, ap); - std::string b(buffer); b += "\012"; - append_to_log(b); -} - -void perfprofd_log_error(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - perfprofd_mocklog("E: ", fmt, ap); - va_end(ap); -} - -void perfprofd_log_warning(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - perfprofd_mocklog("W: ", fmt, ap); - va_end(ap); -} - -void perfprofd_log_info(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - perfprofd_mocklog("I: ", fmt, ap); - va_end(ap); -} - -} diff --git a/perfprofd/tests/perfprofdmockutils.h b/perfprofd/tests/perfprofdmockutils.h deleted file mode 100644 index 12caabb7..00000000 --- a/perfprofd/tests/perfprofdmockutils.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -** -** Copyright 2015, 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. -*/ - -/// -/// Set up mock utilities layer prior to unit test execution -/// -extern void mock_perfprofdutils_init(); - -/// -/// Set up mock utilities layer prior to unit test execution -/// -extern void mock_perfprofdutils_finish(); - -/// -/// Return string containing things logged to logd, plus sleep instances -/// -extern std::string mock_perfprofdutils_getlogged(); |