diff options
author | Yi Kong <yikong@google.com> | 2020-02-19 15:14:42 +0800 |
---|---|---|
committer | Yi Kong <yikong@google.com> | 2020-06-23 03:37:57 +0000 |
commit | 83e3567d6a7dae7612d386a74ddba5de85ee6351 (patch) | |
tree | 988909fc3e6f2ffb92373a4e4d1d771dbd21900d /profcollectd | |
parent | 4d5330155d5e873d81e3764107c17a7228d99c0e (diff) | |
download | extras-83e3567d6a7dae7612d386a74ddba5de85ee6351.tar.gz |
profcollectd: initial import
A daemon to collect, process, and upload sampling traces to aid platform
AutoFDO optimisation.
Bug: 79161490
Change-Id: Icf9927c56c3d4c6ea7562be4706c0d2f309db341
Diffstat (limited to 'profcollectd')
20 files changed, 1191 insertions, 0 deletions
diff --git a/profcollectd/.clang-format b/profcollectd/.clang-format new file mode 120000 index 00000000..f7e9891f --- /dev/null +++ b/profcollectd/.clang-format @@ -0,0 +1 @@ +../../../system/core/.clang-format-2
\ No newline at end of file diff --git a/profcollectd/Android.bp b/profcollectd/Android.bp new file mode 100644 index 00000000..38282274 --- /dev/null +++ b/profcollectd/Android.bp @@ -0,0 +1,60 @@ +// +// Copyright (C) 2020 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: "libprofcollectd_defaults", + + // We are only doing this for C++20. Can be removed after it becomes default. + cpp_std: "experimental", + + tidy: true, + tidy_checks: [ + "-google-runtime-int", + "-google-explicit-constructor", + "-bugprone-unhandled-self-assignment", + "-bugprone-macro-parentheses", + ], +} + +cc_binary { + name: "profcollectctl", + + defaults: ["libprofcollectd_defaults"], + + srcs: ["profcollectctl.cpp"], + + shared_libs: ["libprofcollectd"], +} + +cc_binary { + name: "profcollectd", + + defaults: ["libprofcollectd_defaults"], + + srcs: ["profcollectd.cpp"], + + shared_libs: ["libprofcollectd"], + + init_rc: ["profcollectd.rc"], +} + +filegroup { + name: "profcollectd_aidl", + srcs: [ + "binder/android/os/IProfCollectd.aidl", + ], + path: "binder", +} diff --git a/profcollectd/MODULE_LICENSE_APACHE2 b/profcollectd/MODULE_LICENSE_APACHE2 new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/profcollectd/MODULE_LICENSE_APACHE2 diff --git a/profcollectd/NOTICE b/profcollectd/NOTICE new file mode 100644 index 00000000..f3e7764f --- /dev/null +++ b/profcollectd/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2020, 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/profcollectd/OWNERS b/profcollectd/OWNERS new file mode 100644 index 00000000..b380e395 --- /dev/null +++ b/profcollectd/OWNERS @@ -0,0 +1,3 @@ +srhines@google.com +yabinc@google.com +yikong@google.com diff --git a/profcollectd/binder/android/os/IProfCollectd.aidl b/profcollectd/binder/android/os/IProfCollectd.aidl new file mode 100644 index 00000000..03f6b19b --- /dev/null +++ b/profcollectd/binder/android/os/IProfCollectd.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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 IProfCollectd { + void ReadConfig(); + void ScheduleCollection(); + void TerminateCollection(); + void TraceOnce(); + void ProcessProfile(); +} diff --git a/profcollectd/libprofcollectd/Android.bp b/profcollectd/libprofcollectd/Android.bp new file mode 100644 index 00000000..7b1b2f31 --- /dev/null +++ b/profcollectd/libprofcollectd/Android.bp @@ -0,0 +1,46 @@ +// +// Copyright (C) 2020 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_library { + name: "libprofcollectd", + + defaults: ["libprofcollectd_defaults"], + + shared_libs: [ + "libbase", + "libbinder", + "libjsoncpp", + "liblog", + "libsimpleperf_profcollect", + "libutils", + "libziparchive", + ], + + static_libs: [ + "libc++fs", + ], + + srcs: [ + "binder_service.cpp", + "compress.cpp", + "interface.cpp", + "scheduler.cpp", + "simpleperf_etm_provider.cpp", + ":profcollectd_aidl", + ], + + export_include_dirs: ["include"], +} diff --git a/profcollectd/libprofcollectd/binder_service.cpp b/profcollectd/libprofcollectd/binder_service.cpp new file mode 100644 index 00000000..0eb9caaa --- /dev/null +++ b/profcollectd/libprofcollectd/binder_service.cpp @@ -0,0 +1,71 @@ +// +// Copyright (C) 2020 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 "profcollectd_binder" + +#include "binder_service.h" + +#include <android-base/logging.h> + +#include "hwtrace_provider.h" +#include "scheduler.h" + +namespace android { +namespace profcollectd { + +using ::android::binder::Status; +using ::android::os::IProfCollectd; + +namespace { + +Status HandleIfError(const std::function<OptError()>& action) { + auto errmsg = action(); + if (errmsg) { + LOG(ERROR) << errmsg.value(); + return Status::fromExceptionCode(1, errmsg.value().c_str()); + } + return Status::ok(); +} + +} // namespace + +ProfcollectdBinder::ProfcollectdBinder() { + ProfcollectdBinder::Scheduler = std::make_unique<ProfcollectdScheduler>(); + LOG(INFO) << "Binder service started"; +} + +Status ProfcollectdBinder::ReadConfig() { + return HandleIfError([=]() { return Scheduler->ReadConfig(); }); +} + +Status ProfcollectdBinder::ScheduleCollection() { + return HandleIfError([=]() { return Scheduler->ScheduleCollection(); }); +} + +Status ProfcollectdBinder::TerminateCollection() { + return HandleIfError([=]() { return Scheduler->TerminateCollection(); }); +} + +Status ProfcollectdBinder::TraceOnce() { + return HandleIfError([=]() { return Scheduler->TraceOnce(); }); +} + +Status ProfcollectdBinder::ProcessProfile() { + return HandleIfError([=]() { return Scheduler->ProcessProfile(); }); +} + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/binder_service.h b/profcollectd/libprofcollectd/binder_service.h new file mode 100644 index 00000000..83197666 --- /dev/null +++ b/profcollectd/libprofcollectd/binder_service.h @@ -0,0 +1,45 @@ +// +// Copyright (C) 2020 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. +// + +#pragma once + +#include <binder/BinderService.h> +#include <binder/Status.h> + +#include "android/os/BnProfCollectd.h" +#include "scheduler.h" + +namespace android { +namespace profcollectd { + +class ProfcollectdBinder : public BinderService<ProfcollectdBinder>, public os::BnProfCollectd { + public: + explicit ProfcollectdBinder(); + + static constexpr const char* getServiceName() { return "profcollectd"; } + + binder::Status ReadConfig() override; + binder::Status ScheduleCollection() override; + binder::Status TerminateCollection() override; + binder::Status TraceOnce() override; + binder::Status ProcessProfile() override; + + protected: + inline static std::unique_ptr<ProfcollectdScheduler> Scheduler; +}; + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/compress.cpp b/profcollectd/libprofcollectd/compress.cpp new file mode 100644 index 00000000..6a2b2e8e --- /dev/null +++ b/profcollectd/libprofcollectd/compress.cpp @@ -0,0 +1,49 @@ +// +// Copyright (C) 2020 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 "compress.h" + +#include <ziparchive/zip_writer.h> +#include <cstdlib> +#include <fstream> +#include <iostream> + +namespace android { +namespace profcollectd { + +bool CompressFiles(const std::filesystem::path& output, + std::vector<std::filesystem::path>& inputFiles) { + FILE* outputFile = std::fopen(output.c_str(), "wb"); + ZipWriter writer(outputFile); + + for (const auto& f : inputFiles) { + // read profile into memory. + std::ifstream ifs(f, std::ios::in | std::ios::binary); + std::vector<char> buf((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); + + // append to zip file + writer.StartEntry(f.filename().string(), ZipWriter::kCompress | ZipWriter::kAlign32); + writer.WriteBytes(buf.data(), buf.size()); + writer.FinishEntry(); + } + + auto ret = writer.Finish(); + std::fclose(outputFile); + return ret == 0; +} + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/compress.h b/profcollectd/libprofcollectd/compress.h new file mode 100644 index 00000000..f99deca1 --- /dev/null +++ b/profcollectd/libprofcollectd/compress.h @@ -0,0 +1,27 @@ +// +// Copyright (C) 2020 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. +// + +#pragma once + +#include <filesystem> + +namespace android { +namespace profcollectd { + +bool CompressFiles(const std::filesystem::path& output, std::vector<std::filesystem::path>& files); + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/hwtrace_provider.h b/profcollectd/libprofcollectd/hwtrace_provider.h new file mode 100644 index 00000000..59329dd0 --- /dev/null +++ b/profcollectd/libprofcollectd/hwtrace_provider.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 2020 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. +// + +#pragma once + +#include <chrono> +#include <filesystem> +#include <functional> + +namespace android { +namespace profcollectd { + +class HwtraceProvider { + public: + virtual ~HwtraceProvider() = default; + + /** + * Trace for the given length of time. + * + * @param period Length of time to trace in seconds. + * @return True if successful. + */ + virtual bool Trace(const std::filesystem::path& outputPath, + std::chrono::duration<float> samplingPeriod) = 0; + + /** + * Process the hardware trace to generate simpleperf intermediate profile. + */ + virtual bool Process(const std::filesystem::path& inputPath, + const std::filesystem::path& outputPath, + const std::string& binaryFilter) = 0; +}; + +} // namespace profcollectd +} // namespace android
\ No newline at end of file diff --git a/profcollectd/libprofcollectd/include/libprofcollectd.h b/profcollectd/libprofcollectd/include/libprofcollectd.h new file mode 100644 index 00000000..1beebb61 --- /dev/null +++ b/profcollectd/libprofcollectd/include/libprofcollectd.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 2020 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. +// + +#pragma once + +namespace android { +namespace profcollectd { + +// Daemon side operations. +void InitService(bool start); + +// Client side operations. +void ScheduleCollection(); +void TerminateCollection(); +void TraceOnce(); +void Process(); +void ReadConfig(); + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/interface.cpp b/profcollectd/libprofcollectd/interface.cpp new file mode 100644 index 00000000..f7985761 --- /dev/null +++ b/profcollectd/libprofcollectd/interface.cpp @@ -0,0 +1,86 @@ +// +// Copyright (C) 2020 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 "profcollectd" + +#include "libprofcollectd.h" + +#include <android-base/logging.h> + +#include <iostream> + +#include "binder_service.h" + +namespace android { +namespace profcollectd { + +using ::android::os::IProfCollectd; + +namespace { + +sp<IProfCollectd> GetIProfcollectdService() { + sp<ProcessState> proc(ProcessState::self()); + sp<IBinder> binder = + defaultServiceManager()->getService(String16(ProfcollectdBinder::getServiceName())); + if (!binder) { + std::cerr << "Cannot connect to the daemon, is it running?" << std::endl; + exit(EXIT_FAILURE); + } + + return interface_cast<IProfCollectd>(binder); +} + +} // namespace + +void InitService(bool start) { + if (defaultServiceManager()->checkService(String16(ProfcollectdBinder::getServiceName()))) { + ALOGE("Another instance of profcollectd is already running"); + exit(EXIT_FAILURE); + } + + sp<ProcessState> proc(ProcessState::self()); + sp<IServiceManager> sm(defaultServiceManager()); + auto svc = sp<ProfcollectdBinder>(new ProfcollectdBinder()); + sm->addService(String16(ProfcollectdBinder::getServiceName()), svc); + if (start) { + svc->ScheduleCollection(); + } + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); +} + +void ScheduleCollection() { + GetIProfcollectdService()->ScheduleCollection(); +} + +void TerminateCollection() { + GetIProfcollectdService()->TerminateCollection(); +} + +void TraceOnce() { + GetIProfcollectdService()->TraceOnce(); +} + +void Process() { + GetIProfcollectdService()->ProcessProfile(); +} + +void ReadConfig() { + GetIProfcollectdService()->ReadConfig(); +} + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/scheduler.cpp b/profcollectd/libprofcollectd/scheduler.cpp new file mode 100644 index 00000000..38bf19a4 --- /dev/null +++ b/profcollectd/libprofcollectd/scheduler.cpp @@ -0,0 +1,216 @@ +// +// Copyright (C) 2020 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 "profcollectd_scheduler" + +#include "scheduler.h" + +#include <fstream> +#include <variant> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/properties.h> + +#include "compress.h" +#include "hwtrace_provider.h" +#include "json/json.h" +#include "json/writer.h" + +// Default option values. +using config_t = std::pair<const char*, std::variant<const int, const char*>>; +static constexpr const config_t CONFIG_BUILD_FINGERPRINT = {"Fingerprint", "unknown"}; +static constexpr const config_t CONFIG_COLLECTION_INTERVAL_SEC = {"CollectionInterval", 600}; +static constexpr const config_t CONFIG_SAMPLING_PERIOD_MS = {"SamplingPeriod", 500}; +static constexpr const config_t CONFIG_TRACE_OUTDIR = {"TraceDir", "/data/misc/profcollectd/trace"}; +static constexpr const config_t CONFIG_PROFILE_OUTDIR = {"ProfileDir", + "/data/misc/profcollectd/output"}; +static constexpr const config_t CONFIG_BINARY_FILTER = {"BinaryFilter", ""}; + +namespace android { +namespace profcollectd { + +namespace fs = std::filesystem; +using ::android::base::GetIntProperty; +using ::android::base::GetProperty; + +// Hwtrace provider registry +extern std::unique_ptr<HwtraceProvider> REGISTER_SIMPLEPERF_ETM_PROVIDER(); + +namespace { + +void ClearDir(const fs::path& path) { + if (fs::exists(path)) { + for (const auto& entry : fs::directory_iterator(path)) { + fs::remove_all(entry); + } + } +} + +bool ClearOnConfigChange(const ProfcollectdScheduler::Config& config) { + const fs::path configFile = config.profileOutputDir / "config.json"; + ProfcollectdScheduler::Config oldConfig{}; + + // Read old config, if exists. + if (fs::exists(configFile)) { + std::ifstream ifs(configFile); + ifs >> oldConfig; + } + + if (oldConfig != config) { + LOG(INFO) << "Clearing profiles due to config change."; + ClearDir(config.traceOutputDir); + ClearDir(config.profileOutputDir); + + // Write new config. + std::ofstream ofs(configFile); + ofs << config; + return true; + } + return false; +} + +void PeriodicCollectionWorker(std::future<void> terminationSignal, ProfcollectdScheduler& scheduler, + std::chrono::seconds& interval) { + do { + scheduler.TraceOnce(); + } while ((terminationSignal.wait_for(interval)) == std::future_status::timeout); +} + +} // namespace + +ProfcollectdScheduler::ProfcollectdScheduler() { + ReadConfig(); + + // Load a registered hardware trace provider. + if ((hwtracer = REGISTER_SIMPLEPERF_ETM_PROVIDER())) { + LOG(INFO) << "ETM provider registered."; + return; + } else { + LOG(ERROR) << "No hardware trace provider found for this architecture."; + exit(EXIT_FAILURE); + } +} + +OptError ProfcollectdScheduler::ReadConfig() { + if (workerThread != nullptr) { + static std::string errmsg = "Terminate the collection before refreshing config."; + return errmsg; + } + + const std::lock_guard<std::mutex> lock(mu); + + config.buildFingerprint = GetProperty("ro.build.fingerprint", "unknown"); + config.collectionInterval = std::chrono::seconds( + GetIntProperty("profcollectd.collection_interval", + std::get<const int>(CONFIG_COLLECTION_INTERVAL_SEC.second))); + config.samplingPeriod = std::chrono::milliseconds(GetIntProperty( + "profcollectd.sampling_period_ms", std::get<const int>(CONFIG_SAMPLING_PERIOD_MS.second))); + config.traceOutputDir = GetProperty("profcollectd.trace_output_dir", + std::get<const char*>(CONFIG_TRACE_OUTDIR.second)); + config.profileOutputDir = + GetProperty("profcollectd.output_dir", std::get<const char*>(CONFIG_PROFILE_OUTDIR.second)); + config.binaryFilter = + GetProperty("profcollectd.binary_filter", std::get<const char*>(CONFIG_BINARY_FILTER.second)); + ClearOnConfigChange(config); + + return std::nullopt; +} + +OptError ProfcollectdScheduler::ScheduleCollection() { + if (workerThread != nullptr) { + static std::string errmsg = "Collection is already scheduled."; + return errmsg; + } + + workerThread = + std::make_unique<std::thread>(PeriodicCollectionWorker, terminate.get_future(), + std::ref(*this), std::ref(config.collectionInterval)); + return std::nullopt; +} + +OptError ProfcollectdScheduler::TerminateCollection() { + if (workerThread == nullptr) { + static std::string errmsg = "Collection is not scheduled."; + return errmsg; + } + + terminate.set_value(); + workerThread->join(); + workerThread = nullptr; + terminate = std::promise<void>(); // Reset promise. + return std::nullopt; +} + +OptError ProfcollectdScheduler::TraceOnce() { + const std::lock_guard<std::mutex> lock(mu); + bool success = hwtracer->Trace(config.traceOutputDir, config.samplingPeriod); + if (!success) { + static std::string errmsg = "Trace failed"; + return errmsg; + } + return std::nullopt; +} + +OptError ProfcollectdScheduler::ProcessProfile() { + const std::lock_guard<std::mutex> lock(mu); + hwtracer->Process(config.traceOutputDir, config.profileOutputDir, config.binaryFilter); + std::vector<fs::path> profiles; + profiles.insert(profiles.begin(), fs::directory_iterator(config.profileOutputDir), + fs::directory_iterator()); + bool success = CompressFiles("/sdcard/profile.zip", profiles); + if (!success) { + static std::string errmsg = "Compress files failed"; + return errmsg; + } + return std::nullopt; +} + +std::ostream& operator<<(std::ostream& os, const ProfcollectdScheduler::Config& config) { + Json::Value root; + const auto writer = std::make_unique<Json::StyledStreamWriter>(); + root[CONFIG_BUILD_FINGERPRINT.first] = config.buildFingerprint; + root[CONFIG_COLLECTION_INTERVAL_SEC.first] = config.collectionInterval.count(); + root[CONFIG_SAMPLING_PERIOD_MS.first] = config.samplingPeriod.count(); + root[CONFIG_TRACE_OUTDIR.first] = config.traceOutputDir.c_str(); + root[CONFIG_PROFILE_OUTDIR.first] = config.profileOutputDir.c_str(); + root[CONFIG_BINARY_FILTER.first] = config.binaryFilter.c_str(); + writer->write(os, root); + return os; +} + +std::istream& operator>>(std::istream& is, ProfcollectdScheduler::Config& config) { + Json::Value root; + const auto reader = std::make_unique<Json::Reader>(); + bool success = reader->parse(is, root); + if (!success) { + return is; + } + + config.buildFingerprint = root[CONFIG_BUILD_FINGERPRINT.first].asString(); + config.collectionInterval = + std::chrono::seconds(root[CONFIG_COLLECTION_INTERVAL_SEC.first].asInt64()); + config.samplingPeriod = + std::chrono::duration<float>(root[CONFIG_SAMPLING_PERIOD_MS.first].asFloat()); + config.traceOutputDir = root[CONFIG_TRACE_OUTDIR.first].asString(); + config.profileOutputDir = root[CONFIG_PROFILE_OUTDIR.first].asString(); + config.binaryFilter = root[CONFIG_BINARY_FILTER.first].asString(); + + return is; +} + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/libprofcollectd/scheduler.h b/profcollectd/libprofcollectd/scheduler.h new file mode 100644 index 00000000..3325abab --- /dev/null +++ b/profcollectd/libprofcollectd/scheduler.h @@ -0,0 +1,65 @@ +// +// Copyright (C) 2020 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. +// + +#pragma once + +#include <compare> +#include <future> +#include <mutex> +#include <optional> +#include <thread> + +#include "hwtrace_provider.h" + +using OptError = std::optional<std::string>; + +namespace android { +namespace profcollectd { + +class ProfcollectdScheduler { + public: + struct Config { + std::string buildFingerprint; + std::chrono::seconds collectionInterval; + std::chrono::duration<float> samplingPeriod; + std::filesystem::path traceOutputDir; + std::filesystem::path profileOutputDir; + std::string binaryFilter; + + auto operator<=>(const Config&) const = default; + friend std::ostream& operator<<(std::ostream& os, const Config& config); + friend std::istream& operator>>(std::istream& is, Config& config); + }; + + explicit ProfcollectdScheduler(); + + // Methods below returns optional error message on failure, otherwise returns std::nullopt. + OptError ReadConfig(); + OptError ScheduleCollection(); + OptError TerminateCollection(); + OptError TraceOnce(); + OptError ProcessProfile(); + + private: + Config config; + std::promise<void> terminate; + std::unique_ptr<HwtraceProvider> hwtracer; + std::unique_ptr<std::thread> workerThread; + std::mutex mu; +}; + +} // namespace profcollectd +} // namespace android
\ No newline at end of file diff --git a/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp b/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp new file mode 100644 index 00000000..8882ccd5 --- /dev/null +++ b/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp @@ -0,0 +1,96 @@ +// +// Copyright (C) 2020 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 "profcolelctd_simpleperf_etm" + +#include "hwtrace_provider.h" + +#include <android-base/logging.h> +#include <simpleperf_profcollect.h> + +#include <cstdlib> +#include <filesystem> +#include <sstream> +#include <string> +#include <thread> + +static constexpr const char* ETM_TRACEFILE_EXTENSION = ".etmtrace"; +static constexpr const char* OUTPUT_FILE_EXTENSION = ".data"; + +namespace { + +std::string GetTimestamp() { + auto now = std::time(nullptr); + char timestr[32]; + std::strftime(timestr, sizeof(timestr), "%Y%m%d-%H%M%S", std::localtime(&now)); + return timestr; +} + +} // namespace + +namespace android { +namespace profcollectd { + +namespace fs = std::filesystem; + +class SimpleperfETMProvider : public HwtraceProvider { + public: + static bool IsSupported(); + bool Trace(const fs::path& outputPath, std::chrono::duration<float> samplingPeriod) override; + bool Process(const fs::path& inputPath, const fs::path& outputPath, + const std::string& binaryFilter) override; +}; + +bool SimpleperfETMProvider::IsSupported() { + return simpleperf::etm::HasSupport(); +} + +bool SimpleperfETMProvider::Trace(const fs::path& outputPath, + std::chrono::duration<float> samplingPeriod) { + const std::string timestamp = GetTimestamp(); + auto outputFile = outputPath / (timestamp + ETM_TRACEFILE_EXTENSION); + return simpleperf::etm::Record(outputFile, samplingPeriod); +} + +bool SimpleperfETMProvider::Process(const fs::path& inputPath, const fs::path& outputPath, + const std::string& binaryFilter) { + for (const auto& entry : fs::directory_iterator(inputPath)) { + const fs::path& traceFile = entry.path(); + if (traceFile.extension() != ETM_TRACEFILE_EXTENSION) { + continue; + } + + const fs::path binaryOutput = + outputPath / traceFile.filename().replace_extension(OUTPUT_FILE_EXTENSION); + + bool success = simpleperf::etm::Inject(traceFile, binaryOutput, binaryFilter); + if (success) { + fs::remove(traceFile); + } + } + + return true; +} + +std::unique_ptr<HwtraceProvider> REGISTER_SIMPLEPERF_ETM_PROVIDER() { + if (SimpleperfETMProvider::IsSupported()) { + return std::make_unique<SimpleperfETMProvider>(); + } + return nullptr; +} + +} // namespace profcollectd +} // namespace android diff --git a/profcollectd/profcollectctl.cpp b/profcollectd/profcollectctl.cpp new file mode 100644 index 00000000..897d04e2 --- /dev/null +++ b/profcollectd/profcollectctl.cpp @@ -0,0 +1,71 @@ +// +// Copyright (C) 2020 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 <stdlib.h> + +#include <iostream> + +#include "libprofcollectd.h" + +namespace profcollectd = android::profcollectd; + +namespace { + +void PrintHelp(const std::string& reason = "") { + std::cout << reason; + std::cout << R"( +usage: profcollectctl [command] +command: + start Schedule periodic collection. + stop Terminate periodic collection. + once Request an one-off trace. + process Convert traces to perf profiles. + reconfig Refresh configuration. + help Print this message. +)"; +} + +} // namespace + +int main(int argc, char** argv) { + if (argc != 2) { + PrintHelp("Invalid arguments"); + exit(EXIT_FAILURE); + } + + std::string command(argv[1]); + if (command == "start") { + std::cout << "Scheduling profile collection\n"; + profcollectd::ScheduleCollection(); + } else if (command == "stop") { + std::cout << "Terminating profile collection\n"; + profcollectd::TerminateCollection(); + } else if (command == "once") { + std::cout << "Trace once\n"; + profcollectd::TraceOnce(); + } else if (command == "process") { + std::cout << "Processing traces\n"; + profcollectd::Process(); + } else if (command == "reconfig") { + std::cout << "Refreshing configuration\n"; + profcollectd::ReadConfig(); + } else if (command == "help") { + PrintHelp(); + } else { + PrintHelp("Unknown command: " + command); + exit(EXIT_FAILURE); + } +} diff --git a/profcollectd/profcollectd.cpp b/profcollectd/profcollectd.cpp new file mode 100644 index 00000000..884bf420 --- /dev/null +++ b/profcollectd/profcollectd.cpp @@ -0,0 +1,47 @@ +// +// Copyright (C) 2020 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 <stdlib.h> + +#include <iostream> + +#include "libprofcollectd.h" + +namespace { + +void print_help() { + std::cout << R"( +usage: profcollectd [command] + boot Start daemon and schedule profile collection after a short delay. + run Start daemon but do not schedule profile collection. +)"; +} + +} // namespace + +int main(int argc, char** argv) { + if (argc != 2) { + print_help(); + exit(EXIT_SUCCESS); + } else if (std::string(argv[1]) == "boot") { + android::profcollectd::InitService(/* start = */ true); + } else if (std::string(argv[1]) == "run") { + android::profcollectd::InitService(/* start = */ false); + } else { + print_help(); + exit(EXIT_FAILURE); + } +} diff --git a/profcollectd/profcollectd.rc b/profcollectd/profcollectd.rc new file mode 100644 index 00000000..26379240 --- /dev/null +++ b/profcollectd/profcollectd.rc @@ -0,0 +1,11 @@ +service profcollectd /system/bin/profcollectd boot + class late_start + user root + group root wakelock + writepid /dev/cpuset/system-background/tasks + +on post-fs-data + # Create directory for profcollectd. + mkdir /data/misc/profcollectd 0770 root root + mkdir /data/misc/profcollectd/trace 0770 root root + mkdir /data/misc/profcollectd/output 0770 root root |