scripts: codemap[d3].py: Added --tile-* for tiling by stack/ctx/etc

This replaces the previous fallback-to-what's-available behavior with
explicit flags:

- --tile-code - Tile based on code size (the default)
- --tile-stack - Tile based on stack limits
- --tile-frames - Tile based on stack frames
- --tile-ctx - Tile based on function context
- --tile-1 - Tile functions evenly

This has the benefit of 1. being easier to toggle, 2. being explicit,
and 3. allowing code/stack/ctx in punescapes (titles, labels, etc).

There is an interesting question if --no-stack should be implicit, since
showing two stack treemaps may be confusing, but I think that's trying
to be too clever. Instead I just added the -S/--no-stack shortform to
make it easier to toggle.

Also updated ctx.py's description string. Probably need to check what
else is out of date in other scripts as well.
This commit is contained in:
Christopher Haster
2025-04-16 02:23:27 -05:00
parent 7c26bfc0a3
commit ea535faaba
3 changed files with 96 additions and 18 deletions
+45 -7
View File
@@ -931,6 +931,11 @@ def main_(ring, paths, *,
width=None, width=None,
height=None, height=None,
no_header=False, no_header=False,
tile_code=False,
tile_stack=False,
tile_frames=False,
tile_ctx=False,
tile_1=False,
to_scale=None, to_scale=None,
to_ratio=1/1, to_ratio=1/1,
tiny=False, tiny=False,
@@ -958,6 +963,14 @@ def main_(ring, paths, *,
to_scale = 1 to_scale = 1
no_header = True no_header = True
# default to tiling based on code
if (not tile_code
and not tile_stack
and not tile_frames
and not tile_ctx
and not tile_1):
tile_code = True
# what chars/colors/labels to use? # what chars/colors/labels to use?
chars_ = [] chars_ = []
for char in chars: for char in chars:
@@ -1034,6 +1047,7 @@ def main_(ring, paths, *,
# don't render code/stack/ctx results if we don't have any # don't render code/stack/ctx results if we don't have any
nil_code = not any('code_size' in r for r in results) nil_code = not any('code_size' in r for r in results)
nil_stack = not any('stack_limit' in r for r in results)
nil_frames = not any('stack_frame' in r for r in results) nil_frames = not any('stack_frame' in r for r in results)
nil_ctx = not any('ctx_size' in r for r in results) nil_ctx = not any('ctx_size' in r for r in results)
@@ -1151,6 +1165,7 @@ def main_(ring, paths, *,
totals['ctx'] = max( totals['ctx'] = max(
(f.get('ctx', 0) for f in functions.values()), (f.get('ctx', 0) for f in functions.values()),
default=0) default=0)
totals['count'] = len(functions)
totals['attrs'] = {k: v totals['attrs'] = {k: v
for f in functions.values() for f in functions.values()
for k, v in f['attrs'].items()} for k, v in f['attrs'].items()}
@@ -1169,10 +1184,11 @@ def main_(ring, paths, *,
# build code heirarchy # build code heirarchy
code = Tile.merge( code = Tile.merge(
Tile( (f['subsystem'], f['name']), Tile( (f['subsystem'], f['name']),
# fallback to stack/ctx f.get('code', 0) if tile_code and not nil_code
f.get('code', 0) if not nil_code else f.get('stack', 0) if tile_stack and not nil_stack
else f.get('frame', 0) if not nil_frames else f.get('frame', 0) if tile_frames and not nil_frames
else f.get('ctx', 0), else f.get('ctx', 0) if tile_ctx and not nil_ctx
else 1,
attrs=f) attrs=f)
for f in functions.values()) for f in functions.values())
@@ -1213,9 +1229,11 @@ def main_(ring, paths, *,
# scale width/height if requested now that we have our data # scale width/height if requested now that we have our data
if (to_scale is not None if (to_scale is not None
and (width is None or height is None)): and (width is None or height is None)):
total_value = (totals.get('code', 0) if not nil_code total_value = (totals.get('code', 0) if tile_code
else totals.get('frame', 0) if not nil_frames else totals.get('stack', 0) if tile_stack
else totals.get('ctx', 0)) else totals.get('frame', 0) if tile_frames
else totals.get('ctx', 0) if tile_ctx
else totals.get('count', 0))
if total_value: if total_value:
# don't include header in scale # don't include header in scale
width__ = width_ width__ = width_
@@ -1555,6 +1573,26 @@ if __name__ == "__main__":
'--no-header', '--no-header',
action='store_true', action='store_true',
help="Don't show the header.") help="Don't show the header.")
parser.add_argument(
'--tile-code',
action='store_true',
help="Tile based on code size. This is the default.")
parser.add_argument(
'--tile-stack',
action='store_true',
help="Tile based on stack limits.")
parser.add_argument(
'--tile-frames',
action='store_true',
help="Tile based on stack frames.")
parser.add_argument(
'--tile-ctx',
action='store_true',
help="Tile based on function context.")
parser.add_argument(
'--tile-1',
action='store_true',
help="Tile functions evenly.")
parser.add_argument( parser.add_argument(
'--binary', '--binary',
action='store_true', action='store_true',
+50 -10
View File
@@ -630,6 +630,11 @@ def main(paths, output, *,
stack_ratio=1/5, stack_ratio=1/5,
no_ctx=False, no_ctx=False,
no_frames=False, no_frames=False,
tile_code=False,
tile_stack=False,
tile_frames=False,
tile_ctx=False,
tile_1=False,
no_javascript=False, no_javascript=False,
mode_callgraph=False, mode_callgraph=False,
mode_deepest=False, mode_deepest=False,
@@ -656,6 +661,14 @@ def main(paths, output, *,
no_stack = True no_stack = True
no_javascript = True no_javascript = True
# default to tiling based on code
if (not tile_code
and not tile_stack
and not tile_frames
and not tile_ctx
and not tile_1):
tile_code = True
# default to all modes # default to all modes
if (not mode_callgraph if (not mode_callgraph
and not mode_deepest and not mode_deepest
@@ -737,12 +750,15 @@ def main(paths, output, *,
# don't render code/stack/ctx results if we don't have any # don't render code/stack/ctx results if we don't have any
nil_code = not any('code_size' in r for r in results) nil_code = not any('code_size' in r for r in results)
nil_stack = not any('stack_limit' in r for r in results)
nil_frames = not any('stack_frame' in r for r in results) nil_frames = not any('stack_frame' in r for r in results)
nil_ctx = not any('ctx_size' in r for r in results)
if nil_frames: if nil_frames:
no_frames = True no_frames = True
nil_ctx = not any('ctx_size' in r for r in results)
if nil_ctx: if nil_ctx:
no_ctx = True no_ctx = True
if no_frames and no_ctx: if no_frames and no_ctx:
no_stack = True no_stack = True
@@ -860,6 +876,7 @@ def main(paths, output, *,
totals['ctx'] = max( totals['ctx'] = max(
(f.get('ctx', 0) for f in functions.values()), (f.get('ctx', 0) for f in functions.values()),
default=0) default=0)
totals['count'] = len(functions)
totals['attrs'] = {k: v totals['attrs'] = {k: v
for f in functions.values() for f in functions.values()
for k, v in f['attrs'].items()} for k, v in f['attrs'].items()}
@@ -878,10 +895,11 @@ def main(paths, output, *,
# build code heirarchy # build code heirarchy
code = Tile.merge( code = Tile.merge(
Tile( (f['subsystem'], f['name']), Tile( (f['subsystem'], f['name']),
# fallback to stack/ctx f.get('code', 0) if tile_code and not nil_code
f.get('code', 0) if not nil_code else f.get('stack', 0) if tile_stack and not nil_stack
else f.get('frame', 0) if not nil_frames else f.get('frame', 0) if tile_frames and not nil_frames
else f.get('ctx', 0), else f.get('ctx', 0) if tile_ctx and not nil_ctx
else 1,
attrs=f) attrs=f)
for f in functions.values()) for f in functions.values())
@@ -988,9 +1006,11 @@ def main(paths, output, *,
# scale width/height if requested now that we have our data # scale width/height if requested now that we have our data
if (to_scale is not None if (to_scale is not None
and (width is None or height is None)): and (width is None or height is None)):
total_value = (totals.get('code', 0) if not nil_code total_value = (totals.get('code', 0) if tile_code
else totals.get('frame', 0) if not nil_frames else totals.get('stack', 0) if tile_stack
else totals.get('ctx', 0)) else totals.get('frame', 0) if tile_frames
else totals.get('ctx', 0) if tile_ctx
else totals.get('count', 0))
if total_value: if total_value:
# don't include header/stack in scale # don't include header/stack in scale
width__ = width_ width__ = width_
@@ -2193,11 +2213,11 @@ if __name__ == "__main__":
action='store_true', action='store_true',
help="Don't show the mode state.") help="Don't show the mode state.")
parser.add_argument( parser.add_argument(
'--no-stack', '-S', '--no-stack',
action='store_true', action='store_true',
help="Don't render any stack info.") help="Don't render any stack info.")
parser.add_argument( parser.add_argument(
'-S', '--stack-ratio', '-s', '--stack-ratio',
type=lambda x: ( type=lambda x: (
(lambda a, b: a / b)(*(float(v) for v in x.split(':', 1))) (lambda a, b: a / b)(*(float(v) for v in x.split(':', 1)))
if ':' in x else float(x)), if ':' in x else float(x)),
@@ -2210,6 +2230,26 @@ if __name__ == "__main__":
'--no-frames', '--no-frames',
action='store_true', action='store_true',
help="Don't render function stack frame info.") help="Don't render function stack frame info.")
parser.add_argument(
'--tile-code',
action='store_true',
help="Tile based on code size. This is the default.")
parser.add_argument(
'--tile-stack',
action='store_true',
help="Tile based on stack limits.")
parser.add_argument(
'--tile-frames',
action='store_true',
help="Tile based on stack frames.")
parser.add_argument(
'--tile-ctx',
action='store_true',
help="Tile based on function context.")
parser.add_argument(
'--tile-1',
action='store_true',
help="Tile functions evenly.")
parser.add_argument( parser.add_argument(
'-J', '--no-javascript', '-J', '--no-javascript',
action='store_true', action='store_true',
+1 -1
View File
@@ -1392,7 +1392,7 @@ if __name__ == "__main__":
import argparse import argparse
import sys import sys
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Find struct sizes.", description="Find the overhead of function contexts.",
allow_abbrev=False) allow_abbrev=False)
parser.add_argument( parser.add_argument(
'obj_paths', 'obj_paths',