Attribute a binary's size to its OCaml modules and libraries
0

Configure Feed

Select the types of activity you want to include in your feed.

OCaml 87.6%
Dune 2.7%
Perl 1.4%
Shell 0.6%
Other 7.8%
16 1 0

Clone this repository

https://git.vm.fail/gazagnaire.org/dissect https://git.vm.fail/did:plc:bozorfyf7hzvyzwexovhzlir
ssh://git@git.recoil.org:2222/gazagnaire.org/dissect ssh://git@git.recoil.org:2222/did:plc:bozorfyf7hzvyzwexovhzlir

For self-hosted knots, clone URLs may differ based on your setup.


README.md

dissect#

Attribute a compiled binary's size to the OCaml modules and libraries that fill it -- a native, no-nm answer to "why is this binary so large".

Installation#

Install with opam:

$ opam install dissect

If opam cannot find the package, it may not yet be released in the public opam-repository. Add the overlay repository, then install it:

$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install dissect

Overview#

dissect reads a compiled OCaml binary (Mach-O or ELF, detected from its leading bytes), attributes every byte to the OCaml module and library that produced it, and prints a sorted weight-map of proportional bars in the terminal. The binary formats are parsed natively by mach-o and elf (both built on the wire codec library); the rendering uses console.

Usage#

dissect path/to/binary        # group by library, top 20 (the default)
dissect --by-module ./a.out   # group by individual module
dissect -n 40 ./a.out         # show more rows (0 shows all)
! 5.4 MiB (34%) is not code: symbol & string tables, constants, unwind data
  and headers. Most is removed by `strip`.

╭────────────────────┬───────────┬─────┬───...╮
│ Module             │      Size │   % │      │
├────────────────────┼───────────┼─────┼───...┤
│ Stdlib             │ 713.7 KiB │  6% │ ███  │
│ Parser             │ 700.7 KiB │  6% │ ███  │
│ Typecore           │ 455.0 KiB │  4% │ ██   │
│ (... 190 more)     │   4.9 MiB │ 46% │ ████ │
╰────────────────────┴───────────┴─────┴───...╯

10.5 MiB of code across 210 module(s), 65% of the 15.9 MiB file

The bytes no symbol covers -- the symbol and string tables (Mach-O __LINKEDIT, removed by strip), constant and exception sections, and the headers -- are summarised in the warning rather than given table rows, so the percentages are shares of the attributed code, not of the whole file.

How symbols are bucketed#

Each linker symbol is classified by name. A wrapped library's symbol has the form caml<Lib>__<Module>$<function>, so it is grouped under <Lib>; a symbol with no __ is bucketed by its own module name (the library is not encoded in the symbol); anything that is not a caml-prefixed module symbol goes to the C runtime bucket:

# Dissect.Demangle.classify "_camlStdlib__Arg$go_42"
- : Dissect.Demangle.t =
Dissect.Demangle.Ocaml {Dissect.Demangle.library = "Stdlib"; modul = "Arg"}
# Dissect.Demangle.classify "_caml_alloc"
- : Dissect.Demangle.t = Dissect.Demangle.C_runtime

Mach-O symbol records carry no size, so a symbol's size is reconstructed as the distance to the next symbol in its section; ELF records carry an explicit size, used directly.

Licence#

ISC. See LICENSE.md.