aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src
diff options
context:
space:
mode:
authorFenrir <[email protected]>2017-07-31 23:23:20 -0600
committerFenrir <[email protected]>2017-07-31 23:23:20 -0600
commit5f0fc0c00148c6128ee9354ba7360b386a59bef5 (patch)
tree3fe53b38fa6487a03c8938a1132e0e14f273d430 /ctr-std/src
parentMerge pull request #41 from FenrirWolf/panic-reboot (diff)
downloadctru-rs-5f0fc0c00148c6128ee9354ba7360b386a59bef5.tar.xz
ctru-rs-5f0fc0c00148c6128ee9354ba7360b386a59bef5.zip
Add unwinding strategy for panics
Diffstat (limited to 'ctr-std/src')
-rw-r--r--ctr-std/src/collections/hash/table.rs6
-rw-r--r--ctr-std/src/lib.rs21
-rw-r--r--ctr-std/src/panic.rs11
-rw-r--r--ctr-std/src/panicking.rs616
-rw-r--r--ctr-std/src/rt.rs5
-rw-r--r--ctr-std/src/sys_common/mod.rs4
6 files changed, 548 insertions, 115 deletions
diff --git a/ctr-std/src/collections/hash/table.rs b/ctr-std/src/collections/hash/table.rs
index 06f4f76..3844690 100644
--- a/ctr-std/src/collections/hash/table.rs
+++ b/ctr-std/src/collections/hash/table.rs
@@ -44,7 +44,7 @@ impl TaggedHashUintPtr {
#[inline]
unsafe fn new(ptr: *mut HashUint) -> Self {
debug_assert!(ptr as usize & 1 == 0 || ptr as usize == EMPTY as usize);
- TaggedHashUintPtr(Unique::new(ptr))
+ TaggedHashUintPtr(Unique::new_unchecked(ptr))
}
#[inline]
@@ -56,7 +56,7 @@ impl TaggedHashUintPtr {
} else {
usize_ptr &= !1;
}
- self.0 = Unique::new(usize_ptr as *mut HashUint)
+ self.0 = Unique::new_unchecked(usize_ptr as *mut HashUint)
}
}
@@ -877,7 +877,7 @@ impl<K, V> RawTable<K, V> {
elems_left: elems_left,
marker: marker::PhantomData,
},
- table: unsafe { Shared::new(self) },
+ table: Shared::from(self),
marker: marker::PhantomData,
}
}
diff --git a/ctr-std/src/lib.rs b/ctr-std/src/lib.rs
index 99b77a2..28ab259 100644
--- a/ctr-std/src/lib.rs
+++ b/ctr-std/src/lib.rs
@@ -26,6 +26,7 @@
#![feature(lang_items)]
#![feature(macro_reexport)]
#![feature(needs_drop)]
+#![feature(needs_panic_runtime)]
#![feature(oom)]
#![feature(on_unimplemented)]
#![feature(optin_builtin_traits)]
@@ -45,10 +46,13 @@
#![feature(unicode)]
#![feature(unique)]
#![feature(untagged_unions)]
+#![feature(unwind_attributes)]
#![feature(zero_one)]
#![allow(non_camel_case_types, dead_code, unused_features)]
#![no_std]
+#![needs_panic_runtime]
+
#![cfg_attr(not(stage0), default_lib_allocator)]
#![stable(feature = "rust1", since = "1.0.0")]
@@ -197,20 +201,3 @@ pub mod rt;
pub mod __rand {
pub use rand::{thread_rng, ThreadRng, Rng};
}
-
-// NOTE: These two are "undefined" symbols that LLVM emits but that
-// we never actually use
-#[doc(hidden)]
-
-#[stable(feature = "3ds", since = "1.0.0")]
-#[no_mangle]
-pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() {
- intrinsics::unreachable()
-}
-
-#[stable(feature = "3ds", since = "1.0.0")]
-#[doc(hidden)]
-#[no_mangle]
-pub unsafe extern "C" fn __aeabi_unwind_cpp_pr1() {
- intrinsics::unreachable()
-}
diff --git a/ctr-std/src/panic.rs b/ctr-std/src/panic.rs
index 2e037cd..58356bc 100644
--- a/ctr-std/src/panic.rs
+++ b/ctr-std/src/panic.rs
@@ -22,8 +22,8 @@ use rc::Rc;
use sync::{Arc, Mutex, RwLock, atomic};
use thread::Result;
-//#[stable(feature = "panic_hooks", since = "1.10.0")]
-//pub use panicking::{take_hook, set_hook, PanicInfo, Location};
+#[stable(feature = "panic_hooks", since = "1.10.0")]
+pub use panicking::{take_hook, set_hook, PanicInfo, Location};
/// A marker trait which represents "panic safe" types in Rust.
///
@@ -112,7 +112,7 @@ pub trait UnwindSafe {}
/// This is a "helper marker trait" used to provide impl blocks for the
/// `UnwindSafe` trait, for more information see that documentation.
#[stable(feature = "catch_unwind", since = "1.9.0")]
-#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
+#[rustc_on_unimplemented = "the type {Self} may contain interior mutability \
and a reference may not be safely transferrable \
across a catch_unwind boundary"]
pub trait RefUnwindSafe {}
@@ -388,7 +388,6 @@ pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
/// }
/// ```
#[stable(feature = "resume_unwind", since = "1.9.0")]
-// we always abort so I'm pretty sure there's no reason to ever call this
-pub fn resume_unwind(_payload: Box<Any + Send>) -> ! {
- unimplemented!()
+pub fn resume_unwind(payload: Box<Any + Send>) -> ! {
+ panicking::update_count_then_panic(payload)
}
diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs
index 26914ce..c2e0832 100644
--- a/ctr-std/src/panicking.rs
+++ b/ctr-std/src/panicking.rs
@@ -10,16 +10,26 @@
//! Implementation of various bits and pieces of the `panic!` macro and
//! associated runtime pieces.
+//!
+//! Specifically, this module contains the implementation of:
+//!
+//! * Panic hooks
+//! * Executing a panic up to doing the actual implementation
+//! * Shims around "try"
use io::prelude::*;
use any::Any;
use cell::RefCell;
-use fmt::{self, Display};
+use fmt;
+use intrinsics;
use mem;
use ptr;
use raw;
+use sys_common::rwlock::RWLock;
use sys_common::thread_info;
+use sys_common::util;
+use thread;
thread_local! {
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
@@ -27,97 +37,416 @@ thread_local! {
}
}
-///The compiler wants this to be here. Otherwise it won't be happy. And we like happy compilers.
-#[lang = "eh_personality"]
-pub extern fn eh_personality() {}
+#[allow(improper_ctypes)]
+extern {
+ fn __rust_maybe_catch_panic(f: fn(*mut u8),
+ data: *mut u8,
+ data_ptr: *mut usize,
+ vtable_ptr: *mut usize) -> u32;
+ #[unwind]
+ fn __rust_start_panic(data: usize, vtable: usize) -> u32;
+}
-/// Determines whether the current thread is unwinding because of panic.
-// Which it won't be, since we still don't have stack unwinding.
-pub fn panicking() -> bool {
- false
+#[derive(Copy, Clone)]
+enum Hook {
+ Default,
+ Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
}
-/// Entry point of panic from the libcore crate.
-#[lang = "panic_fmt"]
-pub extern fn rust_begin_panic(msg: fmt::Arguments,
- file: &'static str,
- line: u32,
- col: u32) -> ! {
- begin_panic_fmt(&msg, &(file, line, col))
+static HOOK_LOCK: RWLock = RWLock::new();
+static mut HOOK: Hook = Hook::Default;
+
+/// Registers a custom panic hook, replacing any that was previously registered.
+///
+/// The panic hook is invoked when a thread panics, but before the panic runtime
+/// is invoked. As such, the hook will run with both the aborting and unwinding
+/// runtimes. The default hook prints a message to standard error and generates
+/// a backtrace if requested, but this behavior can be customized with the
+/// `set_hook` and `take_hook` functions.
+///
+/// The hook is provided with a `PanicInfo` struct which contains information
+/// about the origin of the panic, including the payload passed to `panic!` and
+/// the source code location from which the panic originated.
+///
+/// The panic hook is a global resource.
+///
+/// # Panics
+///
+/// Panics if called from a panicking thread.
+///
+/// # Examples
+///
+/// The following will print "Custom panic hook":
+///
+/// ```should_panic
+/// use std::panic;
+///
+/// panic::set_hook(Box::new(|_| {
+/// println!("Custom panic hook");
+/// }));
+///
+/// panic!("Normal panic");
+/// ```
+#[stable(feature = "panic_hooks", since = "1.10.0")]
+pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
+ if thread::panicking() {
+ panic!("cannot modify the panic hook from a panicking thread");
+ }
+
+ unsafe {
+ HOOK_LOCK.write();
+ let old_hook = HOOK;
+ HOOK = Hook::Custom(Box::into_raw(hook));
+ HOOK_LOCK.write_unlock();
+
+ if let Hook::Custom(ptr) = old_hook {
+ Box::from_raw(ptr);
+ }
+ }
}
-/// The entry point for panicking with a formatted message.
+/// Unregisters the current panic hook, returning it.
///
-/// This is designed to reduce the amount of code required at the call
-/// site as much as possible (so that `panic!()` has as low an impact
-/// on (e.g.) the inlining of other functions as possible), by moving
-/// the actual formatting into this shared place.
-#[unstable(feature = "libstd_sys_internals",
- reason = "used by the panic! macro",
- issue = "0")]
-#[inline(never)] #[cold]
-pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
- use fmt::Write;
+/// If no custom hook is registered, the default hook will be returned.
+///
+/// # Panics
+///
+/// Panics if called from a panicking thread.
+///
+/// # Examples
+///
+/// The following will print "Normal panic":
+///
+/// ```should_panic
+/// use std::panic;
+///
+/// panic::set_hook(Box::new(|_| {
+/// println!("Custom panic hook");
+/// }));
+///
+/// let _ = panic::take_hook();
+///
+/// panic!("Normal panic");
+/// ```
+#[stable(feature = "panic_hooks", since = "1.10.0")]
+pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
+ if thread::panicking() {
+ panic!("cannot modify the panic hook from a panicking thread");
+ }
- let mut s = String::new();
- let _ = s.write_fmt(*msg);
- begin_panic(s, file_line_col);
+ unsafe {
+ HOOK_LOCK.write();
+ let hook = HOOK;
+ HOOK = Hook::Default;
+ HOOK_LOCK.write_unlock();
+
+ match hook {
+ Hook::Default => Box::new(default_hook),
+ Hook::Custom(ptr) => Box::from_raw(ptr),
+ }
+ }
+}
+
+/// A struct providing information about a panic.
+///
+/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
+/// function.
+///
+/// [`set_hook`]: ../../std/panic/fn.set_hook.html
+///
+/// # Examples
+///
+/// ```should_panic
+/// use std::panic;
+///
+/// panic::set_hook(Box::new(|panic_info| {
+/// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap());
+/// }));
+///
+/// panic!("Normal panic");
+/// ```
+#[stable(feature = "panic_hooks", since = "1.10.0")]
+#[derive(Debug)]
+pub struct PanicInfo<'a> {
+ payload: &'a (Any + Send),
+ location: Location<'a>,
+}
+
+impl<'a> PanicInfo<'a> {
+ /// Returns the payload associated with the panic.
+ ///
+ /// This will commonly, but not always, be a `&'static str` or [`String`].
+ ///
+ /// [`String`]: ../../std/string/struct.String.html
+ ///
+ /// # Examples
+ ///
+ /// ```should_panic
+ /// use std::panic;
+ ///
+ /// panic::set_hook(Box::new(|panic_info| {
+ /// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap());
+ /// }));
+ ///
+ /// panic!("Normal panic");
+ /// ```
+ #[stable(feature = "panic_hooks", since = "1.10.0")]
+ pub fn payload(&self) -> &(Any + Send) {
+ self.payload
+ }
+
+ /// Returns information about the location from which the panic originated,
+ /// if available.
+ ///
+ /// This method will currently always return [`Some`], but this may change
+ /// in future versions.
+ ///
+ /// [`Some`]: ../../std/option/enum.Option.html#variant.Some
+ ///
+ /// # Examples
+ ///
+ /// ```should_panic
+ /// use std::panic;
+ ///
+ /// panic::set_hook(Box::new(|panic_info| {
+ /// if let Some(location) = panic_info.location() {
+ /// println!("panic occured in file '{}' at line {}", location.file(), location.line());
+ /// } else {
+ /// println!("panic occured but can't get location information...");
+ /// }
+ /// }));
+ ///
+ /// panic!("Normal panic");
+ /// ```
+ #[stable(feature = "panic_hooks", since = "1.10.0")]
+ pub fn location(&self) -> Option<&Location> {
+ Some(&self.location)
+ }
+}
+
+/// A struct containing information about the location of a panic.
+///
+/// This structure is created by the [`location`] method of [`PanicInfo`].
+///
+/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location
+/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html
+///
+/// # Examples
+///
+/// ```should_panic
+/// use std::panic;
+///
+/// panic::set_hook(Box::new(|panic_info| {
+/// if let Some(location) = panic_info.location() {
+/// println!("panic occured in file '{}' at line {}", location.file(), location.line());
+/// } else {
+/// println!("panic occured but can't get location information...");
+/// }
+/// }));
+///
+/// panic!("Normal panic");
+/// ```
+#[derive(Debug)]
+#[stable(feature = "panic_hooks", since = "1.10.0")]
+pub struct Location<'a> {
+ file: &'a str,
+ line: u32,
+ col: u32,
+}
+
+impl<'a> Location<'a> {
+ /// Returns the name of the source file from which the panic originated.
+ ///
+ /// # Examples
+ ///
+ /// ```should_panic
+ /// use std::panic;
+ ///
+ /// panic::set_hook(Box::new(|panic_info| {
+ /// if let Some(location) = panic_info.location() {
+ /// println!("panic occured in file '{}'", location.file());
+ /// } else {
+ /// println!("panic occured but can't get location information...");
+ /// }
+ /// }));
+ ///
+ /// panic!("Normal panic");
+ /// ```
+ #[stable(feature = "panic_hooks", since = "1.10.0")]
+ pub fn file(&self) -> &str {
+ self.file
+ }
+
+ /// Returns the line number from which the panic originated.
+ ///
+ /// # Examples
+ ///
+ /// ```should_panic
+ /// use std::panic;
+ ///
+ /// panic::set_hook(Box::new(|panic_info| {
+ /// if let Some(location) = panic_info.location() {
+ /// println!("panic occured at line {}", location.line());
+ /// } else {
+ /// println!("panic occured but can't get location information...");
+ /// }
+ /// }));
+ ///
+ /// panic!("Normal panic");
+ /// ```
+ #[stable(feature = "panic_hooks", since = "1.10.0")]
+ pub fn line(&self) -> u32 {
+ self.line
+ }
+
+ /// Returns the column from which the panic originated.
+ ///
+ /// # Examples
+ ///
+ /// ```should_panic
+ /// #![feature(panic_col)]
+ /// use std::panic;
+ ///
+ /// panic::set_hook(Box::new(|panic_info| {
+ /// if let Some(location) = panic_info.location() {
+ /// println!("panic occured at column {}", location.column());
+ /// } else {
+ /// println!("panic occured but can't get location information...");
+ /// }
+ /// }));
+ ///
+ /// panic!("Normal panic");
+ /// ```
+ #[unstable(feature = "panic_col", reason = "recently added", issue = "42939")]
+ pub fn column(&self) -> u32 {
+ self.col
+ }
}
-// Citra doesn't support the Error applet yet
#[cfg(feature = "citra")]
-#[unstable(feature = "libstd_sys_internals",
- reason = "used by the panic! macro",
- issue = "0")]
-#[inline(never)] #[cold]
-pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
- let msg = Box::new(msg);
- let (file, line, col) = *file_line_col;
+fn default_hook(info: &PanicInfo) {
+ #[cfg(feature = "backtrace")]
+ use sys_common::backtrace;
+
+ // If this is a double panic, make sure that we print a backtrace
+ // for this panic. Otherwise only print it if logging is enabled.
+ #[cfg(feature = "backtrace")]
+ let log_backtrace = {
+ let panics = update_panic_count(0);
+
+ if panics >= 2 {
+ Some(backtrace::PrintFormat::Full)
+ } else {
+ backtrace::log_enabled()
+ }
+ };
+
+ let file = info.location.file;
+ let line = info.location.line;
+ let col = info.location.col;
+
+ let msg = match info.payload.downcast_ref::<&'static str>() {
+ Some(s) => *s,
+ None => match info.payload.downcast_ref::<String>() {
+ Some(s) => &s[..],
+ None => "Box<Any>",
+ }
+ };
// 3DS-specific code begins here
- use libctru::{consoleInit, consoleClear, gfxScreen_t, threadGetCurrent};
+ use libctru::{consoleInit, consoleDebugInit, debugDevice,
+ consoleClear, gfxScreen_t, threadGetCurrent};
+ use sys::stdio::Stderr;
+
+ let mut err = Stderr::new().ok();
+ let thread = thread_info::current_thread();
+ let name;
unsafe {
- // Set up a new console, overwriting whatever was on the top screen
+ // Set up a new console, overwriting whatever was on the bottom screen
// before we started panicking
- let _console = consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut());
+ let _console = consoleInit(gfxScreen_t::GFX_BOTTOM, ptr::null_mut());
consoleClear();
+ consoleDebugInit(debugDevice::debugDevice_CONSOLE);
// Determine thread name
- let thread = thread_info::current_thread();
- let name = thread.as_ref()
+ name = thread.as_ref()
.and_then(|t| t.name())
.unwrap_or(if threadGetCurrent() == ptr::null_mut() {"main"} else {"<unnamed>"});
+ }
+
+ let write = |err: &mut ::io::Write| {
+ let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}:{}",
+ name, msg, file, line, col);
- println!("thread '{}' panicked at '{}', {}:{}:{}",
- name, msg, file, line, col);
+ #[cfg(feature = "backtrace")]
+ {
+ use sync::atomic::{AtomicBool, Ordering};
- // Citra seems to ignore calls to svcExitProcess, and libc::abort()
- // causes it to lock up and fill the console with endless debug statements.
- // So instead of terminating the program, we just let it spin in the following
- // loop. This means that any background threads might continue to run after
- // this thread panics, but there's not a lot we can do about that currently.
- loop { }
+ static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
+
+ if let Some(format) = log_backtrace {
+ let _ = backtrace::print(err, format);
+ } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
+ let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
+ }
+ }
+ };
+
+ let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
+ match (prev, err.as_mut()) {
+ (Some(mut stderr), _) => {
+ write(&mut *stderr);
+ let mut s = Some(stderr);
+ LOCAL_STDERR.with(|slot| {
+ *slot.borrow_mut() = s.take();
+ });
+ }
+ (None, Some(ref mut err)) => { write(err) }
+ _ => {}
}
+ // Citra will crash and die horribly if we allow it to go further than this,
+ // So we just trap control flow in this loop instead. You'll still see the panic
+ // message, and Citra won't die quite as horribly. It's a win-win! Well, mostly.
+ loop { }
}
#[cfg(not(feature = "citra"))]
-#[unstable(feature = "libstd_sys_internals",
- reason = "used by the panic! macro",
- issue = "0")]
-#[inline(never)] #[cold]
-pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
- let msg = Box::new(msg);
- let (file, line, col) = *file_line_col;
+fn default_hook(info: &PanicInfo) {
+ #[cfg(feature = "backtrace")]
+ use sys_common::backtrace;
+
+ // If this is a double panic, make sure that we print a backtrace
+ // for this panic. Otherwise only print it if logging is enabled.
+ #[cfg(feature = "backtrace")]
+ let log_backtrace = {
+ let panics = update_panic_count(0);
+
+ if panics >= 2 {
+ Some(backtrace::PrintFormat::Full)
+ } else {
+ backtrace::log_enabled()
+ }
+ };
+
+ let file = info.location.file;
+ let line = info.location.line;
+ let col = info.location.col;
+
+ let msg = match info.payload.downcast_ref::<&'static str>() {
+ Some(s) => *s,
+ None => match info.payload.downcast_ref::<String>() {
+ Some(s) => &s[..],
+ None => "Box<Any>",
+ }
+ };
// 3DS-specific code begins here
- use libctru::{errorInit, errorText, errorDisp,
- APT_HardwareResetAsync, svcExitProcess, threadGetCurrent,
+ use libctru::{errorInit, errorText, errorDisp, threadGetCurrent,
errorConf, errorType, CFG_Language};
use libc;
unsafe {
- // Determine thread name
let thread = thread_info::current_thread();
let name = thread.as_ref()
.and_then(|t| t.name())
@@ -126,6 +455,7 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st
// Setup error payload
let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col);
+
let mut error_conf: errorConf = mem::uninitialized();
errorInit(&mut error_conf,
errorType::ERROR_TEXT_WORD_WRAP,
@@ -134,16 +464,26 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st
// Display error
errorDisp(&mut error_conf);
+ }
+}
- // Now that we're all done printing, it's time to exit the program.
- // We don't have stack unwinding yet, so let's just trigger a reboot
- APT_HardwareResetAsync();
+#[cfg(not(test))]
+#[doc(hidden)]
+#[unstable(feature = "update_panic_count", issue = "0")]
+pub fn update_panic_count(amt: isize) -> usize {
+ use cell::Cell;
+ thread_local! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
- // If rebooting fails for some reason, we extra-forcibly end the program
- svcExitProcess()
- }
+ PANIC_COUNT.with(|c| {
+ let next = (c.get() as isize + amt) as usize;
+ c.set(next);
+ return next
+ })
}
+#[cfg(test)]
+pub use realstd::rt::update_panic_count;
+
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
#[allow(unions_with_drop_fields)]
@@ -209,29 +549,129 @@ pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
}
}
-#[cfg(not(test))]
-#[doc(hidden)]
-#[unstable(feature = "update_panic_count", issue = "0")]
-pub fn update_panic_count(amt: isize) -> usize {
- use cell::Cell;
- thread_local! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
+/// Determines whether the current thread is unwinding because of panic.
+pub fn panicking() -> bool {
+ update_panic_count(0) != 0
+}
- PANIC_COUNT.with(|c| {
- let next = (c.get() as isize + amt) as usize;
- c.set(next);
- return next
- })
+/// Entry point of panic from the libcore crate.
+#[lang = "panic_fmt"]
+#[unwind]
+pub extern fn rust_begin_panic(msg: fmt::Arguments,
+ file: &'static str,
+ line: u32,
+ col: u32) -> ! {
+ begin_panic_fmt(&msg, &(file, line, col))
}
-// *Implementation borrowed from the libpanic_abort crate*
-//
-// Rust's "try" function, but if we're aborting on panics we just call the
-// function as there's nothing else we need to do here.
-#[allow(improper_ctypes)]
-extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
- data: *mut u8,
- _data_ptr: *mut usize,
- _vtable_ptr: *mut usize) -> u32 {
- f(data);
- 0
+/// The entry point for panicking with a formatted message.
+///
+/// This is designed to reduce the amount of code required at the call
+/// site as much as possible (so that `panic!()` has as low an impact
+/// on (e.g.) the inlining of other functions as possible), by moving
+/// the actual formatting into this shared place.
+#[unstable(feature = "libstd_sys_internals",
+ reason = "used by the panic! macro",
+ issue = "0")]
+#[inline(never)] #[cold]
+pub fn begin_panic_fmt(msg: &fmt::Arguments,
+ file_line_col: &(&'static str, u32, u32)) -> ! {
+ use fmt::Write;
+
+ // We do two allocations here, unfortunately. But (a) they're
+ // required with the current scheme, and (b) we don't handle
+ // panic + OOM properly anyway (see comment in begin_panic
+ // below).
+
+ let mut s = String::new();
+ let _ = s.write_fmt(*msg);
+ begin_panic(s, file_line_col)
+}
+
+/// This is the entry point of panicking for panic!() and assert!().
+#[unstable(feature = "libstd_sys_internals",
+ reason = "used by the panic! macro",
+ issue = "0")]
+#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
+pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
+ // Note that this should be the only allocation performed in this code path.
+ // Currently this means that panic!() on OOM will invoke this code path,
+ // but then again we're not really ready for panic on OOM anyway. If
+ // we do start doing this, then we should propagate this allocation to
+ // be performed in the parent of this thread instead of the thread that's
+ // panicking.
+
+ rust_panic_with_hook(Box::new(msg), file_line_col)
+}
+
+/// Executes the primary logic for a panic, including checking for recursive
+/// panics and panic hooks.
+///
+/// This is the entry point or panics from libcore, formatted panics, and
+/// `Box<Any>` panics. Here we'll verify that we're not panicking recursively,
+/// run panic hooks, and then delegate to the actual implementation of panics.
+#[inline(never)]
+#[cold]
+fn rust_panic_with_hook(msg: Box<Any + Send>,
+ file_line_col: &(&'static str, u32, u32)) -> ! {
+ let (file, line, col) = *file_line_col;
+
+ let panics = update_panic_count(1);
+
+ // If this is the third nested call (e.g. panics == 2, this is 0-indexed),
+ // the panic hook probably triggered the last panic, otherwise the
+ // double-panic check would have aborted the process. In this case abort the
+ // process real quickly as we don't want to try calling it again as it'll
+ // probably just panic again.
+ if panics > 2 {
+ util::dumb_print(format_args!("thread panicked while processing \
+ panic. aborting.\n"));
+ unsafe { intrinsics::abort() }
+ }
+
+ unsafe {
+ let info = PanicInfo {
+ payload: &*msg,
+ location: Location {
+ file,
+ line,
+ col,
+ },
+ };
+ HOOK_LOCK.read();
+ match HOOK {
+ Hook::Default => default_hook(&info),
+ Hook::Custom(ptr) => (*ptr)(&info),
+ }
+ HOOK_LOCK.read_unlock();
+ }
+
+ if panics > 1 {
+ // If a thread panics while it's already unwinding then we
+ // have limited options. Currently our preference is to
+ // just abort. In the future we may consider resuming
+ // unwinding or otherwise exiting the thread cleanly.
+ util::dumb_print(format_args!("thread panicked while panicking. \
+ aborting.\n"));
+ unsafe { intrinsics::abort() }
+ }
+
+ rust_panic(msg)
+}
+
+/// Shim around rust_panic. Called by resume_unwind.
+pub fn update_count_then_panic(msg: Box<Any + Send>) -> ! {
+ update_panic_count(1);
+ rust_panic(msg)
+}
+
+/// A private no-mangle function on which to slap yer breakpoints.
+#[no_mangle]
+#[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints
+pub fn rust_panic(msg: Box<Any + Send>) -> ! {
+ let code = unsafe {
+ let obj = mem::transmute::<_, raw::TraitObject>(msg);
+ __rust_start_panic(obj.data as usize, obj.vtable as usize)
+ };
+ rtabort!("failed to initiate panic, error {}", code)
}
diff --git a/ctr-std/src/rt.rs b/ctr-std/src/rt.rs
index 178b5a0..ddfb707 100644
--- a/ctr-std/src/rt.rs
+++ b/ctr-std/src/rt.rs
@@ -22,6 +22,7 @@
issue = "0")]
#![doc(hidden)]
+use panic;
use mem;
// Reexport some of our utilities which are expected by other crates.
@@ -31,6 +32,8 @@ pub use panicking::{begin_panic, begin_panic_fmt};
#[lang = "start"]
#[allow(unused_variables)]
fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
- unsafe { mem::transmute::<_, fn()>(main)(); }
+ let _ = unsafe {
+ panic::catch_unwind(mem::transmute::<_, fn()>(main))
+ };
0
}
diff --git a/ctr-std/src/sys_common/mod.rs b/ctr-std/src/sys_common/mod.rs
index 6f47466..4a7d79f 100644
--- a/ctr-std/src/sys_common/mod.rs
+++ b/ctr-std/src/sys_common/mod.rs
@@ -77,6 +77,10 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}
+macro_rules! rtabort {
+ ($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*)))
+}
+
// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).