|
57 | 57 | #![stable(feature = "alloc_module", since = "1.28.0")] |
58 | 58 |
|
59 | 59 | use core::ptr::NonNull; |
60 | | -use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; |
| 60 | +use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; |
61 | 61 | use core::{hint, mem, ptr}; |
62 | 62 |
|
63 | 63 | #[stable(feature = "alloc_module", since = "1.28.0")] |
@@ -287,7 +287,7 @@ unsafe impl Allocator for System { |
287 | 287 | } |
288 | 288 | } |
289 | 289 |
|
290 | | -static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); |
| 290 | +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); |
291 | 291 |
|
292 | 292 | /// Registers a custom allocation error hook, replacing any that was previously registered. |
293 | 293 | /// |
@@ -344,27 +344,84 @@ pub fn take_alloc_error_hook() -> fn(Layout) { |
344 | 344 | if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } |
345 | 345 | } |
346 | 346 |
|
| 347 | +#[optimize(size)] |
347 | 348 | fn default_alloc_error_hook(layout: Layout) { |
| 349 | + if cfg!(panic = "immediate-abort") { |
| 350 | + return; |
| 351 | + } |
| 352 | + |
348 | 353 | // This is the default path taken on OOM, and the only path taken on stable with std. |
349 | 354 | // Crucially, it does *not* call any user-defined code, and therefore users do not have to |
350 | 355 | // worry about allocation failure causing reentrancy issues. That makes it different from |
351 | 356 | // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error |
352 | 357 | // handler that is called when there is no `#[alloc_error_handler]`), which triggers a |
353 | 358 | // regular panic and thus can invoke a user-defined panic hook, executing arbitrary |
354 | 359 | // user-defined code. |
355 | | - rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); |
| 360 | + |
| 361 | + static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false); |
| 362 | + if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) { |
| 363 | + // Don't try to print a backtrace if a previous alloc error happened. This likely means |
| 364 | + // there is not enough memory to print a backtrace, although it could also mean that two |
| 365 | + // threads concurrently run out of memory. |
| 366 | + rtprintpanic!( |
| 367 | + "memory allocation of {} bytes failed while handling another memory allocation error\n", |
| 368 | + layout.size() |
| 369 | + ); |
| 370 | + return; |
| 371 | + } else { |
| 372 | + rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); |
| 373 | + } |
| 374 | + |
| 375 | + let Some(mut out) = crate::sys::stdio::panic_output() else { |
| 376 | + return; |
| 377 | + }; |
| 378 | + |
| 379 | + // Use a lock to prevent mixed output in multithreading context. |
| 380 | + // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. |
| 381 | + // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks |
| 382 | + // when there is too little memory to print a backtrace. |
| 383 | + let mut lock = crate::sys::backtrace::lock(); |
| 384 | + |
| 385 | + match crate::panic::get_backtrace_style() { |
| 386 | + // SAFETY: we took out a lock just a second ago. |
| 387 | + Some(crate::panic::BacktraceStyle::Short) => { |
| 388 | + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short)) |
| 389 | + } |
| 390 | + Some(crate::panic::BacktraceStyle::Full) => { |
| 391 | + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full)) |
| 392 | + } |
| 393 | + Some(crate::panic::BacktraceStyle::Off) => { |
| 394 | + use crate::io::Write; |
| 395 | + let _ = writeln!( |
| 396 | + out, |
| 397 | + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ |
| 398 | + backtrace" |
| 399 | + ); |
| 400 | + if cfg!(miri) { |
| 401 | + let _ = writeln!( |
| 402 | + out, |
| 403 | + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ |
| 404 | + for the environment variable to have an effect" |
| 405 | + ); |
| 406 | + } |
| 407 | + } |
| 408 | + // If backtraces aren't supported or are forced-off, do nothing. |
| 409 | + None => {} |
| 410 | + } |
356 | 411 | } |
357 | 412 |
|
358 | 413 | #[cfg(not(test))] |
359 | 414 | #[doc(hidden)] |
360 | 415 | #[alloc_error_handler] |
361 | 416 | #[unstable(feature = "alloc_internals", issue = "none")] |
362 | 417 | pub fn rust_oom(layout: Layout) -> ! { |
363 | | - let hook = HOOK.load(Ordering::Acquire); |
364 | | - let hook: fn(Layout) = |
365 | | - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; |
366 | | - hook(layout); |
367 | | - crate::process::abort() |
| 418 | + crate::sys::backtrace::__rust_end_short_backtrace(|| { |
| 419 | + let hook = HOOK.load(Ordering::Acquire); |
| 420 | + let hook: fn(Layout) = |
| 421 | + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; |
| 422 | + hook(layout); |
| 423 | + crate::process::abort() |
| 424 | + }) |
368 | 425 | } |
369 | 426 |
|
370 | 427 | #[cfg(not(test))] |
|
0 commit comments