Auto merge of #23922 - angelortiz1007:redir, r=jdm

Redirect stdout/stderr on windows

Added function redirect_stdout_stderr() to support stdout & stderr to be redirected to OutputDebugStringA().

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `23734` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #23734 (GitHub issue number if applicable)

<!-- Either: -->
- [X] There are tests for these changes OR
Functionality was verified by adding logic in unsafe fn init().  By building and running ServoApp, If  redirect_stdout_stderr() failed, a warn!() message would be sent to the "output" window of VS2007 with the GetLastError() value.   If the function redirect_stdout_stderr() succeeded, the println!("Capi/lib.rs: init() function called redirect_stdout_stderr() successfully.\n") output would be seen in the "output" window of VS2007.

- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23922)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-08-09 23:35:11 -04:00 committed by GitHub
commit b3c0ed295f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 1 deletions

2
Cargo.lock generated
View file

@ -4612,8 +4612,10 @@ version = "0.0.1"
dependencies = [ dependencies = [
"cbindgen 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"simpleservo 0.0.1", "simpleservo 0.0.1",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

View file

@ -74,7 +74,7 @@ sig = "1.0"
x11 = "2.0.0" x11 = "2.0.0"
[target.'cfg(target_os = "windows")'.dependencies] [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] [target.'cfg(any(target_os = "macos", all(target_arch = "x86_64", target_os = "linux")))'.dependencies]
osmesa-src = {git = "https://github.com/servo/osmesa-src"} osmesa-src = {git = "https://github.com/servo/osmesa-src"}

View file

@ -17,6 +17,10 @@ simpleservo = { path = "../api" }
log = "0.4" log = "0.4"
env_logger = "0.6" 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] [build-dependencies]
cbindgen = "0.8" cbindgen = "0.8"

View file

@ -22,6 +22,130 @@ fn catch_any_panic<F: FnOnce() + UnwindSafe>(function: F) -> bool {
panic::catch_unwind(function).is_ok() 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::<minwinbase::SECURITY_ATTRIBUTES>() 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: F) fn call<F>(f: F)
where where
F: Fn(&mut ServoGlue) -> Result<(), &'static str>, F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
@ -117,6 +241,10 @@ unsafe fn init(
) { ) {
init_logger(); init_logger();
if let Err(reason) = redirect_stdout_stderr() {
warn!("Error redirecting stdout/stderr: {}", reason);
}
let args = if !opts.args.is_null() { let args = if !opts.args.is_null() {
let args = CStr::from_ptr(opts.args); let args = CStr::from_ptr(opts.args);
args.to_str() args.to_str()