ohos/android: Redirect stdout/stderr to log sink (#32858)

* ohos: redirect stdout/stderr to logging sink

Based on the existing android `redirect_stdout_to_logcat` implementation,
but using the safe abstractions from `nix` and dumping to the `log` sink,
instead of directly writing the log.

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* android: Use new shared implementation for logcat redirection.

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* servoshell: Register cfg(production)

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* Update ports/servoshell/egl/log.rs

Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>

* Change info! to debug! to match original behavior on android

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

---------

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Jonathan Schwender 2024-08-15 17:26:03 +08:00 committed by GitHub
parent 353ceb0ffb
commit 97c84b6127
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 127 additions and 93 deletions

View file

@ -93,6 +93,8 @@ pub extern "C" fn Java_org_servo_servoview_JNIServo_init<'local>(
"servo",
"servoshell",
"servoshell::egl:gl_glue",
// Show redirected stdout / stderr by default
"servoshell::egl::log",
// Show JS errors by default.
"script::dom::bindings::error",
// Show GL errors by default.
@ -120,7 +122,12 @@ pub extern "C" fn Java_org_servo_servoview_JNIServo_init<'local>(
info!("init");
redirect_stdout_to_logcat();
// We only redirect stdout and stderr for non-production builds, since it is
// only used for debugging purposes. This saves us one thread in production.
#[cfg(not(production))]
if let Err(e) = super::log::redirect_stdout_and_stderr() {
error!("Failed to redirect stdout and stderr to logcat due to: {e:?}");
}
let callbacks_ref = match env.new_global_ref(callbacks_obj) {
Ok(r) => r,
@ -692,91 +699,6 @@ extern "C" {
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 = c"servoshell".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 {
unsafe {
__android_log_write(
3,
tag,
c"error in log thread; closing".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;
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 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.
unsafe {
__android_log_write(3, tag, buf.as_ptr());
}
cursor = 0;
} else {
cursor = end;
}
}
});
}
fn throw(env: &mut JNIEnv, err: &str) {
if let Err(e) = env.throw(("java/lang/Exception", err)) {
warn!(