|
|
|
@ -17,18 +17,19 @@
@@ -17,18 +17,19 @@
|
|
|
|
|
//! * Executing a panic up to doing the actual implementation
|
|
|
|
|
//! * Shims around "try"
|
|
|
|
|
|
|
|
|
|
#![allow(unused_imports)] |
|
|
|
|
use core::panic::BoxMeUp; |
|
|
|
|
|
|
|
|
|
use io::prelude::*; |
|
|
|
|
|
|
|
|
|
use any::Any; |
|
|
|
|
use cell::RefCell; |
|
|
|
|
use core::panic::{PanicInfo, Location}; |
|
|
|
|
use fmt; |
|
|
|
|
use intrinsics; |
|
|
|
|
use mem; |
|
|
|
|
use ptr; |
|
|
|
|
use raw; |
|
|
|
|
use sys::stdio::Stderr; |
|
|
|
|
use sys::stdio::{Stderr, stderr_prints_nothing}; |
|
|
|
|
use sys_common::rwlock::RWLock; |
|
|
|
|
use sys_common::thread_info; |
|
|
|
|
use sys_common::util; |
|
|
|
@ -57,7 +58,7 @@ extern {
@@ -57,7 +58,7 @@ extern {
|
|
|
|
|
data_ptr: *mut usize, |
|
|
|
|
vtable_ptr: *mut usize) -> u32; |
|
|
|
|
#[unwind(allowed)] |
|
|
|
|
fn __rust_start_panic(data: usize, vtable: usize) -> u32; |
|
|
|
|
fn __rust_start_panic(payload: usize) -> u32; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)] |
|
|
|
@ -160,182 +161,6 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
@@ -160,182 +161,6 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 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 occurred: {:?}", 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 occurred: {:?}", 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 occurred in file '{}' at line {}", location.file(),
|
|
|
|
|
/// location.line());
|
|
|
|
|
/// } else {
|
|
|
|
|
/// println!("panic occurred 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 occurred in file '{}' at line {}", location.file(), location.line());
|
|
|
|
|
/// } else {
|
|
|
|
|
/// println!("panic occurred 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 occurred in file '{}'", location.file());
|
|
|
|
|
/// } else {
|
|
|
|
|
/// println!("panic occurred 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 occurred at line {}", location.line());
|
|
|
|
|
/// } else {
|
|
|
|
|
/// println!("panic occurred 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
|
|
|
|
|
/// use std::panic;
|
|
|
|
|
///
|
|
|
|
|
/// panic::set_hook(Box::new(|panic_info| {
|
|
|
|
|
/// if let Some(location) = panic_info.location() {
|
|
|
|
|
/// println!("panic occurred at column {}", location.column());
|
|
|
|
|
/// } else {
|
|
|
|
|
/// println!("panic occurred but can't get location information...");
|
|
|
|
|
/// }
|
|
|
|
|
/// }));
|
|
|
|
|
///
|
|
|
|
|
/// panic!("Normal panic");
|
|
|
|
|
/// ```
|
|
|
|
|
#[stable(feature = "panic_col", since = "1.25")] |
|
|
|
|
pub fn column(&self) -> u32 { |
|
|
|
|
self.col |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn default_hook(info: &PanicInfo) { |
|
|
|
|
#[cfg(feature = "backtrace")] |
|
|
|
|
use sys_common::backtrace; |
|
|
|
@ -353,18 +178,16 @@ fn default_hook(info: &PanicInfo) {
@@ -353,18 +178,16 @@ fn default_hook(info: &PanicInfo) {
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let file = info.location.file; |
|
|
|
|
let line = info.location.line; |
|
|
|
|
let col = info.location.col; |
|
|
|
|
let location = info.location().unwrap(); // The current implementation always returns Some
|
|
|
|
|
|
|
|
|
|
let msg = match info.payload.downcast_ref::<&'static str>() { |
|
|
|
|
let msg = match info.payload().downcast_ref::<&'static str>() { |
|
|
|
|
Some(s) => *s, |
|
|
|
|
None => match info.payload.downcast_ref::<String>() { |
|
|
|
|
None => match info.payload().downcast_ref::<String>() { |
|
|
|
|
Some(s) => &s[..], |
|
|
|
|
None => "Box<Any>", |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let mut err = Stderr::new().ok(); |
|
|
|
|
let thread = thread_info::current_thread(); |
|
|
|
|
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>"); |
|
|
|
|
|
|
|
|
@ -372,8 +195,8 @@ fn default_hook(info: &PanicInfo) {
@@ -372,8 +195,8 @@ fn default_hook(info: &PanicInfo) {
|
|
|
|
|
use libctru::{errorInit, errorText, errorDisp, errorConf, ERROR_TEXT_WORD_WRAP, |
|
|
|
|
CFG_LANGUAGE_EN, consoleDebugInit, debugDevice_SVC}; |
|
|
|
|
|
|
|
|
|
let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}", |
|
|
|
|
name, msg, file, line, col); |
|
|
|
|
let error_text = format!("thread '{}' panicked at '{}', {}", name, msg, location); |
|
|
|
|
|
|
|
|
|
unsafe { |
|
|
|
|
// Prepare error message for display
|
|
|
|
|
let mut error_conf: errorConf = mem::uninitialized(); |
|
|
|
@ -393,7 +216,6 @@ fn default_hook(info: &PanicInfo) {
@@ -393,7 +216,6 @@ fn default_hook(info: &PanicInfo) {
|
|
|
|
|
consoleDebugInit(debugDevice_SVC); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut err = Stderr::new().ok(); |
|
|
|
|
let write = |err: &mut ::io::Write| { |
|
|
|
|
let _ = write!(err, "{}", error_text); |
|
|
|
|
|
|
|
|
@ -413,20 +235,19 @@ fn default_hook(info: &PanicInfo) {
@@ -413,20 +235,19 @@ fn default_hook(info: &PanicInfo) {
|
|
|
|
|
|
|
|
|
|
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) } |
|
|
|
|
_ => {} |
|
|
|
|
(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) } |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
// TODO: If we ever implement backtrace functionality, determine the best way to
|
|
|
|
|
// incorporate it with the error applet
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(not(test))] |
|
|
|
|
#[doc(hidden)] |
|
|
|
|
#[unstable(feature = "update_panic_count", issue = "0")] |
|
|
|
@ -544,9 +365,38 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
@@ -544,9 +365,38 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
|
|
|
|
|
// 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) |
|
|
|
|
rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col); |
|
|
|
|
|
|
|
|
|
struct PanicPayload<'a> { |
|
|
|
|
inner: &'a fmt::Arguments<'a>, |
|
|
|
|
string: Option<String>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'a> PanicPayload<'a> { |
|
|
|
|
fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> { |
|
|
|
|
PanicPayload { inner, string: None } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn fill(&mut self) -> &mut String { |
|
|
|
|
let inner = self.inner; |
|
|
|
|
self.string.get_or_insert_with(|| { |
|
|
|
|
let mut s = String::new(); |
|
|
|
|
drop(s.write_fmt(*inner)); |
|
|
|
|
s |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe impl<'a> BoxMeUp for PanicPayload<'a> { |
|
|
|
|
fn box_me_up(&mut self) -> *mut (Any + Send) { |
|
|
|
|
let contents = mem::replace(self.fill(), String::new()); |
|
|
|
|
Box::into_raw(Box::new(contents)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get(&mut self) -> &(Any + Send) { |
|
|
|
|
self.fill() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// This is the entry point of panicking for panic!() and assert!().
|
|
|
|
@ -562,18 +412,43 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
@@ -562,18 +412,43 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
|
|
|
|
|
// 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) |
|
|
|
|
rust_panic_with_hook(&mut PanicPayload::new(msg), None, file_line_col); |
|
|
|
|
|
|
|
|
|
struct PanicPayload<A> { |
|
|
|
|
inner: Option<A>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<A: Send + 'static> PanicPayload<A> { |
|
|
|
|
fn new(inner: A) -> PanicPayload<A> { |
|
|
|
|
PanicPayload { inner: Some(inner) } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> { |
|
|
|
|
fn box_me_up(&mut self) -> *mut (Any + Send) { |
|
|
|
|
let data = match self.inner.take() { |
|
|
|
|
Some(a) => Box::new(a) as Box<Any + Send>, |
|
|
|
|
None => Box::new(()), |
|
|
|
|
}; |
|
|
|
|
Box::into_raw(data) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get(&mut self) -> &(Any + Send) { |
|
|
|
|
match self.inner { |
|
|
|
|
Some(ref a) => a, |
|
|
|
|
None => &(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Executes the primary logic for a panic, including checking for recursive
|
|
|
|
|
/// panics and panic hooks.
|
|
|
|
|
/// Central point for dispatching panics.
|
|
|
|
|
///
|
|
|
|
|
/// 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>, |
|
|
|
|
/// Executes the primary logic for a panic, including checking for recursive
|
|
|
|
|
/// panics, panic hooks, and finally dispatching to the panic runtime to either
|
|
|
|
|
/// abort or unwind.
|
|
|
|
|
fn rust_panic_with_hook(payload: &mut BoxMeUp, |
|
|
|
|
message: Option<&fmt::Arguments>, |
|
|
|
|
file_line_col: &(&'static str, u32, u32)) -> ! { |
|
|
|
|
let (file, line, col) = *file_line_col; |
|
|
|
|
|
|
|
|
@ -591,18 +466,24 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
@@ -591,18 +466,24 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsafe { |
|
|
|
|
let info = PanicInfo { |
|
|
|
|
payload: &*msg, |
|
|
|
|
location: Location { |
|
|
|
|
file, |
|
|
|
|
line, |
|
|
|
|
col, |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
let mut info = PanicInfo::internal_constructor( |
|
|
|
|
message, |
|
|
|
|
Location::internal_constructor(file, line, col), |
|
|
|
|
); |
|
|
|
|
HOOK_LOCK.read(); |
|
|
|
|
match HOOK { |
|
|
|
|
Hook::Default => default_hook(&info), |
|
|
|
|
Hook::Custom(ptr) => (*ptr)(&info), |
|
|
|
|
// Some platforms know that printing to stderr won't ever actually
|
|
|
|
|
// print anything, and if that's the case we can skip the default
|
|
|
|
|
// hook.
|
|
|
|
|
Hook::Default if stderr_prints_nothing() => {} |
|
|
|
|
Hook::Default => { |
|
|
|
|
info.set_payload(payload.get()); |
|
|
|
|
default_hook(&info); |
|
|
|
|
} |
|
|
|
|
Hook::Custom(ptr) => { |
|
|
|
|
info.set_payload(payload.get()); |
|
|
|
|
(*ptr)(&info); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
HOOK_LOCK.read_unlock(); |
|
|
|
|
} |
|
|
|
@ -617,22 +498,35 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
@@ -617,22 +498,35 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
|
|
|
|
|
unsafe { intrinsics::abort() } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rust_panic(msg) |
|
|
|
|
rust_panic(payload) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 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) |
|
|
|
|
|
|
|
|
|
struct RewrapBox(Box<Any + Send>); |
|
|
|
|
|
|
|
|
|
unsafe impl BoxMeUp for RewrapBox { |
|
|
|
|
fn box_me_up(&mut self) -> *mut (Any + Send) { |
|
|
|
|
Box::into_raw(mem::replace(&mut self.0, Box::new(()))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get(&mut self) -> &(Any + Send) { |
|
|
|
|
&*self.0 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rust_panic(&mut RewrapBox(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>) -> ! { |
|
|
|
|
pub fn rust_panic(mut msg: &mut BoxMeUp) -> ! { |
|
|
|
|
let code = unsafe { |
|
|
|
|
let obj = mem::transmute::<_, raw::TraitObject>(msg); |
|
|
|
|
__rust_start_panic(obj.data as usize, obj.vtable as usize) |
|
|
|
|
let obj = &mut msg as *mut &mut BoxMeUp; |
|
|
|
|
__rust_start_panic(obj as usize) |
|
|
|
|
}; |
|
|
|
|
rtabort!("failed to initiate panic, error {}", code) |
|
|
|
|
} |
|
|
|
|