summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-07-10 12:35:15 -0700
committerElliott Hughes <enh@google.com>2019-07-10 12:35:15 -0700
commitf895345280d53dfcde37bd61e9643977c519f6e9 (patch)
treecbb2630439af97b9743398ebccb02b61ef8b2343
parent52aa89d133ac62468fe96bcaa242210510cddc05 (diff)
downloadextras-f895345280d53dfcde37bd61e9643977c519f6e9.tar.gz
Remove perfprofd.
This isn't finished, and no-one's working on finishing it. Test: N/A Change-Id: If52aa7f7dba8991f53bf432c0c3bc59c7a3cd2ca
-rw-r--r--perfprofd/Android.bp267
-rw-r--r--perfprofd/NOTICE190
-rw-r--r--perfprofd/OWNERS2
-rw-r--r--perfprofd/TEST_MAPPING7
-rw-r--r--perfprofd/binder_interface/Android.bp48
-rw-r--r--perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl45
-rw-r--r--perfprofd/binder_interface/perfprofd_binder.cc377
-rw-r--r--perfprofd/binder_interface/perfprofd_binder.h31
-rw-r--r--perfprofd/config.h128
-rw-r--r--perfprofd/configreader.cc620
-rw-r--r--perfprofd/configreader.h83
-rw-r--r--perfprofd/cpuconfig.cc113
-rw-r--r--perfprofd/cpuconfig.h50
-rw-r--r--perfprofd/dropbox/Android.bp52
-rw-r--r--perfprofd/dropbox/dropbox.cc129
-rw-r--r--perfprofd/dropbox/dropbox.h37
-rw-r--r--perfprofd/dropbox/dropbox_host.cc35
-rw-r--r--perfprofd/map_utils.h129
-rw-r--r--perfprofd/perf_data_converter.cc197
-rw-r--r--perfprofd/perf_data_converter.h23
-rw-r--r--perfprofd/perf_profile.proto131
-rw-r--r--perfprofd/perfprofd.conf24
-rw-r--r--perfprofd/perfprofd.rc5
-rw-r--r--perfprofd/perfprofd_cmdline.cc258
-rw-r--r--perfprofd/perfprofd_cmdline.h39
-rw-r--r--perfprofd/perfprofd_config.proto96
-rw-r--r--perfprofd/perfprofd_io.cc310
-rw-r--r--perfprofd/perfprofd_io.h38
-rw-r--r--perfprofd/perfprofd_perf.cc332
-rw-r--r--perfprofd/perfprofd_perf.h57
-rw-r--r--perfprofd/perfprofd_record-fwd.h31
-rw-r--r--perfprofd/perfprofd_record.proto58
-rw-r--r--perfprofd/perfprofd_threaded_handler.h198
-rw-r--r--perfprofd/perfprofdcore.cc732
-rw-r--r--perfprofd/perfprofdcore.h91
-rw-r--r--perfprofd/perfprofdmain.cc60
-rw-r--r--perfprofd/quipper_helper.h146
-rw-r--r--perfprofd/scripts/Android.bp89
-rw-r--r--perfprofd/scripts/perf_config_proto.py193
-rw-r--r--perfprofd/scripts/perf_proto_json2sqlite.py167
-rw-r--r--perfprofd/scripts/perf_proto_stack.py576
-rw-r--r--perfprofd/scripts/perf_proto_stack_sqlite_flame.py272
-rw-r--r--perfprofd/scripts/sorted_collection.py147
-rw-r--r--perfprofd/symbolizer.cc179
-rw-r--r--perfprofd/symbolizer.h35
-rw-r--r--perfprofd/tests/Android.bp73
-rw-r--r--perfprofd/tests/AndroidTest.xml29
-rw-r--r--perfprofd/tests/README.txt64
-rw-r--r--perfprofd/tests/callchain.canned.perf.databin256412 -> 0 bytes
-rw-r--r--perfprofd/tests/canned.perf.databin1366208 -> 0 bytes
-rw-r--r--perfprofd/tests/perfprofd_test.cc1743
-rw-r--r--perfprofd/tests/perfprofdmockutils.cc101
-rw-r--r--perfprofd/tests/perfprofdmockutils.h31
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
deleted file mode 100644
index 8d843935..00000000
--- a/perfprofd/tests/callchain.canned.perf.data
+++ /dev/null
Binary files differ
diff --git a/perfprofd/tests/canned.perf.data b/perfprofd/tests/canned.perf.data
deleted file mode 100644
index e6510d2a..00000000
--- a/perfprofd/tests/canned.perf.data
+++ /dev/null
Binary files differ
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();