Redirect stdout to ML logging.

This commit is contained in:
Josh Matthews 2019-03-20 16:40:17 -04:00
parent daabda7fe1
commit d0e9acf1eb
3 changed files with 82 additions and 0 deletions

1
Cargo.lock generated
View file

@ -2437,6 +2437,7 @@ dependencies = [
name = "libmlservo"
version = "0.0.1"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"libservo 0.0.1",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -15,6 +15,7 @@ bench = false
[dependencies]
libservo = { path = "../../components/servo", features = ["no_static_freetype"] }
simpleservo = { path = "../libsimpleservo/api", features = ["no_static_freetype"] }
libc = "0.2"
log = "0.4"
servo-egl = "0.2"
smallvec = "0.6"

View file

@ -7,6 +7,7 @@ use egl::egl::EGLDisplay;
use egl::egl::EGLSurface;
use egl::egl::MakeCurrent;
use egl::egl::SwapBuffers;
use libc::{dup2, pipe, read};
use log::info;
use log::warn;
use servo::euclid::TypedScale;
@ -25,6 +26,7 @@ use std::ffi::CStr;
use std::ffi::CString;
use std::io::Write;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::rc::Rc;
use std::thread;
@ -67,6 +69,7 @@ pub enum MLKeyType {
}
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct MLLogger(extern "C" fn(MLLogLevel, *const c_char));
#[repr(transparent)]
@ -109,6 +112,7 @@ pub unsafe extern "C" fn init_servo(
height: u32,
hidpi: f32,
) -> *mut ServoInstance {
redirect_stdout_to_log(logger);
let _ = log::set_boxed_logger(Box::new(logger));
log::set_max_level(LOG_LEVEL);
@ -382,3 +386,79 @@ impl log::Log for MLLogger {
fn flush(&self) {}
}
fn redirect_stdout_to_log(logger: MLLogger) {
// The first step is to redirect stdout and stderr to the logs.
// We redirect stdout and stderr to a custom descriptor.
let mut pfd: [c_int; 2] = [0, 0];
unsafe {
pipe(pfd.as_mut_ptr());
dup2(pfd[1], 1);
dup2(pfd[1], 2);
}
let descriptor = pfd[0];
// Then we spawn a thread whose only job is to read from the other side of the
// pipe and redirect to the logs.
let _detached = thread::spawn(move || {
const BUF_LENGTH: usize = 512;
let mut buf = vec![b'\0' as c_char; BUF_LENGTH];
// Always keep at least one null terminator
const BUF_AVAILABLE: usize = BUF_LENGTH - 1;
let buf = &mut buf[..BUF_AVAILABLE];
let mut cursor = 0_usize;
loop {
let result = {
let read_into = &mut buf[cursor..];
unsafe {
read(
descriptor,
read_into.as_mut_ptr() as *mut _,
read_into.len(),
)
}
};
let end = if result == 0 {
return;
} else if result < 0 {
(logger.0)(
MLLogLevel::Error,
b"error in log thread; closing\0".as_ptr() as *const _,
);
return;
} else {
result as usize + cursor
};
// Only modify the portion of the buffer that contains real data.
let buf = &mut buf[0..end];
if let Some(last_newline_pos) = buf.iter().rposition(|&c| c == b'\n' as c_char) {
buf[last_newline_pos] = b'\0' as c_char;
(logger.0)(MLLogLevel::Info, buf.as_ptr());
if last_newline_pos < buf.len() - 1 {
let pos_after_newline = last_newline_pos + 1;
let len_not_logged_yet = buf[pos_after_newline..].len();
for j in 0..len_not_logged_yet as usize {
buf[j] = buf[pos_after_newline + j];
}
cursor = len_not_logged_yet;
} else {
cursor = 0;
}
} else if end == BUF_AVAILABLE {
// No newline found but the buffer is full, flush it anyway.
// `buf.as_ptr()` is null-terminated by BUF_LENGTH being 1 less than BUF_AVAILABLE.
(logger.0)(MLLogLevel::Info, buf.as_ptr());
cursor = 0;
} else {
cursor = end;
}
}
});
}