@@ -160,10 +160,13 @@ mod internal {
160160 use axum:: {
161161 Router ,
162162 extract:: Query ,
163- response:: { Html , IntoResponse } ,
163+ response:: { Html , IntoResponse , Response } ,
164164 routing:: get,
165165 } ;
166- use hyper:: StatusCode ;
166+ use hyper:: {
167+ StatusCode ,
168+ header:: { CONTENT_DISPOSITION , CONTENT_TYPE } ,
169+ } ;
167170 use metrics_exporter_prometheus:: { PrometheusBuilder , PrometheusHandle } ;
168171 use pprof:: { ProfilerGuard , protos:: Message } ;
169172 use pulsebeam_runtime:: actor:: Actor ;
@@ -173,7 +176,7 @@ mod internal {
173176 use crate :: { controller, gateway, participant, room, shard} ;
174177
175178 #[ derive( Deserialize ) ]
176- struct ProfileParams {
179+ pub struct ProfileParams {
177180 #[ serde( default = "default_seconds" ) ]
178181 seconds : u64 ,
179182 #[ serde( default ) ]
@@ -195,7 +198,7 @@ mod internal {
195198 <li><a href="/debug/pprof/profile?seconds=30">CPU Profile (pprof)</a></li>
196199 <li><a href="/debug/pprof/profile?seconds=30&flamegraph=true">CPU Flamegraph</a></li>
197200 <li><a href="/debug/pprof/allocs?seconds=30">Memory Profile (pprof)</a></li>
198- <li><a href="/debug/pprof/allocs/flamegraph ?seconds=30">Memory Flamegraph</a></li>
201+ <li><a href="/debug/pprof/allocs?seconds=30&flamegraph=true ">Memory Flamegraph</a></li>
199202</ul>
200203"# ;
201204
@@ -209,11 +212,7 @@ mod internal {
209212 } ) ,
210213 )
211214 . route ( "/debug/pprof/profile" , get ( pprof_profile) )
212- . route ( "/debug/pprof/allocs" , axum:: routing:: get ( handle_get_heap) )
213- . route (
214- "/debug/pprof/allocs/flamegraph" ,
215- axum:: routing:: get ( handle_get_heap_flamegraph) ,
216- )
215+ . route ( "/debug/pprof/allocs" , axum:: routing:: get ( heap_profile) )
217216 . route ( "/" , get ( || async { Html ( INDEX_HTML ) } ) )
218217 . with_state ( ( ) ) ;
219218
@@ -301,29 +300,44 @@ mod internal {
301300 }
302301 }
303302
304- pub async fn handle_get_heap ( ) -> Result < impl IntoResponse , ( StatusCode , String ) > {
303+ pub async fn heap_profile (
304+ Query ( params) : Query < ProfileParams > ,
305+ ) -> Result < Response , ( StatusCode , String ) > {
305306 let mut prof_ctl = jemalloc_pprof:: PROF_CTL . as_ref ( ) . unwrap ( ) . lock ( ) . await ;
306307 require_profiling_activated ( & prof_ctl) ?;
307- let pprof = prof_ctl
308- . dump_pprof ( )
309- . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) ) ?;
310- Ok ( pprof)
311- }
312308
313- pub async fn handle_get_heap_flamegraph ( ) -> Result < impl IntoResponse , ( StatusCode , String ) > {
314- use axum:: body:: Body ;
315- use axum:: http:: header:: CONTENT_TYPE ;
316- use axum:: response:: Response ;
309+ let resp = if params. flamegraph {
310+ use axum:: http:: header:: CONTENT_TYPE ;
317311
318- let mut prof_ctl = jemalloc_pprof:: PROF_CTL . as_ref ( ) . unwrap ( ) . lock ( ) . await ;
319- require_profiling_activated ( & prof_ctl) ?;
320- let svg = prof_ctl
321- . dump_flamegraph ( )
322- . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) ) ?;
323- Response :: builder ( )
324- . header ( CONTENT_TYPE , "image/svg+xml" )
325- . body ( Body :: from ( svg) )
326- . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) )
312+ let svg = prof_ctl
313+ . dump_flamegraph ( )
314+ . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) ) ?;
315+
316+ (
317+ axum:: http:: StatusCode :: OK ,
318+ [
319+ ( CONTENT_TYPE , "image/svg+xml" ) ,
320+ ( CONTENT_DISPOSITION , "attachment; filename=allocs.svg" ) ,
321+ ] ,
322+ svg,
323+ )
324+ . into_response ( )
325+ } else {
326+ let pprof = prof_ctl
327+ . dump_pprof ( )
328+ . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) ) ?;
329+
330+ (
331+ axum:: http:: StatusCode :: OK ,
332+ [
333+ ( CONTENT_TYPE , "application/octet-stream" ) ,
334+ ( CONTENT_DISPOSITION , "attachment; filename=allocs.pprof" ) ,
335+ ] ,
336+ pprof,
337+ )
338+ . into_response ( )
339+ } ;
340+ Ok ( resp)
327341 }
328342
329343 /// Checks whether jemalloc profiling is activated an returns an error response if not.
@@ -341,32 +355,40 @@ mod internal {
341355 }
342356
343357 /// Handler: /debug/pprof/profile?seconds=30&flamegraph=true
344- async fn pprof_profile ( Query ( params) : Query < ProfileParams > ) -> impl IntoResponse {
358+ async fn pprof_profile (
359+ Query ( params) : Query < ProfileParams > ,
360+ ) -> Result < impl IntoResponse , ( StatusCode , String ) > {
345361 let guard = ProfilerGuard :: new ( 100 ) . unwrap ( ) ; // 100 Hz sampling
346362 tokio:: time:: sleep ( Duration :: from_secs ( params. seconds ) ) . await ;
347363
348- match guard. report ( ) . build ( ) {
364+ let resp = match guard. report ( ) . build ( ) {
349365 Ok ( report) => {
350366 if params. flamegraph {
351367 let mut body = Vec :: new ( ) ;
352- if let Err ( e ) = report. flamegraph ( & mut body ) {
353- return ( axum :: http :: StatusCode :: INTERNAL_SERVER_ERROR , e . to_string ( ) )
354- . into_response ( ) ;
355- }
368+ report
369+ . flamegraph ( & mut body )
370+ . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err . to_string ( ) ) ) ? ;
371+
356372 (
357373 axum:: http:: StatusCode :: OK ,
358- [ ( "Content-Type" , "image/svg+xml" ) ] ,
374+ [
375+ ( CONTENT_TYPE , "image/svg+xml" ) ,
376+ ( CONTENT_DISPOSITION , "attachment; filename=cpu.svg" ) ,
377+ ] ,
359378 body,
360379 )
361380 . into_response ( )
362381 } else {
363- let profile = report. pprof ( ) . unwrap ( ) ;
382+ let profile = report
383+ . pprof ( )
384+ . map_err ( |err| ( StatusCode :: INTERNAL_SERVER_ERROR , err. to_string ( ) ) ) ?;
385+
364386 let body = profile. encode_to_vec ( ) ;
365387 (
366388 axum:: http:: StatusCode :: OK ,
367389 [
368- ( "Content-Type" , "application/octet-stream" ) ,
369- ( "Content-Disposition" , "attachment; filename=cpu.pprof" ) ,
390+ ( CONTENT_TYPE , "application/octet-stream" ) ,
391+ ( CONTENT_DISPOSITION , "attachment; filename=cpu.pprof" ) ,
370392 ] ,
371393 body,
372394 )
@@ -378,7 +400,9 @@ mod internal {
378400 format ! ( "Failed to build pprof report: {e}" ) ,
379401 )
380402 . into_response ( ) ,
381- }
403+ } ;
404+
405+ Ok ( resp)
382406 }
383407}
384408
0 commit comments