406 lines
15 KiB
Markdown
406 lines
15 KiB
Markdown
# Debugging the compiler
|
|
|
|
<!-- toc -->
|
|
|
|
This chapter contains a few tips to debug the compiler. These tips aim to be
|
|
useful no matter what you are working on. Some of the other chapters have
|
|
advice about specific parts of the compiler (e.g. the [Queries Debugging and
|
|
Testing chapter](./incrcomp-debugging.html) or the [LLVM Debugging
|
|
chapter](./backend/debugging.md)).
|
|
|
|
## Configuring the compiler
|
|
|
|
By default, rustc is built without most debug information. To enable debug info,
|
|
set `debug = true` in your config.toml.
|
|
|
|
Setting `debug = true` turns on many different debug options (e.g., `debug-assertions`,
|
|
`debug-logging`, etc.) which can be individually tweaked if you want to, but many people
|
|
simply set `debug = true`.
|
|
|
|
If you want to use GDB to debug rustc, please set `config.toml` with options:
|
|
|
|
```toml
|
|
[rust]
|
|
debug = true
|
|
debuginfo-level = 2
|
|
```
|
|
|
|
> NOTE:
|
|
> This will use a lot of disk space
|
|
> (upwards of <!-- date-check Aug 2022 --> 35GB),
|
|
> and will take a lot more compile time.
|
|
> With `debuginfo-level = 1` (the default when `debug = true`),
|
|
> you will be able to track the execution path,
|
|
> but will lose the symbol information for debugging.
|
|
|
|
The default configuration will enable `symbol-mangling-version` v0.
|
|
This requires at least GDB v10.2,
|
|
otherwise you need to disable new symbol-mangling-version in `config.toml`.
|
|
|
|
```toml
|
|
[rust]
|
|
new-symbol-mangling = false
|
|
```
|
|
|
|
> See the comments in `config.example.toml` for more info.
|
|
|
|
You will need to rebuild the compiler after changing any configuration option.
|
|
|
|
## Suppressing the ICE file
|
|
|
|
By default, if rustc encounters an Internal Compiler Error (ICE) it will dump the ICE contents to an
|
|
ICE file within the current working directory named `rustc-ice-<timestamp>-<pid>.txt`. If this is
|
|
not desirable, you can prevent the ICE file from being created with `RUSTC_ICE=0`.
|
|
|
|
## Getting a backtrace
|
|
[getting-a-backtrace]: #getting-a-backtrace
|
|
|
|
When you have an ICE (panic in the compiler), you can set
|
|
`RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in
|
|
normal Rust programs. IIRC backtraces **don't work** on MinGW,
|
|
sorry. If you have trouble or the backtraces are full of `unknown`,
|
|
you might want to find some way to use Linux, Mac, or MSVC on Windows.
|
|
|
|
In the default configuration (without `debug` set to `true`), you don't have line numbers
|
|
enabled, so the backtrace looks like this:
|
|
|
|
```text
|
|
stack backtrace:
|
|
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
|
|
1: std::sys_common::backtrace::_print
|
|
2: std::panicking::default_hook::{{closure}}
|
|
3: std::panicking::default_hook
|
|
4: std::panicking::rust_panic_with_hook
|
|
5: std::panicking::begin_panic
|
|
(~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
|
|
32: rustc_typeck::check_crate
|
|
33: <std::thread::local::LocalKey<T>>::with
|
|
34: <std::thread::local::LocalKey<T>>::with
|
|
35: rustc::ty::context::TyCtxt::create_and_enter
|
|
36: rustc_driver::driver::compile_input
|
|
37: rustc_driver::run_compiler
|
|
```
|
|
|
|
If you set `debug = true`, you will get line numbers for the stack trace.
|
|
Then the backtrace will look like this:
|
|
|
|
```text
|
|
stack backtrace:
|
|
(~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
|
|
at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:110
|
|
7: rustc_typeck::check::cast::CastCheck::check
|
|
at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:572
|
|
at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:460
|
|
at /home/user/rust/compiler/rustc_typeck/src/check/cast.rs:370
|
|
(~~~~ LINES REMOVED BY ME FOR BREVITY ~~~~)
|
|
33: rustc_driver::driver::compile_input
|
|
at /home/user/rust/compiler/rustc_driver/src/driver.rs:1010
|
|
at /home/user/rust/compiler/rustc_driver/src/driver.rs:212
|
|
34: rustc_driver::run_compiler
|
|
at /home/user/rust/compiler/rustc_driver/src/lib.rs:253
|
|
```
|
|
|
|
## `-Z` flags
|
|
|
|
The compiler has a bunch of `-Z *` flags. These are unstable flags that are only
|
|
enabled on nightly. Many of them are useful for debugging. To get a full listing
|
|
of `-Z` flags, use `-Z help`.
|
|
|
|
One useful flag is `-Z verbose-internals`, which generally enables printing more
|
|
info that could be useful for debugging.
|
|
|
|
Right below you can find elaborate explainers on a selected few.
|
|
|
|
### Getting a backtrace for errors
|
|
[getting-a-backtrace-for-errors]: #getting-a-backtrace-for-errors
|
|
|
|
If you want to get a backtrace to the point where the compiler emits an
|
|
error message, you can pass the `-Z treat-err-as-bug=n`, which will make
|
|
the compiler panic on the `nth` error. If you leave off `=n`, the compiler will
|
|
assume `1` for `n` and thus panic on the first error it encounters.
|
|
|
|
For example:
|
|
|
|
```bash
|
|
$ cat error.rs
|
|
```
|
|
|
|
```rust
|
|
fn main() {
|
|
1 + ();
|
|
}
|
|
```
|
|
|
|
```
|
|
$ rustc +stage1 error.rs
|
|
error[E0277]: cannot add `()` to `{integer}`
|
|
--> error.rs:2:7
|
|
|
|
|
2 | 1 + ();
|
|
| ^ no implementation for `{integer} + ()`
|
|
|
|
|
= help: the trait `Add<()>` is not implemented for `{integer}`
|
|
|
|
error: aborting due to previous error
|
|
```
|
|
|
|
Now, where does the error above come from?
|
|
|
|
```
|
|
$ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z treat-err-as-bug
|
|
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
|
|
--> error.rs:2:7
|
|
|
|
|
2 | 1 + ();
|
|
| ^ no implementation for `{integer} + ()`
|
|
|
|
|
= help: the trait `std::ops::Add<()>` is not implemented for `{integer}`
|
|
|
|
error: internal compiler error: unexpected panic
|
|
|
|
note: the compiler unexpectedly panicked. this is a bug.
|
|
|
|
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
|
|
|
|
note: rustc 1.24.0-dev running on x86_64-unknown-linux-gnu
|
|
|
|
note: run with `RUST_BACKTRACE=1` for a backtrace
|
|
|
|
thread 'rustc' panicked at 'encountered error with `-Z treat_err_as_bug',
|
|
/home/user/rust/compiler/rustc_errors/src/lib.rs:411:12
|
|
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose
|
|
backtrace.
|
|
stack backtrace:
|
|
(~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~)
|
|
7: rustc::traits::error_reporting::<impl rustc::infer::InferCtxt<'a, 'tcx>>
|
|
::report_selection_error
|
|
at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:823
|
|
8: rustc::traits::error_reporting::<impl rustc::infer::InferCtxt<'a, 'tcx>>
|
|
::report_fulfillment_errors
|
|
at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:160
|
|
at /home/user/rust/compiler/rustc_middle/src/traits/error_reporting.rs:112
|
|
9: rustc_typeck::check::FnCtxt::select_obligations_where_possible
|
|
at /home/user/rust/compiler/rustc_typeck/src/check/mod.rs:2192
|
|
(~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~)
|
|
36: rustc_driver::run_compiler
|
|
at /home/user/rust/compiler/rustc_driver/src/lib.rs:253
|
|
```
|
|
|
|
Cool, now I have a backtrace for the error!
|
|
|
|
### Debugging delayed bugs
|
|
|
|
The `-Z eagerly-emit-delayed-bugs` option makes it easy to debug delayed bugs.
|
|
It turns them into normal errors, i.e. makes them visible. This can be used in
|
|
combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and
|
|
get a backtrace.
|
|
|
|
### Getting the error creation location
|
|
|
|
`-Z track-diagnostics` can help figure out where errors are emitted. It uses `#[track_caller]`
|
|
for this and prints its location alongside the error:
|
|
|
|
```
|
|
$ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z track-diagnostics
|
|
error[E0277]: cannot add `()` to `{integer}`
|
|
--> src\error.rs:2:7
|
|
|
|
|
2 | 1 + ();
|
|
| ^ no implementation for `{integer} + ()`
|
|
-Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs:638:39
|
|
|
|
|
= help: the trait `Add<()>` is not implemented for `{integer}`
|
|
= help: the following other types implement trait `Add<Rhs>`:
|
|
<&'a f32 as Add<f32>>
|
|
<&'a f64 as Add<f64>>
|
|
<&'a i128 as Add<i128>>
|
|
<&'a i16 as Add<i16>>
|
|
<&'a i32 as Add<i32>>
|
|
<&'a i64 as Add<i64>>
|
|
<&'a i8 as Add<i8>>
|
|
<&'a isize as Add<isize>>
|
|
and 48 others
|
|
|
|
For more information about this error, try `rustc --explain E0277`.
|
|
```
|
|
|
|
This is similar but different to `-Z treat-err-as-bug`:
|
|
- it will print the locations for all errors emitted
|
|
- it does not require a compiler built with debug symbols
|
|
- you don't have to read through a big stack trace.
|
|
|
|
## Getting logging output
|
|
|
|
The compiler uses the [`tracing`] crate for logging.
|
|
|
|
[`tracing`]: https://docs.rs/tracing
|
|
|
|
For details see [the guide section on tracing](./tracing.md)
|
|
|
|
## Narrowing (Bisecting) Regressions
|
|
|
|
The [cargo-bisect-rustc][bisect] tool can be used as a quick and easy way to
|
|
find exactly which PR caused a change in `rustc` behavior. It automatically
|
|
downloads `rustc` PR artifacts and tests them against a project you provide
|
|
until it finds the regression. You can then look at the PR to get more context
|
|
on *why* it was changed. See [this tutorial][bisect-tutorial] on how to use
|
|
it.
|
|
|
|
[bisect]: https://github.com/rust-lang/cargo-bisect-rustc
|
|
[bisect-tutorial]: https://rust-lang.github.io/cargo-bisect-rustc/tutorial.html
|
|
|
|
## Downloading Artifacts from Rust's CI
|
|
|
|
The [rustup-toolchain-install-master][rtim] tool by kennytm can be used to
|
|
download the artifacts produced by Rust's CI for a specific SHA1 -- this
|
|
basically corresponds to the successful landing of some PR -- and then sets
|
|
them up for your local use. This also works for artifacts produced by `@bors
|
|
try`. This is helpful when you want to examine the resulting build of a PR
|
|
without doing the build yourself.
|
|
|
|
[rtim]: https://github.com/kennytm/rustup-toolchain-install-master
|
|
|
|
## `#[rustc_*]` TEST attributes
|
|
|
|
The compiler defines a whole lot of internal (perma-unstable) attributes some of which are useful
|
|
for debugging by dumping extra compiler-internal information. These are prefixed with `rustc_` and
|
|
are gated behind the internal feature `rustc_attrs` (enabled via e.g. `#![feature(rustc_attrs)]`).
|
|
|
|
For a complete and up to date list, see [`builtin_attrs`]. More specifically, the ones marked `TEST`.
|
|
Here are some notable ones:
|
|
|
|
| Attribute | Description |
|
|
|----------------|-------------|
|
|
| `rustc_def_path` | Dumps the [`def_path_str`] of an item. |
|
|
| `rustc_dump_def_parents` | Dumps the chain of `DefId` parents of certain definitions. |
|
|
| `rustc_dump_item_bounds` | Dumps the [`item_bounds`] of an item. |
|
|
| `rustc_dump_predicates` | Dumps the [`predicates_of`] an item. |
|
|
| `rustc_dump_vtable` | |
|
|
| `rustc_hidden_type_of_opaques` | Dumps the [hidden type of each opaque types][opaq] in the crate. |
|
|
| `rustc_layout` | [See this section](#debugging-type-layouts). |
|
|
| `rustc_object_lifetime_default` | Dumps the [object lifetime defaults] of an item. |
|
|
| `rustc_outlives` | Dumps implied bounds of an item. More precisely, the [`inferred_outlives_of`] an item. |
|
|
| `rustc_regions` | Dumps NLL closure region requirements. |
|
|
| `rustc_symbol_name` | Dumps the mangled & demangled [`symbol_name`] of an item. |
|
|
| `rustc_variances` | Dumps the [variances] of an item. |
|
|
|
|
Right below you can find elaborate explainers on a selected few.
|
|
|
|
[`builtin_attrs`]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_feature/src/builtin_attrs.rs
|
|
[`def_path_str`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.def_path_str
|
|
[`inferred_outlives_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.inferred_outlives_of
|
|
[`item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.item_bounds
|
|
[`predicates_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.predicates_of
|
|
[`symbol_name`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.symbol_name
|
|
[object lifetime defaults]: https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
|
|
[opaq]: ./opaque-types-impl-trait-inference.md
|
|
[variances]: ./variance.md
|
|
|
|
### Formatting Graphviz output (.dot files)
|
|
[formatting-graphviz-output]: #formatting-graphviz-output
|
|
|
|
Some compiler options for debugging specific features yield graphviz graphs -
|
|
e.g. the `#[rustc_mir(borrowck_graphviz_postflow="suffix.dot")]` attribute
|
|
dumps various borrow-checker dataflow graphs.
|
|
|
|
These all produce `.dot` files. To view these files, install graphviz (e.g.
|
|
`apt-get install graphviz`) and then run the following commands:
|
|
|
|
```bash
|
|
$ dot -T pdf maybe_init_suffix.dot > maybe_init_suffix.pdf
|
|
$ firefox maybe_init_suffix.pdf # Or your favorite pdf viewer
|
|
```
|
|
|
|
### Debugging type layouts
|
|
|
|
The internal attribute `#[rustc_layout]` can be used to dump the [`Layout`] of
|
|
the type it is attached to. For example:
|
|
|
|
```rust
|
|
#![feature(rustc_attrs)]
|
|
|
|
#[rustc_layout(debug)]
|
|
type T<'a> = &'a u32;
|
|
```
|
|
|
|
Will emit the following:
|
|
|
|
```text
|
|
error: layout_of(&'a u32) = Layout {
|
|
fields: Primitive,
|
|
variants: Single {
|
|
index: 0,
|
|
},
|
|
abi: Scalar(
|
|
Scalar {
|
|
value: Pointer,
|
|
valid_range: 1..=18446744073709551615,
|
|
},
|
|
),
|
|
largest_niche: Some(
|
|
Niche {
|
|
offset: Size {
|
|
raw: 0,
|
|
},
|
|
scalar: Scalar {
|
|
value: Pointer,
|
|
valid_range: 1..=18446744073709551615,
|
|
},
|
|
},
|
|
),
|
|
align: AbiAndPrefAlign {
|
|
abi: Align {
|
|
pow2: 3,
|
|
},
|
|
pref: Align {
|
|
pow2: 3,
|
|
},
|
|
},
|
|
size: Size {
|
|
raw: 8,
|
|
},
|
|
}
|
|
--> src/lib.rs:4:1
|
|
|
|
|
4 | type T<'a> = &'a u32;
|
|
| ^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
error: aborting due to previous error
|
|
```
|
|
|
|
[`Layout`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html
|
|
|
|
|
|
## Configuring CodeLLDB for debugging `rustc`
|
|
|
|
If you are using VSCode, and have edited your `config.toml` to request debugging
|
|
level 1 or 2 for the parts of the code you're interested in, then you should be
|
|
able to use the [CodeLLDB] extension in VSCode to debug it.
|
|
|
|
Here is a sample `launch.json` file, being used to run a stage 1 compiler direct
|
|
from the directory where it is built (does not have to be "installed"):
|
|
|
|
```javascript
|
|
// .vscode/launch.json
|
|
{
|
|
"version": "0.2.0",
|
|
"configurations": [
|
|
{
|
|
"type": "lldb",
|
|
"request": "launch",
|
|
"name": "Launch",
|
|
"args": [], // array of string command-line arguments to pass to compiler
|
|
"program": "${workspaceFolder}/build/host/stage1/bin/rustc",
|
|
"windows": { // applicable if using windows
|
|
"program": "${workspaceFolder}/build/host/stage1/bin/rustc.exe"
|
|
},
|
|
"cwd": "${workspaceFolder}", // current working directory at program start
|
|
"stopOnEntry": false,
|
|
"sourceLanguages": ["rust"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
[CodeLLDB]: https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb
|