scripts: Added codemapd3.py
Inspired heavily by d3 and brendangregg's flamegraphs, codemapd3.py is
intended to be a powerful high-level code exploring tool.
It's a visual tool, so probably best explained visually:
$ CFLAGS='-DLFS_NO_LOG -DLFS_NO_ASSERT' make -j
$ ./scripts/codemapd3.py \
lfs.o lfs_util.o \
lfs.ci lfs_util.ci \
-otest.svg -W1500 -H700 --dark
updated test.svg, code 35528 stack 2440 ctx 636
And open test.svg in a browser of your choice.
(TODO add a make rule for this)
---
Features include:
- Rendering of code cost in a treemap organized by subsystem (based on
underscore-separated namespaces), making it relatively easy to see
where the bulk of our code cost comes from.
- Rendering of the deepest stack/ctx cost as a set of tiles, making it
relatively easy to see where the bulk of our stack cost comes from.
- Interactive (on mouseover) rendering of callgraph info, showing
dependencies and relevant stack/ctx costs per-function.
This currently includes 4 modes:
1. mode-callgraph - This shows the full callgraph, including all
children's children, which is effectively all dependencies of that
function, i.e. the total code cost necessary for that _specific_
function to work.
2. mode-deepest - This shows the deepest/hot path of calls from that
function, which is every child that contributes to the function's
stack cost.
3. mode-callees - This shows all functions the current function
immediately calls.
4. mode-callers - This shows all functions that call the current
function.
And yes, cycles are handled correctly: We show the deepest
non-cyclical path, but display the measured stack usage as infinite.
For more details see ./scripts/codemapd3.py --help.
---
One particularly neat feature I'm happy about is -t/--tiny, which scales
the resulting image such that 1 pixel ~= 1 byte. This should be useful
for comparing littlefs to other filesystems in a way that is visually
interesting.
- d3 - https://d3js.org
- brendangregg's flamegraphs - https://github.com/brendangregg/FlameGraph
This commit is contained in:
Executable
+2929
File diff suppressed because it is too large
Load Diff
+7
-5
@@ -1141,22 +1141,24 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--to-scale',
|
'--to-scale',
|
||||||
nargs='?',
|
nargs='?',
|
||||||
type=float,
|
type=lambda x: (
|
||||||
|
(lambda a, b: a / b)(*(float(v) for v in x.split(':', 1)))
|
||||||
|
if ':' in x else float(x)),
|
||||||
const=1,
|
const=1,
|
||||||
help="Scale the resulting treemap such that 1 pixel ~= 1/scale "
|
help="Scale the resulting treemap such that 1 pixel ~= 1/scale "
|
||||||
"units. Defaults to scale=1. ")
|
"units. Defaults to scale=1. ")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-R', '--aspect-ratio',
|
'-R', '--aspect-ratio',
|
||||||
type=lambda x: tuple(float(v) for v in x.split(':', 1)),
|
type=lambda x: (
|
||||||
default=(1, 1),
|
tuple(float(v) for v in x.split(':', 1))
|
||||||
|
if ':' in x else (float(x), 1)),
|
||||||
help="Aspect ratio to use with --to-scale. Defaults to 1:1.")
|
help="Aspect ratio to use with --to-scale. Defaults to 1:1.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--title',
|
'--title',
|
||||||
help="Add a title.")
|
help="Add a title. Accepts %% modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--padding',
|
'--padding',
|
||||||
type=float,
|
type=float,
|
||||||
default=0,
|
|
||||||
help="Padding to add to each level of the treemap. Defaults to 0.")
|
help="Padding to add to each level of the treemap. Defaults to 0.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--label',
|
'-l', '--label',
|
||||||
|
|||||||
+13
-8
@@ -577,7 +577,8 @@ def main(csv_paths, output, *,
|
|||||||
**args):
|
**args):
|
||||||
# tiny mode?
|
# tiny mode?
|
||||||
if tiny:
|
if tiny:
|
||||||
to_scale = True
|
if to_scale is None:
|
||||||
|
to_scale = 1
|
||||||
no_header = True
|
no_header = True
|
||||||
no_label = True
|
no_label = True
|
||||||
|
|
||||||
@@ -666,7 +667,9 @@ def main(csv_paths, output, *,
|
|||||||
t.label = punescape(labels_[i, t.key], t.attrs)
|
t.label = punescape(labels_[i, t.key], t.attrs)
|
||||||
|
|
||||||
# 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 and (width is None or height is None) and tile.value != 0:
|
if (to_scale is not None
|
||||||
|
and (width is None or height is None)
|
||||||
|
and tile.value != 0):
|
||||||
# scale width only
|
# scale width only
|
||||||
if height is not None:
|
if height is not None:
|
||||||
width_ = mt.ceil((tile.value * to_scale) / height_)
|
width_ = mt.ceil((tile.value * to_scale) / height_)
|
||||||
@@ -907,7 +910,7 @@ if __name__ == "__main__":
|
|||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Render CSV files as a treemap to a d3-esque svg.",
|
description="Render CSV files as a d3-esque treemap.",
|
||||||
allow_abbrev=False)
|
allow_abbrev=False)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'csv_paths',
|
'csv_paths',
|
||||||
@@ -1022,14 +1025,17 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--to-scale',
|
'--to-scale',
|
||||||
nargs='?',
|
nargs='?',
|
||||||
type=float,
|
type=lambda x: (
|
||||||
|
(lambda a, b: a / b)(*(float(v) for v in x.split(':', 1)))
|
||||||
|
if ':' in x else float(x)),
|
||||||
const=1,
|
const=1,
|
||||||
help="Scale the resulting treemap such that 1 pixel ~= 1/scale "
|
help="Scale the resulting treemap such that 1 pixel ~= 1/scale "
|
||||||
"units. Defaults to scale=1. ")
|
"units. Defaults to scale=1. ")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-R', '--aspect-ratio',
|
'-R', '--aspect-ratio',
|
||||||
type=lambda x: tuple(float(v) for v in x.split(':', 1)),
|
type=lambda x: (
|
||||||
default=(1, 1),
|
tuple(float(v) for v in x.split(':', 1))
|
||||||
|
if ':' in x else (float(x), 1)),
|
||||||
help="Aspect ratio to use with --to-scale. Defaults to 1:1.")
|
help="Aspect ratio to use with --to-scale. Defaults to 1:1.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-t', '--tiny',
|
'-t', '--tiny',
|
||||||
@@ -1042,11 +1048,10 @@ if __name__ == "__main__":
|
|||||||
help="Show nested tiles.")
|
help="Show nested tiles.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--title',
|
'--title',
|
||||||
help="Add a title.")
|
help="Add a title. Accepts %% modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--padding',
|
'--padding',
|
||||||
type=float,
|
type=float,
|
||||||
default=1,
|
|
||||||
help="Padding to add to each level of the treemap. Defaults to 1.")
|
help="Padding to add to each level of the treemap. Defaults to 1.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--no-label',
|
'--no-label',
|
||||||
|
|||||||
Reference in New Issue
Block a user