diff options
-rw-r--r-- | ports/libsimpleservo/src/jniapi.rs | 98 |
1 files changed, 92 insertions, 6 deletions
diff --git a/ports/libsimpleservo/src/jniapi.rs b/ports/libsimpleservo/src/jniapi.rs index a41dbb61088..70a65652727 100644 --- a/ports/libsimpleservo/src/jniapi.rs +++ b/ports/libsimpleservo/src/jniapi.rs @@ -10,11 +10,13 @@ use gl_glue; use jni::{JNIEnv, JavaVM}; use jni::objects::{GlobalRef, JClass, JObject, JString, JValue}; use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE}; +use libc::{pipe, dup2, read}; use log::Level; use std; use std::borrow::Cow; -use std::os::raw::c_void; +use std::os::raw::{c_char, c_int, c_void}; use std::sync::{Arc, Mutex}; +use std::thread; struct HostCallbacks { callbacks: GlobalRef, @@ -61,11 +63,19 @@ pub fn Java_com_mozilla_servoview_JNIServo_init( // Note: Android debug logs are stripped from a release build. // debug!() will only show in a debug build. Use info!() if logs // should show up in adb logcat with a release build. - let mut filter = Filter::default() - .with_min_level(Level::Debug) - .with_allowed_module_path("simpleservo::api") - .with_allowed_module_path("simpleservo::jniapi") - .with_allowed_module_path("simpleservo::gl_glue::egl"); + let filters = [ + "simpleservo::api", + "simpleservo::jniapi", + "simpleservo::gl_glue::egl", + // Show JS errors by default. + "script::dom::bindings::error", + // Show GL errors by default. + "canvas::webgl_thread", + ]; + let mut filter = Filter::default().with_min_level(Level::Debug); + for &module in &filters { + filter = filter.with_allowed_module_path(module); + } let log_str = env.get_string(log_str).ok(); let log_str = log_str.as_ref().map_or(Cow::Borrowed(""), |s| s.to_string_lossy()); for module in log_str.split(',') { @@ -77,6 +87,7 @@ pub fn Java_com_mozilla_servoview_JNIServo_init( info!("init"); initialize_android_glue(&env, activity); + redirect_stdout_to_logcat(); let args = env.get_string(args) .expect("Couldn't get java string") @@ -434,3 +445,78 @@ fn initialize_android_glue(env: &JNIEnv, activity: JObject) { ANDROID_APP = Box::into_raw(Box::new(app)); } } + +extern { + pub fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int; +} + +fn redirect_stdout_to_logcat() { + // 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; + + let tag = b"simpleservo\0".as_ptr() as _; + + 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 { + return; /* TODO: report problem */ + } else { + result as usize + cursor + }; + + 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; + unsafe { + __android_log_write(3, tag, 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 cursor == 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. + unsafe { + __android_log_write(3, tag, buf.as_ptr()); + } + cursor = 0; + } else { + cursor = end; + } + } + }); +} |