aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/panicking.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ctr-std/src/panicking.rs')
-rw-r--r--ctr-std/src/panicking.rs123
1 files changed, 118 insertions, 5 deletions
diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs
index e0f9477..efb828a 100644
--- a/ctr-std/src/panicking.rs
+++ b/ctr-std/src/panicking.rs
@@ -16,6 +16,9 @@ use io::prelude::*;
use any::Any;
use cell::RefCell;
use fmt;
+use mem;
+use ptr;
+use raw;
use __core::fmt::Display;
thread_local! {
@@ -26,11 +29,11 @@ thread_local! {
///The compiler wants this to be here. Otherwise it won't be happy. And we like happy compilers.
#[lang = "eh_personality"]
-extern fn eh_personality() {}
+pub extern fn eh_personality() {}
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
-extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32) -> ! {
+pub extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32) -> ! {
begin_panic_fmt(&msg, &(file, line))
}
@@ -52,17 +55,127 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line: &(&'static str, u32)) ->
begin_panic(s, file_line);
}
-/// This is where the main panic logic happens.
+/// We don't have stack unwinding, so all we do is print the panic message
+/// and then crash or hang the application
#[inline(never)]
#[cold]
pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line: &(&'static str, u32)) -> ! {
let msg = Box::new(msg);
let (file, line) = *file_line;
- print!("--------------------------------------------------");
+ use libctru::console::consoleInit;
+ use libctru::gfx::gfxScreen_t;
+
+ // set up a new console, overwriting whatever was on the top screen
+ // before we started panicking
+ let _console = unsafe { consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut()) };
+
println!("PANIC in {} at line {}:", file, line);
println!(" {}", msg);
- print!("\x1b[29;00H--------------------------------------------------");
+ // Terminate the process to ensure that all threads cease when panicking.
+ unsafe { ::libctru::svc::svcExitProcess() }
+
+ // On 3DS hardware, code execution will have terminated at the above function.
+ //
+ // Citra, however, will simply ignore the function and control flow becomes trapped
+ // in the following loop instead. However, this means that other threads may continue
+ // to run after a panic!
+ //
+ // This is actually a better outcome than calling libc::abort(), which seemingly
+ // causes the emulator to step into unreachable code, prompting it to freak out
+ // and spew endless nonsense into the console log.
loop {}
}
+
+/// 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)]
+ union Data<F, R> {
+ f: F,
+ r: R,
+ }
+
+ // We do some sketchy operations with ownership here for the sake of
+ // performance. We can only pass pointers down to
+ // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
+ // the ownership tracking here manually using a union.
+ //
+ // We go through a transition where:
+ //
+ // * First, we set the data to be the closure that we're going to call.
+ // * When we make the function call, the `do_call` function below, we take
+ // ownership of the function pointer. At this point the `Data` union is
+ // entirely uninitialized.
+ // * If the closure successfully returns, we write the return value into the
+ // data's return slot. Note that `ptr::write` is used as it's overwriting
+ // uninitialized data.
+ // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
+ // in one of two states:
+ //
+ // 1. The closure didn't panic, in which case the return value was
+ // filled in. We move it out of `data` and return it.
+ // 2. The closure panicked, in which case the return value wasn't
+ // filled in. In this case the entire `data` union is invalid, so
+ // there is no need to drop anything.
+ //
+ // Once we stack all that together we should have the "most efficient'
+ // method of calling a catch panic whilst juggling ownership.
+ let mut any_data = 0;
+ let mut any_vtable = 0;
+ let mut data = Data {
+ f: f,
+ };
+
+ let r = __rust_maybe_catch_panic(do_call::<F, R>,
+ &mut data as *mut _ as *mut u8,
+ &mut any_data,
+ &mut any_vtable);
+
+ return if r == 0 {
+ debug_assert!(update_panic_count(0) == 0);
+ Ok(data.r)
+ } else {
+ update_panic_count(-1);
+ debug_assert!(update_panic_count(0) == 0);
+ Err(mem::transmute(raw::TraitObject {
+ data: any_data as *mut _,
+ vtable: any_vtable as *mut _,
+ }))
+ };
+
+ fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
+ unsafe {
+ let data = data as *mut Data<F, R>;
+ let f = ptr::read(&mut (*data).f);
+ ptr::write(&mut (*data).r, f());
+ }
+ }
+}
+
+#[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) }
+
+ PANIC_COUNT.with(|c| {
+ let next = (c.get() as isize + amt) as usize;
+ c.set(next);
+ return next
+ })
+}
+
+// *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
+}