@@ -17,6 +17,7 @@ use databend_common_exception::Result;
1717use log:: debug;
1818use pprof:: ProfilerGuard ;
1919use pprof:: ProfilerGuardBuilder ;
20+ use regex:: Regex ;
2021
2122use crate :: runtime:: ThreadTracker ;
2223use crate :: runtime:: TrackingGuard ;
@@ -65,6 +66,7 @@ impl QueryPerf {
6566 pub fn dump ( profiler_guard : & ProfilerGuard < ' static > ) -> Result < String > {
6667 let reporter = profiler_guard
6768 . report ( )
69+ . frames_post_processor ( frames_post_processor ( ) )
6870 . build ( )
6971 . map_err ( |_e| ErrorCode :: Internal ( "Failed to report profiler data" ) ) ?;
7072 debug ! ( "perf stop, begin to dump flamegraph" ) ;
@@ -126,6 +128,49 @@ impl QueryPerf {
126128 }
127129}
128130
131+ const PPROF_TRACE_SYMBOL : & str =
132+ "<pprof::backtrace::backtrace_rs::Trace as pprof::backtrace::Trace>::trace" ;
133+
134+ fn frames_post_processor ( ) -> impl Fn ( & mut pprof:: Frames ) {
135+ // If pprof cannot get thread name, it will use thread id as fallback
136+ // this will make the flamegraph hard to read, so we rename such threads to "threads"
137+ let thread_rename = [ ( Regex :: new ( r"^\d+$" ) . unwrap ( ) , "threads" ) ] ;
138+
139+ move |frames| {
140+ for ( regex, name) in thread_rename. iter ( ) {
141+ if regex. is_match ( & frames. thread_name ) {
142+ frames. thread_name = name. to_string ( ) ;
143+ }
144+ }
145+
146+ // Remove frames introduced by pprof's own stack collection to keep user stacks clean.
147+ if let Some ( pos) = frames. frames . iter ( ) . position ( |frame| {
148+ frame
149+ . iter ( )
150+ . any ( |symbol| symbol. name ( ) == PPROF_TRACE_SYMBOL )
151+ } ) {
152+ frames. frames . drain ( ..=pos) ;
153+ }
154+
155+ // Mark inlined functions with "(inlined)" suffix
156+ for inline_frames in frames. frames . iter_mut ( ) {
157+ if inline_frames. len ( ) <= 1 {
158+ continue ;
159+ }
160+
161+ // Mark every symbol except the outermost one as inlined.
162+ let last_symbol_index = inline_frames. len ( ) - 1 ;
163+ for symbol in inline_frames. iter_mut ( ) . take ( last_symbol_index) {
164+ let symbol_name = symbol. name ( ) ;
165+ if symbol_name. ends_with ( " (inlined)" ) {
166+ continue ;
167+ }
168+ symbol. name = Some ( format ! ( "{symbol_name} (inlined)" ) . into_bytes ( ) ) ;
169+ }
170+ }
171+ }
172+ }
173+
129174#[ cfg( test) ]
130175mod tests {
131176 use crate :: runtime:: QueryPerf ;
0 commit comments