summaryrefslogtreecommitdiff
path: root/profcollectd
diff options
context:
space:
mode:
authorYi Kong <yikong@google.com>2021-03-02 13:58:25 +0800
committerYi Kong <yikong@google.com>2021-03-03 14:24:33 +0800
commite3aab14ec0bcbbae6ba6edddbe315e1e128dce5f (patch)
tree1f5970b315e4c38542d5baf1d663040d877a8f5d /profcollectd
parentf8abfdbd271086cf276fcf738e123cf36d3eb2ca (diff)
downloadextras-e3aab14ec0bcbbae6ba6edddbe315e1e128dce5f.tar.gz
profcollectd: Full rewrite in Rust 🦀
Rewritten from the C++ version. No major feature difference except that the report zip is not compressed (blocked by upstream bug) and now stored in a different location (to prepare for upload queue support). Test: Manual testing Change-Id: I635c9c56a3870c3b96b31d46df3ef9175490925d
Diffstat (limited to 'profcollectd')
-rw-r--r--profcollectd/Android.bp10
-rw-r--r--profcollectd/binder/com/android/server/profcollect/IProfCollectd.aidl13
-rw-r--r--profcollectd/libprofcollectd/Android.bp81
-rw-r--r--profcollectd/libprofcollectd/binder_service.cpp113
-rw-r--r--profcollectd/libprofcollectd/binder_service.h51
-rw-r--r--profcollectd/libprofcollectd/bindings/libflags/Android.bp2
-rw-r--r--profcollectd/libprofcollectd/compress.cpp49
-rw-r--r--profcollectd/libprofcollectd/compress.h27
-rw-r--r--profcollectd/libprofcollectd/config.rs95
-rw-r--r--profcollectd/libprofcollectd/config_utils.cpp68
-rw-r--r--profcollectd/libprofcollectd/config_utils.h39
-rw-r--r--profcollectd/libprofcollectd/hwtrace_provider.h53
-rw-r--r--profcollectd/libprofcollectd/lib.rs86
-rw-r--r--profcollectd/libprofcollectd/report.rs56
-rw-r--r--profcollectd/libprofcollectd/scheduler.cpp235
-rw-r--r--profcollectd/libprofcollectd/scheduler.h67
-rw-r--r--profcollectd/libprofcollectd/scheduler.rs108
-rw-r--r--profcollectd/libprofcollectd/service.rs118
-rw-r--r--profcollectd/libprofcollectd/simpleperf_etm_provider.cpp103
-rw-r--r--profcollectd/libprofcollectd/simpleperf_etm_trace_provider.rs75
-rw-r--r--profcollectd/libprofcollectd/trace_provider.rs44
-rw-r--r--profcollectd/profcollectctl.rs42
-rw-r--r--profcollectd/profcollectd.rc3
-rw-r--r--profcollectd/profcollectd.rs36
24 files changed, 621 insertions, 953 deletions
diff --git a/profcollectd/Android.bp b/profcollectd/Android.bp
index cd0d8852..f1723006 100644
--- a/profcollectd/Android.bp
+++ b/profcollectd/Android.bp
@@ -36,7 +36,10 @@ rust_binary {
srcs: ["profcollectctl.rs"],
- rustlibs: ["libprofcollectd_rust"],
+ rustlibs: [
+ "libanyhow",
+ "libprofcollectd",
+ ],
}
rust_binary {
@@ -44,7 +47,10 @@ rust_binary {
srcs: ["profcollectd.rs"],
- rustlibs: ["libprofcollectd_rust"],
+ rustlibs: [
+ "libanyhow",
+ "libprofcollectd",
+ ],
init_rc: ["profcollectd.rc"],
}
diff --git a/profcollectd/binder/com/android/server/profcollect/IProfCollectd.aidl b/profcollectd/binder/com/android/server/profcollect/IProfCollectd.aidl
index 908123ab..9bbe4a5c 100644
--- a/profcollectd/binder/com/android/server/profcollect/IProfCollectd.aidl
+++ b/profcollectd/binder/com/android/server/profcollect/IProfCollectd.aidl
@@ -18,11 +18,10 @@ package com.android.server.profcollect;
/** {@hide} */
interface IProfCollectd {
- void ReadConfig();
- void ScheduleCollection();
- void TerminateCollection();
- void TraceOnce(@utf8InCpp String tag);
- void ProcessProfile();
- void CreateProfileReport();
- @utf8InCpp String GetSupportedProvider();
+ void schedule();
+ void terminate();
+ void trace_once(@utf8InCpp String tag);
+ void process(boolean blocking);
+ void report();
+ @utf8InCpp String get_supported_provider();
}
diff --git a/profcollectd/libprofcollectd/Android.bp b/profcollectd/libprofcollectd/Android.bp
index 509ac5de..3c709a2f 100644
--- a/profcollectd/libprofcollectd/Android.bp
+++ b/profcollectd/libprofcollectd/Android.bp
@@ -34,76 +34,27 @@ aidl_interface {
},
}
-rust_bindgen {
- name: "libprofcollectd_bindgen",
- crate_name: "profcollectd_bindgen",
- source_stem: "libprofcollectd_bindgen",
- wrapper_src: "include/libprofcollectd.hpp",
-}
-
rust_library {
- name: "libprofcollectd_rust",
+ name: "libprofcollectd",
stem: "liblibprofcollectd",
crate_name: "libprofcollectd",
srcs: ["lib.rs"],
rustlibs: [
- "profcollectd_aidl_interface-rust",
- "libbinder_rs",
- "libprofcollectd_bindgen",
+ "profcollectd_aidl_interface-rust", // Move to rlib once b/179041242 is fixed.
+ "libandroid_logger",
+ "libanyhow",
+ "libbinder_rs", // Remove once b/179041241 is fixed.
+ "libchrono",
+ "liblazy_static",
+ "liblog_rust",
+ "libserde", // Remove once b/179041241 is fixed.
+ "libserde_json",
+ "libzip",
],
- shared_libs: [
- "libprofcollectd",
+ rlibs: [
+ "libprofcollect_libflags_rust",
+ "libprofcollect_libbase_rust",
+ "libsimpleperf_profcollect_rust",
],
-}
-
-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",
- ],
-
- cflags: [
- // jsoncpp uses volatile.
- "-Wno-deprecated-volatile",
- ],
-}
-
-cc_library {
- name: "libprofcollectd",
-
- defaults: ["libprofcollectd_defaults"],
-
- shared_libs: [
- "libbase",
- "libbinder",
- "libjsoncpp",
- "liblog",
- "libsimpleperf_profcollect",
- "libutils",
- "libziparchive",
- "profcollectd_aidl_interface-cpp",
- "server_configurable_flags",
- ],
-
- static_libs: [
- "libc++fs",
- ],
-
- srcs: [
- "binder_service.cpp",
- "compress.cpp",
- "config_utils.cpp",
- "scheduler.cpp",
- "simpleperf_etm_provider.cpp",
- ],
-
- export_include_dirs: ["include"],
+ shared_libs: ["libsimpleperf_profcollect"],
}
diff --git a/profcollectd/libprofcollectd/binder_service.cpp b/profcollectd/libprofcollectd/binder_service.cpp
deleted file mode 100644
index f1d24b96..00000000
--- a/profcollectd/libprofcollectd/binder_service.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-// 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 "config_utils.h"
-#include "hwtrace_provider.h"
-#include "libprofcollectd.hpp"
-#include "scheduler.h"
-
-namespace android {
-namespace profcollectd {
-
-using ::android::binder::Status;
-using ::android::profcollectd::ProfcollectdBinder;
-using ::com::android::server::profcollect::IProfCollectd;
-
-namespace {
-
-static constexpr const char* NOT_ENABLED_ERRMSG =
- "profcollectd is not enabled through device config.";
-static constexpr config_t CONFIG_ENABLED = {"enabled", "0"}; // Disabled by default.
-
-} // 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();
-}
-
-Status ProfcollectdBinder::ForwardScheduler(const std::function<OptError()>& action) {
- if (Scheduler == nullptr) {
- return Status::fromExceptionCode(1, NOT_ENABLED_ERRMSG);
- }
-
- auto errmsg = action();
- if (errmsg) {
- LOG(ERROR) << errmsg.value();
- return Status::fromExceptionCode(1, errmsg.value().c_str());
- }
- return Status::ok();
-}
-
-ProfcollectdBinder::ProfcollectdBinder() {
- static bool enabled = getConfigFlagBool(CONFIG_ENABLED);
-
- if (enabled) {
- ProfcollectdBinder::Scheduler = std::make_unique<ProfcollectdScheduler>();
- LOG(INFO) << "Binder service started";
- } else {
- LOG(INFO) << NOT_ENABLED_ERRMSG;
- }
-}
-
-Status ProfcollectdBinder::ReadConfig() {
- return ForwardScheduler([=]() { return Scheduler->ReadConfig(); });
-}
-
-Status ProfcollectdBinder::ScheduleCollection() {
- return ForwardScheduler([=]() { return Scheduler->ScheduleCollection(); });
-}
-
-Status ProfcollectdBinder::TerminateCollection() {
- return ForwardScheduler([=]() { return Scheduler->TerminateCollection(); });
-}
-
-Status ProfcollectdBinder::TraceOnce(const std::string& tag) {
- return ForwardScheduler([=]() { return Scheduler->TraceOnce(tag); });
-}
-
-Status ProfcollectdBinder::ProcessProfile() {
- return ForwardScheduler([=]() { return Scheduler->ProcessProfile(); });
-}
-
-Status ProfcollectdBinder::CreateProfileReport() {
- return ForwardScheduler([=]() { return Scheduler->CreateProfileReport(); });
-}
-
-Status ProfcollectdBinder::GetSupportedProvider(std::string* provider) {
- return ForwardScheduler([=]() { return Scheduler->GetSupportedProvider(*provider); });
-}
-
-} // namespace profcollectd
-} // namespace android
diff --git a/profcollectd/libprofcollectd/binder_service.h b/profcollectd/libprofcollectd/binder_service.h
deleted file mode 100644
index 28c69958..00000000
--- a/profcollectd/libprofcollectd/binder_service.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// 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 "com/android/server/profcollect/BnProfCollectd.h"
-#include "scheduler.h"
-
-namespace android {
-namespace profcollectd {
-
-class ProfcollectdBinder : public BinderService<ProfcollectdBinder>,
- public ::com::android::server::profcollect::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(const std::string& tag) override;
- binder::Status ProcessProfile() override;
- binder::Status CreateProfileReport() override;
- binder::Status GetSupportedProvider(std::string* provider) override;
-
- protected:
- inline static std::unique_ptr<ProfcollectdScheduler> Scheduler;
-
- private:
- binder::Status ForwardScheduler(const std::function<OptError()>& action);
-};
-
-} // namespace profcollectd
-} // namespace android
diff --git a/profcollectd/libprofcollectd/bindings/libflags/Android.bp b/profcollectd/libprofcollectd/bindings/libflags/Android.bp
index f7cf2ed9..102a6c05 100644
--- a/profcollectd/libprofcollectd/bindings/libflags/Android.bp
+++ b/profcollectd/libprofcollectd/bindings/libflags/Android.bp
@@ -39,7 +39,7 @@ rust_library {
name: "libprofcollect_libflags_rust",
crate_name: "profcollect_libflags_rust",
srcs: ["lib.rs"],
- rustlibs: ["libprofcollect_libflags_bindgen"],
+ rlibs: ["libprofcollect_libflags_bindgen"],
static_libs: ["libprofcollect_libflags"],
shared_libs: [
"libc++",
diff --git a/profcollectd/libprofcollectd/compress.cpp b/profcollectd/libprofcollectd/compress.cpp
deleted file mode 100644
index 6a2b2e8e..00000000
--- a/profcollectd/libprofcollectd/compress.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// 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
deleted file mode 100644
index f99deca1..00000000
--- a/profcollectd/libprofcollectd/compress.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// 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/config.rs b/profcollectd/libprofcollectd/config.rs
new file mode 100644
index 00000000..e9a1d288
--- /dev/null
+++ b/profcollectd/libprofcollectd/config.rs
@@ -0,0 +1,95 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! ProfCollect configurations.
+
+use anyhow::Result;
+use lazy_static::lazy_static;
+use serde::{Deserialize, Serialize};
+use std::error::Error;
+use std::path::Path;
+use std::str::FromStr;
+use std::time::Duration;
+
+const PROFCOLLECT_CONFIG_NAMESPACE: &str = "profcollect_native_boot";
+
+lazy_static! {
+ pub static ref TRACE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/trace/");
+ pub static ref PROFILE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/output/");
+ pub static ref REPORT_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/report/");
+ pub static ref CONFIG_FILE: &'static Path =
+ Path::new("/data/misc/profcollectd/output/config.json");
+}
+
+#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
+pub struct Config {
+ /// Version of config file scheme, always equals to 1.
+ version: u32,
+ /// Device build fingerprint.
+ pub build_fingerprint: String,
+ /// Interval between collections.
+ pub collection_interval: Duration,
+ /// Length of time each collection lasts for.
+ pub sampling_period: Duration,
+ /// An optional filter to limit which binaries to or not to profile.
+ pub binary_filter: String,
+}
+
+impl Config {
+ pub fn from_env() -> Result<Self> {
+ Ok(Config {
+ version: 1,
+ build_fingerprint: get_build_fingerprint(),
+ collection_interval: Duration::from_secs(get_device_config(
+ "collection_interval",
+ 600,
+ )?),
+ sampling_period: Duration::from_millis(get_device_config("sampling_period", 500)?),
+ binary_filter: get_device_config("binary_filter", "".to_string())?,
+ })
+ }
+}
+
+impl ToString for Config {
+ fn to_string(&self) -> String {
+ serde_json::to_string(self).expect("Failed to deserialise configuration.")
+ }
+}
+
+impl FromStr for Config {
+ type Err = serde_json::Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ serde_json::from_str::<Config>(s)
+ }
+}
+
+fn get_build_fingerprint() -> String {
+ profcollect_libbase_rust::get_property("ro.build.fingerprint", "unknown").to_string()
+}
+
+fn get_device_config<T>(key: &str, default: T) -> Result<T>
+where
+ T: FromStr + ToString,
+ T::Err: Error + Send + Sync + 'static,
+{
+ let default = default.to_string();
+ let config = profcollect_libflags_rust::get_server_configurable_flag(
+ &PROFCOLLECT_CONFIG_NAMESPACE,
+ &key,
+ &default,
+ );
+ Ok(T::from_str(&config)?)
+}
diff --git a/profcollectd/libprofcollectd/config_utils.cpp b/profcollectd/libprofcollectd/config_utils.cpp
deleted file mode 100644
index ce92017d..00000000
--- a/profcollectd/libprofcollectd/config_utils.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// 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 "config_utils.h"
-
-#include <android-base/parsedouble.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <server_configurable_flags/get_flags.h>
-
-static const std::string PROFCOLLECT_CONFIG_NAMESPACE = "profcollect_native_boot";
-
-namespace android {
-namespace profcollectd {
-
-using ::android::base::GetProperty;
-using ::android::base::ParseFloat;
-using ::android::base::ParseInt;
-using ::server_configurable_flags::GetServerConfigurableFlag;
-
-std::string getBuildFingerprint() {
- return GetProperty("ro.build.fingerprint", "unknown");
-}
-
-std::string getConfigFlag(const config_t& config) {
- return GetServerConfigurableFlag(PROFCOLLECT_CONFIG_NAMESPACE, config.name, config.defaultValue);
-}
-
-int getConfigFlagInt(const config_t& config) {
- std::string value = getConfigFlag(config);
- int i;
- if (!ParseInt(value, &i)) {
- // Use default value if the server config value is malformed.
- return ParseInt(config.defaultValue, &i);
- }
- return i;
-}
-
-float getConfigFlagFloat(const config_t& config) {
- std::string value = getConfigFlag(config);
- float f;
- if (!ParseFloat(value, &f)) {
- // Use default value if the server config value is malformed.
- return ParseFloat(config.defaultValue, &f);
- }
- return f;
-}
-
-bool getConfigFlagBool(const config_t& config) {
- std::string value = getConfigFlag(config);
- return value == "true";
-}
-
-} // namespace profcollectd
-} // namespace android
diff --git a/profcollectd/libprofcollectd/config_utils.h b/profcollectd/libprofcollectd/config_utils.h
deleted file mode 100644
index b3a458a0..00000000
--- a/profcollectd/libprofcollectd/config_utils.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// 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 <variant>
-
-#include <android-base/parseint.h>
-#include <server_configurable_flags/get_flags.h>
-
-namespace android {
-namespace profcollectd {
-
-struct config_t {
- const char* name;
- const char* defaultValue;
-};
-
-std::string getBuildFingerprint();
-std::string getConfigFlag(const config_t& config);
-int getConfigFlagInt(const config_t& config);
-float getConfigFlagFloat(const config_t& config);
-bool getConfigFlagBool(const config_t& config);
-
-} // namespace profcollectd
-} // namespace android
diff --git a/profcollectd/libprofcollectd/hwtrace_provider.h b/profcollectd/libprofcollectd/hwtrace_provider.h
deleted file mode 100644
index 8e10745f..00000000
--- a/profcollectd/libprofcollectd/hwtrace_provider.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// 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;
-
- /**
- * Get the name of the trace provider.
- */
- virtual std::string GetName() = 0;
-
- /**
- * 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, const std::string& tag,
- 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/lib.rs b/profcollectd/libprofcollectd/lib.rs
index b8aafb44..0cb4e95a 100644
--- a/profcollectd/libprofcollectd/lib.rs
+++ b/profcollectd/libprofcollectd/lib.rs
@@ -16,54 +16,84 @@
//! ProfCollect Binder client interface.
-use profcollectd_aidl_interface::aidl::com::android::server::profcollect::IProfCollectd;
+mod config;
+mod report;
+mod scheduler;
+mod service;
+mod simpleperf_etm_trace_provider;
+mod trace_provider;
+
+use crate::binder::Status;
+use anyhow::{anyhow, Context, Error, Result};
+use profcollectd_aidl_interface::aidl::com::android::server::profcollect::IProfCollectd::{
+ self, BnProfCollectd,
+};
use profcollectd_aidl_interface::binder;
+use service::ProfcollectdBinderService;
+
+const PROFCOLLECTD_SERVICE_NAME: &str = "profcollectd";
/// Initialise profcollectd service.
-/// * `start` - Immediately schedule collection after service is initialised.
-pub fn init_service(start: bool) {
- unsafe {
- profcollectd_bindgen::android_profcollectd_InitService(start);
+/// * `schedule_now` - Immediately schedule collection after service is initialised.
+pub fn init_service(schedule_now: bool) -> Result<()> {
+ binder::ProcessState::start_thread_pool();
+
+ let profcollect_binder_service = ProfcollectdBinderService::new()?;
+ binder::add_service(
+ &PROFCOLLECTD_SERVICE_NAME,
+ BnProfCollectd::new_binder(profcollect_binder_service).as_binder(),
+ )
+ .context("Failed to register service.")?;
+
+ if schedule_now {
+ trace_once("boot")?;
+ schedule()?;
}
+
+ binder::ProcessState::join_thread_pool();
+ Ok(())
}
fn get_profcollectd_service() -> binder::Strong<dyn IProfCollectd::IProfCollectd> {
- let service_name = "profcollectd";
- binder::get_interface(&service_name).expect("could not get profcollectd binder service")
+ binder::get_interface(&PROFCOLLECTD_SERVICE_NAME)
+ .expect("Could not get profcollectd binder service")
+}
+
+// b/181225442
+fn binder_status_to_err(s: &Status) -> Error {
+ anyhow!(s.to_string())
}
/// Schedule periodic profile collection.
-pub fn schedule_collection() {
- let service = get_profcollectd_service();
- service.ScheduleCollection().unwrap();
+pub fn schedule() -> Result<()> {
+ get_profcollectd_service().schedule().map_err(|e| binder_status_to_err(&e))
}
/// Terminate periodic profile collection.
-pub fn terminate_collection() {
- let service = get_profcollectd_service();
- service.TerminateCollection().unwrap();
+pub fn terminate() -> Result<()> {
+ get_profcollectd_service().terminate().map_err(|e| binder_status_to_err(&e))
}
/// Immediately schedule a one-off trace.
-pub fn trace_once() {
- let service = get_profcollectd_service();
- service.TraceOnce("manual").unwrap();
+pub fn trace_once(tag: &str) -> Result<()> {
+ get_profcollectd_service().trace_once(tag).map_err(|e| binder_status_to_err(&e))
}
-/// Process the profiles.
-pub fn process() {
- let service = get_profcollectd_service();
- service.ProcessProfile().unwrap();
+/// Process traces.
+pub fn process() -> Result<()> {
+ get_profcollectd_service().process(true).map_err(|e| binder_status_to_err(&e))
}
-/// Create profile report.
-pub fn create_profile_report() {
- let service = get_profcollectd_service();
- service.CreateProfileReport().unwrap();
+/// Process traces and report profile.
+pub fn report() -> Result<()> {
+ get_profcollectd_service().report().map_err(|e| binder_status_to_err(&e))
}
-/// Read configs from environment variables.
-pub fn read_config() {
- let service = get_profcollectd_service();
- service.ReadConfig().unwrap();
+/// Inits logging for Android
+pub fn init_logging() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("profcollectd")
+ .with_min_level(log::Level::Error),
+ );
}
diff --git a/profcollectd/libprofcollectd/report.rs b/profcollectd/libprofcollectd/report.rs
new file mode 100644
index 00000000..b3c8c96a
--- /dev/null
+++ b/profcollectd/libprofcollectd/report.rs
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! Pack profiles into reports.
+
+use anyhow::{anyhow, Result};
+use std::fs::{read_dir, remove_file, File};
+use std::io::{Read, Write};
+use std::path::{Path, PathBuf};
+use zip::write::FileOptions;
+use zip::ZipWriter;
+
+pub fn pack_report(profile: &Path, report: &Path) -> Result<()> {
+ // TODO: Allow multiple profiles to be queued for upload.
+ let mut report = PathBuf::from(report);
+ report.push("report.zip");
+
+ // Remove the current report file if exists.
+ remove_file(&report).ok();
+
+ let report = File::create(report)?;
+ let options = FileOptions::default();
+ let mut zip = ZipWriter::new(report);
+
+ read_dir(profile)?
+ .filter_map(|e| e.ok())
+ .map(|e| e.path())
+ .filter(|e| e.is_file())
+ .try_for_each(|e| -> Result<()> {
+ let filename = e
+ .file_name()
+ .and_then(|f| f.to_str())
+ .ok_or_else(|| anyhow!("Malformed profile path: {}", e.display()))?;
+ zip.start_file(filename, options)?;
+ let mut f = File::open(e)?;
+ let mut buffer = Vec::new();
+ f.read_to_end(&mut buffer)?;
+ zip.write_all(&*buffer)?;
+ Ok(())
+ })?;
+ zip.finish()?;
+ Ok(())
+}
diff --git a/profcollectd/libprofcollectd/scheduler.cpp b/profcollectd/libprofcollectd/scheduler.cpp
deleted file mode 100644
index 7777ee46..00000000
--- a/profcollectd/libprofcollectd/scheduler.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-//
-// 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 <vector>
-
-#include <android-base/logging.h>
-
-#include "compress.h"
-#include "config_utils.h"
-#include "hwtrace_provider.h"
-#include "json/json.h"
-#include "json/writer.h"
-
-namespace fs = std::filesystem;
-
-namespace android {
-namespace profcollectd {
-
-// Default option values.
-static constexpr config_t CONFIG_BUILD_FINGERPRINT = {"build_fingerprint", "unknown"};
-static constexpr config_t CONFIG_COLLECTION_INTERVAL_SEC = {"collection_interval", "600"};
-static constexpr config_t CONFIG_SAMPLING_PERIOD_SEC = {"sampling_period", "0.5"};
-static constexpr config_t CONFIG_BINARY_FILTER = {"binary_filter", ""};
-
-static const fs::path OUT_ROOT_DIR("/data/misc/profcollectd");
-static const fs::path TRACE_DIR(OUT_ROOT_DIR / "trace");
-static const fs::path OUTPUT_DIR(OUT_ROOT_DIR / "output");
-static const fs::path REPORT_FILE(OUT_ROOT_DIR / "report.zip");
-
-// 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 = OUTPUT_DIR / "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(TRACE_DIR);
- ClearDir(OUTPUT_DIR);
-
- // 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("periodic");
- } 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.";
- } else {
- LOG(ERROR) << "No hardware trace provider available.";
- }
-}
-
-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 = getBuildFingerprint();
- config.collectionInterval =
- std::chrono::seconds(getConfigFlagInt(CONFIG_COLLECTION_INTERVAL_SEC));
- config.samplingPeriod =
- std::chrono::duration<float>(getConfigFlagFloat(CONFIG_SAMPLING_PERIOD_SEC));
- config.binaryFilter = getConfigFlag(CONFIG_BINARY_FILTER);
- 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::string& tag) {
- if (!hwtracer) {
- return "No trace provider registered.";
- }
-
- const std::lock_guard<std::mutex> lock(mu);
- bool success = hwtracer->Trace(TRACE_DIR, tag, config.samplingPeriod);
- if (!success) {
- static std::string errmsg = "Trace failed";
- return errmsg;
- }
- return std::nullopt;
-}
-
-OptError ProfcollectdScheduler::ProcessProfile() {
- if (!hwtracer) {
- return "No trace provider registered.";
- }
-
- std::thread t([&]() {
- const std::lock_guard<std::mutex> lock(mu);
- hwtracer->Process(TRACE_DIR, OUTPUT_DIR, config.binaryFilter);
- });
- t.detach();
-
- return std::nullopt;
-}
-
-OptError ProfcollectdScheduler::CreateProfileReport() {
- if (!hwtracer) {
- return "No trace provider registered.";
- }
-
- std::thread t([&]() {
- const std::lock_guard<std::mutex> lock(mu);
-
- hwtracer->Process(TRACE_DIR, OUTPUT_DIR, config.binaryFilter);
-
- std::vector<fs::path> profiles;
- if (fs::exists(OUTPUT_DIR)) {
- profiles.insert(profiles.begin(), fs::directory_iterator(OUTPUT_DIR),
- fs::directory_iterator());
- }
- CompressFiles(REPORT_FILE, profiles);
- });
- t.detach();
-
- return std::nullopt;
-}
-
-OptError ProfcollectdScheduler::GetSupportedProvider(std::string& provider) {
- provider = hwtracer ? hwtracer->GetName() : "";
- return std::nullopt;
-}
-
-std::ostream& operator<<(std::ostream& os, const ProfcollectdScheduler::Config& config) {
- Json::Value root;
- root[CONFIG_BUILD_FINGERPRINT.name] = config.buildFingerprint;
- root[CONFIG_COLLECTION_INTERVAL_SEC.name] =
- static_cast<Json::Int64>(config.collectionInterval.count());
- root[CONFIG_SAMPLING_PERIOD_SEC.name] = config.samplingPeriod.count();
- root[CONFIG_BINARY_FILTER.name] = config.binaryFilter.c_str();
- Json::StreamWriterBuilder factory;
- std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
- writer->write(root, &os);
- return os;
-}
-
-std::istream& operator>>(std::istream& is, ProfcollectdScheduler::Config& config) {
- Json::Value root;
- Json::CharReaderBuilder builder;
- std::string errorMessage;
- if (!Json::parseFromStream(builder, is, &root, &errorMessage)) {
- return is;
- }
-
- config.buildFingerprint = root[CONFIG_BUILD_FINGERPRINT.name].asString();
- config.collectionInterval =
- std::chrono::seconds(root[CONFIG_COLLECTION_INTERVAL_SEC.name].asInt64());
- config.samplingPeriod =
- std::chrono::duration<float>(root[CONFIG_SAMPLING_PERIOD_SEC.name].asFloat());
- config.binaryFilter = root[CONFIG_BINARY_FILTER.name].asString();
-
- return is;
-}
-
-} // namespace profcollectd
-} // namespace android
diff --git a/profcollectd/libprofcollectd/scheduler.h b/profcollectd/libprofcollectd/scheduler.h
deleted file mode 100644
index b1ec07e2..00000000
--- a/profcollectd/libprofcollectd/scheduler.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// 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(const std::string& tag);
- OptError ProcessProfile();
- OptError CreateProfileReport();
- OptError GetSupportedProvider(std::string& provider);
-
- 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
diff --git a/profcollectd/libprofcollectd/scheduler.rs b/profcollectd/libprofcollectd/scheduler.rs
new file mode 100644
index 00000000..cfd0381f
--- /dev/null
+++ b/profcollectd/libprofcollectd/scheduler.rs
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! ProfCollect tracing scheduler.
+
+use std::sync::mpsc::{sync_channel, SyncSender};
+use std::sync::Arc;
+use std::sync::Mutex;
+use std::thread;
+
+use crate::config::{Config, PROFILE_OUTPUT_DIR, TRACE_OUTPUT_DIR};
+use crate::trace_provider::{self, TraceProvider};
+use anyhow::{anyhow, ensure, Context, Result};
+
+pub struct Scheduler {
+ /// Signal to terminate the periodic collection worker thread, None if periodic collection is
+ /// not scheduled.
+ termination_ch: Option<SyncSender<()>>,
+ /// The preferred trace provider for the system.
+ trace_provider: Arc<Mutex<dyn TraceProvider + Send>>,
+}
+
+impl Scheduler {
+ pub fn new() -> Result<Self> {
+ let p = trace_provider::get_trace_provider()?;
+ Ok(Scheduler { termination_ch: None, trace_provider: p })
+ }
+
+ fn is_scheduled(&self) -> bool {
+ self.termination_ch.is_some()
+ }
+
+ pub fn schedule_periodic(&mut self, config: &Config) -> Result<()> {
+ ensure!(!self.is_scheduled(), "Already scheduled.");
+
+ let (sender, receiver) = sync_channel(1);
+ self.termination_ch = Some(sender);
+
+ // Clone config and trace_provider ARC for the worker thread.
+ let config = config.clone();
+ let trace_provider = self.trace_provider.clone();
+
+ thread::spawn(move || {
+ loop {
+ match receiver.recv_timeout(config.collection_interval) {
+ Ok(_) => break,
+ Err(_) => {
+ // Did not receive a termination signal, initiate trace event.
+ trace_provider.lock().unwrap().trace(
+ &TRACE_OUTPUT_DIR,
+ "periodic",
+ &config.sampling_period,
+ );
+ }
+ }
+ }
+ });
+ Ok(())
+ }
+
+ pub fn terminate_periodic(&mut self) -> Result<()> {
+ self.termination_ch
+ .as_ref()
+ .ok_or_else(|| anyhow!("Not scheduled"))?
+ .send(())
+ .context("Scheduler worker disappeared.")?;
+ self.termination_ch = None;
+ Ok(())
+ }
+
+ pub fn one_shot(&self, config: &Config, tag: &str) -> Result<()> {
+ let trace_provider = self.trace_provider.clone();
+ trace_provider.lock().unwrap().trace(&TRACE_OUTPUT_DIR, tag, &config.sampling_period);
+ Ok(())
+ }
+
+ pub fn process(&self, blocking: bool) -> Result<()> {
+ let trace_provider = self.trace_provider.clone();
+ let handle = thread::spawn(move || {
+ trace_provider
+ .lock()
+ .unwrap()
+ .process(&TRACE_OUTPUT_DIR, &PROFILE_OUTPUT_DIR)
+ .expect("Failed to process profiles.");
+ });
+ if blocking {
+ handle.join().map_err(|_| anyhow!("Profile process thread panicked."))?;
+ }
+ Ok(())
+ }
+
+ pub fn get_trace_provider_name(&self) -> &'static str {
+ self.trace_provider.lock().unwrap().get_name()
+ }
+}
diff --git a/profcollectd/libprofcollectd/service.rs b/profcollectd/libprofcollectd/service.rs
new file mode 100644
index 00000000..c01b8492
--- /dev/null
+++ b/profcollectd/libprofcollectd/service.rs
@@ -0,0 +1,118 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! ProfCollect Binder service implementation.
+
+use anyhow::{Context, Error, Result};
+use binder::public_api::Result as BinderResult;
+use binder::Status;
+use profcollectd_aidl_interface::aidl::com::android::server::profcollect::IProfCollectd::IProfCollectd;
+use std::ffi::CString;
+use std::fs::{create_dir, read_to_string, remove_dir_all, write};
+use std::{
+ str::FromStr,
+ sync::{Mutex, MutexGuard},
+};
+
+use crate::config::{Config, CONFIG_FILE, PROFILE_OUTPUT_DIR, REPORT_OUTPUT_DIR, TRACE_OUTPUT_DIR};
+use crate::report::pack_report;
+use crate::scheduler::Scheduler;
+
+fn err_to_binder_status(msg: Error) -> Status {
+ let msg = CString::new(msg.to_string()).expect("Failed to convert to CString");
+ Status::new_service_specific_error(1, Some(&msg))
+}
+
+pub struct ProfcollectdBinderService {
+ lock: Mutex<Lock>,
+}
+
+struct Lock {
+ config: Config,
+ scheduler: Scheduler,
+}
+
+impl binder::Interface for ProfcollectdBinderService {}
+
+impl IProfCollectd for ProfcollectdBinderService {
+ fn schedule(&self) -> BinderResult<()> {
+ let lock = &mut *self.lock();
+ lock.scheduler
+ .schedule_periodic(&lock.config)
+ .context("Failed to schedule collection.")
+ .map_err(err_to_binder_status)
+ }
+ fn terminate(&self) -> BinderResult<()> {
+ self.lock()
+ .scheduler
+ .terminate_periodic()
+ .context("Failed to terminate collection.")
+ .map_err(err_to_binder_status)
+ }
+ fn trace_once(&self, tag: &str) -> BinderResult<()> {
+ let lock = &mut *self.lock();
+ lock.scheduler
+ .one_shot(&lock.config, tag)
+ .context("Failed to initiate an one-off trace.")
+ .map_err(err_to_binder_status)
+ }
+ fn process(&self, blocking: bool) -> BinderResult<()> {
+ let lock = &mut *self.lock();
+ lock.scheduler
+ .process(blocking)
+ .context("Failed to process profiles.")
+ .map_err(err_to_binder_status)
+ }
+ fn report(&self) -> BinderResult<()> {
+ self.process(true)?;
+ pack_report(&PROFILE_OUTPUT_DIR, &REPORT_OUTPUT_DIR)
+ .context("Failed to create profile report.")
+ .map_err(err_to_binder_status)
+ }
+ fn get_supported_provider(&self) -> BinderResult<String> {
+ Ok(self.lock().scheduler.get_trace_provider_name().to_string())
+ }
+}
+
+impl ProfcollectdBinderService {
+ pub fn new() -> Result<Self> {
+ let new_scheduler = Scheduler::new()?;
+ let new_config = Config::from_env()?;
+
+ let config_changed = read_to_string(*CONFIG_FILE)
+ .ok()
+ .and_then(|s| Config::from_str(&s).ok())
+ .filter(|c| new_config == *c)
+ .is_none();
+
+ if config_changed {
+ log::info!("Config change detected, clearing traces.");
+ remove_dir_all(*PROFILE_OUTPUT_DIR)?;
+ remove_dir_all(*TRACE_OUTPUT_DIR)?;
+ create_dir(*PROFILE_OUTPUT_DIR)?;
+ create_dir(*TRACE_OUTPUT_DIR)?;
+ write(*CONFIG_FILE, &new_config.to_string())?;
+ }
+
+ Ok(ProfcollectdBinderService {
+ lock: Mutex::new(Lock { scheduler: new_scheduler, config: new_config }),
+ })
+ }
+
+ fn lock(&self) -> MutexGuard<Lock> {
+ self.lock.lock().unwrap()
+ }
+}
diff --git a/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp b/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp
deleted file mode 100644
index 5e8c55bc..00000000
--- a/profcollectd/libprofcollectd/simpleperf_etm_provider.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// 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();
- std::string GetName();
- bool Trace(const fs::path& outputPath, const std::string& tag,
- 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();
-}
-
-std::string SimpleperfETMProvider::GetName() {
- static constexpr const char* name = "simpleperf_etm";
- return name;
-}
-
-bool SimpleperfETMProvider::Trace(const fs::path& outputPath, const std::string& tag,
- std::chrono::duration<float> samplingPeriod) {
- const std::string timestamp = GetTimestamp();
- auto outputFile = outputPath / (timestamp + "_" + tag + 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/libprofcollectd/simpleperf_etm_trace_provider.rs b/profcollectd/libprofcollectd/simpleperf_etm_trace_provider.rs
new file mode 100644
index 00000000..c4b9db76
--- /dev/null
+++ b/profcollectd/libprofcollectd/simpleperf_etm_trace_provider.rs
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! Trace provider backed by ARM Coresight ETM, using simpleperf tool.
+
+use anyhow::{anyhow, Result};
+use std::fs::{read_dir, remove_file};
+use std::path::{Path, PathBuf};
+use std::time::Duration;
+use trace_provider::TraceProvider;
+
+use crate::trace_provider;
+
+static ETM_TRACEFILE_EXTENSION: &str = "etmtrace";
+static ETM_PROFILE_EXTENSION: &str = "data";
+
+pub struct SimpleperfEtmTraceProvider {}
+
+impl TraceProvider for SimpleperfEtmTraceProvider {
+ fn get_name(&self) -> &'static str {
+ "simpleperf_etm"
+ }
+
+ fn trace(&self, trace_dir: &Path, tag: &str, sampling_period: &Duration) {
+ let mut trace_file = PathBuf::from(trace_dir);
+ trace_file.push(trace_provider::construct_file_name(tag));
+ trace_file.set_extension(ETM_TRACEFILE_EXTENSION);
+
+ simpleperf_profcollect::record(&trace_file, sampling_period)
+ }
+
+ fn process(&self, trace_dir: &Path, profile_dir: &Path) -> Result<()> {
+ read_dir(trace_dir)?
+ .filter_map(|e| e.ok())
+ .map(|e| e.path())
+ .filter(|e| {
+ e.is_file()
+ && e.extension()
+ .and_then(|f| f.to_str())
+ .filter(|ext| ext == &ETM_TRACEFILE_EXTENSION)
+ .is_some()
+ })
+ .try_for_each(|trace_file| -> Result<()> {
+ let mut profile_file = PathBuf::from(profile_dir);
+ profile_file.push(
+ trace_file
+ .file_name()
+ .ok_or_else(|| anyhow!("Malformed trace path: {}", trace_file.display()))?,
+ );
+ profile_file.set_extension(ETM_PROFILE_EXTENSION);
+ simpleperf_profcollect::process(&trace_file, &profile_file);
+ remove_file(&trace_file)?;
+ Ok(())
+ })
+ }
+}
+
+impl SimpleperfEtmTraceProvider {
+ pub fn supported() -> bool {
+ simpleperf_profcollect::has_support()
+ }
+}
diff --git a/profcollectd/libprofcollectd/trace_provider.rs b/profcollectd/libprofcollectd/trace_provider.rs
new file mode 100644
index 00000000..ed0d56f5
--- /dev/null
+++ b/profcollectd/libprofcollectd/trace_provider.rs
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2021 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.
+//
+
+//! ProfCollect trace provider trait and helper functions.
+
+use anyhow::{anyhow, Result};
+use chrono::Utc;
+use std::path::Path;
+use std::sync::{Arc, Mutex};
+use std::time::Duration;
+
+use crate::simpleperf_etm_trace_provider::SimpleperfEtmTraceProvider;
+
+pub trait TraceProvider {
+ fn get_name(&self) -> &'static str;
+ fn trace(&self, trace_dir: &Path, tag: &str, sampling_period: &Duration);
+ fn process(&self, trace_dir: &Path, profile_dir: &Path) -> Result<()>;
+}
+
+pub fn get_trace_provider() -> Result<Arc<Mutex<dyn TraceProvider + Send>>> {
+ if SimpleperfEtmTraceProvider::supported() {
+ log::info!("simpleperf_etm trace provider registered.");
+ return Ok(Arc::new(Mutex::new(SimpleperfEtmTraceProvider {})));
+ }
+
+ Err(anyhow!("No trace provider found for this device."))
+}
+
+pub fn construct_file_name(tag: &str) -> String {
+ format!("{}_{}", Utc::now().format("%Y%m%d-%H%M%S"), tag)
+}
diff --git a/profcollectd/profcollectctl.rs b/profcollectd/profcollectctl.rs
index 4e75af44..97213172 100644
--- a/profcollectd/profcollectctl.rs
+++ b/profcollectd/profcollectctl.rs
@@ -16,11 +16,10 @@
//! Command to control profcollectd behaviour.
+use anyhow::{bail, Context, Result};
use std::env;
-fn print_help() {
- println!(
- r#"(
+const HELP_MSG: &str = r#"
usage: profcollectctl [command]
Command to control profcollectd behaviour.
@@ -33,47 +32,40 @@ command:
report Create a report containing all profiles.
reconfig Refresh configuration.
help Print this message.
-)"#
- );
-}
+"#;
+
+fn main() -> Result<()> {
+ libprofcollectd::init_logging();
-fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
- print_help();
- std::process::exit(1);
+ bail!("This program only takes one argument{}", &HELP_MSG);
}
let action = &args[1];
match action.as_str() {
"start" => {
println!("Scheduling profile collection");
- libprofcollectd::schedule_collection();
+ libprofcollectd::schedule().context("Failed to schedule collection.")?;
}
"stop" => {
println!("Terminating profile collection");
- libprofcollectd::terminate_collection();
+ libprofcollectd::terminate().context("Failed to terminate collection.")?;
}
"once" => {
println!("Trace once");
- libprofcollectd::trace_once();
+ libprofcollectd::trace_once("manual").context("Failed to trace.")?;
}
"process" => {
- println!("Processing traces in background");
- libprofcollectd::process();
+ println!("Processing traces");
+ libprofcollectd::process().context("Failed to process traces.")?;
}
"report" => {
- println!("Creating profile report in background");
- libprofcollectd::create_profile_report();
- }
- "reconfig" => {
- println!("Refreshing configuration");
- libprofcollectd::read_config();
- }
- "help" => print_help(),
- _ => {
- print_help();
- std::process::exit(1);
+ println!("Creating profile report");
+ libprofcollectd::report().context("Failed to create profile report.")?;
}
+ "help" => println!("{}", &HELP_MSG),
+ arg => bail!("Unknown argument: {}\n{}", &arg, &HELP_MSG),
}
+ Ok(())
}
diff --git a/profcollectd/profcollectd.rc b/profcollectd/profcollectd.rc
index bc4db95a..37fc846e 100644
--- a/profcollectd/profcollectd.rc
+++ b/profcollectd/profcollectd.rc
@@ -1,4 +1,4 @@
-service profcollectd /system/bin/profcollectd boot
+service profcollectd /system/bin/profcollectd
class late_start
disabled
oneshot
@@ -14,3 +14,4 @@ on post-fs-data
mkdir /data/misc/profcollectd 0770 root root
mkdir /data/misc/profcollectd/trace 0770 root root
mkdir /data/misc/profcollectd/output 0770 root root
+ mkdir /data/misc/profcollectd/report 0770 root root
diff --git a/profcollectd/profcollectd.rs b/profcollectd/profcollectd.rs
index 67563bff..0c2e87d9 100644
--- a/profcollectd/profcollectd.rs
+++ b/profcollectd/profcollectd.rs
@@ -16,33 +16,31 @@
//! Daemon program to collect system traces.
+use anyhow::{bail, Result};
use std::env;
-fn print_help() {
- println!(
- r#"(
+const HELP_MSG: &str = r#"
+profcollectd background daemon.
usage: profcollectd [command]
- boot Start daemon and schedule profile collection after a short delay.
- run Start daemon but do not schedule profile collection.
-)"#
- );
-}
+ nostart Start daemon but do not schedule profile collection.
+"#;
+
+fn main() -> Result<()> {
+ libprofcollectd::init_logging();
-fn main() {
let args: Vec<String> = env::args().collect();
- if args.len() != 2 {
- print_help();
- std::process::exit(1);
+ if args.len() > 2 {
+ bail!("This program only takes one or no argument{}", &HELP_MSG);
+ }
+ if args.len() == 1 {
+ libprofcollectd::init_service(true)?;
}
let action = &args[1];
match action.as_str() {
- "boot" => libprofcollectd::init_service(true),
- "run" => libprofcollectd::init_service(false),
- "help" => print_help(),
- _ => {
- print_help();
- std::process::exit(1);
- }
+ "nostart" => libprofcollectd::init_service(false)?,
+ "help" => println!("{}", &HELP_MSG),
+ arg => bail!("Unknown argument: {}\n{}", &arg, &HELP_MSG),
}
+ Ok(())
}