@@ -226,7 +226,18 @@ def convert_children(children, min_samples, path_info):
226226 out = []
227227 for func , node in children .items ():
228228 samples = node ["samples" ]
229- if samples < min_samples :
229+ significant_for_thread = any (
230+ thread_samples >= max (
231+ 1 ,
232+ int (
233+ self ._root ["thread_samples" ][thread_id ]
234+ * 0.001
235+ ),
236+ )
237+ for thread_id , thread_samples
238+ in node ["thread_samples" ].items ()
239+ )
240+ if samples < min_samples and not significant_for_thread :
230241 continue
231242
232243 # Intern all string components for maximum efficiency
@@ -270,6 +281,14 @@ def convert_children(children, min_samples, path_info):
270281 opcodes = node .get ("opcodes" , {})
271282 if opcodes :
272283 child_entry ["opcodes" ] = dict (opcodes )
284+ thread_opcodes = node .get ("thread_opcodes" )
285+ if thread_opcodes :
286+ child_entry ["thread_opcodes" ] = {
287+ thread_id : dict (counts )
288+ for thread_id , counts in sorted (
289+ thread_opcodes .items ()
290+ )
291+ }
273292
274293 # Recurse
275294 child_entry ["children" ] = convert_children (
@@ -326,7 +345,25 @@ def convert_children(children, min_samples, path_info):
326345 opcode_mapping = get_opcode_mapping ()
327346
328347 # If we only have one root child, make it the root to avoid redundant level
329- if len (root_children ) == 1 :
348+ root_thread_values = {
349+ thread_id : [samples , 0 ]
350+ for thread_id , samples in sorted (
351+ self ._root ["thread_samples" ].items ()
352+ )
353+ }
354+ sole_root_covers_profile = (
355+ len (root_children ) == 1
356+ and root_children [0 ]["value" ] == total_samples
357+ and {
358+ thread_id : values [0 ]
359+ for thread_id , values
360+ in root_children [0 ]["thread_values" ].items ()
361+ } == {
362+ thread_id : values [0 ]
363+ for thread_id , values in root_thread_values .items ()
364+ }
365+ )
366+ if sole_root_covers_profile :
330367 main_child = root_children [0 ]
331368 # Update name and label to indicate it's the program root
332369 old_name = self ._string_table .get_string (main_child ["name" ])
@@ -355,12 +392,7 @@ def convert_children(children, min_samples, path_info):
355392 "per_thread_stats" : per_thread_stats_with_pct
356393 },
357394 "threads" : sorted (list (self ._all_threads )),
358- "thread_values" : {
359- thread_id : [samples , 0 ]
360- for thread_id , samples in sorted (
361- self ._root ["thread_samples" ].items ()
362- )
363- },
395+ "thread_values" : root_thread_values ,
364396 "strings" : self ._string_table .get_strings (),
365397 "opcode_mapping" : opcode_mapping
366398 }
@@ -406,6 +438,10 @@ def process_frames(self, frames, thread_id, weight=1):
406438
407439 if opcode is not None :
408440 node ["opcodes" ][opcode ] += weight
441+ thread_opcodes = node .setdefault ("thread_opcodes" , {})
442+ thread_opcodes .setdefault (
443+ thread_id , collections .Counter ()
444+ )[opcode ] += weight
409445
410446 current = node
411447
0 commit comments