From 3933b432fac75edcfc087c6f961f1d02e70cb8f6 Mon Sep 17 00:00:00 2001 From: Paul Rouget Date: Fri, 16 Aug 2019 11:08:02 +0200 Subject: [PATCH] Panic HoloLens app on rust panic --- Cargo.lock | 2 + ports/libsimpleservo/capi/Cargo.toml | 2 + ports/libsimpleservo/capi/src/lib.rs | 170 ++++++++++-------- .../hololens/ServoApp/ServoControl/Servo.cpp | 20 ++- 4 files changed, 118 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a62b52321f8d..16faf0b82a53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4635,8 +4635,10 @@ dependencies = [ name = "simpleservo_capi" version = "0.0.1" dependencies = [ + "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", "cbindgen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "simpleservo 0.0.1", diff --git a/ports/libsimpleservo/capi/Cargo.toml b/ports/libsimpleservo/capi/Cargo.toml index 1d974d90702b..4cceabd8f266 100644 --- a/ports/libsimpleservo/capi/Cargo.toml +++ b/ports/libsimpleservo/capi/Cargo.toml @@ -16,6 +16,8 @@ bench = false simpleservo = { path = "../api" } log = "0.4" env_logger = "0.6" +lazy_static = "1" +backtrace = "0.3" [target.'cfg(target_os = "windows")'.dependencies] libc = "0.2" diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index e4c701a6cf85..206e70ac4b0e 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -2,12 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#[macro_use] +extern crate lazy_static; + #[macro_use] extern crate log; #[cfg(target_os = "windows")] mod vslogger; +use backtrace::Backtrace; #[cfg(not(target_os = "windows"))] use env_logger; use simpleservo::{self, gl_glue, ServoGlue, SERVO}; @@ -15,11 +19,39 @@ use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOpt use std::ffi::{CStr, CString}; use std::mem; use std::os::raw::{c_char, c_void}; -use std::panic::{self, AssertUnwindSafe, UnwindSafe}; +use std::panic::{self, UnwindSafe}; +use std::sync::RwLock; + +extern "C" fn default_panic_handler(msg: *const c_char) { + let c_str: &CStr = unsafe { CStr::from_ptr(msg) }; + error!("{}", c_str.to_str().unwrap()); +} + +lazy_static! { + static ref ON_PANIC: RwLock = RwLock::new(default_panic_handler); +} + +#[no_mangle] +pub extern "C" fn register_panic_handler(on_panic: extern "C" fn(*const c_char)) { + *ON_PANIC.write().unwrap() = on_panic; +} /// Catch any panic function used by extern "C" functions. -fn catch_any_panic(function: F) -> bool { - panic::catch_unwind(function).is_ok() +fn catch_any_panic T + UnwindSafe>(function: F) -> T { + match panic::catch_unwind(function) { + Err(_) => { + let thread = std::thread::current() + .name() + .map(|n| format!(" for thread \"{}\"", n)) + .unwrap_or("".to_owned()); + let message = format!("Stack trace{}\n{:?}", thread, Backtrace::new()); + let error = CString::new(message).expect("Can't create string"); + (ON_PANIC.read().unwrap())(error.as_ptr()); + // At that point the embedder is supposed to have panicked + panic!("Uncaught Rust panic"); + }, + Ok(r) => r, + } } #[cfg(not(target_os = "windows"))] @@ -150,17 +182,12 @@ fn call(f: F) where F: Fn(&mut ServoGlue) -> Result<(), &'static str>, { - SERVO.with(|s| { - if let Err(error) = match s.borrow_mut().as_mut() { - Some(ref mut s) => (f)(s), - None => Err("Servo not available in this thread"), - } { - // FIXME: All C calls should have a have generic Result-like - // return type. For now, we just panic instead of notifying - // the embedder. - panic!(error); - } - }); + if let Err(e) = SERVO.with(|s| match s.borrow_mut().as_mut() { + Some(ref mut s) => (f)(s), + None => Err("Servo not available in this thread"), + }) { + panic!(e); + }; } /// Callback used by Servo internals @@ -197,18 +224,13 @@ pub struct CInitOptions { /// The returned string is not freed. This will leak. #[no_mangle] pub extern "C" fn servo_version() -> *const c_char { - let result = panic::catch_unwind(AssertUnwindSafe(|| { + catch_any_panic(|| { let v = simpleservo::servo_version(); let text = CString::new(v).expect("Can't create string"); let ptr: *const c_char = text.as_ptr(); mem::forget(text); ptr - })); - - match result { - Ok(ptr) => ptr, - Err(_) => std::ptr::null(), - } + }) } #[cfg(target_os = "windows")] @@ -289,7 +311,7 @@ pub extern "C" fn init_with_egl( opts: CInitOptions, wakeup: extern "C" fn(), callbacks: CHostCallbacks, -) -> bool { +) { catch_any_panic(|| { init_logger(); let gl = gl_glue::egl::init().unwrap(); @@ -303,7 +325,7 @@ pub extern "C" fn init_with_egl( callbacks, ) } - }) + }); } #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] @@ -312,101 +334,109 @@ pub extern "C" fn init_with_gl( opts: CInitOptions, wakeup: extern "C" fn(), callbacks: CHostCallbacks, -) -> bool { +) { catch_any_panic(|| { init_logger(); let gl = gl_glue::gl::init().unwrap(); unsafe { init(opts, gl, None, None, wakeup, callbacks) } - }) + }); } #[no_mangle] pub extern "C" fn deinit() { - debug!("deinit"); - simpleservo::deinit(); + catch_any_panic(|| { + debug!("deinit"); + simpleservo::deinit(); + }); } #[no_mangle] pub extern "C" fn request_shutdown() { - debug!("request_shutdown"); - call(|s| s.request_shutdown()); + catch_any_panic(|| { + debug!("request_shutdown"); + call(|s| s.request_shutdown()); + }); } #[no_mangle] pub extern "C" fn set_batch_mode(batch: bool) { - debug!("set_batch_mode"); - call(|s| s.set_batch_mode(batch)); + catch_any_panic(|| { + debug!("set_batch_mode"); + call(|s| s.set_batch_mode(batch)); + }); } #[no_mangle] pub extern "C" fn resize(width: i32, height: i32) { - debug!("resize {}/{}", width, height); - call(|s| { - let coordinates = Coordinates::new(0, 0, width, height, width, height); - s.resize(coordinates) + catch_any_panic(|| { + debug!("resize {}/{}", width, height); + call(|s| { + let coordinates = Coordinates::new(0, 0, width, height, width, height); + s.resize(coordinates) + }); }); } #[no_mangle] -pub extern "C" fn perform_updates() -> bool { +pub extern "C" fn perform_updates() { catch_any_panic(|| { debug!("perform_updates"); call(|s| s.perform_updates()); - }) + }); } #[no_mangle] -pub extern "C" fn load_uri(url: *const c_char) -> bool { +pub extern "C" fn load_uri(url: *const c_char) { catch_any_panic(|| { debug!("load_url"); let url = unsafe { CStr::from_ptr(url) }; let url = url.to_str().expect("Can't read string"); call(|s| s.load_uri(url)); - }) + }); } #[no_mangle] -pub extern "C" fn reload() -> bool { +pub extern "C" fn reload() { catch_any_panic(|| { debug!("reload"); call(|s| s.reload()); - }) + }); } #[no_mangle] -pub extern "C" fn stop() -> bool { +pub extern "C" fn stop() { catch_any_panic(|| { debug!("stop"); call(|s| s.stop()); - }) + }); } #[no_mangle] -pub extern "C" fn refresh() -> bool { +pub extern "C" fn refresh() { catch_any_panic(|| { debug!("refresh"); call(|s| s.refresh()); - }) + }); } #[no_mangle] -pub extern "C" fn go_back() -> bool { +pub extern "C" fn go_back() { catch_any_panic(|| { debug!("go_back"); call(|s| s.go_back()); - }) + }); } #[no_mangle] -pub extern "C" fn go_forward() -> bool { +pub extern "C" fn go_forward() { catch_any_panic(|| { debug!("go_forward"); call(|s| s.go_forward()); - }) + }); } #[no_mangle] -pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) -> bool { +pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) { catch_any_panic(|| { debug!("scroll_start"); call(|s| s.scroll_start(dx as f32, dy as f32, x, y)); @@ -414,83 +444,83 @@ pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) -> bool { } #[no_mangle] -pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) -> bool { +pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) { catch_any_panic(|| { debug!("scroll_end"); call(|s| s.scroll_end(dx as f32, dy as f32, x, y)); - }) + }); } #[no_mangle] -pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) -> bool { +pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) { catch_any_panic(|| { debug!("scroll"); call(|s| s.scroll(dx as f32, dy as f32, x, y)); - }) + }); } #[no_mangle] -pub extern "C" fn touch_down(x: f32, y: f32, pointer_id: i32) -> bool { +pub extern "C" fn touch_down(x: f32, y: f32, pointer_id: i32) { catch_any_panic(|| { debug!("touch down"); call(|s| s.touch_down(x, y, pointer_id)); - }) + }); } #[no_mangle] -pub extern "C" fn touch_up(x: f32, y: f32, pointer_id: i32) -> bool { +pub extern "C" fn touch_up(x: f32, y: f32, pointer_id: i32) { catch_any_panic(|| { debug!("touch up"); call(|s| s.touch_up(x, y, pointer_id)); - }) + }); } #[no_mangle] -pub extern "C" fn touch_move(x: f32, y: f32, pointer_id: i32) -> bool { +pub extern "C" fn touch_move(x: f32, y: f32, pointer_id: i32) { catch_any_panic(|| { debug!("touch move"); call(|s| s.touch_move(x, y, pointer_id)); - }) + }); } #[no_mangle] -pub extern "C" fn touch_cancel(x: f32, y: f32, pointer_id: i32) -> bool { +pub extern "C" fn touch_cancel(x: f32, y: f32, pointer_id: i32) { catch_any_panic(|| { debug!("touch cancel"); call(|s| s.touch_cancel(x, y, pointer_id)); - }) + }); } #[no_mangle] -pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) -> bool { +pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) { catch_any_panic(|| { debug!("pinchzoom_start"); call(|s| s.pinchzoom_start(factor, x as u32, y as u32)); - }) + }); } #[no_mangle] -pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) -> bool { +pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) { catch_any_panic(|| { debug!("pinchzoom"); call(|s| s.pinchzoom(factor, x as u32, y as u32)); - }) + }); } #[no_mangle] -pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) -> bool { +pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) { catch_any_panic(|| { debug!("pinchzoom_end"); call(|s| s.pinchzoom_end(factor, x as u32, y as u32)); - }) + }); } #[no_mangle] -pub extern "C" fn click(x: i32, y: i32) -> bool { +pub extern "C" fn click(x: i32, y: i32) { catch_any_panic(|| { debug!("click"); call(|s| s.click(x as f32, y as f32)); - }) + }); } pub struct WakeupCallback(extern "C" fn()); diff --git a/support/hololens/ServoApp/ServoControl/Servo.cpp b/support/hololens/ServoApp/ServoControl/Servo.cpp index fbc6d7d9c24c..84077811c964 100644 --- a/support/hololens/ServoApp/ServoControl/Servo.cpp +++ b/support/hololens/ServoApp/ServoControl/Servo.cpp @@ -22,12 +22,16 @@ void flush() { sServo->Delegate().Flush(); } void make_current() { sServo->Delegate().MakeCurrent(); } void wakeup() { sServo->Delegate().WakeUp(); } bool on_allow_navigation(const char *url) { - return sServo->Delegate().OnServoAllowNavigation(char2hstring(url)); + return sServo->Delegate().OnServoAllowNavigation(char2hstring(url)); }; void on_animating_changed(bool aAnimating) { sServo->Delegate().OnServoAnimatingChanged(aAnimating); } +void on_panic(const char *backtrace) { + throw hresult_error(E_FAIL, char2hstring(backtrace)); +} + Servo::Servo(GLsizei width, GLsizei height, ServoDelegate &aDelegate) : mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) { @@ -55,7 +59,9 @@ Servo::Servo(GLsizei width, GLsizei height, ServoDelegate &aDelegate) c.on_shutdown_complete = &on_shutdown_complete; c.on_allow_navigation = &on_allow_navigation; - init_with_egl(o, &wakeup, c); + capi::register_panic_handler(&on_panic); + + capi::init_with_egl(o, &wakeup, c); } Servo::~Servo() { sServo = nullptr; } @@ -63,11 +69,13 @@ Servo::~Servo() { sServo = nullptr; } winrt::hstring char2hstring(const char *c_str) { // FIXME: any better way of doing this? auto str = std::string(c_str); - int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + int size_needed = + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring str2(size_needed, 0); - MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0], size_needed); - winrt::hstring str3 {str2}; + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0], + size_needed); + winrt::hstring str3{str2}; return str3; } -} // namespace servo +} // namespace winrt::servo