forked from mirrors/gecko-dev
277 lines
8.2 KiB
Rust
277 lines
8.2 KiB
Rust
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
#![cfg_attr(feature = "oom_with_global_alloc",
|
|
feature(global_allocator, alloc, alloc_system, allocator_api))]
|
|
#![cfg_attr(feature = "oom_with_hook", feature(alloc_error_hook))]
|
|
|
|
#[cfg(feature="servo")]
|
|
extern crate geckoservo;
|
|
|
|
extern crate mp4parse_capi;
|
|
extern crate nsstring;
|
|
extern crate nserror;
|
|
extern crate xpcom;
|
|
extern crate netwerk_helper;
|
|
extern crate prefs_parser;
|
|
extern crate mozurl;
|
|
#[cfg(feature = "quantum_render")]
|
|
extern crate webrender_bindings;
|
|
#[cfg(feature = "cubeb_pulse_rust")]
|
|
extern crate cubeb_pulse;
|
|
extern crate encoding_c;
|
|
extern crate encoding_glue;
|
|
#[cfg(feature = "cubeb-remoting")]
|
|
extern crate audioipc_client;
|
|
#[cfg(feature = "cubeb-remoting")]
|
|
extern crate audioipc_server;
|
|
extern crate env_logger;
|
|
extern crate u2fhid;
|
|
extern crate log;
|
|
extern crate cosec;
|
|
extern crate rsdparsa_capi;
|
|
|
|
use std::boxed::Box;
|
|
use std::env;
|
|
use std::ffi::{CStr, CString};
|
|
use std::os::raw::c_char;
|
|
#[cfg(target_os = "android")]
|
|
use std::os::raw::c_int;
|
|
#[cfg(target_os = "android")]
|
|
use log::Level;
|
|
#[cfg(not(target_os = "android"))]
|
|
use log::Log;
|
|
use std::panic;
|
|
|
|
extern "C" {
|
|
fn gfx_critical_note(msg: *const c_char);
|
|
#[cfg(target_os = "android")]
|
|
fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
|
|
}
|
|
|
|
struct GeckoLogger {
|
|
logger: env_logger::Logger
|
|
}
|
|
|
|
impl GeckoLogger {
|
|
fn new() -> GeckoLogger {
|
|
let mut builder = env_logger::Builder::new();
|
|
let default_level = if cfg!(debug_assertions) { "warn" } else { "error" };
|
|
let logger = match env::var("RUST_LOG") {
|
|
Ok(v) => builder.parse(&v).build(),
|
|
_ => builder.parse(default_level).build(),
|
|
};
|
|
|
|
GeckoLogger {
|
|
logger
|
|
}
|
|
}
|
|
|
|
fn init() -> Result<(), log::SetLoggerError> {
|
|
let gecko_logger = Self::new();
|
|
|
|
log::set_max_level(gecko_logger.logger.filter());
|
|
log::set_boxed_logger(Box::new(gecko_logger))
|
|
}
|
|
|
|
fn should_log_to_gfx_critical_note(record: &log::Record) -> bool {
|
|
if record.level() == log::Level::Error &&
|
|
record.target().contains("webrender") {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn maybe_log_to_gfx_critical_note(&self, record: &log::Record) {
|
|
if Self::should_log_to_gfx_critical_note(record) {
|
|
let msg = CString::new(format!("{}", record.args())).unwrap();
|
|
unsafe {
|
|
gfx_critical_note(msg.as_ptr());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
fn log_out(&self, record: &log::Record) {
|
|
self.logger.log(record);
|
|
}
|
|
|
|
#[cfg(target_os = "android")]
|
|
fn log_out(&self, record: &log::Record) {
|
|
let msg = CString::new(format!("{}", record.args())).unwrap();
|
|
let tag = CString::new(record.module_path().unwrap()).unwrap();
|
|
let prio = match record.metadata().level() {
|
|
Level::Error => 6 /* ERROR */,
|
|
Level::Warn => 5 /* WARN */,
|
|
Level::Info => 4 /* INFO */,
|
|
Level::Debug => 3 /* DEBUG */,
|
|
Level::Trace => 2 /* VERBOSE */,
|
|
};
|
|
// Output log directly to android log, since env_logger can output log
|
|
// only to stderr or stdout.
|
|
unsafe {
|
|
__android_log_write(prio, tag.as_ptr(), msg.as_ptr());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl log::Log for GeckoLogger {
|
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
self.logger.enabled(metadata)
|
|
}
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
// Forward log to gfxCriticalNote, if the log should be in gfx crash log.
|
|
self.maybe_log_to_gfx_critical_note(record);
|
|
self.log_out(record);
|
|
}
|
|
|
|
fn flush(&self) { }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn GkRust_Init() {
|
|
// Initialize logging.
|
|
let _ = GeckoLogger::init();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn GkRust_Shutdown() {
|
|
}
|
|
|
|
/// Used to implement `nsIDebug2::RustPanic` for testing purposes.
|
|
#[no_mangle]
|
|
pub extern "C" fn intentional_panic(message: *const c_char) {
|
|
panic!("{}", unsafe { CStr::from_ptr(message) }.to_string_lossy());
|
|
}
|
|
|
|
/// Contains the panic message, if set.
|
|
static mut PANIC_REASON: Option<*const str> = None;
|
|
|
|
/// Configure a panic hook to capture panic messages for crash reports.
|
|
///
|
|
/// We don't store this in `gMozCrashReason` because:
|
|
/// a) Rust strings aren't null-terminated, so we'd have to allocate
|
|
/// memory to get a null-terminated string
|
|
/// b) The panic=abort handler is going to call `abort()` on non-Windows,
|
|
/// which is `mozalloc_abort` for us, which will use `MOZ_CRASH` and
|
|
/// overwrite `gMozCrashReason` with an unhelpful string.
|
|
#[no_mangle]
|
|
pub extern "C" fn install_rust_panic_hook() {
|
|
let default_hook = panic::take_hook();
|
|
panic::set_hook(Box::new(move |info| {
|
|
// Try to handle &str/String payloads, which should handle 99% of cases.
|
|
let payload = info.payload();
|
|
// We'll hold a raw *const str here, but it will be OK because
|
|
// Rust is going to abort the process before the payload could be
|
|
// deallocated.
|
|
if let Some(s) = payload.downcast_ref::<&str>() {
|
|
unsafe { PANIC_REASON = Some(*s as *const str); }
|
|
} else if let Some(s) = payload.downcast_ref::<String>() {
|
|
unsafe { PANIC_REASON = Some(s.as_str() as *const str); }
|
|
} else {
|
|
// Not the most helpful thing, but seems unlikely to happen
|
|
// in practice.
|
|
println!("Unhandled panic payload!");
|
|
}
|
|
// Fall through to the default hook so we still print the reason and
|
|
// backtrace to the console.
|
|
default_hook(info);
|
|
}));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn get_rust_panic_reason(reason: *mut *const c_char, length: *mut usize) -> bool {
|
|
unsafe {
|
|
if let Some(s) = PANIC_REASON {
|
|
*reason = s as *const c_char;
|
|
*length = (*s).len();
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wrap the rust system allocator to override the OOM handler, redirecting
|
|
// to Gecko's, which interacts with the crash reporter.
|
|
// This relies on unstable APIs that have not changed between 1.24 and 1.27.
|
|
// In 1.27, the API changed, so we'll need to adapt to those changes before
|
|
// we can ship with 1.27. As of writing, there might still be further changes
|
|
// to those APIs before 1.27 is released, so we wait for those.
|
|
#[cfg(feature = "oom_with_global_alloc")]
|
|
mod global_alloc {
|
|
extern crate alloc;
|
|
extern crate alloc_system;
|
|
|
|
use self::alloc::allocator::{Alloc, AllocErr, Layout};
|
|
use self::alloc_system::System;
|
|
|
|
pub struct GeckoHeap;
|
|
|
|
extern "C" {
|
|
fn GeckoHandleOOM(size: usize) -> !;
|
|
}
|
|
|
|
unsafe impl<'a> Alloc for &'a GeckoHeap {
|
|
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
|
System.alloc(layout)
|
|
}
|
|
|
|
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
|
System.dealloc(ptr, layout)
|
|
}
|
|
|
|
fn oom(&mut self, e: AllocErr) -> ! {
|
|
match e {
|
|
AllocErr::Exhausted { request } => unsafe { GeckoHandleOOM(request.size()) },
|
|
_ => System.oom(e),
|
|
}
|
|
}
|
|
|
|
unsafe fn realloc(
|
|
&mut self,
|
|
ptr: *mut u8,
|
|
layout: Layout,
|
|
new_layout: Layout,
|
|
) -> Result<*mut u8, AllocErr> {
|
|
System.realloc(ptr, layout, new_layout)
|
|
}
|
|
|
|
unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
|
System.alloc_zeroed(layout)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(feature = "oom_with_global_alloc")]
|
|
#[global_allocator]
|
|
static HEAP: global_alloc::GeckoHeap = global_alloc::GeckoHeap;
|
|
|
|
#[cfg(feature = "oom_with_hook")]
|
|
mod oom_hook {
|
|
use std::alloc::{Layout, set_alloc_error_hook};
|
|
|
|
extern "C" {
|
|
fn GeckoHandleOOM(size: usize) -> !;
|
|
}
|
|
|
|
pub fn hook(layout: Layout) {
|
|
unsafe {
|
|
GeckoHandleOOM(layout.size());
|
|
}
|
|
}
|
|
|
|
pub fn install() {
|
|
set_alloc_error_hook(hook);
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn install_rust_oom_hook() {
|
|
#[cfg(feature = "oom_with_hook")]
|
|
oom_hook::install();
|
|
}
|