|
| 1 | +use std::env::{self, VarError}; |
| 2 | +use std::str::FromStr; |
| 3 | +use std::sync::{Mutex, OnceLock}; |
| 4 | + |
| 5 | +use rustc_middle::ty::TyCtxt; |
| 6 | +use rustc_session::{CtfeBacktrace, EarlyDiagCtxt}; |
| 7 | + |
| 8 | +/// The tracing layer from `tracing-chrome` starts a thread in the background that saves data to |
| 9 | +/// file and closes the file when stopped. If the thread is not stopped properly, the file will be |
| 10 | +/// missing end terminators (`]` for JSON arrays) and other data may also not be flushed. Therefore |
| 11 | +/// we need to keep a guard that, when [Drop]ped, will send a signal to stop the thread. Make sure |
| 12 | +/// to manually drop this guard using [deinit_loggers], if you are exiting the program with |
| 13 | +/// [std::process::exit]! |
| 14 | +#[must_use] |
| 15 | +struct TracingGuard { |
| 16 | + #[cfg(feature = "tracing")] |
| 17 | + _chrome: super::tracing_chrome::FlushGuard, |
| 18 | + _no_construct: (), |
| 19 | +} |
| 20 | + |
| 21 | +// This ensures TracingGuard is always a drop-type, even when the `_chrome` field is disabled. |
| 22 | +impl Drop for TracingGuard { |
| 23 | + fn drop(&mut self) {} |
| 24 | +} |
| 25 | + |
| 26 | +fn rustc_logger_config() -> rustc_log::LoggerConfig { |
| 27 | + // Start with the usual env vars. |
| 28 | + let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG"); |
| 29 | + |
| 30 | + // Overwrite if MIRI_LOG is set. |
| 31 | + if let Ok(var) = env::var("MIRI_LOG") { |
| 32 | + // MIRI_LOG serves as default for RUSTC_LOG, if that is not set. |
| 33 | + if matches!(cfg.filter, Err(VarError::NotPresent)) { |
| 34 | + // We try to be a bit clever here: if `MIRI_LOG` is just a single level |
| 35 | + // used for everything, we only apply it to the parts of rustc that are |
| 36 | + // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`. |
| 37 | + // This way, if you set `MIRI_LOG=trace`, you get only the right parts of |
| 38 | + // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`. |
| 39 | + if tracing::Level::from_str(&var).is_ok() { |
| 40 | + cfg.filter = Ok(format!( |
| 41 | + "rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var},miri={var}" |
| 42 | + )); |
| 43 | + } else { |
| 44 | + cfg.filter = Ok(var); |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + cfg |
| 50 | +} |
| 51 | + |
| 52 | +/// The global logger can only be set once per process, so track whether that already happened and |
| 53 | +/// keep a [TracingGuard] so it can be [Drop]ped later using [deinit_loggers]. |
| 54 | +static LOGGER_INITED: OnceLock<Mutex<Option<TracingGuard>>> = OnceLock::new(); |
| 55 | + |
| 56 | +fn init_logger_once(early_dcx: &EarlyDiagCtxt) { |
| 57 | + // If the logger is not yet initialized, initialize it. |
| 58 | + LOGGER_INITED.get_or_init(|| { |
| 59 | + let guard = if env::var_os("MIRI_TRACING").is_some() { |
| 60 | + #[cfg(not(feature = "tracing"))] |
| 61 | + { |
| 62 | + crate::fatal_error!( |
| 63 | + "fatal error: cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature" |
| 64 | + ); |
| 65 | + } |
| 66 | + |
| 67 | + #[cfg(feature = "tracing")] |
| 68 | + { |
| 69 | + let (chrome_layer, chrome_guard) = |
| 70 | + super::tracing_chrome::ChromeLayerBuilder::new().include_args(true).build(); |
| 71 | + rustc_driver::init_logger_with_additional_layer( |
| 72 | + early_dcx, |
| 73 | + rustc_logger_config(), |
| 74 | + || { |
| 75 | + tracing_subscriber::layer::SubscriberExt::with( |
| 76 | + tracing_subscriber::Registry::default(), |
| 77 | + chrome_layer, |
| 78 | + ) |
| 79 | + }, |
| 80 | + ); |
| 81 | + |
| 82 | + Some(TracingGuard { _chrome: chrome_guard, _no_construct: () }) |
| 83 | + } |
| 84 | + } else { |
| 85 | + // initialize the logger without any tracing enabled |
| 86 | + rustc_driver::init_logger(early_dcx, rustc_logger_config()); |
| 87 | + None |
| 88 | + }; |
| 89 | + Mutex::new(guard) |
| 90 | + }); |
| 91 | +} |
| 92 | + |
| 93 | +pub fn init_early_loggers(early_dcx: &EarlyDiagCtxt) { |
| 94 | + // We only initialize `rustc` if the env var is set (so the user asked for it). |
| 95 | + // If it is not set, we avoid initializing now so that we can initialize later with our custom |
| 96 | + // settings, and *not* log anything for what happens before `miri` starts interpreting. |
| 97 | + if env::var_os("RUSTC_LOG").is_some() { |
| 98 | + init_logger_once(early_dcx); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +pub fn init_late_loggers(early_dcx: &EarlyDiagCtxt, tcx: TyCtxt<'_>) { |
| 103 | + // If the logger is not yet initialized, initialize it. |
| 104 | + init_logger_once(early_dcx); |
| 105 | + |
| 106 | + // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`. |
| 107 | + // Do this late, so we ideally only apply this to Miri's errors. |
| 108 | + if let Some(val) = env::var_os("MIRI_BACKTRACE") { |
| 109 | + let ctfe_backtrace = match &*val.to_string_lossy() { |
| 110 | + "immediate" => CtfeBacktrace::Immediate, |
| 111 | + "0" => CtfeBacktrace::Disabled, |
| 112 | + _ => CtfeBacktrace::Capture, |
| 113 | + }; |
| 114 | + *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace; |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +/// Must be called before the program terminates to ensure the trace file is closed correctly. Not |
| 119 | +/// doing so will result in invalid trace files. Also see [TracingGuard]. |
| 120 | +pub fn deinit_loggers() { |
| 121 | + if let Some(guard) = LOGGER_INITED.get() |
| 122 | + && let Ok(mut guard) = guard.lock() |
| 123 | + { |
| 124 | + std::mem::drop(guard.take()); |
| 125 | + } |
| 126 | +} |
0 commit comments