common_mem_prof/
jemalloc.rs1mod error;
16
17use std::ffi::{c_char, CString};
18use std::io::BufReader;
19use std::path::PathBuf;
20
21use error::{
22 BuildTempPathSnafu, DumpProfileDataSnafu, OpenTempFileSnafu, ProfilingNotEnabledSnafu,
23 ReadOptProfSnafu,
24};
25use jemalloc_pprof_mappings::MAPPINGS;
26use jemalloc_pprof_utils::{parse_jeheap, FlamegraphOptions, StackProfile};
27use snafu::{ensure, ResultExt};
28use tokio::io::AsyncReadExt;
29
30use crate::error::{FlamegraphSnafu, ParseJeHeapSnafu, Result};
31
32const PROF_DUMP: &[u8] = b"prof.dump\0";
33const OPT_PROF: &[u8] = b"opt.prof\0";
34
35pub async fn dump_profile() -> Result<Vec<u8>> {
36 ensure!(is_prof_enabled()?, ProfilingNotEnabledSnafu);
37 let tmp_path = tempfile::tempdir().map_err(|_| {
38 BuildTempPathSnafu {
39 path: std::env::temp_dir(),
40 }
41 .build()
42 })?;
43
44 let mut path_buf = PathBuf::from(tmp_path.path());
45 path_buf.push("greptimedb.hprof");
46
47 let path = path_buf
48 .to_str()
49 .ok_or_else(|| BuildTempPathSnafu { path: &path_buf }.build())?
50 .to_string();
51
52 let mut bytes = CString::new(path.as_str())
53 .map_err(|_| BuildTempPathSnafu { path: &path_buf }.build())?
54 .into_bytes_with_nul();
55
56 {
57 let ptr = bytes.as_mut_ptr() as *mut c_char;
59 unsafe {
60 tikv_jemalloc_ctl::raw::write(PROF_DUMP, ptr)
61 .context(DumpProfileDataSnafu { path: path_buf })?
62 }
63 }
64
65 let mut f = tokio::fs::File::open(path.as_str())
66 .await
67 .context(OpenTempFileSnafu { path: &path })?;
68 let mut buf = vec![];
69 let _ = f
70 .read_to_end(&mut buf)
71 .await
72 .context(OpenTempFileSnafu { path })?;
73 Ok(buf)
74}
75
76async fn dump_profile_to_stack_profile() -> Result<StackProfile> {
77 let profile = dump_profile().await?;
78 let profile = BufReader::new(profile.as_slice());
79 parse_jeheap(profile, MAPPINGS.as_deref()).context(ParseJeHeapSnafu)
80}
81
82pub async fn dump_pprof() -> Result<Vec<u8>> {
83 let profile = dump_profile_to_stack_profile().await?;
84 let pprof = profile.to_pprof(("inuse_space", "bytes"), ("space", "bytes"), None);
85 Ok(pprof)
86}
87
88pub async fn dump_flamegraph() -> Result<Vec<u8>> {
89 let profile = dump_profile_to_stack_profile().await?;
90 let mut opts = FlamegraphOptions::default();
91 opts.title = "inuse_space".to_string();
92 opts.count_name = "bytes".to_string();
93 let flamegraph = profile.to_flamegraph(&mut opts).context(FlamegraphSnafu)?;
94 Ok(flamegraph)
95}
96fn is_prof_enabled() -> Result<bool> {
97 Ok(unsafe { tikv_jemalloc_ctl::raw::read::<bool>(OPT_PROF).context(ReadOptProfSnafu)? })
99}