diff --git a/ctr-std/Cargo.toml b/ctr-std/Cargo.toml index 1a883f5..c4cbce4 100644 --- a/ctr-std/Cargo.toml +++ b/ctr-std/Cargo.toml @@ -15,3 +15,6 @@ default-features = false [dependencies.ctru-sys] path = "../ctru-sys" + +[features] +citra = [] diff --git a/ctr-std/src/macros.rs b/ctr-std/src/macros.rs index 0ce6b0a..21a7da9 100644 --- a/ctr-std/src/macros.rs +++ b/ctr-std/src/macros.rs @@ -43,8 +43,8 @@ macro_rules! panic { ($msg:expr) => ({ $crate::rt::begin_panic($msg, { // static requires less code at runtime, more constant data - static _FILE_LINE: (&'static str, u32) = (file!(), line!()); - &_FILE_LINE + static _FILE_LINE_COL: (&'static str, u32, u32) = (file!(), line!(), column!()); + &_FILE_LINE_COL }) }); ($fmt:expr, $($arg:tt)+) => ({ @@ -53,8 +53,8 @@ macro_rules! panic { // used inside a dead function. Just `#[allow(dead_code)]` is // insufficient, since the user may have // `#[forbid(dead_code)]` and which cannot be overridden. - static _FILE_LINE: (&'static str, u32) = (file!(), line!()); - &_FILE_LINE + static _FILE_LINE_COL: (&'static str, u32, u32) = (file!(), line!(), column!()); + &_FILE_LINE_COL }) }); } diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs index 0de44be..097be6f 100644 --- a/ctr-std/src/panicking.rs +++ b/ctr-std/src/panicking.rs @@ -15,11 +15,10 @@ use io::prelude::*; use any::Any; use cell::RefCell; -use fmt; +use fmt::{self, Display}; use mem; use ptr; use raw; -use __core::fmt::Display; thread_local! { pub static LOCAL_STDERR: RefCell>> = { @@ -33,8 +32,11 @@ pub extern fn eh_personality() {} /// 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) -> ! { - begin_panic_fmt(&msg, &(file, line)) +pub extern fn rust_begin_panic(msg: fmt::Arguments, + file: &'static str, + line: u32, + col: u32) -> ! { + begin_panic_fmt(&msg, &(file, line, col)) } /// The entry point for panicking with a formatted message. @@ -47,45 +49,75 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u3 reason = "used by the panic! macro", issue = "0")] #[inline(never)] #[cold] -pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line: &(&'static str, u32)) -> ! { +pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { use fmt::Write; let mut s = String::new(); let _ = s.write_fmt(*msg); - begin_panic(s, file_line); + begin_panic(s, file_line_col); } -/// 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(msg: M, file_line: &(&'static str, u32)) -> ! { +// 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(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { let msg = Box::new(msg); - let (file, line) = *file_line; + let (file, line, col) = *file_line_col; - use libctru::consoleInit; - use libctru::gfxScreen_t; + // 3DS-specific code begins here + use libctru::{consoleInit, 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()) }; + unsafe { + // set up a new console, overwriting whatever was on the top screen + // before we started panicking + let _console = consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut()); - println!("PANIC in {} at line {}:", file, line); - println!(" {}", msg); + println!("thread '{}' panicked at '{}', {}:{}:{}", + "", msg, file, line, col); - // Terminate the process to ensure that all threads cease when panicking. - unsafe { ::libctru::svcExitProcess() } + // 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 { } + } +} - // 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 {} +#[cfg(not(feature = "citra"))] +#[unstable(feature = "libstd_sys_internals", + reason = "used by the panic! macro", + issue = "0")] +#[inline(never)] #[cold] +pub fn begin_panic(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { + let msg = Box::new(msg); + let (file, line, col) = *file_line_col; + + // 3DS-specific code begins here + use libctru::{errorInit, errorText, errorDisp, svcExitProcess, + errorConf, errorType, CFG_Language}; + use libc; + + unsafe { + // Setup error payload + let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}", + "", msg, file, line, col); + let mut error_conf: errorConf = mem::uninitialized(); + errorInit(&mut error_conf, + errorType::ERROR_TEXT_WORD_WRAP, + CFG_Language::CFG_LANGUAGE_EN); + errorText(&mut error_conf, error_text.as_ptr() as *const libc::c_char); + + // 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 we just forcibly end the process + svcExitProcess() + } } /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. diff --git a/ctru-sys/src/bindings.rs b/ctru-sys/src/bindings.rs index 7acfa38..2afcb00 100644 --- a/ctru-sys/src/bindings.rs +++ b/ctru-sys/src/bindings.rs @@ -1844,7 +1844,7 @@ extern "C" { pub fn svcOpenProcess(process: *mut Handle, processId: u32) -> Result; } extern "C" { - pub fn svcExitProcess(); + pub fn svcExitProcess() -> !; } extern "C" { pub fn svcTerminateProcess(process: Handle) -> Result;