Skip to content

Commit ab4fae4

Browse files
bors[bot]matklad
andauthored
Merge #8262
8262: internal: revive google_cpu_profile infra r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 0b68e03 + fb00b92 commit ab4fae4

File tree

3 files changed

+46
-12
lines changed

3 files changed

+46
-12
lines changed

crates/profile/src/google_cpu_profiler.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,31 @@ extern "C" {
1414
fn ProfilerStop();
1515
}
1616

17-
static PROFILER_STATE: AtomicUsize = AtomicUsize::new(OFF);
1817
const OFF: usize = 0;
1918
const ON: usize = 1;
2019
const PENDING: usize = 2;
2120

22-
pub fn start(path: &Path) {
23-
if PROFILER_STATE.compare_and_swap(OFF, PENDING, Ordering::SeqCst) != OFF {
21+
fn transition(current: usize, new: usize) -> bool {
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) {
2429
panic!("profiler already started");
2530
}
2631
let path = CString::new(path.display().to_string()).unwrap();
2732
if unsafe { ProfilerStart(path.as_ptr()) } == 0 {
2833
panic!("profiler failed to start")
2934
}
30-
assert!(PROFILER_STATE.compare_and_swap(PENDING, ON, Ordering::SeqCst) == PENDING);
35+
assert!(transition(PENDING, ON));
3136
}
3237

33-
pub fn stop() {
34-
if PROFILER_STATE.compare_and_swap(ON, PENDING, Ordering::SeqCst) != ON {
38+
pub(crate) fn stop() {
39+
if !transition(ON, PENDING) {
3540
panic!("profiler is not started")
3641
}
3742
unsafe { ProfilerStop() };
38-
assert!(PROFILER_STATE.compare_and_swap(PENDING, OFF, Ordering::SeqCst) == PENDING);
43+
assert!(transition(PENDING, OFF));
3944
}

crates/profile/src/lib.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,25 @@ impl Drop for Scope {
5252
/// Usage:
5353
/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro.
5454
/// 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.
5656
/// 4. Install pprof for visualization (https://github.com/google/pprof).
5757
/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
5858
/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
5959
///
6060
/// For example, here's how I run profiling on NixOS:
6161
///
6262
/// ```bash
63-
/// $ nix-shell -p gperftools --run \
64-
/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null'
63+
/// $ bat -p shell.nix
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
6574
/// ```
6675
///
6776
/// See this diff for how to profile completions:
@@ -81,7 +90,9 @@ pub fn cpu_span() -> CpuSpan {
8190

8291
#[cfg(not(feature = "cpu_profiler"))]
8392
{
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+
)
8596
}
8697

8798
CpuSpan { _private: () }
@@ -91,7 +102,23 @@ impl Drop for CpuSpan {
91102
fn drop(&mut self) {
92103
#[cfg(feature = "cpu_profiler")]
93104
{
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+
}
95122
}
96123
}
97124
}

crates/rust-analyzer/src/benchmarks.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ fn benchmark_integrated_highlighting() {
5151
}
5252

5353
profile::init_from("*>100");
54+
// let _s = profile::heartbeat_span();
5455

5556
{
5657
let _it = stdx::timeit("change");
@@ -63,6 +64,7 @@ fn benchmark_integrated_highlighting() {
6364

6465
{
6566
let _it = stdx::timeit("after change");
67+
let _span = profile::cpu_span();
6668
let analysis = host.analysis();
6769
analysis.highlight_as_html(file_id, false).unwrap();
6870
}

0 commit comments

Comments
 (0)