diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-03-20 16:40:17 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-05-01 18:57:44 -0400 |
commit | d0e9acf1eb81c2b535a01c42b7541d2b33a0acb2 (patch) | |
tree | 32778fc43e85f18c78df44a0e708cb293e9b5ee5 | |
parent | daabda7fe14cb79ddc1b06019bec77f953eb9cf8 (diff) | |
download | servo-d0e9acf1eb81c2b535a01c42b7541d2b33a0acb2.tar.gz servo-d0e9acf1eb81c2b535a01c42b7541d2b33a0acb2.zip |
Redirect stdout to ML logging.
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | ports/libmlservo/Cargo.toml | 1 | ||||
-rw-r--r-- | ports/libmlservo/src/lib.rs | 80 |
3 files changed, 82 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock index 334d517ae5f..aeb8120e189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/ports/libmlservo/Cargo.toml b/ports/libmlservo/Cargo.toml index b0db3605df2..2534dbe4a9d 100644 --- a/ports/libmlservo/Cargo.toml +++ b/ports/libmlservo/Cargo.toml @@ -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" diff --git a/ports/libmlservo/src/lib.rs b/ports/libmlservo/src/lib.rs index 3af71da4b99..5f27e3f2b58 100644 --- a/ports/libmlservo/src/lib.rs +++ b/ports/libmlservo/src/lib.rs @@ -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; + } + } + }); +} |