diff options
Diffstat (limited to 'crates/profile/src')
-rw-r--r-- | crates/profile/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/profile/src/memory_usage.rs | 51 |
2 files changed, 51 insertions, 6 deletions
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)] |
80 | pub struct CpuSpan { | 80 | pub 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")))] | ||
60 | fn 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)] |
46 | pub struct Bytes(isize); | 91 | pub struct Bytes(isize); |
47 | 92 | ||