Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 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 tracing scheduler. |
| 18 | |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 19 | use std::fs; |
Yabin Cui | f158a75 | 2022-01-10 15:35:59 -0800 | [diff] [blame] | 20 | use std::mem; |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 21 | use std::path::Path; |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 22 | use std::sync::mpsc::{sync_channel, SyncSender}; |
| 23 | use std::sync::Arc; |
| 24 | use std::sync::Mutex; |
| 25 | use std::thread; |
Yabin Cui | f158a75 | 2022-01-10 15:35:59 -0800 | [diff] [blame] | 26 | use std::time::{Duration, Instant}; |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 27 | |
Yi Kong | 0507c80 | 2024-04-08 06:56:57 +0000 | [diff] [blame] | 28 | use crate::config::{get_sampling_period, Config, LOG_FILE, PROFILE_OUTPUT_DIR, TRACE_OUTPUT_DIR}; |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 29 | use crate::trace_provider::{self, TraceProvider}; |
| 30 | use anyhow::{anyhow, ensure, Context, Result}; |
| 31 | |
| 32 | pub struct Scheduler { |
| 33 | /// Signal to terminate the periodic collection worker thread, None if periodic collection is |
| 34 | /// not scheduled. |
| 35 | termination_ch: Option<SyncSender<()>>, |
| 36 | /// The preferred trace provider for the system. |
| 37 | trace_provider: Arc<Mutex<dyn TraceProvider + Send>>, |
Yabin Cui | f158a75 | 2022-01-10 15:35:59 -0800 | [diff] [blame] | 38 | provider_ready_callbacks: Arc<Mutex<Vec<Box<dyn FnOnce() + Send>>>>, |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 39 | } |
| 40 | |
| 41 | impl Scheduler { |
| 42 | pub fn new() -> Result<Self> { |
| 43 | let p = trace_provider::get_trace_provider()?; |
Yabin Cui | f1d91d2 | 2023-04-27 12:50:59 -0700 | [diff] [blame] | 44 | p.lock().map_err(|e| anyhow!(e.to_string()))?.set_log_file(&LOG_FILE); |
Yabin Cui | f158a75 | 2022-01-10 15:35:59 -0800 | [diff] [blame] | 45 | Ok(Scheduler { |
| 46 | termination_ch: None, |
| 47 | trace_provider: p, |
| 48 | provider_ready_callbacks: Arc::new(Mutex::new(Vec::new())), |
| 49 | }) |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | fn is_scheduled(&self) -> bool { |
| 53 | self.termination_ch.is_some() |
| 54 | } |
| 55 | |
| 56 | pub fn schedule_periodic(&mut self, config: &Config) -> Result<()> { |
| 57 | ensure!(!self.is_scheduled(), "Already scheduled."); |
| 58 | |
| 59 | let (sender, receiver) = sync_channel(1); |
| 60 | self.termination_ch = Some(sender); |
| 61 | |
| 62 | // Clone config and trace_provider ARC for the worker thread. |
| 63 | let config = config.clone(); |
| 64 | let trace_provider = self.trace_provider.clone(); |
| 65 | |
| 66 | thread::spawn(move || { |
| 67 | loop { |
| 68 | match receiver.recv_timeout(config.collection_interval) { |
| 69 | Ok(_) => break, |
| 70 | Err(_) => { |
| 71 | // Did not receive a termination signal, initiate trace event. |
Chris Wailes | 56a7a42 | 2022-11-16 15:49:28 -0800 | [diff] [blame] | 72 | if check_space_limit(&TRACE_OUTPUT_DIR, &config).unwrap() { |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 73 | trace_provider.lock().unwrap().trace_system( |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 74 | &TRACE_OUTPUT_DIR, |
| 75 | "periodic", |
Yi Kong | 0507c80 | 2024-04-08 06:56:57 +0000 | [diff] [blame] | 76 | &get_sampling_period(), |
Yabin Cui | fc1f178 | 2023-05-02 15:12:32 -0700 | [diff] [blame] | 77 | &config.binary_filter, |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 78 | ); |
| 79 | } |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 80 | } |
| 81 | } |
| 82 | } |
| 83 | }); |
| 84 | Ok(()) |
| 85 | } |
| 86 | |
| 87 | pub fn terminate_periodic(&mut self) -> Result<()> { |
| 88 | self.termination_ch |
| 89 | .as_ref() |
| 90 | .ok_or_else(|| anyhow!("Not scheduled"))? |
| 91 | .send(()) |
| 92 | .context("Scheduler worker disappeared.")?; |
| 93 | self.termination_ch = None; |
| 94 | Ok(()) |
| 95 | } |
| 96 | |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 97 | pub fn trace_system(&self, config: &Config, tag: &str) -> Result<()> { |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 98 | let trace_provider = self.trace_provider.clone(); |
Chris Wailes | 56a7a42 | 2022-11-16 15:49:28 -0800 | [diff] [blame] | 99 | if check_space_limit(&TRACE_OUTPUT_DIR, config)? { |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 100 | trace_provider.lock().unwrap().trace_system( |
Yabin Cui | fc1f178 | 2023-05-02 15:12:32 -0700 | [diff] [blame] | 101 | &TRACE_OUTPUT_DIR, |
| 102 | tag, |
Yi Kong | 0507c80 | 2024-04-08 06:56:57 +0000 | [diff] [blame] | 103 | &get_sampling_period(), |
Yabin Cui | fc1f178 | 2023-05-02 15:12:32 -0700 | [diff] [blame] | 104 | &config.binary_filter, |
| 105 | ); |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 106 | } |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 107 | Ok(()) |
| 108 | } |
| 109 | |
Yi Kong | 90fc4c9 | 2024-07-22 14:30:54 +0900 | [diff] [blame] | 110 | pub fn trace_process( |
| 111 | &self, |
| 112 | config: &Config, |
| 113 | tag: &str, |
| 114 | processes: &str, |
| 115 | samplng_period: f32, |
| 116 | ) -> Result<()> { |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 117 | let trace_provider = self.trace_provider.clone(); |
Yi Kong | 90fc4c9 | 2024-07-22 14:30:54 +0900 | [diff] [blame] | 118 | let duration = match samplng_period { |
| 119 | 0.0 => get_sampling_period(), |
| 120 | _ => Duration::from_millis(samplng_period as u64), |
| 121 | }; |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 122 | if check_space_limit(&TRACE_OUTPUT_DIR, config)? { |
| 123 | trace_provider.lock().unwrap().trace_process( |
| 124 | &TRACE_OUTPUT_DIR, |
| 125 | tag, |
Yi Kong | 90fc4c9 | 2024-07-22 14:30:54 +0900 | [diff] [blame] | 126 | &duration, |
Yi Kong | bae2748 | 2024-07-09 02:41:54 +0900 | [diff] [blame] | 127 | processes, |
| 128 | ); |
| 129 | } |
| 130 | Ok(()) |
| 131 | } |
| 132 | |
Yi Kong | 87d0a17 | 2021-12-09 01:37:57 +0800 | [diff] [blame] | 133 | pub fn process(&self, config: &Config) -> Result<()> { |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 134 | let trace_provider = self.trace_provider.clone(); |
Yi Kong | fd24c6e | 2021-12-05 13:17:39 +0800 | [diff] [blame] | 135 | trace_provider |
| 136 | .lock() |
| 137 | .unwrap() |
Yi Kong | 87d0a17 | 2021-12-09 01:37:57 +0800 | [diff] [blame] | 138 | .process(&TRACE_OUTPUT_DIR, &PROFILE_OUTPUT_DIR, &config.binary_filter) |
Yi Kong | fd24c6e | 2021-12-05 13:17:39 +0800 | [diff] [blame] | 139 | .context("Failed to process profiles.")?; |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 140 | Ok(()) |
| 141 | } |
| 142 | |
| 143 | pub fn get_trace_provider_name(&self) -> &'static str { |
| 144 | self.trace_provider.lock().unwrap().get_name() |
| 145 | } |
Yabin Cui | f158a75 | 2022-01-10 15:35:59 -0800 | [diff] [blame] | 146 | |
| 147 | pub fn is_provider_ready(&self) -> bool { |
| 148 | self.trace_provider.lock().unwrap().is_ready() |
| 149 | } |
| 150 | |
| 151 | pub fn register_provider_ready_callback(&self, cb: Box<dyn FnOnce() + Send>) { |
| 152 | let mut locked_callbacks = self.provider_ready_callbacks.lock().unwrap(); |
| 153 | locked_callbacks.push(cb); |
| 154 | if locked_callbacks.len() == 1 { |
| 155 | self.start_thread_waiting_for_provider_ready(); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | fn start_thread_waiting_for_provider_ready(&self) { |
| 160 | let provider = self.trace_provider.clone(); |
| 161 | let callbacks = self.provider_ready_callbacks.clone(); |
| 162 | |
| 163 | thread::spawn(move || { |
| 164 | let start_time = Instant::now(); |
| 165 | loop { |
| 166 | let elapsed = Instant::now().duration_since(start_time); |
| 167 | if provider.lock().unwrap().is_ready() { |
| 168 | break; |
| 169 | } |
| 170 | // Decide check period based on how long we have waited: |
| 171 | // For the first 10s waiting, check every 100ms (likely to work on EVT devices). |
| 172 | // For the first 10m waiting, check every 10s (likely to work on DVT devices). |
| 173 | // For others, check every 10m. |
| 174 | let sleep_duration = if elapsed < Duration::from_secs(10) { |
| 175 | Duration::from_millis(100) |
| 176 | } else if elapsed < Duration::from_secs(60 * 10) { |
| 177 | Duration::from_secs(10) |
| 178 | } else { |
| 179 | Duration::from_secs(60 * 10) |
| 180 | }; |
| 181 | thread::sleep(sleep_duration); |
| 182 | } |
| 183 | |
| 184 | let mut locked_callbacks = callbacks.lock().unwrap(); |
| 185 | let v = mem::take(&mut *locked_callbacks); |
| 186 | for cb in v { |
| 187 | cb(); |
| 188 | } |
| 189 | }); |
| 190 | } |
Yabin Cui | f1d91d2 | 2023-04-27 12:50:59 -0700 | [diff] [blame] | 191 | |
| 192 | pub fn clear_trace_log(&self) -> Result<()> { |
| 193 | let provider = self.trace_provider.lock().map_err(|e| anyhow!(e.to_string()))?; |
| 194 | provider.reset_log_file(); |
| 195 | let mut result = Ok(()); |
| 196 | if LOG_FILE.exists() { |
| 197 | result = fs::remove_file(*LOG_FILE).map_err(|e| anyhow!(e)); |
| 198 | } |
| 199 | provider.set_log_file(&LOG_FILE); |
| 200 | result |
| 201 | } |
Yi Kong | e3aab14 | 2021-03-02 13:58:25 +0800 | [diff] [blame] | 202 | } |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 203 | |
| 204 | /// Run if space usage is under limit. |
| 205 | fn check_space_limit(path: &Path, config: &Config) -> Result<bool> { |
Yi Kong | c006585 | 2021-12-14 15:57:01 +0800 | [diff] [blame] | 206 | // Returns the size of a directory, non-recursive. |
| 207 | let dir_size = |path| -> Result<u64> { |
| 208 | fs::read_dir(path)?.try_fold(0, |acc, file| { |
| 209 | let metadata = file?.metadata()?; |
| 210 | let size = if metadata.is_file() { metadata.len() } else { 0 }; |
| 211 | Ok(acc + size) |
| 212 | }) |
| 213 | }; |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 214 | |
Yi Kong | 610ca1c | 2024-06-04 05:25:02 +0000 | [diff] [blame] | 215 | if dir_size(path)? > config.max_trace_limit_mb * 1024 * 1024 { |
Yi Kong | c006585 | 2021-12-14 15:57:01 +0800 | [diff] [blame] | 216 | log::error!("trace storage exhausted."); |
| 217 | return Ok(false); |
| 218 | } |
| 219 | Ok(true) |
Yi Kong | 581aa3a | 2021-11-24 00:19:30 +0800 | [diff] [blame] | 220 | } |