From 2bd2281853408b113a9af699c260d68cdfe8e6c7 Mon Sep 17 00:00:00 2001 From: angelortiz1007 Date: Fri, 26 Jul 2019 14:32:37 -0500 Subject: [PATCH] Added support for stdout/stderr (ref issue# 23734) to be redirected to OutputDebugStringA(). --- Cargo.lock | 2 + ports/glutin/Cargo.toml | 2 +- ports/libsimpleservo/capi/Cargo.toml | 4 + ports/libsimpleservo/capi/src/lib.rs | 128 +++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 11f2a610db5..8f91e121511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4530,8 +4530,10 @@ version = "0.0.1" dependencies = [ "cbindgen 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "simpleservo 0.0.1", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index 77717076cde..591d6d93c56 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -74,7 +74,7 @@ sig = "1.0" x11 = "2.0.0" [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["wingdi", "winuser"] } +winapi = { version = "0.3", features = ["wingdi", "winuser", "winnt", "winbase", "processenv", "namedpipeapi", "ntdef", "minwindef", "handleapi", "debugapi"] } [target.'cfg(any(target_os = "macos", all(target_arch = "x86_64", target_os = "linux")))'.dependencies] osmesa-src = {git = "https://github.com/servo/osmesa-src"} diff --git a/ports/libsimpleservo/capi/Cargo.toml b/ports/libsimpleservo/capi/Cargo.toml index 7d508495022..15698ba4a24 100644 --- a/ports/libsimpleservo/capi/Cargo.toml +++ b/ports/libsimpleservo/capi/Cargo.toml @@ -17,6 +17,10 @@ simpleservo = { path = "../api" } log = "0.4" env_logger = "0.6" +[target.'cfg(target_os = "windows")'.dependencies] +libc = "0.2" +winapi = {version = "0.3", features = ["wingdi", "winuser", "winnt", "winbase", "processenv", "namedpipeapi", "ntdef", "minwindef", "handleapi", "debugapi"]} + [build-dependencies] cbindgen = "0.8" diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index 9b14c8bc394..e4c701a6cf8 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -22,6 +22,130 @@ fn catch_any_panic(function: F) -> bool { panic::catch_unwind(function).is_ok() } +#[cfg(not(target_os = "windows"))] +fn redirect_stdout_stderr() -> Result<(), String> { + Ok(()) +} + +#[cfg(target_os = "windows")] +fn redirect_stdout_stderr() -> Result<(), String> { + do_redirect_stdout_stderr().map_err(|()| { + format!("GetLastError() = {}", unsafe { + winapi::um::errhandlingapi::GetLastError() + }) + }) +} + +#[cfg(target_os = "windows")] +// Function to redirect STDOUT (1) and STDERR(2) to Windows API +// OutputDebugString(). +// Return Value: Result<(), String> +// Ok() - stdout and stderr redirects. +// Err(str) - The Err value can contain the string value of GetLastError. +fn do_redirect_stdout_stderr() -> Result<(), ()> { + use std::thread; + use winapi::shared; + use winapi::um::debugapi; + use winapi::um::handleapi; + use winapi::um::minwinbase; + use winapi::um::namedpipeapi; + use winapi::um::processenv; + use winapi::um::winbase; + use winapi::um::winnt; + + let mut h_read_pipe: winnt::HANDLE = handleapi::INVALID_HANDLE_VALUE; + let mut h_write_pipe: winnt::HANDLE = handleapi::INVALID_HANDLE_VALUE; + let mut secattr: minwinbase::SECURITY_ATTRIBUTES = unsafe { mem::zeroed() }; + const BUF_LENGTH: usize = 1024; + + secattr.nLength = mem::size_of::() as u32; + secattr.bInheritHandle = shared::minwindef::TRUE; + secattr.lpSecurityDescriptor = shared::ntdef::NULL; + + unsafe { + if namedpipeapi::CreatePipe( + &mut h_read_pipe, + &mut h_write_pipe, + &mut secattr, + BUF_LENGTH as u32, + ) == 0 + { + return Err(()); + } + + if processenv::SetStdHandle(winbase::STD_OUTPUT_HANDLE, h_write_pipe) == 0 || + processenv::SetStdHandle(winbase::STD_ERROR_HANDLE, h_write_pipe) == 0 + { + return Err(()); + } + + if handleapi::SetHandleInformation( + h_read_pipe, + winbase::HANDLE_FLAG_INHERIT, + winbase::HANDLE_FLAG_INHERIT, + ) == 0 || + handleapi::SetHandleInformation( + h_write_pipe, + winbase::HANDLE_FLAG_INHERIT, + winbase::HANDLE_FLAG_INHERIT, + ) == 0 + { + return Err(()); + } + + let h_read_pipe_fd = libc::open_osfhandle(h_read_pipe as libc::intptr_t, libc::O_RDONLY); + let h_write_pipe_fd = libc::open_osfhandle(h_write_pipe as libc::intptr_t, libc::O_WRONLY); + + if h_read_pipe_fd == -1 || h_write_pipe_fd == -1 { + return Err(()); + } + + // 0 indicates success. + if libc::dup2(h_write_pipe_fd, 1) != 0 || libc::dup2(h_write_pipe_fd, 2) != 0 { + return Err(()); + } + + // If SetStdHandle(winbase::STD_OUTPUT_HANDLE, hWritePipe) is not called prior, + // this will fail. GetStdHandle() is used to make certain "servo" has the stdout + // file descriptor associated. + let h_stdout = processenv::GetStdHandle(winbase::STD_OUTPUT_HANDLE); + if h_stdout == handleapi::INVALID_HANDLE_VALUE || h_stdout == shared::ntdef::NULL { + return Err(()); + } + + // If SetStdHandle(winbase::STD_ERROR_HANDLE, hWritePipe) is not called prior, + // this will fail. GetStdHandle() is used to make certain "servo" has the stderr + // file descriptor associated. + let h_stderr = processenv::GetStdHandle(winbase::STD_ERROR_HANDLE); + if h_stderr == handleapi::INVALID_HANDLE_VALUE || h_stderr == shared::ntdef::NULL { + return Err(()); + } + + // Spawn a thread. The thread will redirect all STDOUT and STDERR messages + // to OutputDebugString() + let _handler = thread::spawn(move || { + loop { + let mut read_buf: [i8; BUF_LENGTH] = [0; BUF_LENGTH]; + + let result = libc::read( + h_read_pipe_fd, + read_buf.as_mut_ptr() as *mut _, + read_buf.len() as u32 - 1, + ); + + if result == -1 { + break; + } + + // Write to Debug port. + debugapi::OutputDebugStringA(read_buf.as_mut_ptr() as winnt::LPSTR); + } + }); + } + + Ok(()) +} + fn call(f: F) where F: Fn(&mut ServoGlue) -> Result<(), &'static str>, @@ -117,6 +241,10 @@ unsafe fn init( ) { init_logger(); + if let Err(reason) = redirect_stdout_stderr() { + warn!("Error redirecting stdout/stderr: {}", reason); + } + let args = if !opts.args.is_null() { let args = CStr::from_ptr(opts.args); args.to_str()