1 // Copyright 2019 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy 5 // of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 #[cfg(feature = "profiling")] use cpuprofiler::PROFILER; 16 use failure::Fallible; 17 use std::path::Path; 18 19 /// Facade for `cpuprofiler::PROFILER` to cope with the optional gperftools dependency and to 20 /// ensure profiling stops on `drop`. 21 pub struct ScopedProfiler {} 22 23 impl ScopedProfiler { 24 #[cfg(not(feature = "profiling"))] real_start<P: AsRef<Path>>(_path: P) -> Fallible<ScopedProfiler>25 fn real_start<P: AsRef<Path>>(_path: P) -> Fallible<ScopedProfiler> { 26 Err(format_err!("Compile-time \"profiling\" feature not enabled")) 27 } 28 29 #[cfg(feature = "profiling")] real_start<P: AsRef<Path>>(path: P) -> Fallible<ScopedProfiler>30 fn real_start<P: AsRef<Path>>(path: P) -> Fallible<ScopedProfiler> { 31 let path = path.as_ref(); 32 let path_str = match path.to_str() { 33 Some(path_str) => path_str, 34 None => return Err(format_err!("Invalid path {}", path.display())), 35 }; 36 let mut profiler = PROFILER.lock().unwrap(); 37 info!("Starting CPU profiler and writing results to {}", path_str); 38 profiler.start(path_str.as_bytes()).unwrap(); 39 Ok(ScopedProfiler {}) 40 } 41 42 /// Starts the CPU profiler and stores the profile in the given `path`. 43 /// 44 /// This will fail if sandboxfs was built without the "profiler" feature. This may fail if 45 /// there are problems initializing the profiler. 46 /// 47 /// Note that, due to the nature of profiling, there can only be one `ScopedPointer` active at 48 /// any given time. Trying to create two instances of this will cause this method to block 49 /// until the other object is dropped. start<P: AsRef<Path>>(path: P) -> Fallible<ScopedProfiler>50 pub fn start<P: AsRef<Path>>(path: P) -> Fallible<ScopedProfiler> { 51 ScopedProfiler::real_start(path) 52 } 53 54 #[cfg(not(feature = "profiling"))] real_stop(&mut self)55 fn real_stop(&mut self) { 56 } 57 58 #[cfg(feature = "profiling")] real_stop(&mut self)59 fn real_stop(&mut self) { 60 let mut profiler = PROFILER.lock().unwrap(); 61 profiler.stop().expect("Profiler apparently not active, but it must have been"); 62 info!("CPU profiler stopped"); 63 } 64 } 65 66 impl Drop for ScopedProfiler { drop(&mut self)67 fn drop(&mut self) { 68 self.real_stop() 69 } 70 } 71