Commit Graph

2123 Commits

Author SHA1 Message Date
Christopher Haster
002c2ea1e6 scripts: Tried to simplify optional path returns
So, instead of trying to be clever with python's tuple globbing, just
rely on lazy tuple unpacking and a whole bunch of if statements.

This is more verbose, but less magical. And generally, the less magic
there is, the easier things are to read.

This also drops the always-tupled lookup_ variants, which were
cluttering up the various namespaces.
2025-04-16 15:22:12 -05:00
Christopher Haster
82f4fd3c0f scripts: Dropped list/tuple distinction in Rbyd.fetch
Also tweaked how we fetch shrubs, adding Rbyd.fetchshrub and
Btree.fetchshrub instead of overloading the bd argument.

Oh, and also added --trunk to dbgmtree.py and dbglfs.py. Actually
_using_ --trunk isn't advised, since it will probably just result in a
corrupted filesystem, but these scripts are for accessing things that
aren't normally allowed anyways.

The reason for dropping the list/tuple distinction is because it was a
big ugly hack, unpythonic, and likely to catch users (and myself) by
surprise. Now, Rbyd.fetch and friends always require separate
block/trunk arguments, and the exercise of deciding which trunk to use
is left up to the caller.
2025-04-16 15:22:11 -05:00
Christopher Haster
1ac3aae92b scripts: test.py/bench.py: Added -e/--exec shortform flag
Why not, -e/--exec seems useful/general purpose enough to deserve a
shortform flag. Especially since much of our testing involves emulation.

The only risk of conflicts is with -e/--error-* in other scripts, but
the _whole point_ of test.py is to error on failure, so I don't think
this will be an issue.

Note that -E may be more useful for environment variables in the future.

I feel like -e/--exec was more common in other programs, but I've only
found sed -e and perl -e so far. Most programs stick to -c/--command
(bash, python) which would conflict with -c/--compile here.
2025-04-16 15:22:10 -05:00
Christopher Haster
59251a755c scripts: dbgflags.py: Tweaked to accept lfs-prefixed prefixes
So:

  $ ./scripts/dbgflags.py -l LFS_I

Is equivalent to:

  $ ./scripts/dbgflags.py -l I

This matches some of the implicit prefixing during name lookup:

  $ ./scripts/dbgflags.py LFS_I_SYNC
  $ ./scripts/dbgflags.py I_SYNC
  $ ./scripts/dbgflags.py SYNC
2025-04-16 15:22:09 -05:00
Christopher Haster
270230a833 scripts: Adopted del to resolve shadowed builtins
So:

  all_ = all; del all

Instead of:

  import builtins
  all_, all = all, builtins.all

The del exposes the globally scoped builtin we accidentally shadow.

This requires less megic, and no module imports, though tbh I'm
surprised it works.

It also works in the case where you change a builtin globally, but
that's a bit too crazy even for me...
2025-04-16 15:22:08 -05:00
Christopher Haster
8324786121 scripts: Reverted skipped branches in -t/--tree render
The inconsistency between inner/non-inner (-i/--inner) views was a bit
too confusing.

At least now the bptr rendering in dbglfs.py matches behavior, showing
the bptr tag -> bptr jump even when not showing inner nodes.

If the point of these renderers is to show all jumps necessary to reach
a given piece of data, hiding bptr jumps only sometimes is somewhat
counterproductive...
2025-04-16 15:22:07 -05:00
Christopher Haster
97b6489883 scripts: Reworked dbglfs.py, adopted Lfs, Config, Gstate, etc
I'm starting to regret these reworks. They've been a big time sink. But
at least these should be much easier to extend with the future planned
auxiliary trees?

New classes:

- Bptr - A representation of littlefs's data-only block pointers.

  Extra fun is the lazily checked Bptr.__bool__ method, which should
  prevent slowing down scripts that don't actually verify checksums.

- Config - The set of littlefs config entries.

- Gstate - The set of littlefs gstate.

  I may have had too much fun with Config and Gstate. Not only do these
  provide lookup functions for config/gstate, but known config/gstate
  get lazily parsed classes that can provide easy access to the relevant
  metadata.

  These even abuse Python's __subclasses__, so all you need to do to add
  a new known config/gstate is extend the relevant Config.Config/
  Gstate.Gstate class.

  The __subclasses__ API is a weird but powerful one.

- Lfs - The big one, a high-level abstraction of littlefs itself.

  Contains subclasses for known files: Lfs.Reg, Lfs.Dir, Lfs.Stickynote,
  etc, which can be accessed by path, did+name, mid, etc. It even
  supports iterating over orphaned files, though it's expensive (but
  incredibly valuable for debugging!).

  Note that all file types can currently have attached bshrubs/btrees.
  In the existing implementation only reg files should actually end up
  with bshrubs/btrees, but the whole point of these scripts is to debug
  things that _shouldn't_ happen.

  I intentionally gave up on providing depth bounds in Lfs. Too
  complicated for something so high-level.

On noteworthy change is not recursing into directories by default. This
hopefully avoids overloading new users and matches the behavior of most
other Linux/Unix tools.

This adopts -r/--recurse/--file-depth for controlling how far to recurse
down directories, and -z/--depth/--tree-depth for controlling how far to
recurse down tree structures (mostly files). I like this API. It's
consistent with -z/--depth in the other dbg scripts, and -r/--recurse is
probably intuitive for most Linux/Unix users.

To make this work we did need to change -r/--raw -> -x/--raw. But --raw
is already a bit of a weird name for what really means "include a hex
dump".

Note that -z/--depth/--tree-depth does _not_ imply --files. Right now
only files can contain tree structures, but this will change when we get
around to adding the auxiliary trees.

This also adds the ability to specify a file path to use as the root
directory, though we need the leading slash to disambiguate file paths
and mroot addresses.

---

Also tagrepr has been tweaked to include the global/delta names,
toggleable with the optional global_ kwarg.

Rattr now has its own lazy parsers for did + name. A more organized
codebase would probably have a separate Name type, but it just wasn't
worth the hassle.

And the abstraction classes have all been tweaked to require the
explicit Rbyd.repr() function for a CLI-friendly representation. Relying
on __str__ hurt readability and debugging, especially since Python
prefers __str__ over __repr__ when printing things.
2025-04-16 15:22:06 -05:00
Christopher Haster
cc20610488 scripts: Skip branches in -t/--tree render, fixed color-repro issues
The main difference between -t/--tree and -R/--tree-rbyd is that only
the latter shows all internal jumps (unconditional alt->alt), so it
makes sense to also hide internal branches (rbyd->rbyd).

Note that we already hide the rbyd->block branches in dbglfs.py.

Also added color-ignoring comparison operators to our internal
TreeBranch struct. This fixes an issue where our non-inner branch
merging logic could end up with identical branches with different
colors, resulting in different colorings per run. Not the end of the
world, but something we want to avoid.
2025-04-16 15:22:05 -05:00
Christopher Haster
45d9ea1f62 scripts: dbgmtree.py: Tightened dynamic mrid width
This requires an additional traversal of the mtree just to precalculate
the mrid width (mbits provides an upper-bound, but the actual number of
mrids in any given mdir may be much less), but it makes the output look
nicer.
2025-04-16 15:22:04 -05:00
Christopher Haster
582f92d073 scripts: Reworked dbgmtree.py, adopted Mtree, Mdir, etc
This is where the high-level structure of littlefs starts to reveal
itself.

This is also where a lot of really annoying Mtree vs Btree API questions
come to a head, like should Mtree.lookup return an Mdir or an Rattr?
What about Btree.lookup? What gets included in the returned path in all
of these? Well, at least this is an interesting exercise in rethinking
littlefs's internal APIs...

New classes:

- Mid - A representation of littlefs's metadata ids. I've just gone
  ahead and included the block_size-dependent mbits as a field in every
  Mid instance to try to make Mid operations easier.

  It's not like we care about one extra word of storage in Python.

- Mdir - Again, we intentionally _don't_ inherit Rbyd to try to reduce
  type errors, though Mdirs really are just Rbyds in this design.

- Mtree - The skeleton of littlefs. Tricky bits include traversing the
  mroot chain and handling mroot-inlined mdirs. Note mroots are included
  in the mdir/mid iteration methods.

Getting the tree renderers all working again was a real pain in the ass.
2025-04-16 15:22:03 -05:00
Christopher Haster
0e2a302d35 scripts: Dropped tag/weight when returning rattrs
Now that these are contained in the Rattr class, including the
tag/weight just clutters these APIs and makes things more confusing.

To make this more convenient, I've adding __iter__ methods that allow
unpacking both the Rattr and Ralt classes. These more-or-less represent
tag+weight+data tuples anyways.
2025-04-16 15:22:02 -05:00
Christopher Haster
46c55722a5 scripts: Reworked dbgbtree.py, adopted Btree class
Like the Rbyd class, Btree serves as an abstraction for littlefs's
btrees in Python.

New classes:

- Btree - btree abstraction, note this does _not_ inherit from Rbyd. I
  find that sort of inheritance too error-prone. Instead Btree
  _contains_ the root rbyd, which can always be accessed via Btree.rbyd.

  If you want low-level root-rbyd details, just access Btree.rbyd.

  Though most fields that are relevant to the Btree are also forwarded
  via Python's @property properties.

- Bd - This just serves as a handle for the disk file that includes
  block_size/block_count metadata.

One important change to note is the adoption of required vestigial names
in all btree nodes (yes this scripts was written... checks notes...
2 years ago... even the same month huh). This means we don't need the
parent name mapping, so the non-inner btree printing code no longer
needs to be extremely confusing at all times.

Also adopted the Rbyd class and friends, and backported Bd to
dbgrbyd.py.

Also tried to give a couple useful algorithms their own self-contained
functions, mainly:

- pathdelta - for emulating a traversal over exhaustive paths

- treerepr - for the common ascii tree rendering code
2025-04-16 15:22:00 -05:00
Christopher Haster
73127470f9 scripts: Adopted rbydaddr/tagrepr changes across scripts
Just some minor tweaks:

- rbydaddr: Return list instead of tuple, note we rely on the type
  distinction in Rbyd.fetch now.

- tagrepr: Rename w -> weight.
2025-04-16 15:21:59 -05:00
Christopher Haster
21049321a5 scripts: Reworked dbgrbyd.py, adopted Rbyd class
This reworks dbgrbyd.py to use the Rbyd class (well, a rewrite of the
Rbyd class) as an abstraction of littlefs's rbyd disk structure in
Python.

Duplicating common classes/functions across these scripts has proven
useful for sharing code without preventing these scripts from being
standalone (a problem for _actual_ code sharing, relative imports, etc).
And, because of how these scripts were written, dbgrbyd.py humorously
ended up the only script not sharing the Rbyd class.

I'm also trying to make the actual Rbyd abstraction a bit more concrete
now that the filesystem's design has had some time to mature. This means
more classes for things like Rattrs that reduce the sheer number of
tuples that were flying around.

New classes:

- Rattr - rbyd attrs, tag + weight + data, this includes all relevant
  offsets which is useful for rendering hexdumps/etc.

- Ralt - rbyd alt pointers, useful for building tree representations.

- Rbyd - rbyd abstraction, including lookup/traversal methods

Note also that while the Rbyd class replaces most of the dbg_tree logic,
dbg_log is still pretty low-level and abstractionless.

---

Eventually I hope to have well defined classes for Btrees, Mdirs, Files,
etc, to make it easier to write more interesting debug scripts such as
dbgbmap.py.

Separating Btree, Mdirs, etc also means we shouldn't need the hacky
btree_lookup/tree_lookup methods in every script anymore. Having those
in dbgrbyd.py would've been a bit weird.
2025-04-16 15:21:57 -05:00
Christopher Haster
50e22ac690 scripts: codemap[d3].py: Added -e/--error-on-recursion
Might as well, since we already need to find this to calculate stack
info.

I've been considering adding -z/--depth to these scripts as well, but
that would require quite a bit more work. It's probably not worth the
added complexity/headache. Depth termination would need to happen on the
javascript side, and we'd still need cycle detection anyways.

But an error code is easy to add.
2025-04-16 15:21:57 -05:00
Christopher Haster
460d8870ec scripts: Adopted -c for --cat shortform
This avoids conflicts with -z/--depth, which will likely be an issue for
dbgbmap.py.
2025-04-16 15:21:55 -05:00
Christopher Haster
262ad7c08e scripts: Simplified dbgtag.py, tweaked -x/--hex decoding
This drops the option to read tags from a disk file. I don't think I've
ever used this, and it requires quite a bit of circuitry to implement.

Also dropped -s/--string, because most tags can't be represented as
strings?

And tweaked -x/--hex flags to correctly parse spaces in arguments, so
now these are equivalent:

- ./scripts/dbgtag.py -x 00 03 00 08
- ./scripts/dbgtag.py -x "00 03 00 08"
2025-04-16 15:21:54 -05:00
Christopher Haster
cb9d14cc15 scripts: dbgblock.py: Allow multiple blocks, matching dbgcat.py
I mean, why not. dbgblock.py is already a bit special compared to the
other dbg scripts:

  $ ./scripts/dbgblock.py disk -b4096 0 1 -n16
  block 0x0, size 16, cksum a90f45b6
  00000000: 68 69 21 0e 00 03 00 08 6c 69 74 74 6c 65 66 73  hi!.....littlefs
  block 0x1, size 16, cksum 01e5f5e4
  00000000: 68 69 21 0c 80 03 00 08 6c 69 74 74 6c 65 66 73  hi!.....littlefs

This matches dbgcat.py, which is useful when switching between the two
for debugging pipelines, etc.

We want dbgblock.py/dbgcat.py to be as identical as possible, and if you
removed the multiple blocks from dbgcat.py you'd have to really start
asking why it's named dbgCAT.py.
2025-04-16 15:21:52 -05:00
Christopher Haster
c028735741 scripts: dbgblock.py/dbgcat.py: Fixed some range corner cases
Mainly fixing unbounded ranges, which required a bit of tweaking of when
we flatten block arguments.

This adopts the trick of using slice as the representation of, well,
slices in arguments instead of tuples. This avoids type confusion with
rbydaddr also returning tuples (of tuples!).
2025-04-16 15:21:51 -05:00
Christopher Haster
cb0119681d scripts: Adopted -k/--keep-open in treemap.py/codemap.py
It probably makes sense to adopt this in all ascii-art scripts that
operate on files.
2025-04-16 15:21:49 -05:00
Christopher Haster
6c36f76ffd scripts: plot.py: Tweaked -H/--height to allow negative carve-outs
This finally solves the how-do-I-make-space-for-shell-prompts problem:

- plot.py -H0  => use full terminal height
- plot.py -H-1 => use height-1, making space for shell prompts
- plot.py -H   => automatic based on other flags

While also allowing other carveouts in case your prompt takes up more
than 1 line.

Unfortunately this does make -H (no arg) subtly different from -H0, but
sometimes you can't have everything.
2025-04-16 15:21:45 -05:00
Christopher Haster
5e8344a1c6 scripts: Renamed report -> main_ in perf.py/perfbd.py
This is a useful convention. It indicates these functions don't do
anything a main function wouldn't normally do.
2025-04-16 15:21:42 -05:00
Christopher Haster
59703f3b16 scripts: plot.py: Reorganized main -> main_ to isolate -k/--keep-open logic
This simplifies plot.py's -k/--keep-open logic into a self-contained
loop that just calls main_ on an update.

This is a compromise on getting rid of -k/--keep-open completely, since
we _could_ just rely on watch.py. But plot.py knowing which argument is
the file to watch is convenient.

The eventual plan is to adopt this small bit of copy-pastable-code in
the other ascii-art scripts (treemap.py, dbgbmap.py, etc).
2025-04-16 15:21:40 -05:00
Christopher Haster
9b03933f2d scripts: tracebd.py/dbgbmap.py: Made off range a subparser of block range
So:

- before: ./scripts/dbgbmap.py disk -b4096 -@0 -n16,32
- after:  ./scripts/dbgbmap.py disk -b4096 -@'0 -n16,32'

This is mainly to avoid the naming conflict between -n/--size and
-n/--lines, while also separating out the namespaces a bit.

It's probably not the most intuitive CLI UI, but --off and -n/--size are
probably infrequent arguments at this level of script anyways.
2025-04-16 15:21:37 -05:00
Christopher Haster
e18cecc3fb scripts: Tweaked some flags in dbgmap.py/tracebd.py
Mostly to move away from unnecessary shortform flags. Using shortform
flags for what is roughly an unbounded enum just causes too many flag
conflicts as scripts grow:

- -r/--read -> --reads
- -p/--prog -> --progs
- -e/--erase -> --erases
- -w/--wear -> --wear

- -i/--in-use -> -%/--usage
- -M/--mdirs -> --mdirs
- -B/--btrees -> --btress
- -D/--datas -> --data/--datas

I may have had too much fun forcing argparse to make -%/--usage to work.
The percent sign caused a lot of problems for argparse internally.
2025-04-16 15:21:34 -05:00
Christopher Haster
9781055d8c scripts: Dropped -N shortform for --no-header
--no-header doesn't really deserve a shortform, and this risks conflicts
with -N/--notes in the future, not to mention any other number of flags
that can start with --no-*.
2025-04-16 15:21:32 -05:00
Christopher Haster
0dbd1561ae scripts: Fixed some issues with -k/--keep-open
- Fixed a NameError in watch.py caused by an outdated variable name
  (renamed paths -> keep_open_paths). Yay for dynamic typing.

- Fixed fieldnames is None issue when csv file is empty.
2025-04-16 15:21:27 -05:00
Christopher Haster
3a290c41ab scripts: Reverted -o/-O to include all by-fields by default
For the same reason we output all field fields by default: Because
machines can process more information than humans can.

Worst case, by fields can still be limited via explicit -b/--by flags.
2025-03-12 21:26:11 -05:00
Christopher Haster
f3889d8932 scripts: Adopted Canvas class in plot.py
This should have no noticeable impact on plot.py, but shared classes
have proven helpful for maintaining these scripts.

Unfortunately, this did require some tweaking of the Canvas class to get
things working.

Now, instead of storing things in an internal high-resolution grid,
the Canvas class only keeps track of the most recent character, with
bitmasked ints storing sub-char info.

This makes it so sub-char draws overwrite full characters, which is
necessary for plot.py's axis/data overlap to work.
2025-03-12 21:23:16 -05:00
Christopher Haster
4df90dfa0a scripts: Added --squarify-ratio to treemap[d3].py/codemap[d3].py
Might as well. The internal algorithm already supports this.
2025-03-12 21:23:09 -05:00
Christopher Haster
f033a55cc5 scripts: Fixed codemap[d3].py ignoring tiling flags
Not sure how this was missed. Fortunately it's just a small tweak to
make the internal partition function accept tiling args, matching
treemap[d3].py.
2025-03-12 21:21:57 -05:00
Christopher Haster
313696ecf9 scripts: Fixed openio issue where some scripts didn't import os
This only failed if "-" was used as an argument (for stdin/stdout), so
the issue was pretty hard to spot.

openio is a heavily copy-pasted function, so it makes sense to just add
the import os to openio directly. Otherwise this mistake will likely
happen again in the future.
2025-03-12 21:18:51 -05:00
Christopher Haster
b2646148c1 scripts: Tweaked -./-p/-P flags in ascii scripts
- -*/--add-char/--chars -> -./--add-char/--chars
- -./--points -> -p/--points
- -!/--points-and-lines -> -P/--points-and-lines

Also fixed an issue in plot.py/Attr where non-list default were failing
to concatenate.
2025-03-12 21:18:15 -05:00
Christopher Haster
5b5745bca9 scripts: treemap.py/codemap.py: Tweaked -L to imply -l
And added the optional --no-label to explicitly opt out.

This is a bit more consistent with treemapd3.py/codemapd3.py's handling
of labels, while still keeping the no-label default. It also makes it
easier to temporarily hide labels when editing commands.
2025-03-12 21:14:37 -05:00
Christopher Haster
ff803dfc0f scripts: Added vestigial -:/--dots flag to plot.py
This just makes it easier to jump between plot.py and plotmpl.py.
2025-03-12 21:13:40 -05:00
Christopher Haster
59ffbde3ad scripts: treemap.py/codemap.py: Use parts of name for char defaults
So by default, instead of just using "." for tiles, we use interesting
parts of the tile's name:

- For treemap.py, we use the first character of the last by-field (so
  "lfs.c,lfsr_file_write,1234" -> "1").

- For codemap.py, we use the first character of the non-subsystem part
  of the function name (so "lfsr_file_write" -> "w").

This nice thing about this, is the resulting treemap is somewhat
understandable even without colors:

  $ ./scripts/codemap.py lfs.o lfs_util.o lfs.ci lfs_util.ci -W60 -H8
  code 35528 stack 2440 ctx 636
  ffffffoooffaaaaaaaaaaaacccccccccttttccccrrrrpgffmmrraifmmcss
  ffffffwwwttaaaaaaaaaaaacccccccccttttccccrprrpcscmmoommrrcepp
  ffffffwwwttaaaaaaaaalllcccccccccttttccccrpppccscmmsrmmrrrrss
  ccccssrrfclaaaaanneeasscccccccccgpppccccrpppsgsummstmmrrlfgf
  ccccssrrfccaaaaanneeaaaccccccsaagpppcccccrrrfrrcccrrfiiilucs
  ccccssrrtfcfffffaapplcccccccclssgnnllllcrrffrrrccccifssscmcm
  ccccssrrtrdfffffaapppapcccfffllsgnnllllcrrrffrrcccorfsssicnu

Ok, so maybe the word "somewhat" is doing a lot of heavy lifting...
2025-03-12 21:12:12 -05:00
Christopher Haster
3d53f5393d scripts: Added codemap.py
Like codemapd3.py, but with an ascii renderer.

This is basically just codemapd3.py and treemap.py smooshed together.
It's not the cleanest, but it gets the job done. codemap.py is not
the most critical of scripts.

Unfortunately callgraph and stack/ctx info are difficult (impossible?)
to render usefully in ascii, but we can at least do the script calling,
parsing, namespacing, etc, necessary to create the code cost tilemap.
2025-03-12 21:12:12 -05:00
Christopher Haster
34aa054f18 scripts: codemapd3.py cleanup
Just removing outdated/commented-out code, and TODOs that have now been
taken care of.
2025-03-12 21:12:12 -05:00
Christopher Haster
4ea710f62c scripts: Adopted % modifiers in all attr arguments
This turns out to be extremely useful, for the sole purpose of being
able to specify colors/formats/etc in csv fields (-C'%(fields)s' for
example, or -C'#%(field)06x' for a cooler example).

This is a bit tricky for --chars, but doable with a psplit helper
function.

Also fixed a bug in plot.py where we weren't using dataattrs_ correctly.
2025-03-12 21:12:12 -05:00
Christopher Haster
3d355d7783 scripts: Added -t/--tiny to treemap.py
Even though I think this makes less sense for the ascii-rendering
scripts, it's useful to have this flag around when jumping between
treemap.py and treemapd3.py.

And it might actually make sense sometimes now that -t/--tiny does not
override --to-scale.
2025-03-12 21:12:12 -05:00
Christopher Haster
92ac2a757e scripts: Adopted json -> is_json tweak, avoiding name conflict
This was a humorous name conflict that went unnoticed only because we
lazily import json in read_csv.
2025-03-12 21:12:12 -05:00
Christopher Haster
c60301719a scripts: Adopted dat tweak in other scripts
This just makes dat behave similarly to Python's getattr, etc:

- dat("bogus")       -> raises ValueError
- dat("bogus", 1234) -> returns 1234

This replaces try_dat, which is easy to forget about when copy-pasting
between scripts.

Though all of this wouldn't be necessary if only we could catch
exceptions in expressions...
2025-03-12 21:12:12 -05:00
Christopher Haster
e780fd40f7 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
2025-03-12 21:08:23 -05:00
Christopher Haster
021e03cdc9 scripts: treemapd3.py: Tweaked nested rendering, precomputed alpha
This replaces the default colors with colors with precomputed alpha.
They should look the same as long as you don't change the background
color.

The reason for this is I need non-transparent colors for a fork of
treemapd3.py that I am working on. I'm attempting to render some arrows
behind the tiles, and with transparency the result is just too noisy.

---

This then broke the nested rendering, which relied on opacity to make
each layer darker, so I've replaced that with an explicit svg
filter-effect.

The opacity hack was non-linear and kinda ugly past depth >~3, so it
should have eventually been replaced anyways.
2025-03-12 20:14:37 -05:00
Christopher Haster
fb03e27baf scripts: Added --no-stats to treemap.py/treemapd3.py
The previous behavior of -N/--no-header still rendering a header when
--title is also provided was confusing. I think this is a better API,
at the minor cost of needing to pass one more flag if you don't want
stats in the header.
2025-03-12 20:07:27 -05:00
Christopher Haster
0d134a2830 scripts: Re-added -q/--quiet to result scripts
I forgot that this is still useful for erroring scripts, such as
stack.py when checking for recursion.

Technically this is possible with -o/dev/null, but that's both
unnecessarily complicated and includes the csv encoding cost for no
reason.
2025-03-12 20:02:19 -05:00
Christopher Haster
675a805164 scripts: Added -! as a short-form for --everything
-!/--everything has been useful enough to warrant a short form flag,
and -! is unlikely to conflict with other flags while also getting the
point across that this is a bit of an unusual option.
2025-03-12 20:02:12 -05:00
Christopher Haster
b0976379d7 scripts: Added -i/--internal to ctx.py/structs.py, re-limiting structs.py
This adds -i/--internal to ctx.py and structs.py, which has proven
useful for introspection/debugging. Being able to view the ctx/args of
internal functions is nice, even if they don't actually contribute to
the high-level cost.

This also reverts structs.py to limit to .h files by default, to match
ctx.py, once again relying on dwarf file info. This has been a bit
unreliable in the past, but there's not much else that determines if a
struct is part of the "public interface" in C.

But that's what ctx.py is for.

---

Also fixed an issue where structs appearing in multiple files would have
their sizes added together, which ends up with some pretty confusing
results (sizeof(uint32_t) => 8?).
2025-03-12 20:00:56 -05:00
Christopher Haster
1cc38acc91 scripts: Strip compiler suffixes in result scripts
This can be explicitly disabled with -x/--no-strip in the relevant
scripts, but stripping by default seems to be more useful for composing
results in higher-level scripts. It's better for the result names to be
consistent, even if they don't match the .o symbols exactly.

Note some scripts are unaffected:

- cov.py - gcov doesn't seem to have an option for getting the
  unstripped symbols, so we only output the stripped names.

- structs.py - structs.py deals with struct names, which are notably not
  symbols.
2025-03-12 20:00:21 -05:00
Christopher Haster
9e22167a31 scripts: Re-adopted result prefixes
Now that I'm looking into some higher-level scripts, being able to merge
results without first renaming everything is useful.

This gives most scripts an implicit prefix for field fields, but _not_
by fields, allowing easy merging of results from different scripts:

  $ ./scripts/stack.py lfs.ci -o-
  function,stack_frame,stack_limit
  lfs_alloc,288,1328
  lfs_alloc_discard,8,8
  lfs_alloc_findfree,16,32
  ...

At least now these have better support in scripts with the addition of
the --prefix flag (this was tricky for csv.py), which allows explicit
control over field field prefixes:

  $ ./scripts/stack.py lfs.ci -o- --prefix=
  function,frame,limit
  lfs_alloc,288,1328
  lfs_alloc_discard,8,8
  lfs_alloc_findfree,16,32
  ...

  $ ./scripts/stack.py lfs.ci -o- --prefix=wonky_
  function,wonky_frame,wonky_limit
  lfs_alloc,288,1328
  lfs_alloc_discard,8,8
  lfs_alloc_findfree,16,32
  ...
2025-03-12 19:10:17 -05:00