### Reliable Flamegraphs for Substrate-Based Binaries
This note documents how to profile a *substrate-based* node and generate nice flame graphs. Some tweaks are required for building process: all *obvious* technical details are left here for future investigators.
- Make sure this [PR](https://github.com/paritytech/polkadot-sdk/pull/9821) is on your branch, if not cherry-pick support for [`perfmap` in wasmtime](https://github.com/paritytech/polkadot-sdk/commit/8978c005de6631dce20e204380bb43149127cdce),
- enable *debug* symbols for the build (not sure if also needed for runtime - needs to be confirmed) in `Cargo.toml`:
```toml
[profile.release]
debug = true
[profile.production]
debug = true
```
- The default DWARF-based stack-unwinding method used by perf is not always reliable. More accurate results can be achieved by using *frame pointer*. This requires building the codebase with `force-frame-pointers=yes` compiler flag. Rebuild the benchmark (or other binary, e.g. the node):
```bash
export RUSTFLAGS="-C force-frame-pointers=yes"
cargo bench --bench block_production
```
_Note_: using this flag may slightly increase the binary size and also have a small impact on performance. In most cases it should be negligible. Some references: [[1](https://rfc.archlinux.page/0026-fno-omit-frame-pointer/)], [[2](https://ubuntu.com/blog/ubuntu-performance-engineering-with-frame-pointers-by-default)],
- Before profiling: some linux distributions may require following setup to allow `perf` tool accessing kernel interfaces for profiling:
```bash
# note: some OSs may require this:
sysctl -w kernel.perf_event_paranoid=0
sysctl -w kernel.kptr_restrict=0
```
This can be checked by profiling any binary, e.g. `perf record ls`.
- Profile the binary:
```bash
# /tmp/perf-<pid>.map will be created
export WASMTIME_PROFILING_STRATEGY=perfmap
perf record -F 999 -g -- /path-to/target/release/deps/block_production-xxxx --bench
```
When working with *zombienet* the node execution needs to be wrapped with `perf record` command. This can be done by creating a simple wrapper script (`polkadot-parachain-wrapper`) for target node binary. This script shall be used in zombienet config as the node *command*:
```bash
#!/bin/bash -x
export WASMTIME_PROFILING_STRATEGY=perfmap
# /tmp/perf-<pid>.map and perd.data files will be created
perf record -F 999 -g --output=perf.data -- /path/to/node "$@"
```
- Converting recorded results (`perf.data` and `/tmp/perf-<pid>.map` files) to *flame graph*:
```bash
#pre-requisites:
cargo install addr2line --features=bin
git clone https://github.com/brendangregg/FlameGraph.git
#processing recorded perf.data (/tmp/perf-<pid>.map created by wasmtime will also be read by perf)
perf script | rustfilt > perf.script
FlameGraph/stackcollapse-perf.pl perf.script > perf.folded
FlameGraph/flamegraph.pl perf.folded > flamegraph.svg
```
- To make flamegraph even better, `FlameGraph/stackcollapse-perf.pl` can be patched to improve generic-heavy rust function names:
```
diff --git a/stackcollapse-perf.pl b/stackcollapse-perf.pl
index 3ff39bf..d23dd55 100755
--- a/stackcollapse-perf.pl
+++ b/stackcollapse-perf.pl
@@ -348,7 +348,10 @@ while (defined($_ = <>)) {
# "net/http.(*Client).Do"), so everything after the first open
# paren (that is not part of an "(anonymous namespace)") is
# just noise.
- $func =~ s/\((?!anonymous namespace\)).*//;
+ # Preserve Rust template parameters in angle brackets
+ if ($func !~ /<.*>/) {
+ $func =~ s/\((?!anonymous namespace\)).*//;
+ }
}
# now tidy this horrible thing:
# 13a80b608e0a RegExp:[&<>\"\'] (/tmp/perf-7539.map)
```
- Helper script for generating *svg*. Useful when lot of runs are made. Not really needed, just improves a flow a bit.
```bash
#!/bin/bash -x
if [ "$#" -ne 1 ]; then
echo "Error: name shall be given"
exit 1
fi
DIR=`mktemp -d -p . -t log---$1--XXXXX`
echo $DIR
ls -al $DIR
mv perf.data $DIR
pushd $DIR
perf script | rustfilt > perf.script
../FlameGraph/stackcollapse-perf.pl perf.script > perf.folded
../FlameGraph/flamegraph.pl perf.folded > flamegraph.svg
popd
ls -al $DIR/flamegraph.svg
/opt/google/chrome/chrome $DIR/flamegraph.svg
```