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