aboutsummaryrefslogtreecommitdiff
path: root/crates/profile
diff options
context:
space:
mode:
Diffstat (limited to 'crates/profile')
-rw-r--r--crates/profile/Cargo.toml3
-rw-r--r--crates/profile/src/lib.rs6
-rw-r--r--crates/profile/src/memory_usage.rs51
3 files changed, 54 insertions, 6 deletions
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 1a8c8f862..653d3d983 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -20,6 +20,9 @@ jemalloc-ctl = { version = "0.4.1", package = "tikv-jemalloc-ctl", optional = tr
20[target.'cfg(target_os = "linux")'.dependencies] 20[target.'cfg(target_os = "linux")'.dependencies]
21perf-event = "0.4" 21perf-event = "0.4"
22 22
23[target.'cfg(windows)'.dependencies]
24winapi = { version = "0.3.8", features = ["psapi"] }
25
23[features] 26[features]
24cpu_profiler = [] 27cpu_profiler = []
25jemalloc = ["jemalloc-ctl"] 28jemalloc = ["jemalloc-ctl"]
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index 7cd01a7df..5ea5039db 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -50,10 +50,10 @@ impl Drop for Scope {
50/// A wrapper around google_cpu_profiler. 50/// A wrapper around google_cpu_profiler.
51/// 51///
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. Run 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.
59/// 59///
@@ -75,7 +75,7 @@ impl Drop for Scope {
75/// 75///
76/// See this diff for how to profile completions: 76/// See this diff for how to profile completions:
77/// 77///
78/// https://github.com/rust-analyzer/rust-analyzer/pull/5306 78/// <https://github.com/rust-analyzer/rust-analyzer/pull/5306>
79#[derive(Debug)] 79#[derive(Debug)]
80pub struct CpuSpan { 80pub struct CpuSpan {
81 _private: (), 81 _private: (),
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs
index 2917ded60..fbcb9e3c2 100644
--- a/crates/profile/src/memory_usage.rs
+++ b/crates/profile/src/memory_usage.rs
@@ -32,9 +32,23 @@ impl MemoryUsage {
32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), 32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
33 } 33 }
34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { 34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
35 // Note: This is incredibly slow. 35 memusage_linux()
36 let alloc = unsafe { libc::mallinfo() }.uordblks as isize; 36 } else if #[cfg(windows)] {
37 MemoryUsage { allocated: Bytes(alloc) } 37 // There doesn't seem to be an API for determining heap usage, so we try to
38 // approximate that by using the Commit Charge value.
39
40 use winapi::um::processthreadsapi::*;
41 use winapi::um::psapi::*;
42 use std::mem::{MaybeUninit, size_of};
43
44 let proc = unsafe { GetCurrentProcess() };
45 let mut mem_counters = MaybeUninit::uninit();
46 let cb = size_of::<PROCESS_MEMORY_COUNTERS>();
47 let ret = unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) };
48 assert!(ret != 0);
49
50 let usage = unsafe { mem_counters.assume_init().PagefileUsage };
51 MemoryUsage { allocated: Bytes(usage as isize) }
38 } else { 52 } else {
39 MemoryUsage { allocated: Bytes(0) } 53 MemoryUsage { allocated: Bytes(0) }
40 } 54 }
@@ -42,6 +56,37 @@ impl MemoryUsage {
42 } 56 }
43} 57}
44 58
59#[cfg(all(target_os = "linux", target_env = "gnu", not(feature = "jemalloc")))]
60fn memusage_linux() -> MemoryUsage {
61 // Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2.
62 // mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB.
63 // mallinfo2 is very recent, so its presence needs to be detected at runtime.
64 // Both are abysmally slow.
65
66 use std::ffi::CStr;
67 use std::sync::atomic::{AtomicUsize, Ordering};
68
69 static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
70
71 let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
72 if mallinfo2 == 1 {
73 let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
74 mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
75 // NB: races don't matter here, since they'll always store the same value
76 MALLINFO2.store(mallinfo2, Ordering::Relaxed);
77 }
78
79 if mallinfo2 == 0 {
80 // mallinfo2 does not exist, use mallinfo.
81 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
82 MemoryUsage { allocated: Bytes(alloc) }
83 } else {
84 let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) };
85 let alloc = mallinfo2().uordblks as isize;
86 MemoryUsage { allocated: Bytes(alloc) }
87 }
88}
89
45#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] 90#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
46pub struct Bytes(isize); 91pub struct Bytes(isize);
47 92