blob: 63af8706ce749687dc8cd3d74cc2f70f88da5552 [file] [log] [blame]
use std::env::{self, VarError};
use std::str::FromStr;
use std::sync::{Mutex, OnceLock};
use rustc_log::tracing;
use rustc_middle::ty::TyCtxt;
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
/// The tracing layer from `tracing-chrome` starts a thread in the background that saves data to
/// file and closes the file when stopped. If the thread is not stopped properly, the file will be
/// missing end terminators (`]` for JSON arrays) and other data may also not be flushed. Therefore
/// we need to keep a guard that, when [Drop]ped, will send a signal to stop the thread. Make sure
/// to manually drop this guard using [deinit_loggers], if you are exiting the program with
/// [std::process::exit]!
#[must_use]
struct TracingGuard {
#[cfg(feature = "tracing")]
_chrome: super::tracing_chrome::FlushGuard,
_no_construct: (),
}
// This ensures TracingGuard is always a drop-type, even when the `_chrome` field is disabled.
impl Drop for TracingGuard {
fn drop(&mut self) {}
}
fn rustc_logger_config() -> rustc_log::LoggerConfig {
// Start with the usual env vars.
let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG");
// Overwrite if MIRI_LOG is set.
if let Ok(var) = env::var("MIRI_LOG") {
// MIRI_LOG serves as default for RUSTC_LOG, if that is not set.
if matches!(cfg.filter, Err(VarError::NotPresent)) {
// We try to be a bit clever here: if `MIRI_LOG` is just a single level
// used for everything, we only apply it to the parts of rustc that are
// CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
// This way, if you set `MIRI_LOG=trace`, you get only the right parts of
// rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`.
if tracing::Level::from_str(&var).is_ok() {
cfg.filter = Ok(format!(
"rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var},miri={var}"
));
} else {
cfg.filter = Ok(var);
}
}
}
cfg
}
/// The global logger can only be set once per process, so track whether that already happened and
/// keep a [TracingGuard] so it can be [Drop]ped later using [deinit_loggers].
static LOGGER_INITED: OnceLock<Mutex<Option<TracingGuard>>> = OnceLock::new();
fn init_logger_once(early_dcx: &EarlyDiagCtxt) {
// If the logger is not yet initialized, initialize it.
LOGGER_INITED.get_or_init(|| {
let guard = if env::var_os("MIRI_TRACING").is_some() {
#[cfg(not(feature = "tracing"))]
{
crate::fatal_error!(
"Cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature"
);
}
#[cfg(feature = "tracing")]
{
let (chrome_layer, chrome_guard) =
super::tracing_chrome::ChromeLayerBuilder::new().include_args(true).build();
rustc_driver::init_logger_with_additional_layer(
early_dcx,
rustc_logger_config(),
|| {
rustc_log::tracing_subscriber::layer::SubscriberExt::with(
rustc_log::tracing_subscriber::Registry::default(),
chrome_layer,
)
},
);
Some(TracingGuard { _chrome: chrome_guard, _no_construct: () })
}
} else {
// initialize the logger without any tracing enabled
rustc_driver::init_logger(early_dcx, rustc_logger_config());
None
};
Mutex::new(guard)
});
}
pub fn init_early_loggers(early_dcx: &EarlyDiagCtxt) {
// We only initialize `rustc` if the env var is set (so the user asked for it).
// If it is not set, we avoid initializing now so that we can initialize later with our custom
// settings, and *not* log anything for what happens before `miri` starts interpreting.
if env::var_os("RUSTC_LOG").is_some() {
init_logger_once(early_dcx);
}
}
pub fn init_late_loggers(early_dcx: &EarlyDiagCtxt, tcx: TyCtxt<'_>) {
// If the logger is not yet initialized, initialize it.
init_logger_once(early_dcx);
// If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
// Do this late, so we ideally only apply this to Miri's errors.
if let Some(val) = env::var_os("MIRI_BACKTRACE") {
let ctfe_backtrace = match &*val.to_string_lossy() {
"immediate" => CtfeBacktrace::Immediate,
"0" => CtfeBacktrace::Disabled,
_ => CtfeBacktrace::Capture,
};
*tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
}
}
/// Must be called before the program terminates to ensure the trace file is closed correctly. Not
/// doing so will result in invalid trace files. Also see [TracingGuard].
pub fn deinit_loggers() {
if let Some(guard) = LOGGER_INITED.get()
&& let Ok(mut guard) = guard.lock()
{
std::mem::drop(guard.take());
}
}