Skip to content

Commit 0209e8e

Browse files
Add the option to generate performance flamegraphs (#4138)
Adds the ability for `cargo kani` to optionally output the info needed for generating flamegraphs of the compiler & driver's performance. When enabled with the `FLAMEGRAPH` environment variable, it will generate an output file for the driver and one for each crate in the workspace as they are compiled. These output files can then be rendered as flamegraphs to see where Kani is spending most of its execution time. See [this draft docs page](https://github.com/AlexanderPortland/kani/blob/flamegraph/docs/src/profiling.md) for more detailed instructions on how it would be used. This should help us pinpoint which pieces of the compilation process are most to blame for our compilation times and focus future efforts to improve them. Resolves #4075 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech <carolynzech@gmail.com> Co-authored-by: Carolyn Zech <cmzech@amazon.com>
1 parent d9951b7 commit 0209e8e

File tree

9 files changed

+288
-8
lines changed

9 files changed

+288
-8
lines changed

Cargo.lock

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ dependencies = [
3939
"memchr",
4040
]
4141

42+
[[package]]
43+
name = "android-tzdata"
44+
version = "0.1.1"
45+
source = "registry+https://github.com/rust-lang/crates.io-index"
46+
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
47+
48+
[[package]]
49+
name = "android_system_properties"
50+
version = "0.1.5"
51+
source = "registry+https://github.com/rust-lang/crates.io-index"
52+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
53+
dependencies = [
54+
"libc",
55+
]
56+
4257
[[package]]
4358
name = "annotate-snippets"
4459
version = "0.11.5"
@@ -190,6 +205,12 @@ dependencies = [
190205
"which",
191206
]
192207

208+
[[package]]
209+
name = "bumpalo"
210+
version = "3.18.1"
211+
source = "registry+https://github.com/rust-lang/crates.io-index"
212+
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
213+
193214
[[package]]
194215
name = "byteorder"
195216
version = "1.5.0"
@@ -303,6 +324,18 @@ dependencies = [
303324
"which",
304325
]
305326

327+
[[package]]
328+
name = "chrono"
329+
version = "0.4.41"
330+
source = "registry+https://github.com/rust-lang/crates.io-index"
331+
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
332+
dependencies = [
333+
"android-tzdata",
334+
"iana-time-zone",
335+
"num-traits",
336+
"windows-link",
337+
]
338+
306339
[[package]]
307340
name = "clap"
308341
version = "4.5.39"
@@ -408,6 +441,12 @@ dependencies = [
408441
"unicode-segmentation",
409442
]
410443

444+
[[package]]
445+
name = "core-foundation-sys"
446+
version = "0.8.7"
447+
source = "registry+https://github.com/rust-lang/crates.io-index"
448+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
449+
411450
[[package]]
412451
name = "cprover_bindings"
413452
version = "0.63.0"
@@ -783,6 +822,30 @@ dependencies = [
783822
"windows-sys 0.59.0",
784823
]
785824

825+
[[package]]
826+
name = "iana-time-zone"
827+
version = "0.1.63"
828+
source = "registry+https://github.com/rust-lang/crates.io-index"
829+
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
830+
dependencies = [
831+
"android_system_properties",
832+
"core-foundation-sys",
833+
"iana-time-zone-haiku",
834+
"js-sys",
835+
"log",
836+
"wasm-bindgen",
837+
"windows-core",
838+
]
839+
840+
[[package]]
841+
name = "iana-time-zone-haiku"
842+
version = "0.1.2"
843+
source = "registry+https://github.com/rust-lang/crates.io-index"
844+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
845+
dependencies = [
846+
"cc",
847+
]
848+
786849
[[package]]
787850
name = "icu_collections"
788851
version = "2.0.0"
@@ -987,6 +1050,16 @@ version = "2.1.0"
9871050
source = "registry+https://github.com/rust-lang/crates.io-index"
9881051
checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5"
9891052

1053+
[[package]]
1054+
name = "js-sys"
1055+
version = "0.3.77"
1056+
source = "registry+https://github.com/rust-lang/crates.io-index"
1057+
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
1058+
dependencies = [
1059+
"once_cell",
1060+
"wasm-bindgen",
1061+
]
1062+
9901063
[[package]]
9911064
name = "kani"
9921065
version = "0.63.0"
@@ -1038,6 +1111,7 @@ version = "0.63.0"
10381111
dependencies = [
10391112
"anyhow",
10401113
"cargo_metadata",
1114+
"chrono",
10411115
"clap",
10421116
"comfy-table",
10431117
"console",
@@ -2390,6 +2464,64 @@ dependencies = [
23902464
"wit-bindgen-rt",
23912465
]
23922466

2467+
[[package]]
2468+
name = "wasm-bindgen"
2469+
version = "0.2.100"
2470+
source = "registry+https://github.com/rust-lang/crates.io-index"
2471+
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
2472+
dependencies = [
2473+
"cfg-if",
2474+
"once_cell",
2475+
"rustversion",
2476+
"wasm-bindgen-macro",
2477+
]
2478+
2479+
[[package]]
2480+
name = "wasm-bindgen-backend"
2481+
version = "0.2.100"
2482+
source = "registry+https://github.com/rust-lang/crates.io-index"
2483+
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
2484+
dependencies = [
2485+
"bumpalo",
2486+
"log",
2487+
"proc-macro2",
2488+
"quote",
2489+
"syn",
2490+
"wasm-bindgen-shared",
2491+
]
2492+
2493+
[[package]]
2494+
name = "wasm-bindgen-macro"
2495+
version = "0.2.100"
2496+
source = "registry+https://github.com/rust-lang/crates.io-index"
2497+
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
2498+
dependencies = [
2499+
"quote",
2500+
"wasm-bindgen-macro-support",
2501+
]
2502+
2503+
[[package]]
2504+
name = "wasm-bindgen-macro-support"
2505+
version = "0.2.100"
2506+
source = "registry+https://github.com/rust-lang/crates.io-index"
2507+
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
2508+
dependencies = [
2509+
"proc-macro2",
2510+
"quote",
2511+
"syn",
2512+
"wasm-bindgen-backend",
2513+
"wasm-bindgen-shared",
2514+
]
2515+
2516+
[[package]]
2517+
name = "wasm-bindgen-shared"
2518+
version = "0.2.100"
2519+
source = "registry+https://github.com/rust-lang/crates.io-index"
2520+
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
2521+
dependencies = [
2522+
"unicode-ident",
2523+
]
2524+
23932525
[[package]]
23942526
name = "which"
23952527
version = "7.0.3"
@@ -2433,6 +2565,65 @@ version = "0.4.0"
24332565
source = "registry+https://github.com/rust-lang/crates.io-index"
24342566
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
24352567

2568+
[[package]]
2569+
name = "windows-core"
2570+
version = "0.61.2"
2571+
source = "registry+https://github.com/rust-lang/crates.io-index"
2572+
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
2573+
dependencies = [
2574+
"windows-implement",
2575+
"windows-interface",
2576+
"windows-link",
2577+
"windows-result",
2578+
"windows-strings",
2579+
]
2580+
2581+
[[package]]
2582+
name = "windows-implement"
2583+
version = "0.60.0"
2584+
source = "registry+https://github.com/rust-lang/crates.io-index"
2585+
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
2586+
dependencies = [
2587+
"proc-macro2",
2588+
"quote",
2589+
"syn",
2590+
]
2591+
2592+
[[package]]
2593+
name = "windows-interface"
2594+
version = "0.59.1"
2595+
source = "registry+https://github.com/rust-lang/crates.io-index"
2596+
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
2597+
dependencies = [
2598+
"proc-macro2",
2599+
"quote",
2600+
"syn",
2601+
]
2602+
2603+
[[package]]
2604+
name = "windows-link"
2605+
version = "0.1.1"
2606+
source = "registry+https://github.com/rust-lang/crates.io-index"
2607+
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
2608+
2609+
[[package]]
2610+
name = "windows-result"
2611+
version = "0.3.4"
2612+
source = "registry+https://github.com/rust-lang/crates.io-index"
2613+
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
2614+
dependencies = [
2615+
"windows-link",
2616+
]
2617+
2618+
[[package]]
2619+
name = "windows-strings"
2620+
version = "0.4.2"
2621+
source = "registry+https://github.com/rust-lang/crates.io-index"
2622+
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
2623+
dependencies = [
2624+
"windows-link",
2625+
]
2626+
24362627
[[package]]
24372628
name = "windows-sys"
24382629
version = "0.52.0"

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ path = "src/bin/cargo_kani.rs"
3232
[profile.release]
3333
strip = "debuginfo"
3434

35+
[profile.profiling]
36+
inherits = "release"
37+
debug = true
38+
strip = "none"
39+
lto = "off"
40+
3541
# Below is the workspace (vs above is "kani-verifier" crate) config:
3642

3743
[workspace]

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- [`benchcomp` command line](./benchcomp-cli.md)
4343
- [`benchcomp` configuration file](./benchcomp-conf.md)
4444
- [Custom parsers](./benchcomp-parse.md)
45+
- [Profiling Kani](./profiling.md)
4546

4647
- [Limitations](./limitations.md)
4748
- [Undefined behaviour](./undefined-behaviour.md)

docs/src/dev-documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ developers (including external contributors):
1515
4. [Development setup recommendations for working with `rustc`](./rustc-hacks.md).
1616
5. [Guide for testing in Kani](./testing.md).
1717
6. [Transition to StableMIR](./stable-mir.md).
18+
7. [Profiling Kani's performance](./profiling.md)
1819

1920
> **NOTE**: The developer documentation is intended for Kani developers and not
2021
users. At present, the project is under heavy development and some items

docs/src/profiling.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Profiling Kani's Performance
2+
3+
To profile Kani's performance at a fine-grained level, we use a tool called [`samply`](https://github.com/mstange/samply) that allows the compiler & driver to periodically record the current stack trace, allowing us to construct flamegraphs of where they are spending most of their time.
4+
5+
## Install samply
6+
First, install `samply` using [the instructions](https://github.com/mstange/samply?tab=readme-ov-file#installation) from their repo. The easier methods include installing a prebuilt binary or installing from crates.io.
7+
8+
9+
## Running Kani for profiling output
10+
1. First, build Kani from source with `cargo build-dev --profile profiling` to ensure you are getting all release mode optimizations without stripping useful debug info.
11+
2. Then, you can profile the Kani compiler on a crate of your choice by [exporting Kani to your local PATH](build-from-source.md#adding-kani-to-your-path) and running `FLAMEGRAPH=[OPTION] cargo kani` within the crate.
12+
13+
The `FLAMEGRAPH` environment variable can be set to `driver` (to profile the complete `kani-driver` execution) or `compiler` (to profile each time the `kani-compiler` is called).
14+
15+
We have to instrument the driver and compiler separately because samply's instrumentation usually cannot handle detecting the subprocess the driver uses to call the compiler.
16+
17+
Our default sampling rate is *8000 Hz*, but you can change it yourself in [`session.rs`](../../kani-driver/src/session.rs) for the compiler or the [cargo-kani](../../scripts/cargo-kani) script for the driver.
18+
19+
> Note: Specifically when profiling the compiler, ensure you are running `cargo clean` immediately before `cargo kani`, or parts of the workspace may not be recompiled by the Kani compiler.
20+
21+
22+
## Displaying profiling output
23+
This will create a new `flamegraphs` directory in the crate which will contain a single `driver.json.gz` output file and one `compiler-{crate_name}.json.gz` file for each crate in the workspace. Run `samply load flamegraphs/XXX.json.gz` on any of these to open a local server that will display the file's flamegraph.
24+
25+
Once the server has opened, you'll see a display with the list of threads in rows at the top, and a flamegraph for the currently selected thread at the bottom. There is typically only one process when profiling the driver. When profiling the compiler, the process that runs the `kani-compiler` and handles all codegen is usually at the very bottom of the thread window.
26+
27+
In the flamegraph view, I've found it very useful to right click on a function of interest and select "focus on subtree only" so that it zooms in and you can more clearly see the callees it uses. This can then be undone with the breadcrumb trail at the top of the flamegraph.

kani-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ tracing-subscriber = {version = "0.3.8", features = ["env-filter", "json", "fmt"
3535
which = "7"
3636
time = {version = "0.3.36", features = ["formatting"]}
3737
tokio = { version = "1.40.0", features = ["io-util", "process", "rt", "time"] }
38+
chrono = { version = "0.4.41", default-features = false, features = [ "clock" ]}
3839

3940

4041
# A good set of suggested dependencies can be found in rustup:

kani-driver/src/call_cargo.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::call_single_file::{LibConfig, to_rustc_arg};
66
use crate::project::Artifact;
77
use crate::session::{
88
KaniSession, get_cargo_path, lib_folder, lib_no_core_folder, setup_cargo_command,
9+
setup_cargo_command_inner,
910
};
1011
use crate::util;
1112
use anyhow::{Context, Result, bail};
@@ -202,7 +203,8 @@ crate-type = ["lib"]
202203
let mut failed_targets = vec![];
203204
for package in packages {
204205
for verification_target in package_targets(&self.args, package) {
205-
let mut cmd = setup_cargo_command()?;
206+
let mut cmd =
207+
setup_cargo_command_inner(Some(verification_target.target().name.clone()))?;
206208
cmd.args(&cargo_args)
207209
.args(vec!["-p", &package.id.to_string()])
208210
.args(verification_target.to_args())

0 commit comments

Comments
 (0)