Skip to content

Commit 201c5c7

Browse files
committed
Move logger setup out of miri.rs and add tracing setup
The tracing_chrome Layer has a connected Guard that needs to be dropped before exiting the process, or else data may not be flushed to the tracing file. This required making a few changes to main() to ensure std::process::exit() is not called without making sure to first drop the guard.
1 parent 0974c7d commit 201c5c7

File tree

5 files changed

+142
-79
lines changed

5 files changed

+142
-79
lines changed

src/bin/miri.rs

Lines changed: 43 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,25 @@ extern crate rustc_driver;
1818
extern crate rustc_hir;
1919
extern crate rustc_hir_analysis;
2020
extern crate rustc_interface;
21-
extern crate rustc_log;
2221
extern crate rustc_metadata;
2322
extern crate rustc_middle;
2423
extern crate rustc_session;
2524
extern crate rustc_span;
2625

27-
use std::env::{self, VarError};
26+
use std::env;
2827
use std::num::NonZero;
2928
use std::ops::Range;
3029
use std::path::PathBuf;
30+
use std::process::ExitCode;
3131
use std::rc::Rc;
3232
use std::str::FromStr;
3333
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
34-
use std::sync::{Arc, Once};
34+
use std::sync::{Arc, Mutex};
3535

3636
use miri::{
3737
BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType,
38-
ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode,
38+
ProvenanceMode, RetagFields, TracingGuard, TreeBorrowsParams, ValidationMode,
39+
init_early_loggers, init_late_loggers,
3940
};
4041
use rustc_abi::ExternAbi;
4142
use rustc_data_structures::sync;
@@ -52,9 +53,9 @@ use rustc_middle::query::LocalCrate;
5253
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
5354
use rustc_middle::ty::{self, Ty, TyCtxt};
5455
use rustc_middle::util::Providers;
56+
use rustc_session::EarlyDiagCtxt;
5557
use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
5658
use rustc_session::search_paths::PathKind;
57-
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
5859
use rustc_span::def_id::DefId;
5960
use tracing::debug;
6061

@@ -63,6 +64,7 @@ struct MiriCompilerCalls {
6364
many_seeds: Option<ManySeedsConfig>,
6465
/// Settings for using GenMC with Miri.
6566
genmc_config: Option<GenmcConfig>,
67+
tracing_guard: Option<TracingGuard>,
6668
}
6769

6870
struct ManySeedsConfig {
@@ -75,8 +77,9 @@ impl MiriCompilerCalls {
7577
miri_config: MiriConfig,
7678
many_seeds: Option<ManySeedsConfig>,
7779
genmc_config: Option<GenmcConfig>,
80+
tracing_guard: Option<TracingGuard>,
7881
) -> Self {
79-
Self { miri_config: Some(miri_config), many_seeds, genmc_config }
82+
Self { miri_config: Some(miri_config), many_seeds, genmc_config, tracing_guard }
8083
}
8184
}
8285

@@ -156,7 +159,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
156159
}
157160

158161
let early_dcx = EarlyDiagCtxt::new(tcx.sess.opts.error_format);
159-
init_late_loggers(&early_dcx, tcx);
162+
let tracing_guard = init_late_loggers(&early_dcx, tcx);
163+
if self.tracing_guard.is_none() {
164+
// either tracing_guard or self.tracing_guard are None, due to LOGGER_INITED.call_once()
165+
self.tracing_guard = tracing_guard;
166+
}
160167
if !tcx.crate_types().contains(&CrateType::Executable) {
161168
tcx.dcx().fatal("miri only makes sense on bin crates");
162169
}
@@ -196,6 +203,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
196203
assert!(config.seed.is_none());
197204
let exit_code = sync::IntoDynSyncSend(AtomicI32::new(rustc_driver::EXIT_SUCCESS));
198205
let num_failed = sync::IntoDynSyncSend(AtomicU32::new(0));
206+
let tracing_guard = sync::IntoDynSyncSend(Mutex::new(self.tracing_guard.take()));
199207
sync::par_for_each_in(many_seeds.seeds.clone(), |seed| {
200208
let mut config = config.clone();
201209
config.seed = Some((*seed).into());
@@ -211,6 +219,10 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
211219
if return_code != rustc_driver::EXIT_SUCCESS {
212220
eprintln!("FAILING SEED: {seed}");
213221
if !many_seeds.keep_going {
222+
// drop the tracing guard before exiting, so tracing calls are flushed correctly
223+
if let Ok(mut lock) = tracing_guard.try_lock() {
224+
let _guard_being_dropped = (*lock).take();
225+
}
214226
// `abort_if_errors` would actually not stop, since `par_for_each` waits for the
215227
// rest of the to finish, so we just exit immediately.
216228
std::process::exit(return_code);
@@ -223,6 +235,9 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
223235
if num_failed > 0 {
224236
eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count());
225237
}
238+
// drop the tracing guard before exiting, so tracing calls are flushed correctly
239+
#[allow(clippy::drop_non_drop)]
240+
std::mem::drop(tracing_guard);
226241
std::process::exit(exit_code.0.into_inner());
227242
} else {
228243
let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
@@ -232,6 +247,10 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
232247
tcx.dcx().abort_if_errors();
233248
rustc_driver::EXIT_FAILURE
234249
});
250+
251+
// drop the tracing guard before exiting, so tracing calls are flushed correctly
252+
#[allow(clippy::drop_non_drop)]
253+
std::mem::drop(self.tracing_guard.take());
235254
std::process::exit(return_code);
236255
}
237256

@@ -336,74 +355,19 @@ macro_rules! show_error {
336355
($($tt:tt)*) => { show_error(&format_args!($($tt)*)) };
337356
}
338357

339-
fn rustc_logger_config() -> rustc_log::LoggerConfig {
340-
// Start with the usual env vars.
341-
let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG");
342-
343-
// Overwrite if MIRI_LOG is set.
344-
if let Ok(var) = env::var("MIRI_LOG") {
345-
// MIRI_LOG serves as default for RUSTC_LOG, if that is not set.
346-
if matches!(cfg.filter, Err(VarError::NotPresent)) {
347-
// We try to be a bit clever here: if `MIRI_LOG` is just a single level
348-
// used for everything, we only apply it to the parts of rustc that are
349-
// CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
350-
// This way, if you set `MIRI_LOG=trace`, you get only the right parts of
351-
// rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`.
352-
if tracing::Level::from_str(&var).is_ok() {
353-
cfg.filter = Ok(format!(
354-
"rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var},miri={var}"
355-
));
356-
} else {
357-
cfg.filter = Ok(var);
358-
}
359-
}
360-
}
361-
362-
cfg
363-
}
364-
365-
/// The global logger can only be set once per process, so track
366-
/// whether that already happened.
367-
static LOGGER_INITED: Once = Once::new();
368-
369-
fn init_early_loggers(early_dcx: &EarlyDiagCtxt) {
370-
// We only initialize `rustc` if the env var is set (so the user asked for it).
371-
// If it is not set, we avoid initializing now so that we can initialize later with our custom
372-
// settings, and *not* log anything for what happens before `miri` starts interpreting.
373-
if env::var_os("RUSTC_LOG").is_some() {
374-
LOGGER_INITED.call_once(|| {
375-
rustc_driver::init_logger(early_dcx, rustc_logger_config());
376-
});
377-
}
378-
}
379-
380-
fn init_late_loggers(early_dcx: &EarlyDiagCtxt, tcx: TyCtxt<'_>) {
381-
// If the logger is not yet initialized, initialize it.
382-
LOGGER_INITED.call_once(|| {
383-
rustc_driver::init_logger(early_dcx, rustc_logger_config());
384-
});
385-
386-
// If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
387-
// Do this late, so we ideally only apply this to Miri's errors.
388-
if let Some(val) = env::var_os("MIRI_BACKTRACE") {
389-
let ctfe_backtrace = match &*val.to_string_lossy() {
390-
"immediate" => CtfeBacktrace::Immediate,
391-
"0" => CtfeBacktrace::Disabled,
392-
_ => CtfeBacktrace::Capture,
393-
};
394-
*tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
395-
}
396-
}
397-
398358
/// Execute a compiler with the given CLI arguments and callbacks.
359+
/// Does not call [std::process::exit] directly, but rather returns an [ExitCode],
360+
/// to allow the tracing guard to be dropped before exiting.
361+
#[must_use]
399362
fn run_compiler_and_exit(
400363
args: &[String],
401364
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
402-
) -> ! {
403-
// Invoke compiler, and handle return code.
404-
let exit_code =
405-
rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks));
406-
std::process::exit(exit_code)
365+
) -> ExitCode {
366+
// Invoke compiler, catch any unwinding panics and handle return code.
367+
match rustc_driver::catch_fatal_errors(move || rustc_driver::run_compiler(args, callbacks)) {
368+
Ok(()) => ExitCode::SUCCESS,
369+
_ => ExitCode::FAILURE,
370+
}
407371
}
408372

409373
/// Parses a comma separated list of `T` from the given string:
@@ -471,7 +435,7 @@ fn jemalloc_magic() {
471435
}
472436
}
473437

474-
fn main() {
438+
fn main() -> ExitCode {
475439
#[cfg(any(target_os = "linux", target_os = "macos"))]
476440
jemalloc_magic();
477441

@@ -515,15 +479,12 @@ fn main() {
515479
}
516480

517481
// We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments.
518-
run_compiler_and_exit(&args, &mut MiriBeRustCompilerCalls { target_crate })
482+
return run_compiler_and_exit(&args, &mut MiriBeRustCompilerCalls { target_crate });
519483
}
520484

521485
// Add an ICE bug report hook.
522486
rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ());
523487

524-
// Init loggers the Miri way.
525-
init_early_loggers(&early_dcx);
526-
527488
// Parse our arguments and split them across `rustc` and `miri`.
528489
let mut many_seeds: Option<Range<u32>> = None;
529490
let mut many_seeds_keep_going = false;
@@ -793,6 +754,11 @@ fn main() {
793754
);
794755
};
795756

757+
// Init loggers the Miri way. Do so after arguments have been parsed, so no tracing file is
758+
// created if argument parsing fails, and also so that we don't have to worry about dropping
759+
// the guard before calling std::process::exit() in show_error!().
760+
let tracing_guard = init_early_loggers(&early_dcx);
761+
796762
debug!("rustc arguments: {:?}", rustc_args);
797763
debug!("crate arguments: {:?}", miri_config.args);
798764
#[cfg(target_os = "linux")]
@@ -807,6 +773,6 @@ fn main() {
807773
}
808774
run_compiler_and_exit(
809775
&rustc_args,
810-
&mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config),
776+
&mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config, tracing_guard),
811777
)
812778
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
// Some "regular" crates we want to share with rustc
5252
extern crate either;
5353
extern crate tracing;
54+
extern crate tracing_subscriber;
5455

5556
// The rustc crates we need
5657
extern crate rustc_abi;
@@ -62,6 +63,7 @@ extern crate rustc_data_structures;
6263
extern crate rustc_errors;
6364
extern crate rustc_hir;
6465
extern crate rustc_index;
66+
extern crate rustc_log;
6567
extern crate rustc_middle;
6668
extern crate rustc_session;
6769
extern crate rustc_span;
@@ -160,6 +162,7 @@ pub use crate::shims::os_str::EvalContextExt as _;
160162
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
161163
pub use crate::shims::time::EvalContextExt as _;
162164
pub use crate::shims::tls::TlsData;
165+
pub use crate::trace::setup::{TracingGuard, init_early_loggers, init_late_loggers};
163166

164167
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
165168
/// set per default, for maximal validation power.

src/trace/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod setup;
12
mod tracing_chrome;

src/trace/setup.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::env::{self, VarError};
2+
use std::str::FromStr;
3+
use std::sync::Once;
4+
5+
use rustc_middle::ty::TyCtxt;
6+
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
7+
use tracing_subscriber::Registry;
8+
9+
#[must_use]
10+
pub struct TracingGuard {
11+
#[cfg(feature = "tracing")]
12+
_chrome: super::tracing_chrome::FlushGuard,
13+
_no_construct: (),
14+
}
15+
16+
fn rustc_logger_config() -> rustc_log::LoggerConfig {
17+
// Start with the usual env vars.
18+
let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG");
19+
20+
// Overwrite if MIRI_LOG is set.
21+
if let Ok(var) = env::var("MIRI_LOG") {
22+
// MIRI_LOG serves as default for RUSTC_LOG, if that is not set.
23+
if matches!(cfg.filter, Err(VarError::NotPresent)) {
24+
// We try to be a bit clever here: if `MIRI_LOG` is just a single level
25+
// used for everything, we only apply it to the parts of rustc that are
26+
// CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
27+
// This way, if you set `MIRI_LOG=trace`, you get only the right parts of
28+
// rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`.
29+
if tracing::Level::from_str(&var).is_ok() {
30+
cfg.filter = Ok(format!(
31+
"rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var},miri={var}"
32+
));
33+
} else {
34+
cfg.filter = Ok(var);
35+
}
36+
}
37+
}
38+
39+
cfg
40+
}
41+
42+
/// The global logger can only be set once per process, so track
43+
/// whether that already happened.
44+
static LOGGER_INITED: Once = Once::new();
45+
46+
#[must_use]
47+
fn init_logger_once(early_dcx: &EarlyDiagCtxt) -> Option<TracingGuard> {
48+
// If the logger is not yet initialized, initialize it.
49+
let mut guard = None;
50+
LOGGER_INITED.call_once(|| {
51+
#[cfg(feature = "tracing")]
52+
let (chrome_layer, chrome_guard) =
53+
super::tracing_chrome::ChromeLayerBuilder::new().include_args(true).build();
54+
guard = Some(TracingGuard {
55+
#[cfg(feature = "tracing")]
56+
_chrome: chrome_guard,
57+
_no_construct: (),
58+
});
59+
rustc_driver::init_logger_with_additional_layer(early_dcx, rustc_logger_config(), || {
60+
let registry = Registry::default();
61+
#[cfg(feature = "tracing")]
62+
let registry = tracing_subscriber::layer::SubscriberExt::with(registry, chrome_layer);
63+
registry
64+
});
65+
});
66+
guard
67+
}
68+
69+
#[must_use]
70+
pub fn init_early_loggers(early_dcx: &EarlyDiagCtxt) -> Option<TracingGuard> {
71+
// We only initialize `rustc` if the env var is set (so the user asked for it).
72+
// If it is not set, we avoid initializing now so that we can initialize later with our custom
73+
// settings, and *not* log anything for what happens before `miri` starts interpreting.
74+
if env::var_os("RUSTC_LOG").is_some() { init_logger_once(early_dcx) } else { None }
75+
}
76+
77+
#[must_use]
78+
pub fn init_late_loggers(early_dcx: &EarlyDiagCtxt, tcx: TyCtxt<'_>) -> Option<TracingGuard> {
79+
// If the logger is not yet initialized, initialize it.
80+
let guard = init_logger_once(early_dcx);
81+
82+
// If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
83+
// Do this late, so we ideally only apply this to Miri's errors.
84+
if let Some(val) = env::var_os("MIRI_BACKTRACE") {
85+
let ctfe_backtrace = match &*val.to_string_lossy() {
86+
"immediate" => CtfeBacktrace::Immediate,
87+
"0" => CtfeBacktrace::Disabled,
88+
_ => CtfeBacktrace::Capture,
89+
};
90+
*tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
91+
}
92+
93+
guard
94+
}

src/trace/tracing_chrome.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
#![allow(warnings)]
88
#![cfg(feature = "tracing")]
99

10-
// these are here and not in src/lib.rs since it is a direct dependency of tracing_chrome.rs and
10+
// this is here and not in src/lib.rs since it is a direct dependency of tracing_chrome.rs and
1111
// should not be included if the "tracing" feature is disabled
1212
extern crate tracing_core;
13-
extern crate tracing_subscriber;
1413

1514

1615
use tracing_core::{field::Field, span, Event, Subscriber};

0 commit comments

Comments
 (0)