41
41
# pandas
42
42
# plotly
43
43
44
+ import fnmatch
44
45
import logging
45
46
import re
46
47
import subprocess
@@ -362,6 +363,41 @@ def test_tree_display_name():
362
363
) == ["(anonymous namespace)" , "AccessControlAttribute" , "Read(args)" ]
363
364
364
365
366
+ def shorten_name (full_name : str ) -> str :
367
+ """
368
+ Remove namespaces, but keep template parts
369
+
370
+ This tries to convert:
371
+ foo::bar::baz(int, double) -> baz(int, double)
372
+ foo::bar::baz<x::y>(int, double) -> baz<x::y>(int, double)
373
+ foo::bar::baz(some::ns::bit, double) -> baz(some::ns::bit, double)
374
+ foo::bar::baz<x::y>(some::ns::bit, double) -> baz<x::y>(some::ns::bit, double)
375
+
376
+ Remove all before '::', however '::' found before the first of < or (
377
+ """
378
+ limit1 = full_name .find ('<' )
379
+ limit2 = full_name .find ('(' )
380
+ if limit1 >= 0 and limit1 < limit2 :
381
+ limit = limit1
382
+ else :
383
+ limit = limit2
384
+ separate_idx = full_name .rfind ('::' , 0 , limit )
385
+ if separate_idx > 0 :
386
+ short_name = full_name [separate_idx + 2 :]
387
+ else :
388
+ short_name = full_name
389
+ return short_name
390
+
391
+
392
+ def test_shorten_name ():
393
+ assert shorten_name ("foo::bar::baz(int, double)" ) == "baz(int, double)"
394
+ assert shorten_name ("foo::bar::baz<x::y>(int, double)" ) == "baz<x::y>(int, double)"
395
+ assert shorten_name ("foo::bar::baz(some::ns::bit, double)" ) == "baz(some::ns::bit, double)"
396
+ assert shorten_name ("foo::bar::baz<x::y>(some::ns::bit, double)" ) == "baz<x::y>(some::ns::bit, double)"
397
+ assert shorten_name ("chip::app:EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint()" ) == "EnsureMatchingEndpoint()"
398
+ assert shorten_name ("void chip::app:EnabledEndpointsWithServerCluster::operator++()" ) == "operator++()"
399
+
400
+
365
401
def build_treemap (
366
402
name : str ,
367
403
symbols : list [Symbol ],
@@ -381,7 +417,7 @@ def build_treemap(
381
417
root = f"FILE: { name } "
382
418
if zoom :
383
419
root = root + f" (FILTER: { zoom } )"
384
- data : dict [str , list ] = dict (name = [root ], parent = ["" ], size = [0 ], hover = ["" ], name_with_size = ["" ], short_name = ["" ])
420
+ data : dict [str , list ] = dict (name = [root ], parent = ["" ], size = [0 ], hover = ["" ], name_with_size = ["" ], short_name = ["" ], id = [ root ] )
385
421
386
422
known_parents : set [str ] = set ()
387
423
total_sizes : dict = {}
@@ -410,15 +446,19 @@ def build_treemap(
410
446
continue
411
447
412
448
partial = ""
449
+ path = ""
413
450
for name in tree_name [:- 1 ]:
414
451
if not partial :
415
452
next_value = name
416
453
else :
417
454
next_value = partial + separator + name
455
+ parent_path = path if partial else root
456
+ path = path + separator + name
418
457
if next_value not in known_parents :
419
458
known_parents .add (next_value )
420
459
data ["name" ].append (next_value )
421
- data ["parent" ].append (partial if partial else root )
460
+ data ["id" ].append (path )
461
+ data ["parent" ].append (parent_path )
422
462
data ["size" ].append (0 )
423
463
data ["hover" ].append (next_value )
424
464
data ["name_with_size" ].append ("" )
@@ -428,7 +468,8 @@ def build_treemap(
428
468
429
469
# the name MUST be added
430
470
data ["name" ].append (cxxfilt .demangle (symbol .name ))
431
- data ["parent" ].append (partial if partial else root )
471
+ data ["id" ].append (symbol .name )
472
+ data ["parent" ].append (path if partial else root )
432
473
data ["size" ].append (symbol .size )
433
474
data ["hover" ].append (f"{ symbol .name } of type { symbol .symbol_type } " )
434
475
data ["name_with_size" ].append ("" )
@@ -446,27 +487,7 @@ def build_treemap(
446
487
else :
447
488
# When using object files, the paths hare are the full "foo::bar::....::method"
448
489
# so clean them up a bit
449
- short_name = data ["short_name" ][idx ]
450
-
451
- # remove namespaces, but keep template parts
452
- # This tries to convert:
453
- # foo::bar::baz(int, double) -> baz(int, double)
454
- # foo::bar::baz<x::y>(int, double) -> baz<x::y>(int, double)
455
- # foo::bar::baz(some::ns:bit, double) -> baz(some::ns::bit, double)
456
- # foo::bar::baz<x::y>(some::ns:bit, double) -> baz<x::y>(some::ns::bit, double)
457
- #
458
- # Remove all before '::', however '::' found before the first of < or (
459
- #
460
- limit1 = short_name .find ('<' )
461
- limit2 = short_name .find ('(' )
462
- if limit1 >= 0 and limit1 < limit2 :
463
- limit = limit1
464
- else :
465
- limit = limit2
466
- separate_idx = short_name .rfind ('::' , 0 , limit )
467
- if separate_idx :
468
- short_name = short_name [separate_idx + 2 :]
469
-
490
+ short_name = shorten_name (data ["short_name" ][idx ])
470
491
data ["name_with_size" ][idx ] = f"{ short_name } : { data ["size" ][idx ]} "
471
492
472
493
extra_args = {}
@@ -477,7 +498,7 @@ def build_treemap(
477
498
fig = figure_generator (
478
499
data ,
479
500
names = "name_with_size" ,
480
- ids = "name " ,
501
+ ids = "id " ,
481
502
parents = "parent" ,
482
503
values = "size" ,
483
504
maxdepth = max_depth ,
@@ -698,14 +719,18 @@ def symbols_from_nm(elf_file: str) -> list[Symbol]:
698
719
return symbols
699
720
700
721
701
- def fetch_symbols (elf_file : str , fetch : FetchStyle ) -> Tuple [list [Symbol ], str ]:
722
+ def fetch_symbols (elf_file : str , fetch : FetchStyle , glob_filter : Optional [ str ] ) -> Tuple [list [Symbol ], str ]:
702
723
"""Returns the sumbol list and the separator used to split symbols
703
724
"""
704
725
match fetch :
705
726
case FetchStyle .NM :
706
- return symbols_from_nm (elf_file ), "::"
727
+ symbols , separator = symbols_from_nm (elf_file ), "::"
707
728
case FetchStyle .OBJDUMP :
708
- return symbols_from_objdump (elf_file ), '/'
729
+ symbols , separator = symbols_from_objdump (elf_file ), '/'
730
+ if glob_filter is not None :
731
+ symbols = [s for s in symbols if fnmatch .fnmatch (s .name , glob_filter )]
732
+
733
+ return symbols , separator
709
734
710
735
711
736
def list_id (tree_path : list [str ]) -> str :
@@ -813,6 +838,11 @@ def compute_symbol_diff(orig: list[Symbol], base: list[Symbol]) -> list[Symbol]:
813
838
default = None ,
814
839
help = "Zoom in the graph to ONLY the specified path as root (e.g. ::chip::app)" ,
815
840
)
841
+ @click .option (
842
+ "--glob-filter" ,
843
+ default = None ,
844
+ help = "Glob filter by name" ,
845
+ )
816
846
@click .option (
817
847
"--strip" ,
818
848
default = None ,
@@ -835,17 +865,21 @@ def main(
835
865
zoom : Optional [str ],
836
866
strip : Optional [str ],
837
867
diff : Optional [str ],
868
+ glob_filter : Optional [str ],
838
869
):
839
870
log_fmt = "%(asctime)s %(levelname)-7s %(message)s"
840
871
coloredlogs .install (level = __LOG_LEVELS__ [log_level ], fmt = log_fmt )
841
872
842
- symbols , separator = fetch_symbols (elf_file , __FETCH_STYLES__ [fetch_via ])
873
+ symbols , separator = fetch_symbols (elf_file , __FETCH_STYLES__ [fetch_via ], glob_filter )
843
874
title = elf_file
844
875
876
+ if glob_filter :
877
+ title += f" FILTER { glob_filter } "
878
+
845
879
if diff :
846
- diff_symbols , _ = fetch_symbols (diff , __FETCH_STYLES__ [fetch_via ])
880
+ diff_symbols , _ = fetch_symbols (diff , __FETCH_STYLES__ [fetch_via ], glob_filter )
847
881
symbols = compute_symbol_diff (symbols , diff_symbols )
848
- title = f"{ elf_file } COMPARED TO { diff } "
882
+ title + = f" COMPARED TO { diff } "
849
883
850
884
build_treemap (
851
885
title , symbols , separator , __CHART_STYLES__ [display_type ], __COLOR_SCALES__ [color ], max_depth , zoom , strip
0 commit comments