Skip to main content

solar_cli/
utils.rs

1//! Utility functions used by the Solar CLI.
2
3use solar_interface::diagnostics::DiagCtxt;
4
5#[cfg(feature = "tracing")]
6use solar_sema::ast::Either;
7#[cfg(feature = "tracing")]
8use std::io;
9
10#[cfg(feature = "mimalloc")]
11use mimalloc as _;
12#[cfg(all(feature = "jemalloc", unix))]
13use tikv_jemallocator as _;
14
15// Keep the system allocator in tests, where we spawn a ton of processes and any extra startup cost
16// slows down tests massively.
17cfg_if::cfg_if! {
18    if #[cfg(debug_assertions)] {
19        type AllocatorInner = std::alloc::System;
20    } else if #[cfg(feature = "mimalloc")] {
21        type AllocatorInner = mimalloc::MiMalloc;
22    } else if #[cfg(all(feature = "jemalloc", unix))] {
23        type AllocatorInner = tikv_jemallocator::Jemalloc;
24    } else {
25        type AllocatorInner = std::alloc::System;
26    }
27}
28
29cfg_if::cfg_if! {
30    if #[cfg(feature = "tracy-allocator")] {
31        pub(super) type WrappedAllocator = tracing_tracy::client::ProfiledAllocator<AllocatorInner>;
32        pub(super) const fn new_wrapped_allocator() -> WrappedAllocator {
33            Allocator::new(AllocatorInner {}, 100)
34        }
35    } else {
36        pub(super) type WrappedAllocator = AllocatorInner;
37        pub(super) const fn new_wrapped_allocator() -> WrappedAllocator {
38            AllocatorInner {}
39        }
40    }
41}
42
43/// The global allocator used by the compiler.
44pub type Allocator = WrappedAllocator;
45
46/// Create a new instance of the global allocator.
47pub const fn new_allocator() -> Allocator {
48    new_wrapped_allocator()
49}
50
51/// `tracing` logger destination.
52#[derive(Default)]
53pub enum LogDestination {
54    /// [`io::stdout`].
55    #[default]
56    Stdout,
57    /// [`io::stderr`].
58    Stderr,
59}
60
61#[cfg(feature = "tracing")]
62impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for LogDestination {
63    type Writer = Either<io::Stdout, io::Stderr>;
64
65    fn make_writer(&'a self) -> Self::Writer {
66        match self {
67            Self::Stdout => Either::Left(io::stdout()),
68            Self::Stderr => Either::Right(io::stderr()),
69        }
70    }
71}
72
73/// Initialize the tracing logger.
74#[must_use]
75pub fn init_logger(dst: LogDestination) -> impl Sized {
76    #[cfg(not(feature = "tracing"))]
77    {
78        let _ = dst;
79        if std::env::var_os("RUST_LOG").is_some() {
80            let msg = "`RUST_LOG` is set, but \"tracing\" support was not enabled at compile time";
81            DiagCtxt::new_early().warn(msg).emit();
82        }
83        if std::env::var_os("SOLAR_PROFILE").is_some() {
84            let msg =
85                "`SOLAR_PROFILE` is set, but \"tracing\" support was not enabled at compile time";
86            DiagCtxt::new_early().warn(msg).emit();
87        }
88    }
89
90    #[cfg(feature = "tracing")]
91    match try_init_logger(dst) {
92        Ok(guard) => guard,
93        Err(e) => DiagCtxt::new_early().fatal(e).emit(),
94    }
95}
96
97#[cfg(feature = "tracing")]
98fn try_init_logger(dst: LogDestination) -> Result<impl Sized, String> {
99    use tracing_subscriber::prelude::*;
100
101    let (profile_layer, guard) = match std::env::var("SOLAR_PROFILE").as_deref() {
102        Ok("chrome") => {
103            if !cfg!(feature = "tracing-chrome") {
104                return Err("chrome profiler support is not compiled in".to_string());
105            }
106            let (layer, guard) = chrome_layer();
107            (Some(layer.boxed()), Some(guard))
108        }
109        Ok("tracy") => {
110            if !cfg!(feature = "tracy") {
111                return Err("tracy profiler support is not compiled in".to_string());
112            }
113            (Some(tracy_layer().boxed()), Default::default())
114        }
115        Ok("samply") => {
116            if !cfg!(feature = "tracing-samply") {
117                return Err("samply profiler support is not compiled in".to_string());
118            }
119            (Some(samply_layer().map_err(|e| e.to_string())?.boxed()), Default::default())
120        }
121        Ok(s) => {
122            return Err(format!(
123                "unknown profiler '{s}'; valid values: 'chrome', 'tracy', 'samply'"
124            ));
125        }
126        Err(_) => Default::default(),
127    };
128    tracing_subscriber::Registry::default()
129        .with(tracing_subscriber::EnvFilter::from_default_env())
130        .with(profile_layer)
131        .with(tracing_subscriber::fmt::layer().with_writer(dst))
132        .try_init()
133        .map(|()| guard)
134        .map_err(|e| e.to_string())
135}
136
137#[cfg(feature = "tracing")]
138#[cfg(feature = "tracy")]
139fn tracy_layer() -> tracing_tracy::TracyLayer<impl tracing_tracy::Config> {
140    struct Config(tracing_subscriber::fmt::format::DefaultFields);
141    impl tracing_tracy::Config for Config {
142        type Formatter = tracing_subscriber::fmt::format::DefaultFields;
143        fn formatter(&self) -> &Self::Formatter {
144            &self.0
145        }
146        fn format_fields_in_zone_name(&self) -> bool {
147            false
148        }
149    }
150
151    tracing_tracy::client::register_demangler!();
152
153    tracing_tracy::TracyLayer::new(Config(Default::default()))
154}
155#[cfg(feature = "tracing")]
156#[cfg(not(feature = "tracy"))]
157fn tracy_layer() -> tracing_subscriber::layer::Identity {
158    tracing_subscriber::layer::Identity::new()
159}
160
161#[cfg(feature = "tracing")]
162#[cfg(feature = "tracing-chrome")]
163fn chrome_layer<S>() -> (tracing_chrome::ChromeLayer<S>, tracing_chrome::FlushGuard)
164where
165    S: tracing::Subscriber
166        + for<'span> tracing_subscriber::registry::LookupSpan<'span>
167        + Send
168        + Sync,
169{
170    tracing_chrome::ChromeLayerBuilder::new().include_args(true).build()
171}
172#[cfg(feature = "tracing")]
173#[cfg(not(feature = "tracing-chrome"))]
174fn chrome_layer() -> (tracing_subscriber::layer::Identity, ()) {
175    (tracing_subscriber::layer::Identity::new(), ())
176}
177
178#[cfg(feature = "tracing")]
179#[cfg(feature = "tracing-samply")]
180fn samply_layer() -> std::io::Result<tracing_samply::SamplyLayer> {
181    tracing_samply::SamplyLayer::new()
182}
183#[cfg(feature = "tracing")]
184#[cfg(not(feature = "tracing-samply"))]
185fn samply_layer() -> std::io::Result<tracing_subscriber::layer::Identity> {
186    Ok(tracing_subscriber::layer::Identity::new())
187}
188
189/*
190pub(crate) fn env_to_bool(value: Option<&std::ffi::OsStr>) -> bool {
191    value.is_some_and(|value| value == "1" || value == "true")
192}
193*/