1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 //! ProfCollect Binder service implementation.
18
19 use anyhow::{anyhow, Context, Error, Result};
20 use binder::Result as BinderResult;
21 use binder::{SpIBinder, Status};
22 use profcollectd_aidl_interface::aidl::com::android::server::profcollect::IProfCollectd::IProfCollectd;
23 use profcollectd_aidl_interface::aidl::com::android::server::profcollect::IProviderStatusCallback::IProviderStatusCallback;
24 use std::ffi::CString;
25 use std::fs::{read_dir, read_to_string, remove_file, write};
26 use std::str::FromStr;
27 use std::sync::{Mutex, MutexGuard};
28 use std::time::Duration;
29
30 use crate::config::{
31 clear_data, Config, CONFIG_FILE, PROFILE_OUTPUT_DIR, REPORT_OUTPUT_DIR, REPORT_RETENTION_SECS,
32 };
33 use crate::report::{get_report_ts, pack_report};
34 use crate::scheduler::Scheduler;
35
err_to_binder_status(msg: Error) -> Status36 pub fn err_to_binder_status(msg: Error) -> Status {
37 let msg = format!("{:#?}", msg);
38 let msg = CString::new(msg).expect("Failed to convert to CString");
39 Status::new_service_specific_error(1, Some(&msg))
40 }
41
42 pub struct ProfcollectdBinderService {
43 lock: Mutex<Lock>,
44 }
45
46 struct Lock {
47 config: Config,
48 scheduler: Scheduler,
49 }
50
51 impl binder::Interface for ProfcollectdBinderService {}
52
53 impl IProfCollectd for ProfcollectdBinderService {
schedule(&self) -> BinderResult<()>54 fn schedule(&self) -> BinderResult<()> {
55 let lock = &mut *self.lock();
56 lock.scheduler
57 .schedule_periodic(&lock.config)
58 .context("Failed to schedule collection.")
59 .map_err(err_to_binder_status)
60 }
terminate(&self) -> BinderResult<()>61 fn terminate(&self) -> BinderResult<()> {
62 self.lock()
63 .scheduler
64 .terminate_periodic()
65 .context("Failed to terminate collection.")
66 .map_err(err_to_binder_status)
67 }
trace_once(&self, tag: &str) -> BinderResult<()>68 fn trace_once(&self, tag: &str) -> BinderResult<()> {
69 let lock = &mut *self.lock();
70 lock.scheduler
71 .one_shot(&lock.config, tag)
72 .context("Failed to initiate an one-off trace.")
73 .map_err(err_to_binder_status)
74 }
process(&self) -> BinderResult<()>75 fn process(&self) -> BinderResult<()> {
76 let lock = &mut *self.lock();
77 lock.scheduler
78 .process(&lock.config)
79 .context("Failed to process profiles.")
80 .map_err(err_to_binder_status)
81 }
report(&self, usage_setting: i32) -> BinderResult<String>82 fn report(&self, usage_setting: i32) -> BinderResult<String> {
83 self.process()?;
84
85 let lock = &mut *self.lock();
86 pack_report(&PROFILE_OUTPUT_DIR, &REPORT_OUTPUT_DIR, &lock.config, usage_setting)
87 .context("Failed to create profile report.")
88 .map_err(err_to_binder_status)
89 }
get_supported_provider(&self) -> BinderResult<String>90 fn get_supported_provider(&self) -> BinderResult<String> {
91 Ok(self.lock().scheduler.get_trace_provider_name().to_string())
92 }
93
registerProviderStatusCallback( &self, cb: &binder::Strong<(dyn IProviderStatusCallback)>, ) -> BinderResult<()>94 fn registerProviderStatusCallback(
95 &self,
96 cb: &binder::Strong<(dyn IProviderStatusCallback)>,
97 ) -> BinderResult<()> {
98 if self.lock().scheduler.is_provider_ready() {
99 if let Err(e) = cb.onProviderReady() {
100 log::error!("Failed to call ProviderStatusCallback {:?}", e);
101 }
102 return Ok(());
103 }
104
105 let cb_binder: SpIBinder = cb.as_binder();
106 self.lock().scheduler.register_provider_ready_callback(Box::new(move || {
107 if let Ok(cb) = cb_binder.into_interface::<dyn IProviderStatusCallback>() {
108 if let Err(e) = cb.onProviderReady() {
109 log::error!("Failed to call ProviderStatusCallback {:?}", e)
110 }
111 } else {
112 log::error!("SpIBinder is not a IProviderStatusCallback.");
113 }
114 }));
115 Ok(())
116 }
117 }
118
119 impl ProfcollectdBinderService {
new() -> Result<Self>120 pub fn new() -> Result<Self> {
121 let new_scheduler = Scheduler::new()?;
122 let new_config = Config::from_env()?;
123
124 let config_changed = read_to_string(*CONFIG_FILE)
125 .ok()
126 .and_then(|s| Config::from_str(&s).ok())
127 .filter(|c| new_config == *c)
128 .is_none();
129
130 if config_changed {
131 log::info!("Config change detected, resetting profcollect.");
132 clear_data()?;
133
134 write(*CONFIG_FILE, new_config.to_string())?;
135 new_scheduler.clear_trace_log()?;
136 }
137
138 // Clear profile reports out of rentention period.
139 for report in read_dir(*REPORT_OUTPUT_DIR)? {
140 let report = report?.path();
141 let report_name = report
142 .file_stem()
143 .and_then(|f| f.to_str())
144 .ok_or_else(|| anyhow!("Malformed path {}", report.display()))?;
145 let report_ts = get_report_ts(report_name);
146 if let Err(e) = report_ts {
147 log::error!(
148 "Cannot decode creation timestamp for report {}, caused by {}, deleting",
149 report_name,
150 e
151 );
152 remove_file(report)?;
153 continue;
154 }
155 let report_age = report_ts.unwrap().elapsed()?;
156 if report_age > Duration::from_secs(REPORT_RETENTION_SECS) {
157 log::info!("Report {} past rentention period, deleting", report_name);
158 remove_file(report)?;
159 }
160 }
161
162 Ok(ProfcollectdBinderService {
163 lock: Mutex::new(Lock { scheduler: new_scheduler, config: new_config }),
164 })
165 }
166
lock(&self) -> MutexGuard<Lock>167 fn lock(&self) -> MutexGuard<Lock> {
168 self.lock.lock().unwrap()
169 }
170 }
171