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