]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/read2.rs
ignore known paths when deciding whether to abbreviate the output
[rust.git] / src / tools / compiletest / src / read2.rs
1 // FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
2 // Consider unify the read2() in libstd, cargo and this to prevent further code duplication.
3
4 pub use self::imp::read2;
5 use std::io;
6 use std::process::{Child, Output};
7
8 pub fn read2_abbreviated(mut child: Child, exclude_from_len: &[String]) -> io::Result<Output> {
9     use io::Write;
10     use std::mem::replace;
11
12     const HEAD_LEN: usize = 160 * 1024;
13     const TAIL_LEN: usize = 256 * 1024;
14
15     enum ProcOutput {
16         Full { bytes: Vec<u8>, excluded_len: usize },
17         Abbreviated { head: Vec<u8>, skipped: usize, tail: Box<[u8]> },
18     }
19
20     impl ProcOutput {
21         fn extend(&mut self, data: &[u8], exclude_from_len: &[String]) {
22             let new_self = match *self {
23                 ProcOutput::Full { ref mut bytes, ref mut excluded_len } => {
24                     bytes.extend_from_slice(data);
25
26                     // We had problems in the past with tests failing only in some environments,
27                     // due to the length of the base path pushing the output size over the limit.
28                     //
29                     // To make those failures deterministic across all environments we ignore known
30                     // paths when calculating the string length, while still including the full
31                     // path in the output. This could result in some output being larger than the
32                     // threshold, but it's better than having nondeterministic failures.
33                     for pattern in exclude_from_len {
34                         let pattern_bytes = pattern.as_bytes();
35                         let matches = data
36                             .windows(pattern_bytes.len())
37                             .filter(|window| window == &pattern_bytes)
38                             .count();
39                         *excluded_len += matches * pattern_bytes.len();
40                     }
41
42                     let new_len = bytes.len();
43                     if new_len.saturating_sub(*excluded_len) <= HEAD_LEN + TAIL_LEN {
44                         return;
45                     }
46
47                     let mut head = replace(bytes, Vec::new());
48                     let tail = head.split_off(new_len - TAIL_LEN).into_boxed_slice();
49                     let skipped = new_len - HEAD_LEN - TAIL_LEN;
50                     ProcOutput::Abbreviated { head, skipped, tail }
51                 }
52                 ProcOutput::Abbreviated { ref mut skipped, ref mut tail, .. } => {
53                     *skipped += data.len();
54                     if data.len() <= TAIL_LEN {
55                         tail[..data.len()].copy_from_slice(data);
56                         tail.rotate_left(data.len());
57                     } else {
58                         tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]);
59                     }
60                     return;
61                 }
62             };
63             *self = new_self;
64         }
65
66         fn into_bytes(self) -> Vec<u8> {
67             match self {
68                 ProcOutput::Full { bytes, .. } => bytes,
69                 ProcOutput::Abbreviated { mut head, skipped, tail } => {
70                     write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
71                     head.extend_from_slice(&tail);
72                     head
73                 }
74             }
75         }
76     }
77
78     let mut stdout = ProcOutput::Full { bytes: Vec::new(), excluded_len: 0 };
79     let mut stderr = ProcOutput::Full { bytes: Vec::new(), excluded_len: 0 };
80
81     drop(child.stdin.take());
82     read2(
83         child.stdout.take().unwrap(),
84         child.stderr.take().unwrap(),
85         &mut |is_stdout, data, _| {
86             if is_stdout { &mut stdout } else { &mut stderr }.extend(data, exclude_from_len);
87             data.clear();
88         },
89     )?;
90     let status = child.wait()?;
91
92     Ok(Output { status, stdout: stdout.into_bytes(), stderr: stderr.into_bytes() })
93 }
94
95 #[cfg(not(any(unix, windows)))]
96 mod imp {
97     use std::io::{self, Read};
98     use std::process::{ChildStderr, ChildStdout};
99
100     pub fn read2(
101         out_pipe: ChildStdout,
102         err_pipe: ChildStderr,
103         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
104     ) -> io::Result<()> {
105         let mut buffer = Vec::new();
106         out_pipe.read_to_end(&mut buffer)?;
107         data(true, &mut buffer, true);
108         buffer.clear();
109         err_pipe.read_to_end(&mut buffer)?;
110         data(false, &mut buffer, true);
111         Ok(())
112     }
113 }
114
115 #[cfg(unix)]
116 mod imp {
117     use std::io;
118     use std::io::prelude::*;
119     use std::mem;
120     use std::os::unix::prelude::*;
121     use std::process::{ChildStderr, ChildStdout};
122
123     pub fn read2(
124         mut out_pipe: ChildStdout,
125         mut err_pipe: ChildStderr,
126         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
127     ) -> io::Result<()> {
128         unsafe {
129             libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
130             libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
131         }
132
133         let mut out_done = false;
134         let mut err_done = false;
135         let mut out = Vec::new();
136         let mut err = Vec::new();
137
138         let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
139         fds[0].fd = out_pipe.as_raw_fd();
140         fds[0].events = libc::POLLIN;
141         fds[1].fd = err_pipe.as_raw_fd();
142         fds[1].events = libc::POLLIN;
143         let mut nfds = 2;
144         let mut errfd = 1;
145
146         while nfds > 0 {
147             // wait for either pipe to become readable using `select`
148             let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
149             if r == -1 {
150                 let err = io::Error::last_os_error();
151                 if err.kind() == io::ErrorKind::Interrupted {
152                     continue;
153                 }
154                 return Err(err);
155             }
156
157             // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
158             // EAGAIN. If we hit EOF, then this will happen because the underlying
159             // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
160             // this case we flip the other fd back into blocking mode and read
161             // whatever's leftover on that file descriptor.
162             let handle = |res: io::Result<_>| match res {
163                 Ok(_) => Ok(true),
164                 Err(e) => {
165                     if e.kind() == io::ErrorKind::WouldBlock {
166                         Ok(false)
167                     } else {
168                         Err(e)
169                     }
170                 }
171             };
172             if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
173                 err_done = true;
174                 nfds -= 1;
175             }
176             data(false, &mut err, err_done);
177             if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
178                 out_done = true;
179                 fds[0].fd = err_pipe.as_raw_fd();
180                 errfd = 0;
181                 nfds -= 1;
182             }
183             data(true, &mut out, out_done);
184         }
185         Ok(())
186     }
187 }
188
189 #[cfg(windows)]
190 mod imp {
191     use std::io;
192     use std::os::windows::prelude::*;
193     use std::process::{ChildStderr, ChildStdout};
194     use std::slice;
195
196     use miow::iocp::{CompletionPort, CompletionStatus};
197     use miow::pipe::NamedPipe;
198     use miow::Overlapped;
199     use winapi::shared::winerror::ERROR_BROKEN_PIPE;
200
201     struct Pipe<'a> {
202         dst: &'a mut Vec<u8>,
203         overlapped: Overlapped,
204         pipe: NamedPipe,
205         done: bool,
206     }
207
208     pub fn read2(
209         out_pipe: ChildStdout,
210         err_pipe: ChildStderr,
211         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
212     ) -> io::Result<()> {
213         let mut out = Vec::new();
214         let mut err = Vec::new();
215
216         let port = CompletionPort::new(1)?;
217         port.add_handle(0, &out_pipe)?;
218         port.add_handle(1, &err_pipe)?;
219
220         unsafe {
221             let mut out_pipe = Pipe::new(out_pipe, &mut out);
222             let mut err_pipe = Pipe::new(err_pipe, &mut err);
223
224             out_pipe.read()?;
225             err_pipe.read()?;
226
227             let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
228
229             while !out_pipe.done || !err_pipe.done {
230                 for status in port.get_many(&mut status, None)? {
231                     if status.token() == 0 {
232                         out_pipe.complete(status);
233                         data(true, out_pipe.dst, out_pipe.done);
234                         out_pipe.read()?;
235                     } else {
236                         err_pipe.complete(status);
237                         data(false, err_pipe.dst, err_pipe.done);
238                         err_pipe.read()?;
239                     }
240                 }
241             }
242
243             Ok(())
244         }
245     }
246
247     impl<'a> Pipe<'a> {
248         unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
249             Pipe {
250                 dst: dst,
251                 pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
252                 overlapped: Overlapped::zero(),
253                 done: false,
254             }
255         }
256
257         unsafe fn read(&mut self) -> io::Result<()> {
258             let dst = slice_to_end(self.dst);
259             match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
260                 Ok(_) => Ok(()),
261                 Err(e) => {
262                     if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
263                         self.done = true;
264                         Ok(())
265                     } else {
266                         Err(e)
267                     }
268                 }
269             }
270         }
271
272         unsafe fn complete(&mut self, status: &CompletionStatus) {
273             let prev = self.dst.len();
274             self.dst.set_len(prev + status.bytes_transferred() as usize);
275             if status.bytes_transferred() == 0 {
276                 self.done = true;
277             }
278         }
279     }
280
281     unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
282         if v.capacity() == 0 {
283             v.reserve(16);
284         }
285         if v.capacity() == v.len() {
286             v.reserve(1);
287         }
288         slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), v.capacity() - v.len())
289     }
290 }