xref: /aosp_15_r20/system/extras/profcollectd/libprofcollectd/service.rs (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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_system(&self, tag: &str) -> BinderResult<()>68     fn trace_system(&self, tag: &str) -> BinderResult<()> {
69         let lock = &mut *self.lock();
70         lock.scheduler
71             .trace_system(&lock.config, tag)
72             .context("Failed to perform system-wide trace.")
73             .map_err(err_to_binder_status)
74     }
trace_process(&self, tag: &str, process: &str, duration: f32) -> BinderResult<()>75     fn trace_process(&self, tag: &str, process: &str, duration: f32) -> BinderResult<()> {
76         let lock = &mut *self.lock();
77         lock.scheduler
78             .trace_process(&lock.config, tag, process, duration)
79             .context("Failed to perform process trace.")
80             .map_err(err_to_binder_status)
81     }
process(&self) -> BinderResult<()>82     fn process(&self) -> BinderResult<()> {
83         let lock = &mut *self.lock();
84         lock.scheduler
85             .process(&lock.config)
86             .context("Failed to process profiles.")
87             .map_err(err_to_binder_status)
88     }
report(&self, usage_setting: i32) -> BinderResult<String>89     fn report(&self, usage_setting: i32) -> BinderResult<String> {
90         self.process()?;
91 
92         let lock = &mut *self.lock();
93         pack_report(&PROFILE_OUTPUT_DIR, &REPORT_OUTPUT_DIR, &lock.config, usage_setting)
94             .context("Failed to create profile report.")
95             .map_err(err_to_binder_status)
96     }
get_supported_provider(&self) -> BinderResult<String>97     fn get_supported_provider(&self) -> BinderResult<String> {
98         Ok(self.lock().scheduler.get_trace_provider_name().to_string())
99     }
100 
registerProviderStatusCallback( &self, cb: &binder::Strong<(dyn IProviderStatusCallback)>, ) -> BinderResult<()>101     fn registerProviderStatusCallback(
102         &self,
103         cb: &binder::Strong<(dyn IProviderStatusCallback)>,
104     ) -> BinderResult<()> {
105         if self.lock().scheduler.is_provider_ready() {
106             if let Err(e) = cb.onProviderReady() {
107                 log::error!("Failed to call ProviderStatusCallback {:?}", e);
108             }
109             return Ok(());
110         }
111 
112         let cb_binder: SpIBinder = cb.as_binder();
113         self.lock().scheduler.register_provider_ready_callback(Box::new(move || {
114             if let Ok(cb) = cb_binder.into_interface::<dyn IProviderStatusCallback>() {
115                 if let Err(e) = cb.onProviderReady() {
116                     log::error!("Failed to call ProviderStatusCallback {:?}", e)
117                 }
118             } else {
119                 log::error!("SpIBinder is not a IProviderStatusCallback.");
120             }
121         }));
122         Ok(())
123     }
124 }
125 
126 impl ProfcollectdBinderService {
new() -> Result<Self>127     pub fn new() -> Result<Self> {
128         let new_scheduler = Scheduler::new()?;
129         let new_config = Config::from_env()?;
130 
131         let config_changed = read_to_string(*CONFIG_FILE)
132             .ok()
133             .and_then(|s| Config::from_str(&s).ok())
134             .filter(|c| new_config == *c)
135             .is_none();
136 
137         if config_changed {
138             log::info!("Config change detected, resetting profcollect.");
139             clear_data()?;
140 
141             write(*CONFIG_FILE, new_config.to_string())?;
142             new_scheduler.clear_trace_log()?;
143         }
144 
145         // Clear profile reports out of rentention period.
146         for report in read_dir(*REPORT_OUTPUT_DIR)? {
147             let report = report?.path();
148             let report_name = report
149                 .file_stem()
150                 .and_then(|f| f.to_str())
151                 .ok_or_else(|| anyhow!("Malformed path {}", report.display()))?;
152             let report_ts = get_report_ts(report_name);
153             if let Err(e) = report_ts {
154                 log::error!(
155                     "Cannot decode creation timestamp for report {}, caused by {}, deleting",
156                     report_name,
157                     e
158                 );
159                 remove_file(report)?;
160                 continue;
161             }
162             let report_age = report_ts.unwrap().elapsed()?;
163             if report_age > Duration::from_secs(REPORT_RETENTION_SECS) {
164                 log::info!("Report {} past rentention period, deleting", report_name);
165                 remove_file(report)?;
166             }
167         }
168 
169         Ok(ProfcollectdBinderService {
170             lock: Mutex::new(Lock { scheduler: new_scheduler, config: new_config }),
171         })
172     }
173 
lock(&self) -> MutexGuard<Lock>174     fn lock(&self) -> MutexGuard<Lock> {
175         self.lock.lock().unwrap()
176     }
177 }
178