diff options
Diffstat (limited to 'crates/ra_prof')
-rw-r--r-- | crates/ra_prof/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/ra_prof/src/google_cpu_profiler.rs | 39 | ||||
-rw-r--r-- | crates/ra_prof/src/lib.rs | 32 |
3 files changed, 62 insertions, 12 deletions
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml index 84dcc9813..d35adeeeb 100644 --- a/crates/ra_prof/Cargo.toml +++ b/crates/ra_prof/Cargo.toml | |||
@@ -9,10 +9,9 @@ publish = false | |||
9 | once_cell = "0.2.0" | 9 | once_cell = "0.2.0" |
10 | itertools = "0.8.0" | 10 | itertools = "0.8.0" |
11 | backtrace = "0.3.28" | 11 | backtrace = "0.3.28" |
12 | cpuprofiler = { version = "0.0.3", optional = true } | ||
13 | jemallocator = { version = "0.3.2", optional = true } | 12 | jemallocator = { version = "0.3.2", optional = true } |
14 | jemalloc-ctl = { version = "0.3.2", optional = true } | 13 | jemalloc-ctl = { version = "0.3.2", optional = true } |
15 | 14 | ||
16 | |||
17 | [features] | 15 | [features] |
18 | jemalloc = [ "jemallocator", "jemalloc-ctl" ] | 16 | jemalloc = [ "jemallocator", "jemalloc-ctl" ] |
17 | cpu_profiler = [] | ||
diff --git a/crates/ra_prof/src/google_cpu_profiler.rs b/crates/ra_prof/src/google_cpu_profiler.rs new file mode 100644 index 000000000..db865c65b --- /dev/null +++ b/crates/ra_prof/src/google_cpu_profiler.rs | |||
@@ -0,0 +1,39 @@ | |||
1 | //! https://github.com/gperftools/gperftools | ||
2 | |||
3 | use std::{ | ||
4 | ffi::CString, | ||
5 | os::raw::c_char, | ||
6 | path::Path, | ||
7 | sync::atomic::{AtomicUsize, Ordering}, | ||
8 | }; | ||
9 | |||
10 | #[link(name = "profiler")] | ||
11 | #[allow(non_snake_case)] | ||
12 | extern "C" { | ||
13 | fn ProfilerStart(fname: *const c_char) -> i32; | ||
14 | fn ProfilerStop(); | ||
15 | } | ||
16 | |||
17 | static PROFILER_STATE: AtomicUsize = AtomicUsize::new(OFF); | ||
18 | const OFF: usize = 0; | ||
19 | const ON: usize = 1; | ||
20 | const PENDING: usize = 2; | ||
21 | |||
22 | pub fn start(path: &Path) { | ||
23 | if PROFILER_STATE.compare_and_swap(OFF, PENDING, Ordering::SeqCst) != OFF { | ||
24 | panic!("profiler already started"); | ||
25 | } | ||
26 | let path = CString::new(path.display().to_string()).unwrap(); | ||
27 | if unsafe { ProfilerStart(path.as_ptr()) } == 0 { | ||
28 | panic!("profiler failed to start") | ||
29 | } | ||
30 | assert!(PROFILER_STATE.compare_and_swap(PENDING, ON, Ordering::SeqCst) == PENDING); | ||
31 | } | ||
32 | |||
33 | pub fn stop() { | ||
34 | if PROFILER_STATE.compare_and_swap(ON, PENDING, Ordering::SeqCst) != ON { | ||
35 | panic!("profiler is not started") | ||
36 | } | ||
37 | unsafe { ProfilerStop() }; | ||
38 | assert!(PROFILER_STATE.compare_and_swap(PENDING, OFF, Ordering::SeqCst) == PENDING); | ||
39 | } | ||
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index 6d44fef33..d32a289be 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | mod memory_usage; | 1 | mod memory_usage; |
2 | #[cfg(feature = "cpu_profiler")] | ||
3 | mod google_cpu_profiler; | ||
2 | 4 | ||
3 | use std::{ | 5 | use std::{ |
4 | cell::RefCell, | 6 | cell::RefCell, |
@@ -268,25 +270,35 @@ impl Drop for Scope { | |||
268 | } | 270 | } |
269 | } | 271 | } |
270 | 272 | ||
271 | /// A wrapper around https://github.com/AtheMathmo/cpuprofiler | 273 | /// A wrapper around google_cpu_profiler. |
272 | /// | 274 | /// |
273 | /// It can be used to capture sampling profiles of sections of code. | 275 | /// Usage: |
274 | /// It is not exactly out-of-the-box, as it relies on gperftools. | 276 | /// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. |
275 | /// See the docs for the crate for more! | 277 | /// 2. Build with `cpu_profiler` feature. |
278 | /// 3. Tun the code, the *raw* output would be in the `./out.profile` file. | ||
279 | /// 4. Install pprof for visualization (https://github.com/google/pprof). | ||
280 | /// 5. Use something like `pprof -svg target/release/ra_cli ./out.profile` to see the results. | ||
281 | /// | ||
282 | /// For example, here's how I run profiling on NixOS: | ||
283 | /// | ||
284 | /// ```bash | ||
285 | /// $ nix-shell -p gperftools --run \ | ||
286 | /// 'cargo run --release -p ra_cli -- parse < ~/projects/rustbench/parser.rs > /dev/null' | ||
287 | /// ``` | ||
276 | #[derive(Debug)] | 288 | #[derive(Debug)] |
277 | pub struct CpuProfiler { | 289 | pub struct CpuProfiler { |
278 | _private: (), | 290 | _private: (), |
279 | } | 291 | } |
280 | 292 | ||
281 | pub fn cpu_profiler() -> CpuProfiler { | 293 | pub fn cpu_profiler() -> CpuProfiler { |
282 | #[cfg(feature = "cpuprofiler")] | 294 | #[cfg(feature = "cpu_profiler")] |
283 | { | 295 | { |
284 | cpuprofiler::PROFILER.lock().unwrap().start("./out.profile").unwrap(); | 296 | google_cpu_profiler::start("./out.profile".as_ref()) |
285 | } | 297 | } |
286 | 298 | ||
287 | #[cfg(not(feature = "cpuprofiler"))] | 299 | #[cfg(not(feature = "cpu_profiler"))] |
288 | { | 300 | { |
289 | eprintln!("cpuprofiler feature is disabled") | 301 | eprintln!("cpu_profiler feature is disabled") |
290 | } | 302 | } |
291 | 303 | ||
292 | CpuProfiler { _private: () } | 304 | CpuProfiler { _private: () } |
@@ -294,9 +306,9 @@ pub fn cpu_profiler() -> CpuProfiler { | |||
294 | 306 | ||
295 | impl Drop for CpuProfiler { | 307 | impl Drop for CpuProfiler { |
296 | fn drop(&mut self) { | 308 | fn drop(&mut self) { |
297 | #[cfg(feature = "cpuprofiler")] | 309 | #[cfg(feature = "cpu_profiler")] |
298 | { | 310 | { |
299 | cpuprofiler::PROFILER.lock().unwrap().stop().unwrap(); | 311 | google_cpu_profiler::stop() |
300 | } | 312 | } |
301 | } | 313 | } |
302 | } | 314 | } |