diff options
author | Aleksey Kladov <[email protected]> | 2021-03-30 15:20:27 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-03-30 15:29:43 +0100 |
commit | fb00b92dde25af239c53192365b79682c7f6666d (patch) | |
tree | 4ddd8f38c780effbfd86bf1e5f4b0c2a6ae2ffa2 /crates/profile/src | |
parent | 8447ecf747f7488a904a47a53a770848e5beaf12 (diff) |
internal: revive google_cpu_profile infra
Diffstat (limited to 'crates/profile/src')
-rw-r--r-- | crates/profile/src/google_cpu_profiler.rs | 19 | ||||
-rw-r--r-- | crates/profile/src/lib.rs | 37 |
2 files changed, 44 insertions, 12 deletions
diff --git a/crates/profile/src/google_cpu_profiler.rs b/crates/profile/src/google_cpu_profiler.rs index db865c65b..cae6caeaa 100644 --- a/crates/profile/src/google_cpu_profiler.rs +++ b/crates/profile/src/google_cpu_profiler.rs | |||
@@ -14,26 +14,31 @@ extern "C" { | |||
14 | fn ProfilerStop(); | 14 | fn ProfilerStop(); |
15 | } | 15 | } |
16 | 16 | ||
17 | static PROFILER_STATE: AtomicUsize = AtomicUsize::new(OFF); | ||
18 | const OFF: usize = 0; | 17 | const OFF: usize = 0; |
19 | const ON: usize = 1; | 18 | const ON: usize = 1; |
20 | const PENDING: usize = 2; | 19 | const PENDING: usize = 2; |
21 | 20 | ||
22 | pub fn start(path: &Path) { | 21 | fn transition(current: usize, new: usize) -> bool { |
23 | if PROFILER_STATE.compare_and_swap(OFF, PENDING, Ordering::SeqCst) != OFF { | 22 | static STATE: AtomicUsize = AtomicUsize::new(OFF); |
23 | |||
24 | STATE.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst).is_ok() | ||
25 | } | ||
26 | |||
27 | pub(crate) fn start(path: &Path) { | ||
28 | if !transition(OFF, PENDING) { | ||
24 | panic!("profiler already started"); | 29 | panic!("profiler already started"); |
25 | } | 30 | } |
26 | let path = CString::new(path.display().to_string()).unwrap(); | 31 | let path = CString::new(path.display().to_string()).unwrap(); |
27 | if unsafe { ProfilerStart(path.as_ptr()) } == 0 { | 32 | if unsafe { ProfilerStart(path.as_ptr()) } == 0 { |
28 | panic!("profiler failed to start") | 33 | panic!("profiler failed to start") |
29 | } | 34 | } |
30 | assert!(PROFILER_STATE.compare_and_swap(PENDING, ON, Ordering::SeqCst) == PENDING); | 35 | assert!(transition(PENDING, ON)); |
31 | } | 36 | } |
32 | 37 | ||
33 | pub fn stop() { | 38 | pub(crate) fn stop() { |
34 | if PROFILER_STATE.compare_and_swap(ON, PENDING, Ordering::SeqCst) != ON { | 39 | if !transition(ON, PENDING) { |
35 | panic!("profiler is not started") | 40 | panic!("profiler is not started") |
36 | } | 41 | } |
37 | unsafe { ProfilerStop() }; | 42 | unsafe { ProfilerStop() }; |
38 | assert!(PROFILER_STATE.compare_and_swap(PENDING, OFF, Ordering::SeqCst) == PENDING); | 43 | assert!(transition(PENDING, OFF)); |
39 | } | 44 | } |
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index 9ca6341db..a31fb8f43 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs | |||
@@ -52,7 +52,7 @@ impl Drop for Scope { | |||
52 | /// Usage: | 52 | /// Usage: |
53 | /// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. | 53 | /// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. |
54 | /// 2. Build with `cpu_profiler` feature. | 54 | /// 2. Build with `cpu_profiler` feature. |
55 | /// 3. Tun the code, the *raw* output would be in the `./out.profile` file. | 55 | /// 3. Run the code, the *raw* output would be in the `./out.profile` file. |
56 | /// 4. Install pprof for visualization (https://github.com/google/pprof). | 56 | /// 4. Install pprof for visualization (https://github.com/google/pprof). |
57 | /// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` | 57 | /// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` |
58 | /// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. | 58 | /// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. |
@@ -60,8 +60,17 @@ impl Drop for Scope { | |||
60 | /// For example, here's how I run profiling on NixOS: | 60 | /// For example, here's how I run profiling on NixOS: |
61 | /// | 61 | /// |
62 | /// ```bash | 62 | /// ```bash |
63 | /// $ nix-shell -p gperftools --run \ | 63 | /// $ bat -p shell.nix |
64 | /// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null' | 64 | /// with import <nixpkgs> {}; |
65 | /// mkShell { | ||
66 | /// buildInputs = [ gperftools ]; | ||
67 | /// shellHook = '' | ||
68 | /// export LD_LIBRARY_PATH="${gperftools}/lib:" | ||
69 | /// ''; | ||
70 | /// } | ||
71 | /// $ set -x CPUPROFILE_FREQUENCY 1000 | ||
72 | /// $ nix-shell --run 'cargo test --release --package rust-analyzer --lib -- benchmarks::benchmark_integrated_highlighting --exact --nocapture' | ||
73 | /// $ pprof -svg target/release/deps/rust_analyzer-8739592dc93d63cb crates/rust-analyzer/out.profile > profile.svg | ||
65 | /// ``` | 74 | /// ``` |
66 | /// | 75 | /// |
67 | /// See this diff for how to profile completions: | 76 | /// See this diff for how to profile completions: |
@@ -81,7 +90,9 @@ pub fn cpu_span() -> CpuSpan { | |||
81 | 90 | ||
82 | #[cfg(not(feature = "cpu_profiler"))] | 91 | #[cfg(not(feature = "cpu_profiler"))] |
83 | { | 92 | { |
84 | eprintln!("cpu_profiler feature is disabled") | 93 | eprintln!( |
94 | r#"cpu profiling is disabled, uncomment `default = [ "cpu_profiler" ]` in Cargo.toml to enable."# | ||
95 | ) | ||
85 | } | 96 | } |
86 | 97 | ||
87 | CpuSpan { _private: () } | 98 | CpuSpan { _private: () } |
@@ -91,7 +102,23 @@ impl Drop for CpuSpan { | |||
91 | fn drop(&mut self) { | 102 | fn drop(&mut self) { |
92 | #[cfg(feature = "cpu_profiler")] | 103 | #[cfg(feature = "cpu_profiler")] |
93 | { | 104 | { |
94 | google_cpu_profiler::stop() | 105 | google_cpu_profiler::stop(); |
106 | let profile_data = std::env::current_dir().unwrap().join("out.profile"); | ||
107 | eprintln!("Profile data saved to:\n\n {}\n", profile_data.display()); | ||
108 | let mut cmd = std::process::Command::new("pprof"); | ||
109 | cmd.arg("-svg").arg(std::env::current_exe().unwrap()).arg(&profile_data); | ||
110 | let out = cmd.output(); | ||
111 | |||
112 | match out { | ||
113 | Ok(out) if out.status.success() => { | ||
114 | let svg = profile_data.with_extension("svg"); | ||
115 | std::fs::write(&svg, &out.stdout).unwrap(); | ||
116 | eprintln!("Profile rendered to:\n\n {}\n", svg.display()); | ||
117 | } | ||
118 | _ => { | ||
119 | eprintln!("Failed to run:\n\n {:?}\n", cmd); | ||
120 | } | ||
121 | } | ||
95 | } | 122 | } |
96 | } | 123 | } |
97 | } | 124 | } |