From b986c862ade173d84013831d363263a5cf0586f6 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Tue, 4 Aug 2020 16:07:32 -0700 Subject: [PATCH 1/4] enable error backtraces --- build.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 6 +++--- src/lib.rs | 1 + 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..b4207faf --- /dev/null +++ b/build.rs @@ -0,0 +1,60 @@ +use std::env; +use std::fs; +use std::path::Path; +use std::process::{Command, ExitStatus, Stdio}; + +// This code exercises the surface area that we expect of the std Backtrace +// type. If the current toolchain is able to compile it, we go ahead and use +// backtrace in anyhow. +const PROBE: &str = r#" + #![feature(backtrace)] + #![allow(dead_code)] + + use std::backtrace::{Backtrace, BacktraceStatus}; + use std::error::Error; + use std::fmt::{self, Display}; + + #[derive(Debug)] + struct E; + + impl Display for E { + fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + } + + impl Error for E { + fn backtrace(&self) -> Option<&Backtrace> { + let backtrace = Backtrace::capture(); + match backtrace.status() { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + unimplemented!() + } + } +"#; + +fn main() { + match compile_probe() { + Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), + _ => {} + } +} + +fn compile_probe() -> Option { + let rustc = env::var_os("RUSTC")?; + let out_dir = env::var_os("OUT_DIR")?; + let probefile = Path::new(&out_dir).join("probe.rs"); + fs::write(&probefile, PROBE).ok()?; + Command::new(rustc) + .stderr(Stdio::null()) + .arg("--edition=2018") + .arg("--crate-name=http_types_build") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile) + .status() + .ok() +} diff --git a/src/error.rs b/src/error.rs index 8b887dce..7e2375f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -72,7 +72,7 @@ impl Error { /// [tracking]: https://github.com/rust-lang/rust/issues/53487 #[cfg(backtrace)] pub fn backtrace(&self) -> &std::backtrace::Backtrace { - self.error.downcast_ref::() + self.error.backtrace() } /// Attempt to downcast the error object to a concrete type. @@ -106,13 +106,13 @@ impl Error { impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "{}", self.error) + Display::fmt(&self.error, formatter) } } impl Debug for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "{}", self.error) + Debug::fmt(&self.error, formatter) } } diff --git a/src/lib.rs b/src/lib.rs index 213cf885..1e482127 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,7 @@ #![deny(missing_debug_implementations, nonstandard_style)] #![warn(missing_docs, unreachable_pub)] #![allow(clippy::new_without_default)] +#![cfg_attr(backtrace, feature(backtrace))] #![cfg_attr(test, deny(warnings))] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![doc(html_favicon_url = "https://yoshuawuyts.com/assets/http-rs/favicon.ico")] From 9446815f9c24eec65c7172197553908e7891c863 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Tue, 4 Aug 2020 17:34:27 -0700 Subject: [PATCH 2/4] make it easier to conditionally use a backtrace --- src/error.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7e2375f5..6a230742 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,8 +71,19 @@ impl Error { /// /// [tracking]: https://github.com/rust-lang/rust/issues/53487 #[cfg(backtrace)] - pub fn backtrace(&self) -> &std::backtrace::Backtrace { - self.error.backtrace() + pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> { + let backtrace = self.error.backtrace(); + if let std::backtrace::BacktraceStatus::Captured = backtrace.status() { + Some(backtrace) + } else { + None + } + } + + #[cfg(not(backtrace))] + #[allow(missing_docs)] + pub fn backtrace(&self) -> Option<()> { + None } /// Attempt to downcast the error object to a concrete type. From 963b1ca272a2a8d60024bf9585c6be427eb16848 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Wed, 5 Aug 2020 10:43:38 -0700 Subject: [PATCH 3/4] add explanatory comment --- build.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index b4207faf..b43a9787 100644 --- a/build.rs +++ b/build.rs @@ -3,9 +3,18 @@ use std::fs; use std::path::Path; use std::process::{Command, ExitStatus, Stdio}; -// This code exercises the surface area that we expect of the std Backtrace -// type. If the current toolchain is able to compile it, we go ahead and use -// backtrace in anyhow. +// This build script is copied from +// [anyhow](https://github.com/dtolnay/anyhow/blob/master/build.rs), +// and is a type of feature detection to determine if the current rust +// toolchain has backtraces available. +// +// It exercises the surface area that we expect of the std Backtrace +// type. If the current toolchain is able to compile it, we enable a +// backtrace compiler configuration flag in http-types. We then +// conditionally require the compiler feature in src/lib.rs with +// `#![cfg_attr(backtrace, feature(backtrace))]` +// and gate our backtrace code behind `#[cfg(backtrace)]` + const PROBE: &str = r#" #![feature(backtrace)] #![allow(dead_code)] From 4a8fb79a8d60861991b43b8fdcebe37e11af6a0e Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Wed, 5 Aug 2020 10:49:29 -0700 Subject: [PATCH 4/4] explains that Error::backtrace can always be called --- src/error.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/error.rs b/src/error.rs index 6a230742..edbb8a8f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -70,6 +70,12 @@ impl Error { /// capturing them all over the place all the time. /// /// [tracking]: https://github.com/rust-lang/rust/issues/53487 + /// + /// Note: This function can be called whether or not backtraces + /// are enabled and available. It will return a `None` variant if + /// compiled on a toolchain that does not support backtraces, or + /// if executed without backtraces enabled with + /// `RUST_LIB_BACKTRACE=1`. #[cfg(backtrace)] pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> { let backtrace = self.error.backtrace();