h1
Runtime Diagnostics
-
2019-11-03
Rust is well-known for its helpful error messages, good tooling, and generally empathic compiler interface. If something goes wrong, Rust tries hard to help you get back on track.
In this post I’d like to talk about the runtime aspect of debugging errors.
Compiler Diagnostics
The umbrella term for the messages the compiler prints during compilation is “diagnostics”. When something goes wrong, the compiler has a lot of code in place to try and identify what kind of error occurred, and if there’s perhaps a suggestion available to help you fix it.
fn main() {
printline!("hello world!");
}
error: cannot find macro `printline!` in this scope
--> src/main.rs:2:5
|
2 | printline!("hello world!");
| ^^^^^^^^^ help: a macro with a similar name exists: `println`
As ekuber once described it: in order to provide helpful error messages, the compiler needs to understand a super set of the language. That way when it rejects invalid code, it can still understand what you were trying to do and guide you to the allowed subset.
Runtime Diagnostics
During runtime the compiler no longer plays a role, and all error messages are handled by the binary we’ve compiled. If a bug occurs in a Rust program during Runtime, there several methods we can employ to figure out why the program was rejected:
RUST_BACKTRACE=1
can be passed during compilation to print stack traces when the program panics.dbg!
is likeprintln!
, but includes line numbers, filenames, and is convenient to write.gdb
andrr
can be invoked on Rust programs to step through execution.bpftrace
,dtrace
, andperf
can be invoked on Rust programs.failure
,fehler
, andanyhow
provideContext
error types that record the flow of an error through the system.
Individual tools are often referred to as “backtrace support”, “debugging”, or “performance analysis”. But I think these are all quite closely related, and and it would make sense to group group them together under the umbrella of “runtime diagnostics”. Because in the end all of these are similarly tools built to help diagnose invalid code, and convert it to valid code. The only difference is at which stage of development these tools are run.
Looking Ahead
If you consider Rust’s runtime diagnostics to be a single system, then there are probably some things that could be improved. The first one being: figuring out how to debug something often requires memorizing quite a few different tools.
It’d be nice if cargo run
integrated nicely with debuggers, so you could for
example do: cargo run --debug
to spin up a debugger on a breakpoint. Or cargo run --backtrace
to enable backtraces? Or what if we had rust’s diagnostics
printing available when something panics as
well?
fn main() {
todo!();
}
thread 'main' panicked at 'todo!', src/main.rs:2:5
--> src/main.rs:2:5
|
2 | todo!();
| ^^^^^^^^^ help: `fn main` has not yet been implemented
Or what about being able to detect and report deadlocks?
use std::sync::Mutex;
fn main() {
let data = Mutex::new(());
let d1 = data.lock();
let d2 = data.lock();
}
mutex deadlocked, src/main.rs:6:5
|
4 | let data = Mutex::new(());
| ---- mutex was created here.
5 | let d1 = data.lock();
| -- lock was first acquired here
6 | let d2 = data.lock();
| ^^ lock could not be acquired here
There are a whole range of tools we may want to apply, from miri to thread-safety sanitizers.
I think it would be fantastic if we could treat all of these systems as a cohesive set of runtime diagnostics, that form a counterpart to Rust’s already excellent compile diagnostics.
Conclusion
In this post we’ve looked at compile diagnostics, discussed what runtime diagnostics are, and looked at possible future directions.
This post is not so much intended to present any new innovations, but instead provide a lens through which we can look at a general group of challenges and the tools that help address them.
I think the Rust compiler truly nailed the ergonomics of reporting errors during compilation. My hope is that someday we’ll be have a similarly cohesive UX for runtime errors too.
Thanks to ekuber for all the work he’s done on compiler diagnostics, and a very entertaining chat during Rustconf about diagnostics.