toolkit/library/rust/shared/lib.rs
author Dorel Luca <dluca@mozilla.com>
Wed, 14 Nov 2018 19:00:29 +0200
changeset 446190 38516fcc409fdc8f82ae10fa96fe474e7b4ac251
parent 446167 a0f255b660ce2d49fc7a07abe7d546c51e12da02
child 446420 35f916cb6452aa55dd906c0c4f63b47e05dfa316
permissions -rw-r--r--
Backed out 4 changesets (bug 1496503) for Valgrind bustage. CLOSED TREE Backed out changeset 033a89b3e00d (bug 1496503) Backed out changeset a0f255b660ce (bug 1496503) Backed out changeset 963d8ac1cfee (bug 1496503) Backed out changeset 43e44f8439ec (bug 1496503)

// 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;
#[cfg(feature = "gecko_profiler")]
extern crate profiler_helper;
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;
#[cfg(feature = "spidermonkey_rust")]
extern crate jsrust_shared;

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();
}