Skip to content

Commit ef76731

Browse files
committed
feat: Support jemalloc pprof to simplify memory profile
1 parent 1c3caf7 commit ef76731

File tree

9 files changed

+1763
-802
lines changed

9 files changed

+1763
-802
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ path = "src/lib.rs"
3838
#default = ["jemalloc", "allocator-analysis"]
3939

4040
# whether to use jemalloc as the global allocator or not
41-
jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemalloc-sys", "dep:tikv-jemallocator"]
41+
jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemalloc-sys", "dep:tikv-jemallocator", "dep:jemalloc_pprof"]
4242

4343
# whether to enable memory profiling or not, once enabled, it implies using jemalloc as the global allocator
4444
memory-prof = ["jemalloc", "tikv-jemallocator/profiling", "allocator-analysis"]
@@ -118,17 +118,22 @@ features = ["kerberos"]
118118

119119
# jemalloc related optional dependencies
120120
[dependencies.tikv-jemalloc-ctl]
121-
version = "0.5.0"
121+
version = "0.6.0"
122122
optional = true
123123
features = ["use_std"]
124124

125125
[dependencies.tikv-jemalloc-sys]
126-
version = "0.5.0"
126+
version = "0.6.0"
127127
optional = true
128128
features = ["stats", "profiling", "unprefixed_malloc_on_supported_platforms"]
129129

130130
[dependencies.tikv-jemallocator]
131-
version = "0.5.0"
131+
version = "0.6.0"
132+
optional = true
133+
features = ["profiling", "unprefixed_malloc_on_supported_platforms"]
134+
135+
[dependencies.jemalloc_pprof]
136+
version = "0.6.0"
132137
optional = true
133138

134139
[build-dependencies]

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ KRB5_CONFIG=/etc/krb5.conf KRB5CCNAME=/tmp/krb5cc_2002 LOG=info ./uniffle-worker
128128
```
129129
2. Start with profile
130130
```shell
131-
_RJEM_MALLOC_CONF=prof:true,prof_prefix:jeprof.out,lg_prof_interval:30 ./uniffle-worker
131+
curl localhost:20010/debug/heap/profile > heap.pb.gz
132+
go tool pprof -http="0.0.0.0:8081" heap.pb.gz
132133
```
133134

134135
### CPU Profiling

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
# under the License.
1717

1818
[toolchain]
19-
channel = "nightly-2023-05-31"
19+
channel = "nightly-2024-10-01"
2020
components = ["rustfmt", "clippy", "rust-analyzer"]

src/http/jeprof.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
use log::error;
1919
use serde::{Deserialize, Serialize};
20+
use std::error::Error;
2021
use std::time::Duration;
2122

2223
use poem::error::ResponseError;
@@ -53,6 +54,12 @@ impl ResponseError for ProfError {
5354
}
5455
}
5556

57+
#[poem::handler]
58+
async fn jemalloc_pprof_handler(req: &Request) -> poem::Result<impl IntoResponse> {
59+
let pprof = dump_prof("").await?;
60+
Ok(pprof)
61+
}
62+
5663
#[poem::handler]
5764
async fn jeprof_handler(req: &Request) -> poem::Result<impl IntoResponse> {
5865
let req = req.params::<JeProfRequest>()?;
@@ -76,7 +83,7 @@ async fn jeprof_handler(req: &Request) -> poem::Result<impl IntoResponse> {
7683
let path = tmp_file.path();
7784
delay_for(Duration::from_secs(req.duration)).await;
7885
// dump heap profile
79-
let buf: Vec<u8> = dump_prof(&path.to_string_lossy()).map_err(|e| {
86+
let buf: Vec<u8> = dump_prof(&path.to_string_lossy()).await.map_err(|e| {
8087
let msg = format!("could not dump heap profile: {:?}", e);
8188
error!("{}", msg);
8289
e
@@ -109,7 +116,7 @@ impl Default for JeProfHandler {
109116

110117
impl Handler for JeProfHandler {
111118
fn get_route_method(&self) -> RouteMethod {
112-
RouteMethod::new().get(jeprof_handler)
119+
RouteMethod::new().get(jemalloc_pprof_handler)
113120
}
114121

115122
fn get_route_path(&self) -> String {
@@ -126,6 +133,7 @@ mod test {
126133
use tonic::codegen::http::StatusCode;
127134

128135
#[tokio::test]
136+
#[ignore]
129137
async fn test_router() {
130138
let handler = JeProfHandler::default();
131139
let app = Route::new().at(handler.get_route_path(), handler.get_route_method());

src/http/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ fn new_server() -> Box<PoemHTTPServer> {
6262
server.register_handler(MetricsHTTPHandler::default());
6363
server.register_handler(AwaitTreeHandler::default());
6464
server.register_handler(JeProfHandler::default());
65+
6566
Box::new(server)
6667
}

src/mem_allocator/default.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn is_prof_enabled() -> bool {
2121
false
2222
}
2323

24-
pub fn dump_prof(_path: &str) -> ProfResult<Vec<u8>> {
24+
pub async fn dump_prof(_path: &str) -> ProfResult<Vec<u8>> {
2525
Err(ProfError::MemProfilingNotEnabled)
2626
}
2727

src/mem_allocator/jemalloc.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
#[allow(non_upper_case_globals)]
19+
#[export_name = "malloc_conf"]
20+
pub static malloc_conf: &[u8] = b"prof:true,prof_active:true,lg_prof_sample:19\0";
21+
1822
pub type Allocator = tikv_jemallocator::Jemalloc;
1923
pub const fn allocator() -> Allocator {
2024
tikv_jemallocator::Jemalloc

src/mem_allocator/profiling.rs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use log::error;
1819
use std::ffi::{c_char, CString};
1920
use std::fs::File;
2021
use std::io::Read;
@@ -55,19 +56,12 @@ pub fn deactivate_prof() -> ProfResult<()> {
5556
}
5657

5758
/// Dump the profile to the `path`. path usually is a temp file generated by `tempfile` crate.
58-
pub fn dump_prof(path: &str) -> ProfResult<Vec<u8>> {
59-
let mut bytes = CString::new(path)?.into_bytes_with_nul();
60-
let ptr = bytes.as_mut_ptr() as *mut c_char;
61-
unsafe {
62-
if let Err(e) = tikv_jemalloc_ctl::raw::write(PROF_DUMP, ptr) {
63-
return Err(ProfError::JemallocError(format!(
64-
"failed to dump the profile to {:?}: {}",
65-
path, e
66-
)));
67-
}
68-
}
69-
let mut f = File::open(path)?;
70-
let mut buf = Vec::new();
71-
f.read_to_end(&mut buf)?;
72-
Ok(buf)
59+
pub async fn dump_prof(path: &str) -> ProfResult<Vec<u8>> {
60+
let mut prof_ctl = jemalloc_pprof::PROF_CTL.as_ref().unwrap().lock().await;
61+
let pprof = prof_ctl.dump_pprof().map_err(|err| {
62+
let msg = format!("Errors on jemalloc profile. err: {:?}", &err);
63+
error!("{}", &msg);
64+
ProfError::JemallocError(msg)
65+
})?;
66+
Ok(pprof)
7367
}

0 commit comments

Comments
 (0)