1919
2020use crate :: profiling:: mallctl;
2121use http:: { header, Method , Request , Response , StatusCode } ;
22- use std:: env;
22+ use std:: { collections :: HashMap , env, fmt } ;
2323
2424#[ inline]
2525pub fn router ( req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
2626 match ( req. method ( ) , req. uri ( ) . path ( ) ) {
27- ( & Method :: GET , "/pprof/conf" ) => get_pprof_conf_handler ( req) ,
28- ( & Method :: POST , "/pprof/conf" ) => post_pprof_conf_handler ( req) ,
29- ( & Method :: GET , "/pprof/heap" ) => get_pprof_heap_handler ( req) ,
30- ( & Method :: GET , "/pprof/cmdline" ) => get_pprof_cmdline_handler ( req) ,
31- ( & Method :: GET , "/pprof/symbol" ) => get_pprof_symbol_handler ( req) ,
32- ( & Method :: POST , "/pprof/symbol" ) => post_pprof_symbol_handler ( req) ,
33- ( & Method :: GET , "/pprof/stats" ) => get_pprof_stats_handler ( req) ,
27+ ( & Method :: GET , "/pprof/conf" ) => JeprofHandler ( get_pprof_conf_handler) . call ( req) ,
28+ ( & Method :: POST , "/pprof/conf" ) => JeprofHandler ( post_pprof_conf_handler) . call ( req) ,
29+ ( & Method :: GET , "/pprof/heap" ) => JeprofHandler ( get_pprof_heap_handler) . call ( req) ,
30+ ( & Method :: GET , "/pprof/cmdline" ) => JeprofHandler ( get_pprof_cmdline_handler) . call ( req) ,
31+ ( & Method :: GET , "/pprof/symbol" ) => JeprofHandler ( get_pprof_symbol_handler) . call ( req) ,
32+ ( & Method :: POST , "/pprof/symbol" ) => JeprofHandler ( post_pprof_symbol_handler) . call ( req) ,
33+ ( & Method :: GET , "/pprof/stats" ) => JeprofHandler ( get_pprof_stats_handler) . call ( req) ,
3434 _ => {
3535 let body = b"Bad Request\r \n " ;
3636 Response :: builder ( )
@@ -42,101 +42,207 @@ pub fn router(req: Request<Vec<u8>>) -> http::Result<Response<Vec<u8>>> {
4242 }
4343}
4444
45+ #[ cfg( feature = "actix-handlers" ) ]
4546#[ inline]
46- pub fn get_pprof_conf_handler ( _req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
47+ pub fn actix_routes ( cfg : & mut actix_web:: web:: ServiceConfig ) {
48+ cfg. service (
49+ actix_web:: web:: scope ( "/pprof" )
50+ . route ( "/conf" , actix_web:: web:: get ( ) . to ( JeprofHandler ( get_pprof_conf_handler) ) )
51+ . route ( "/conf" , actix_web:: web:: post ( ) . to ( JeprofHandler ( post_pprof_conf_handler) ) )
52+ . route ( "/heap" , actix_web:: web:: get ( ) . to ( JeprofHandler ( get_pprof_heap_handler) ) )
53+ . route ( "/cmdline" , actix_web:: web:: get ( ) . to ( JeprofHandler ( get_pprof_cmdline_handler) ) )
54+ . route ( "/symbol" , actix_web:: web:: get ( ) . to ( JeprofHandler ( get_pprof_symbol_handler) ) )
55+ . route ( "/symbol" , actix_web:: web:: post ( ) . to ( JeprofHandler ( post_pprof_symbol_handler) ) )
56+ . route ( "/stats" , actix_web:: web:: get ( ) . to ( JeprofHandler ( get_pprof_stats_handler) ) ) ,
57+ ) ;
58+ }
59+
60+ #[ derive( Debug ) ]
61+ pub struct ErrorResponse ( String ) ;
62+
63+ impl fmt:: Display for ErrorResponse {
64+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
65+ write ! ( f, "ERROR: {}" , self . 0 )
66+ }
67+ }
68+
69+ #[ cfg( feature = "actix-handlers" ) ]
70+ impl actix_web:: ResponseError for ErrorResponse { }
71+
72+ #[ derive( Clone , Debug ) ]
73+ struct JeprofHandler < F > ( F )
74+ where
75+ F : Fn ( & [ u8 ] , & HashMap < String , String > ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse >
76+ + Clone
77+ + ' static ;
78+
79+ impl < F > JeprofHandler < F >
80+ where
81+ F : Fn ( & [ u8 ] , & HashMap < String , String > ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse >
82+ + Clone
83+ + ' static ,
84+ {
85+ fn call ( & self , req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
86+ let params: HashMap < String , String > = parse_malloc_conf_query ( req. uri ( ) . query ( ) )
87+ . iter ( )
88+ . map ( |( k, v) | ( ( * k) . to_string ( ) , v. unwrap_or_default ( ) . to_string ( ) ) )
89+ . collect ( ) ;
90+ match self . 0 ( req. body ( ) , & params) {
91+ Ok ( ( body, Some ( content_disposition) ) ) => response_ok_binary ( body, & content_disposition) ,
92+ Ok ( ( body, None ) ) => response_ok ( body) ,
93+ Err ( err) => response_err ( & err. 0 ) ,
94+ }
95+ }
96+ }
97+
98+ #[ cfg( feature = "actix-handlers" ) ]
99+ impl < F >
100+ actix_web:: Handler < ( actix_web:: web:: Payload , actix_web:: web:: Query < HashMap < String , String > > ) >
101+ for JeprofHandler < F >
102+ where
103+ F : Fn ( & [ u8 ] , & HashMap < String , String > ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse >
104+ + Clone
105+ + ' static ,
106+ {
107+ type Output = Result < actix_web:: HttpResponse , ErrorResponse > ;
108+ type Future = std:: pin:: Pin < Box < dyn std:: future:: Future < Output = Self :: Output > > > ;
109+
110+ fn call (
111+ & self ,
112+ ( mut body, query) : (
113+ actix_web:: web:: Payload ,
114+ actix_web:: web:: Query < HashMap < String , String > > ,
115+ ) ,
116+ ) -> Self :: Future {
117+ use futures_util:: StreamExt as _;
118+
119+ let f = self . 0 . clone ( ) ;
120+ Box :: pin ( async move {
121+ let mut data = Vec :: < u8 > :: new ( ) ;
122+ while let Some ( item) = body. next ( ) . await {
123+ data. extend_from_slice ( & item. map_err ( |e| ErrorResponse ( e. to_string ( ) ) ) ?) ;
124+ }
125+ f ( & data, & query. 0 ) . map ( |( body, content_disposition) | {
126+ let mut resp = actix_web:: HttpResponse :: Ok ( ) ;
127+ if let Some ( filename) = content_disposition {
128+ resp. insert_header ( actix_web:: http:: header:: ContentDisposition :: attachment (
129+ filename,
130+ ) ) ;
131+ }
132+ resp. body ( actix_web:: web:: Bytes :: from ( body) )
133+ } )
134+ } )
135+ }
136+ }
137+
138+ #[ inline]
139+ pub fn get_pprof_conf_handler (
140+ _body : & [ u8 ] ,
141+ _params : & HashMap < String , String > ,
142+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
47143 match mallctl:: enabled ( ) {
48144 Ok ( true ) => ( ) ,
49- _ => return response_err ( "jemalloc profiling not enabled" ) ,
145+ _ => return Err ( ErrorResponse ( "jemalloc profiling not enabled" . to_owned ( ) ) ) ,
50146 } ;
51147
52148 let Ok ( state) = mallctl:: active ( ) else {
53- return response_err ( "failed to read prof.active\r \n " ) ;
149+ return Err ( ErrorResponse ( "failed to read prof.active\r \n " . to_owned ( ) ) ) ;
54150 } ;
55151 let Ok ( sample) = mallctl:: sample_interval ( ) else {
56- return response_err ( "failed to read prof.lg_sample\r \n " ) ;
152+ return Err ( ErrorResponse ( "failed to read prof.lg_sample\r \n " . to_owned ( ) ) ) ;
57153 } ;
58154 let body = format ! ( "prof.active:{state},prof.lg_sample:{sample}\r \n " ) ;
59- response_ok ( body. into_bytes ( ) )
155+ Ok ( ( body. into_bytes ( ) , None ) )
60156}
61157
62158#[ inline]
63- pub fn post_pprof_conf_handler ( req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
159+ pub fn post_pprof_conf_handler (
160+ _body : & [ u8 ] ,
161+ params : & HashMap < String , String > ,
162+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
64163 match mallctl:: enabled ( ) {
65164 Ok ( true ) => ( ) ,
66- _ => return response_err ( "jemalloc profiling not enabled\r \n " ) ,
165+ _ => return Err ( ErrorResponse ( "jemalloc profiling not enabled\r \n " . to_owned ( ) ) ) ,
67166 } ;
68167
69- let query = parse_malloc_conf_query ( req. uri ( ) . query ( ) ) ;
70-
71- for ( name, value) in query {
72- if let Err ( e) = match name {
168+ for ( name, value) in params {
169+ if let Err ( e) = match name. as_str ( ) {
73170 "prof.reset" => {
74- let Some ( sample) = value. map ( |v| v . parse ( ) . ok ( ) ) else {
75- return response_err ( format ! ( "invalid prof.reset value: {value:?}\r \n " ) . as_str ( ) ) ;
76- } ;
77- mallctl:: reset ( sample)
171+ let sample = value. parse ( ) . map_err ( |_| {
172+ ErrorResponse ( format ! ( "invalid prof.reset value: {value:?}\r \n " ) )
173+ } ) ? ;
174+ mallctl:: reset ( Some ( sample) )
78175 }
79176 "prof.active" => {
80- let Some ( value) = value else {
81- return response_err ( "prof.active needs value\r \n " ) ;
82- } ;
83177 let Some ( state) = value. parse ( ) . ok ( ) else {
84- return response_err ( format ! ( "invalid prof.active value: {value:?}\r \n " ) . as_str ( ) ) ;
178+ return Err ( ErrorResponse ( format ! ( "invalid prof.active value: {value:?}\r \n " ) ) ) ;
85179 } ;
86180 mallctl:: set_active ( state)
87181 }
88182 _ => {
89- return response_err ( format ! ( "{name}={value:?} unknown\r \n " ) . as_str ( ) ) ;
183+ return Err ( ErrorResponse ( format ! ( "{name}={value:?} unknown\r \n " ) ) ) ;
90184 }
91185 } {
92- return response_err ( format ! ( "{name}={value:?} failed: {e}\r \n " ) . as_str ( ) ) ;
186+ return Err ( ErrorResponse ( format ! ( "{name}={value:?} failed: {e}\r \n " ) ) ) ;
93187 }
94188 }
95189
96- response_ok ( b"OK\r \n " . to_vec ( ) )
190+ Ok ( ( b"OK\r \n " . to_vec ( ) , None ) )
97191}
98192
99193#[ inline]
100- pub fn get_pprof_heap_handler ( _req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
194+ pub fn get_pprof_heap_handler (
195+ _body : & [ u8 ] ,
196+ _params : & HashMap < String , String > ,
197+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
101198 match mallctl:: enabled ( ) {
102199 Ok ( true ) => ( ) ,
103- _ => return response_err ( "jemalloc profiling not enabled\r \n " ) ,
200+ _ => return Err ( ErrorResponse ( "jemalloc profiling not enabled\r \n " . to_owned ( ) ) ) ,
104201 } ;
105202
106203 let Ok ( f) = tempfile:: Builder :: new ( ) . prefix ( "jemalloc." ) . suffix ( ".prof" ) . tempfile ( ) else {
107- return response_err ( "cannot create temporary file for profile dump\r \n " ) ;
204+ return Err ( ErrorResponse ( "cannot create temporary file for profile dump\r \n " . to_owned ( ) ) ) ;
108205 } ;
109206
110207 let Ok ( profile) = mallctl:: dump ( f. path ( ) . to_str ( ) ) else {
111- return response_err ( "failed to dump profile\r \n " ) ;
208+ return Err ( ErrorResponse ( "failed to dump profile\r \n " . to_owned ( ) ) ) ;
112209 } ;
113210
114211 let filename = f. path ( ) . file_name ( ) . expect ( "proper filename from tempfile" ) ;
115- response_ok_binary ( profile. expect ( "profile not None" ) , filename. to_string_lossy ( ) . as_ref ( ) )
212+ Ok ( ( profile. expect ( "profile not None" ) , Some ( filename. to_string_lossy ( ) . to_string ( ) ) ) )
116213}
117214
118215/// HTTP handler for GET /pprof/cmdline.
119216#[ inline]
120- pub fn get_pprof_cmdline_handler ( _req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
217+ pub fn get_pprof_cmdline_handler (
218+ _body : & [ u8 ] ,
219+ _params : & HashMap < String , String > ,
220+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
121221 let mut body = String :: new ( ) ;
122222 for arg in env:: args ( ) {
123223 body. push_str ( arg. as_str ( ) ) ;
124224 body. push_str ( "\r \n " ) ;
125225 }
126- response_ok ( body. into_bytes ( ) )
226+ Ok ( ( body. into_bytes ( ) , None ) )
127227}
128228
129229/// HTTP handler for GET /pprof/symbol.
130230#[ inline]
131- pub fn get_pprof_symbol_handler ( _req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
231+ pub fn get_pprof_symbol_handler (
232+ _body : & [ u8 ] ,
233+ _params : & HashMap < String , String > ,
234+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
132235 // TODO: any quick way to check if binary is stripped?
133236 let body = b"num_symbols: 1\r \n " ;
134- response_ok ( body. to_vec ( ) )
237+ Ok ( ( body. to_vec ( ) , None ) )
135238}
136239
137240/// HTTP handler for POST /pprof/symbol.
138241#[ inline]
139- pub fn post_pprof_symbol_handler ( req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
242+ pub fn post_pprof_symbol_handler (
243+ body : & [ u8 ] ,
244+ _params : & HashMap < String , String > ,
245+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
140246 fn lookup_symbol ( addr : u64 ) -> Option < String > {
141247 let mut s: Option < String > = None ;
142248 backtrace:: resolve ( addr as * mut _ , |symbol| {
@@ -145,7 +251,7 @@ pub fn post_pprof_symbol_handler(req: Request<Vec<u8>>) -> http::Result<Response
145251 s
146252 }
147253
148- let body = String :: from_utf8_lossy ( req . body ( ) ) ;
254+ let body = String :: from_utf8_lossy ( body) ;
149255 let addrs = body
150256 . split ( '+' )
151257 . filter_map ( |addr| u64:: from_str_radix ( addr. trim_start_matches ( "0x" ) , 16 ) . ok ( ) )
@@ -157,17 +263,20 @@ pub fn post_pprof_symbol_handler(req: Request<Vec<u8>>) -> http::Result<Response
157263 body. push_str ( format ! ( "{addr:#x}\t {sym}\r \n " ) . as_str ( ) ) ;
158264 }
159265
160- response_ok ( body. into_bytes ( ) )
266+ Ok ( ( body. into_bytes ( ) , None ) )
161267}
162268
163269/// HTTP handler for GET /pprof/stats.
164270#[ inline]
165- pub fn get_pprof_stats_handler ( _req : Request < Vec < u8 > > ) -> http:: Result < Response < Vec < u8 > > > {
271+ pub fn get_pprof_stats_handler (
272+ _body : & [ u8 ] ,
273+ _params : & HashMap < String , String > ,
274+ ) -> Result < ( Vec < u8 > , Option < String > ) , ErrorResponse > {
166275 let body = match mallctl:: stats ( ) {
167276 Ok ( body) => body,
168- Err ( e) => return response_err ( format ! ( "failed to print stats: {e}\r \n " ) . as_str ( ) ) ,
277+ Err ( e) => return Err ( ErrorResponse ( format ! ( "failed to print stats: {e}\r \n " ) ) ) ,
169278 } ;
170- response_ok ( body)
279+ Ok ( ( body, None ) )
171280}
172281
173282fn parse_malloc_conf_query ( query : Option < & str > ) -> Vec < ( & str , Option < & str > ) > {
0 commit comments