@@ -67,6 +67,11 @@ def export(self, filename):
6767 return True
6868
6969
70+ # Extra recursion budget for the recursive flamegraph tree walk during export,
71+ # large enough to cover the unwinder's maximum captured stack depth.
72+ _FLAMEGRAPH_RECURSION_MARGIN = 2000
73+
74+
7075class FlamegraphCollector (StackTraceCollector ):
7176 def __init__ (self , * args , ** kwargs ):
7277 super ().__init__ (* args , ** kwargs )
@@ -163,34 +168,44 @@ def set_replay_stats(self, info):
163168 )
164169
165170 def export (self , filename ):
166- flamegraph_data = self . _convert_to_flamegraph_format ()
167-
168- # Debug output with string table statistics
169- num_functions = len ( flamegraph_data . get ( "children" , []))
170- total_time = flamegraph_data . get ( "value" , 0 )
171- string_count = len ( self . _string_table )
172- s1 = "" if num_functions == 1 else "s"
173- s2 = "" if total_time == 1 else "s"
174- s3 = "" if string_count == 1 else "s"
175- print (
176- f"Flamegraph data: { num_functions } root function { s1 } , "
177- f" { total_time } total sample { s2 } , "
178- f" { string_count } unique string { s3 } "
179- )
180-
181- if num_functions == 0 :
171+ # The flamegraph tree is as deep as the deepest sampled stack and is
172+ # walked recursively here (and by json.dumps), so raise the recursion
173+ # limit while exporting. The remote unwinder caps stack depth at
174+ # MAX_FRAMES, so this margin is bounded.
175+ old_limit = sys . getrecursionlimit ( )
176+ sys . setrecursionlimit ( old_limit + _FLAMEGRAPH_RECURSION_MARGIN )
177+ try :
178+ flamegraph_data = self . _convert_to_flamegraph_format ()
179+
180+ # Debug output with string table statistics
181+ num_functions = len ( flamegraph_data . get ( "children" , []))
182+ total_time = flamegraph_data . get ( "value" , 0 )
183+ string_count = len ( self . _string_table )
184+ s1 = "" if num_functions == 1 else "s"
185+ s2 = "" if total_time == 1 else "s"
186+ s3 = "" if string_count == 1 else "s"
182187 print (
183- "Warning: No functions found in profiling data. Check if sampling captured any data."
188+ f"Flamegraph data: { num_functions } root function{ s1 } , "
189+ f"{ total_time } total sample{ s2 } , "
190+ f"{ string_count } unique string{ s3 } "
184191 )
185- return False
186192
187- html_content = self ._create_flamegraph_html (flamegraph_data )
193+ if num_functions == 0 :
194+ print (
195+ "Warning: No functions found in profiling data. "
196+ "Check if sampling captured any data."
197+ )
198+ return False
188199
189- with open (filename , "w" , encoding = "utf-8" ) as f :
190- f .write (html_content )
200+ html_content = self ._create_flamegraph_html (flamegraph_data )
191201
192- print (f"Flamegraph saved to: { filename } " )
193- return True
202+ with open (filename , "w" , encoding = "utf-8" ) as f :
203+ f .write (html_content )
204+
205+ print (f"Flamegraph saved to: { filename } " )
206+ return True
207+ finally :
208+ sys .setrecursionlimit (old_limit )
194209
195210 @staticmethod
196211 @functools .lru_cache (maxsize = None )
0 commit comments