Skip to content

Commit 841da82

Browse files
committed
feat: add heap profiling to dfdaemon
Signed-off-by: Gaius <gaius.qi@gmail.com>
1 parent c0abdab commit 841da82

File tree

4 files changed

+177
-35
lines changed

4 files changed

+177
-35
lines changed

Cargo.lock

Lines changed: 107 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dragonfly-client/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,8 @@ tokio-rustls = "0.25.0-alpha.4"
7878
http-body-util = "0.1.0"
7979
futures-util = "0.3.30"
8080
termion = "3.0.0"
81+
[target.'cfg(not(target_env = "msvc"))'.dependencies]
82+
tikv-jemallocator = { version = "0.5.4", features = ["profiling", "unprefixed_malloc_on_supported_platforms"] }
83+
84+
[target.'cfg(target_os = "linux")'.dependencies]
85+
jemalloc_pprof = "0.1.0"

dragonfly-client/src/bin/dfdaemon/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ use std::sync::Arc;
3939
use tokio::sync::mpsc;
4040
use tracing::{error, info, Level};
4141

42+
#[cfg(not(target_env = "msvc"))]
43+
#[global_allocator]
44+
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
45+
46+
#[allow(non_upper_case_globals)]
47+
#[export_name = "malloc_conf"]
48+
pub static malloc_conf: &[u8] = b"prof:true,prof_active:true,lg_prof_sample:19\0";
49+
4250
#[derive(Debug, Parser)]
4351
#[command(
4452
name = dfdaemon::NAME,

dragonfly-client/src/stats/mod.rs

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,19 @@ const DEFAULT_PROFILER_SECONDS: u64 = 10;
3030
// DEFAULT_PROFILER_FREQUENCY is the default frequency to start profiling.
3131
const DEFAULT_PROFILER_FREQUENCY: i32 = 1000;
3232

33-
// PProfQueryParams is the query params to start profiling.
33+
// PProfProfileQueryParams is the query params to start profiling.
3434
#[derive(Deserialize, Serialize)]
3535
#[serde(default)]
36-
pub struct PProfQueryParams {
36+
pub struct PProfProfileQueryParams {
3737
// seconds is the seconds to start profiling.
3838
pub seconds: u64,
3939

4040
// frequency is the frequency to start profiling.
4141
pub frequency: i32,
4242
}
4343

44-
// PProfQueryParams implements the default.
45-
impl Default for PProfQueryParams {
44+
// PProfProfileQueryParams implements the default.
45+
impl Default for PProfProfileQueryParams {
4646
fn default() -> Self {
4747
Self {
4848
seconds: DEFAULT_PROFILER_SECONDS,
@@ -84,16 +84,24 @@ impl Stats {
8484
// Clone the shutdown channel.
8585
let mut shutdown = self.shutdown.clone();
8686

87-
// Create the stats route.
88-
let stats_route = warp::path!("debug" / "pprof" / "profile")
87+
// Create the pprof profile route.
88+
let pprof_profile_route = warp::path!("debug" / "pprof" / "profile")
8989
.and(warp::get())
90-
.and(warp::query::<PProfQueryParams>())
91-
.and_then(Self::stats_handler);
90+
.and(warp::query::<PProfProfileQueryParams>())
91+
.and_then(Self::pprof_profile_handler);
92+
93+
// Create the pprof heap route.
94+
let pprof_heap_route = warp::path!("debug" / "pprof" / "heap")
95+
.and(warp::get())
96+
.and_then(Self::pprof_heap_handler);
97+
98+
// Create the pprof routes.
99+
let pprof_routes = pprof_profile_route.or(pprof_heap_route);
92100

93101
// Start the stats server and wait for it to finish.
94102
info!("stats server listening on {}", self.addr);
95103
tokio::select! {
96-
_ = warp::serve(stats_route).run(self.addr) => {
104+
_ = warp::serve(pprof_routes).run(self.addr) => {
97105
// Stats server ended.
98106
info!("stats server ended");
99107
}
@@ -105,35 +113,29 @@ impl Stats {
105113
}
106114

107115
// stats_handler handles the stats request.
108-
async fn stats_handler(query_params: PProfQueryParams) -> Result<impl Reply, Rejection> {
116+
async fn pprof_profile_handler(
117+
query_params: PProfProfileQueryParams,
118+
) -> Result<impl Reply, Rejection> {
109119
info!(
110120
"start profiling for {} seconds with {} frequency",
111121
query_params.seconds, query_params.frequency
112122
);
113-
let guard = match ProfilerGuard::new(query_params.frequency) {
114-
Ok(guard) => guard,
115-
Err(err) => {
116-
error!("failed to create profiler guard: {}", err);
117-
return Err(warp::reject::reject());
118-
}
119-
};
123+
124+
let guard = ProfilerGuard::new(query_params.frequency).map_err(|err| {
125+
error!("failed to create profiler guard: {}", err);
126+
warp::reject::reject()
127+
})?;
120128

121129
tokio::time::sleep(Duration::from_secs(query_params.seconds)).await;
122-
let report = match guard.report().build() {
123-
Ok(report) => report,
124-
Err(err) => {
125-
error!("failed to build profiler report: {}", err);
126-
return Err(warp::reject::reject());
127-
}
128-
};
130+
let report = guard.report().build().map_err(|err| {
131+
error!("failed to build profiler report: {}", err);
132+
warp::reject::reject()
133+
})?;
129134

130-
let profile = match report.pprof() {
131-
Ok(profile) => profile,
132-
Err(err) => {
133-
error!("failed to get pprof profile: {}", err);
134-
return Err(warp::reject::reject());
135-
}
136-
};
135+
let profile = report.pprof().map_err(|err| {
136+
error!("failed to get pprof profile: {}", err);
137+
warp::reject::reject()
138+
})?;
137139

138140
let mut body: Vec<u8> = Vec::new();
139141
profile.write_to_vec(&mut body).map_err(|err| {
@@ -143,4 +145,28 @@ impl Stats {
143145

144146
Ok(body)
145147
}
148+
149+
// pprof_heap_handler handles the pprof heap request.
150+
async fn pprof_heap_handler() -> Result<impl Reply, Rejection> {
151+
info!("start heap profiling");
152+
#[cfg(target_os = "linux")]
153+
{
154+
let mut prof_ctl = jemalloc_pprof::PROF_CTL.as_ref().unwrap().lock().await;
155+
if !prof_ctl.activated() {
156+
return Err(warp::reject::reject());
157+
}
158+
159+
let pprof = prof_ctl.dump_pprof().map_err(|err| {
160+
error!("failed to dump pprof: {}", err);
161+
warp::reject::reject()
162+
})?;
163+
164+
let mut body: Vec<u8> = Vec::new();
165+
body.extend_from_slice(pprof.as_bytes());
166+
Ok(body)
167+
}
168+
169+
#[cfg(not(target_os = "linux"))]
170+
Err::<warp::http::Error, Rejection>(warp::reject::reject())
171+
}
146172
}

0 commit comments

Comments
 (0)