Profiling Rust Memory Usage with Jemalloc
Our jemalloc integration makes it possible to profile heap usage of Rust programs, as long as you are willing to use the jemalloc allocator.
Setup Instructions
Using jemalloc and rust-jemalloc-pprof
Add the
jemalloc_pprof
and tikv-jemallocator
packages to your project. Make sure the latter has the profiling
and
unprefixed_malloc_on_supported_platforms
features:
cargo add jemalloc_pprof
cargo add tikv-jemallocator --features profiling,unprefixed_malloc_on_supported_platforms
Then, in your program's main.rs
set your global allocator to
jemalloc and configure it with the special malloc_conf
symbol:
#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
#[unsafe(export_name = "malloc_conf")]
#[allow(non_upper_case_globals)]
pub static malloc_conf: &[u8] = b"prof:true,prof_active:true,lg_prof_sample:19\0";
Exposing pprof profiles with http:
Call the dump_pprof
method to dump profiles to memory. We
recommending exposing an HTTP interface for these profiles that can be
scraped by Parca.
Here is how to do so using Axum:
async fn handle_get_heap() -> Result<impl IntoResponse, (StatusCode, String)> {
let mut prof_ctl = jemalloc_pprof::PROF_CTL
.as_ref()
.ok_or((
StatusCode::INTERNAL_SERVER_ERROR,
"Profiling not available".to_string(),
))?
.lock()
.await;
let pprof = prof_ctl
.dump_pprof()
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
Ok(pprof)
}
fn main() {
let app = Router::new().route("/debug/pprof/heap", get(handle_get_heap));
let rt = Runtime::new().unwrap();
rt.spawn(async {
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.expect("Failed to bind to port 3000");
axum::serve(listener, app).await.expect("Server failed");
});
}
Uploading symbols with parca-debuginfo
(only if not using
parca-agent
).
If you are already using parca-agent
, all relevant symbols will be
found and uploaded to the backend automatically. Otherwise, you will
need to manually upload them using the parca-debuginfo
CLI. For example,
assuming Parca is running on localhost:
parca-debuginfo upload --store-address=localhost:7070 --insecure path/to/your/binary
Scraping with Parca
In order to continually scrape the endpoint, add a stanza like the
following to your parca.yaml
, assuming (as in the example above) the
profiles are being served via HTTP on 127.0.0.1:3000
:
scrape_configs:
- job_name: "rjemp"
scrape_interval: "10s"
static_configs:
- targets: [ '127.0.0.1:3000' ]
profiling_config:
pprof_config:
heap:
enabled: true
path: /debug/pprof/heap
This should cause profiles to appear in the Parca UI.