common_mem_prof/
jemalloc.rs1mod error;
16
17use std::ffi::{CString, c_char};
18use std::io::BufReader;
19use std::path::PathBuf;
20
21use error::{
22 ActivateProfSnafu, BuildTempPathSnafu, DeactivateProfSnafu, DumpProfileDataSnafu,
23 OpenTempFileSnafu, ProfilingNotEnabledSnafu, ReadOptProfSnafu, ReadProfActiveSnafu,
24};
25use jemalloc_pprof_mappings::MAPPINGS;
26use jemalloc_pprof_utils::{FlamegraphOptions, StackProfile, parse_jeheap};
27use snafu::{ResultExt, ensure};
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";
34const PROF_ACTIVE: &[u8] = b"prof.active\0";
35const PROF_GDUMP: &[u8] = b"prof.gdump\0";
36
37pub async fn dump_profile() -> Result<Vec<u8>> {
38 ensure!(is_prof_enabled()?, ProfilingNotEnabledSnafu);
39 let tmp_path = tempfile::tempdir().map_err(|_| {
40 BuildTempPathSnafu {
41 path: std::env::temp_dir(),
42 }
43 .build()
44 })?;
45
46 let mut path_buf = PathBuf::from(tmp_path.path());
47 path_buf.push("greptimedb.hprof");
48
49 let path = path_buf
50 .to_str()
51 .ok_or_else(|| BuildTempPathSnafu { path: &path_buf }.build())?
52 .to_string();
53
54 let mut bytes = CString::new(path.as_str())
55 .map_err(|_| BuildTempPathSnafu { path: &path_buf }.build())?
56 .into_bytes_with_nul();
57
58 {
59 let ptr = bytes.as_mut_ptr() as *mut c_char;
61 unsafe {
62 tikv_jemalloc_ctl::raw::write(PROF_DUMP, ptr)
63 .context(DumpProfileDataSnafu { path: path_buf })?
64 }
65 }
66
67 let mut f = tokio::fs::File::open(path.as_str())
68 .await
69 .context(OpenTempFileSnafu { path: &path })?;
70 let mut buf = vec![];
71 let _ = f
72 .read_to_end(&mut buf)
73 .await
74 .context(OpenTempFileSnafu { path })?;
75 Ok(buf)
76}
77
78async fn dump_profile_to_stack_profile() -> Result<StackProfile> {
79 let profile = dump_profile().await?;
80 let profile = BufReader::new(profile.as_slice());
81 parse_jeheap(profile, MAPPINGS.as_deref()).context(ParseJeHeapSnafu)
82}
83
84pub async fn dump_pprof() -> Result<Vec<u8>> {
85 let profile = dump_profile_to_stack_profile().await?;
86 let pprof = profile.to_pprof(("inuse_space", "bytes"), ("space", "bytes"), None);
87 Ok(pprof)
88}
89
90pub async fn dump_flamegraph() -> Result<Vec<u8>> {
91 let profile = dump_profile_to_stack_profile().await?;
92 let mut opts = FlamegraphOptions::default();
93 opts.title = "inuse_space".to_string();
94 opts.count_name = "bytes".to_string();
95 let flamegraph = profile.to_flamegraph(&mut opts).context(FlamegraphSnafu)?;
96 Ok(flamegraph)
97}
98
99pub fn activate_heap_profile() -> Result<()> {
100 ensure!(is_prof_enabled()?, ProfilingNotEnabledSnafu);
101 unsafe {
102 tikv_jemalloc_ctl::raw::update(PROF_ACTIVE, true).context(ActivateProfSnafu)?;
103 }
104 Ok(())
105}
106
107pub fn deactivate_heap_profile() -> Result<()> {
108 ensure!(is_prof_enabled()?, ProfilingNotEnabledSnafu);
109 unsafe {
110 tikv_jemalloc_ctl::raw::update(PROF_ACTIVE, false).context(DeactivateProfSnafu)?;
111 }
112 Ok(())
113}
114
115pub fn is_heap_profile_active() -> Result<bool> {
116 unsafe { Ok(tikv_jemalloc_ctl::raw::read::<bool>(PROF_ACTIVE).context(ReadProfActiveSnafu)?) }
117}
118
119fn is_prof_enabled() -> Result<bool> {
120 Ok(unsafe { tikv_jemalloc_ctl::raw::read::<bool>(OPT_PROF).context(ReadOptProfSnafu)? })
122}
123
124pub fn set_gdump_active(active: bool) -> Result<()> {
125 ensure!(is_prof_enabled()?, ProfilingNotEnabledSnafu);
126 unsafe {
127 tikv_jemalloc_ctl::raw::update(PROF_GDUMP, active).context(error::UpdateGdumpSnafu)?;
128 }
129 Ok(())
130}
131
132pub fn is_gdump_active() -> Result<bool> {
133 unsafe { Ok(tikv_jemalloc_ctl::raw::read::<bool>(PROF_GDUMP).context(error::ReadGdumpSnafu)?) }
135}