atomic_dbg/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(windows), no_std)]
3
4#[cfg(feature = "log")]
5pub mod log;
6
7use core::cmp::min;
8use core::fmt::{self, Write};
9#[cfg(windows)]
10use {
11    core::ptr::null_mut,
12    io_lifetimes::BorrowedHandle,
13    std::io::IsTerminal,
14    windows_sys::Win32::Foundation::{GetLastError, SetLastError, HANDLE},
15    windows_sys::Win32::Storage::FileSystem::WriteFile,
16    windows_sys::Win32::System::Console::STD_ERROR_HANDLE,
17    windows_sys::Win32::System::Console::{GetStdHandle, WriteConsoleW},
18};
19
20// No specific size is documented, so just pick a number.
21#[cfg(windows)]
22const BUF_LEN: usize = 4096;
23
24struct Writer {
25    pos: usize,
26
27    // `PIPE_BUF` is the biggest buffer we can write atomically.
28    #[cfg(unix)]
29    buf: [u8; rustix::pipe::PIPE_BUF],
30
31    #[cfg(windows)]
32    buf: [u8; BUF_LEN],
33
34    // A buffer of UTF-16, for the Windows console.
35    #[cfg(windows)]
36    console_buf: [u16; BUF_LEN],
37
38    #[cfg(windows)]
39    console_pos: usize,
40
41    #[cfg(feature = "errno")]
42    #[cfg(unix)]
43    saved_errno: errno::Errno,
44
45    #[cfg(windows)]
46    saved_error: u32,
47}
48
49impl Writer {
50    fn new() -> Self {
51        Self {
52            pos: 0,
53
54            #[cfg(unix)]
55            buf: [0_u8; rustix::pipe::PIPE_BUF],
56
57            #[cfg(windows)]
58            buf: [0_u8; BUF_LEN],
59
60            #[cfg(windows)]
61            console_buf: [0_u16; BUF_LEN],
62
63            #[cfg(windows)]
64            console_pos: 0,
65
66            #[cfg(feature = "errno")]
67            #[cfg(unix)]
68            saved_errno: errno::errno(),
69
70            #[cfg(windows)]
71            saved_error: unsafe { GetLastError() },
72        }
73    }
74
75    fn flush(&mut self) {
76        #[cfg(unix)]
77        {
78            self.flush_buf().unwrap();
79        }
80
81        #[cfg(windows)]
82        {
83            let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
84            if unsafe { BorrowedHandle::borrow_raw(stderr as _) }.is_terminal() {
85                self.flush_console(stderr)
86            } else {
87                self.flush_buf()
88            }
89            .unwrap();
90        }
91    }
92
93    fn flush_buf(&mut self) -> fmt::Result {
94        let mut s = &self.buf[..self.pos];
95
96        // SAFETY: Users using `dbg`/`eprintln`/`eprint` APIs should be aware
97        // that these assume that the stderr file descriptor is open and valid
98        // to write to.
99        #[cfg(unix)]
100        let stderr = unsafe { rustix::stdio::stderr() };
101
102        #[cfg(windows)]
103        let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
104
105        while !s.is_empty() {
106            #[cfg(unix)]
107            match rustix::io::write(stderr, s) {
108                Ok(n) => s = &s[n..],
109                Err(rustix::io::Errno::INTR) => (),
110                Err(_) => return Err(fmt::Error),
111            }
112
113            #[cfg(windows)]
114            unsafe {
115                let mut n = 0;
116                if WriteFile(
117                    stderr,
118                    s.as_ptr().cast(),
119                    min(s.len(), u32::MAX as usize) as u32,
120                    &mut n,
121                    null_mut(),
122                ) == 0
123                {
124                    return Err(fmt::Error);
125                }
126                s = &s[n as usize..];
127            }
128        }
129
130        self.pos = 0;
131        Ok(())
132    }
133
134    #[cfg(windows)]
135    fn flush_console(&mut self, handle: HANDLE) -> fmt::Result {
136        // Write `self.console_buf` to the console.
137        let mut s = &self.console_buf[..self.console_pos];
138        while !s.is_empty() {
139            unsafe {
140                let mut n16 = 0;
141                if WriteConsoleW(
142                    handle,
143                    s.as_ptr().cast(),
144                    min(s.len(), u32::MAX as usize) as u32,
145                    &mut n16,
146                    null_mut(),
147                ) == 0
148                {
149                    return Err(fmt::Error);
150                }
151                s = &s[n16 as usize..];
152            }
153        }
154
155        self.console_pos = 0;
156        Ok(())
157    }
158}
159
160impl Drop for Writer {
161    fn drop(&mut self) {
162        #[cfg(feature = "errno")]
163        #[cfg(unix)]
164        errno::set_errno(self.saved_errno);
165
166        #[cfg(windows)]
167        unsafe {
168            SetLastError(self.saved_error);
169        }
170    }
171}
172
173impl fmt::Write for Writer {
174    fn write_str(&mut self, s: &str) -> fmt::Result {
175        // On Windows, to write Unicode to the console, we need to use some
176        // form of `WriteConsole` instead of `WriteFile`.
177        #[cfg(windows)]
178        {
179            let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
180            if unsafe { BorrowedHandle::borrow_raw(stderr as _) }.is_terminal() {
181                // If the stream switched on us, just fail.
182                if self.pos != 0 {
183                    return Err(fmt::Error);
184                }
185
186                // Transcode `s` to UTF-16 and write it to the console.
187                for c in s.chars() {
188                    if self.console_buf.len() - self.console_pos < 2 {
189                        self.flush_console(stderr)?;
190                    }
191                    self.console_pos += c
192                        .encode_utf16(&mut self.console_buf[self.console_pos..])
193                        .len();
194                }
195                return Ok(());
196            }
197
198            // If the stream switched on us, just fail.
199            if self.console_pos != 0 {
200                return Err(fmt::Error);
201            }
202        }
203
204        let mut bytes = s.as_bytes();
205        while !bytes.is_empty() {
206            let mut sink = &mut self.buf[self.pos..];
207            if sink.is_empty() {
208                self.flush_buf()?;
209                sink = &mut self.buf;
210            }
211
212            let len = min(sink.len(), bytes.len());
213            let (now, later) = bytes.split_at(len);
214            sink[..len].copy_from_slice(now);
215
216            self.pos += len;
217            bytes = later;
218        }
219        Ok(())
220    }
221}
222
223#[doc(hidden)]
224pub fn _eprint(args: fmt::Arguments<'_>) {
225    let mut writer = Writer::new();
226    writer.write_fmt(args).unwrap();
227    writer.flush();
228}
229
230#[doc(hidden)]
231pub fn _eprintln(args: fmt::Arguments<'_>) {
232    let mut writer = Writer::new();
233    writer.write_fmt(args).unwrap();
234    writer.write_fmt(format_args!("\n")).unwrap();
235    writer.flush();
236}
237
238#[doc(hidden)]
239pub struct _Dbg(Writer);
240
241#[doc(hidden)]
242pub fn _dbg_start() -> _Dbg {
243    _Dbg(Writer::new())
244}
245
246#[doc(hidden)]
247pub fn _dbg_write(dbg: &mut _Dbg, file: &str, line: u32, name: &str, args: fmt::Arguments<'_>) {
248    writeln!(&mut dbg.0, "[{}:{}] {} = {}", file, line, name, args).unwrap();
249}
250
251#[doc(hidden)]
252pub fn _dbg_finish(mut dbg: _Dbg) {
253    dbg.0.flush();
254}
255
256/// Prints to the standard error.
257///
258/// Similar to [`std::eprint`], except it:
259///  - Writes atomically, up to the greatest length supported on the platform.
260///  - Doesn't use locks (in userspace).
261///  - Preserve libc's `errno` and Windows' last-error code value.
262///
263/// This allows it to be used to debug allocators, multi-threaded code,
264/// synchronization routines, startup code, and more.
265#[macro_export]
266macro_rules! eprint {
267    ($($arg:tt)*) => {
268        $crate::_eprint(core::format_args!($($arg)*))
269    };
270}
271
272/// Prints to the standard error, with a newline.
273///
274/// Similar to [`std::eprintln`], except it:
275///  - Writes atomically, up to the greatest length supported on the platform.
276///  - Doesn't use locks (in userspace).
277///  - Preserve libc's `errno` and Windows' last-error code value.
278///
279/// This allows it to be used to debug allocators, multi-threaded code,
280/// synchronization routines, startup code, and more.
281#[macro_export]
282macro_rules! eprintln {
283    () => {
284        $crate::eprint!("\n")
285    };
286    ($($arg:tt)*) => {
287        // TODO: Use `format_args_nl` when it's stabilized.
288        $crate::_eprintln(core::format_args!($($arg)*))
289    };
290}
291
292/// Prints and returns the value of a given expression for quick and dirty
293/// debugging.
294///
295/// Similar to [`std::dbg`], except it:
296///  - Writes atomically, up to the greatest length supported on the platform.
297///  - Doesn't use locks (in userspace).
298///  - Preserve libc's `errno` and Windows' last-error code value.
299///
300/// This allows it to be used to debug allocators, multi-threaded code,
301/// synchronization routines, startup code, and more.
302#[macro_export]
303macro_rules! dbg {
304    () => {
305        $crate::eprintln!("[{}:{}]", core::file!(), core::line!())
306    };
307    ($val:expr $(,)?) => {
308        // Use the same `match` trick that `std` does.
309        match $val {
310            tmp => {
311                $crate::eprintln!("[{}:{}] {} = {:#?}",
312                    core::file!(), core::line!(), core::stringify!($val), &tmp);
313                tmp
314            }
315        }
316    };
317    ($($val:expr),+ $(,)?) => {
318        let mut dbg = $crate::_dbg_start();
319
320        // Use the same `match` trick that `std` does.
321        ($(match $val {
322            tmp => {
323                $crate::_dbg_write(
324                    &mut dbg,
325                    core::file!(),
326                    core::line!(),
327                    core::stringify!($val),
328                    core::format_args!("{:#?}", &tmp),
329                );
330                tmp
331            }
332        }),+);
333
334        $crate::_dbg_finish(dbg);
335    };
336}