Skip to content

Commit 51e2247

Browse files
committed
Abbreviate some stdout/stderr output in compiletest.
This is intended to prevent the spurious OOM error from run-pass/rustc-rust-log.rs, by skipping the output in the middle when the size is over 416 KB, so that the log output will not be overwhelmed.
1 parent d517668 commit 51e2247

File tree

5 files changed

+302
-7
lines changed

5 files changed

+302
-7
lines changed

src/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/compiletest/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ getopts = "0.2"
1111
log = "0.3"
1212
rustc-serialize = "0.3"
1313
libc = "0.2"
14+
15+
[target.'cfg(windows)'.dependencies]
16+
miow = "0.2"
17+
winapi = "0.2"

src/tools/compiletest/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
#![crate_name = "compiletest"]
1212

1313
#![feature(test)]
14+
#![feature(slice_rotate)]
1415

1516
#![deny(warnings)]
1617

17-
#[cfg(any(target_os = "macos", target_os = "ios"))]
18+
#[cfg(unix)]
1819
extern crate libc;
1920
extern crate test;
2021
extern crate getopts;
@@ -47,6 +48,7 @@ pub mod runtest;
4748
pub mod common;
4849
pub mod errors;
4950
mod raise_fd_limit;
51+
mod read2;
5052

5153
fn main() {
5254
env_logger::init().unwrap();

src/tools/compiletest/src/read2.rs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
12+
// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.
13+
14+
pub use self::imp::read2;
15+
16+
#[cfg(not(any(unix, windows)))]
17+
mod imp {
18+
use std::io::{self, Read};
19+
use std::process::{ChildStdout, ChildStderr};
20+
21+
pub fn read2(out_pipe: ChildStdout,
22+
err_pipe: ChildStderr,
23+
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
24+
let mut buffer = Vec::new();
25+
out_pipe.read_to_end(&mut buffer)?;
26+
data(true, &mut buffer, true);
27+
buffer.clear();
28+
err_pipe.read_to_end(&mut buffer)?;
29+
data(false, &mut buffer, true);
30+
Ok(())
31+
}
32+
}
33+
34+
#[cfg(unix)]
35+
mod imp {
36+
use std::io::prelude::*;
37+
use std::io;
38+
use std::mem;
39+
use std::os::unix::prelude::*;
40+
use std::process::{ChildStdout, ChildStderr};
41+
use libc;
42+
43+
pub fn read2(mut out_pipe: ChildStdout,
44+
mut err_pipe: ChildStderr,
45+
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
46+
unsafe {
47+
libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
48+
libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
49+
}
50+
51+
let mut out_done = false;
52+
let mut err_done = false;
53+
let mut out = Vec::new();
54+
let mut err = Vec::new();
55+
56+
let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
57+
fds[0].fd = out_pipe.as_raw_fd();
58+
fds[0].events = libc::POLLIN;
59+
fds[1].fd = err_pipe.as_raw_fd();
60+
fds[1].events = libc::POLLIN;
61+
loop {
62+
// wait for either pipe to become readable using `select`
63+
let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) };
64+
if r == -1 {
65+
let err = io::Error::last_os_error();
66+
if err.kind() == io::ErrorKind::Interrupted {
67+
continue
68+
}
69+
return Err(err)
70+
}
71+
72+
// Read as much as we can from each pipe, ignoring EWOULDBLOCK or
73+
// EAGAIN. If we hit EOF, then this will happen because the underlying
74+
// reader will return Ok(0), in which case we'll see `Ok` ourselves. In
75+
// this case we flip the other fd back into blocking mode and read
76+
// whatever's leftover on that file descriptor.
77+
let handle = |res: io::Result<_>| {
78+
match res {
79+
Ok(_) => Ok(true),
80+
Err(e) => {
81+
if e.kind() == io::ErrorKind::WouldBlock {
82+
Ok(false)
83+
} else {
84+
Err(e)
85+
}
86+
}
87+
}
88+
};
89+
if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
90+
out_done = true;
91+
}
92+
data(true, &mut out, out_done);
93+
if !err_done && fds[1].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
94+
err_done = true;
95+
}
96+
data(false, &mut err, err_done);
97+
98+
if out_done && err_done {
99+
return Ok(())
100+
}
101+
}
102+
}
103+
}
104+
105+
#[cfg(windows)]
106+
mod imp {
107+
extern crate miow;
108+
extern crate winapi;
109+
110+
use std::io;
111+
use std::os::windows::prelude::*;
112+
use std::process::{ChildStdout, ChildStderr};
113+
use std::slice;
114+
115+
use self::miow::iocp::{CompletionPort, CompletionStatus};
116+
use self::miow::pipe::NamedPipe;
117+
use self::miow::Overlapped;
118+
use self::winapi::ERROR_BROKEN_PIPE;
119+
120+
struct Pipe<'a> {
121+
dst: &'a mut Vec<u8>,
122+
overlapped: Overlapped,
123+
pipe: NamedPipe,
124+
done: bool,
125+
}
126+
127+
pub fn read2(out_pipe: ChildStdout,
128+
err_pipe: ChildStderr,
129+
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
130+
let mut out = Vec::new();
131+
let mut err = Vec::new();
132+
133+
let port = CompletionPort::new(1)?;
134+
port.add_handle(0, &out_pipe)?;
135+
port.add_handle(1, &err_pipe)?;
136+
137+
unsafe {
138+
let mut out_pipe = Pipe::new(out_pipe, &mut out);
139+
let mut err_pipe = Pipe::new(err_pipe, &mut err);
140+
141+
out_pipe.read()?;
142+
err_pipe.read()?;
143+
144+
let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
145+
146+
while !out_pipe.done || !err_pipe.done {
147+
for status in port.get_many(&mut status, None)? {
148+
if status.token() == 0 {
149+
out_pipe.complete(status);
150+
data(true, out_pipe.dst, out_pipe.done);
151+
out_pipe.read()?;
152+
} else {
153+
err_pipe.complete(status);
154+
data(false, err_pipe.dst, err_pipe.done);
155+
err_pipe.read()?;
156+
}
157+
}
158+
}
159+
160+
Ok(())
161+
}
162+
}
163+
164+
impl<'a> Pipe<'a> {
165+
unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
166+
Pipe {
167+
dst: dst,
168+
pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
169+
overlapped: Overlapped::zero(),
170+
done: false,
171+
}
172+
}
173+
174+
unsafe fn read(&mut self) -> io::Result<()> {
175+
let dst = slice_to_end(self.dst);
176+
match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
177+
Ok(_) => Ok(()),
178+
Err(e) => {
179+
if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
180+
self.done = true;
181+
Ok(())
182+
} else {
183+
Err(e)
184+
}
185+
}
186+
}
187+
}
188+
189+
unsafe fn complete(&mut self, status: &CompletionStatus) {
190+
let prev = self.dst.len();
191+
self.dst.set_len(prev + status.bytes_transferred() as usize);
192+
if status.bytes_transferred() == 0 {
193+
self.done = true;
194+
}
195+
}
196+
}
197+
198+
unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
199+
if v.capacity() == 0 {
200+
v.reserve(16);
201+
}
202+
if v.capacity() == v.len() {
203+
v.reserve(1);
204+
}
205+
slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
206+
v.capacity() - v.len())
207+
}
208+
}

src/tools/compiletest/src/runtest.rs

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use std::fmt;
2929
use std::io::prelude::*;
3030
use std::io::{self, BufReader};
3131
use std::path::{Path, PathBuf};
32-
use std::process::{Command, Output, ExitStatus, Stdio};
32+
use std::process::{Command, Output, ExitStatus, Stdio, Child};
3333
use std::str;
3434

3535
use extract_gdb_version;
@@ -1344,12 +1344,14 @@ actual:\n\
13441344
if let Some(input) = input {
13451345
child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
13461346
}
1347-
let Output { status, stdout, stderr } = child.wait_with_output().unwrap();
1347+
1348+
let Output { status, stdout, stderr } = read2_abbreviated(child)
1349+
.expect("failed to read output");
13481350

13491351
let result = ProcRes {
13501352
status,
1351-
stdout: String::from_utf8(stdout).unwrap(),
1352-
stderr: String::from_utf8(stderr).unwrap(),
1353+
stdout: String::from_utf8_lossy(&stdout).into_owned(),
1354+
stderr: String::from_utf8_lossy(&stderr).into_owned(),
13531355
cmdline,
13541356
};
13551357

@@ -1634,7 +1636,9 @@ actual:\n\
16341636
cmd.arg("-a").arg("-u");
16351637
cmd.arg(filename);
16361638
cmd.arg("-nobanner");
1637-
let output = match cmd.output() {
1639+
cmd.stdout(Stdio::piped());
1640+
cmd.stderr(Stdio::piped());
1641+
let output = match cmd.spawn().and_then(read2_abbreviated) {
16381642
Ok(output) => output,
16391643
Err(_) => return,
16401644
};
@@ -2094,6 +2098,8 @@ actual:\n\
20942098

20952099
let mut cmd = Command::new(make);
20962100
cmd.current_dir(&self.testpaths.file)
2101+
.stdout(Stdio::piped())
2102+
.stderr(Stdio::piped())
20972103
.env("TARGET", &self.config.target)
20982104
.env("PYTHON", &self.config.docck_python)
20992105
.env("S", src_root)
@@ -2142,7 +2148,7 @@ actual:\n\
21422148
}
21432149
}
21442150

2145-
let output = cmd.output().expect("failed to spawn `make`");
2151+
let output = cmd.spawn().and_then(read2_abbreviated).expect("failed to spawn `make`");
21462152
if !output.status.success() {
21472153
let res = ProcRes {
21482154
status: output.status,
@@ -2534,3 +2540,76 @@ fn nocomment_mir_line(line: &str) -> &str {
25342540
line
25352541
}
25362542
}
2543+
2544+
fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
2545+
use std::mem::replace;
2546+
use read2::read2;
2547+
2548+
const HEAD_LEN: usize = 160 * 1024;
2549+
const TAIL_LEN: usize = 256 * 1024;
2550+
2551+
enum ProcOutput {
2552+
Full(Vec<u8>),
2553+
Abbreviated {
2554+
head: Vec<u8>,
2555+
skipped: usize,
2556+
tail: Box<[u8]>,
2557+
}
2558+
}
2559+
2560+
impl ProcOutput {
2561+
fn extend(&mut self, data: &[u8]) {
2562+
let new_self = match *self {
2563+
ProcOutput::Full(ref mut bytes) => {
2564+
bytes.extend_from_slice(data);
2565+
let new_len = bytes.len();
2566+
if new_len <= HEAD_LEN + TAIL_LEN {
2567+
return;
2568+
}
2569+
let tail = bytes.split_off(new_len - TAIL_LEN).into_boxed_slice();
2570+
let head = replace(bytes, Vec::new());
2571+
let skipped = new_len - HEAD_LEN - TAIL_LEN;
2572+
ProcOutput::Abbreviated { head, skipped, tail }
2573+
}
2574+
ProcOutput::Abbreviated { ref mut skipped, ref mut tail, .. } => {
2575+
*skipped += data.len();
2576+
if data.len() <= TAIL_LEN {
2577+
tail[..data.len()].copy_from_slice(data);
2578+
tail.rotate(data.len());
2579+
} else {
2580+
tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]);
2581+
}
2582+
return;
2583+
}
2584+
};
2585+
*self = new_self;
2586+
}
2587+
2588+
fn into_bytes(self) -> Vec<u8> {
2589+
match self {
2590+
ProcOutput::Full(bytes) => bytes,
2591+
ProcOutput::Abbreviated { mut head, skipped, tail } => {
2592+
write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
2593+
head.extend_from_slice(&tail);
2594+
head
2595+
}
2596+
}
2597+
}
2598+
}
2599+
2600+
let mut stdout = ProcOutput::Full(Vec::new());
2601+
let mut stderr = ProcOutput::Full(Vec::new());
2602+
2603+
drop(child.stdin.take());
2604+
read2(child.stdout.take().unwrap(), child.stderr.take().unwrap(), &mut |is_stdout, data, _| {
2605+
if is_stdout { &mut stdout } else { &mut stderr }.extend(data);
2606+
data.clear();
2607+
})?;
2608+
let status = child.wait()?;
2609+
2610+
Ok(Output {
2611+
status,
2612+
stdout: stdout.into_bytes(),
2613+
stderr: stderr.into_bytes(),
2614+
})
2615+
}

0 commit comments

Comments
 (0)