test_runners_lib/
logs.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Helpers for capturing logs from Fuchsia processes.
6
7use fuchsia_async as fasync;
8use futures::{AsyncReadExt as _, AsyncWriteExt as _, FutureExt as _, future};
9use std::num::NonZeroUsize;
10use thiserror::Error;
11use zx::HandleBased as _;
12
13/// Buffer size for socket read calls to `LoggerStream::buffer_and_drain`.
14const SOCKET_BUFFER_SIZE: usize = 2048;
15
16/// Maximum length we will buffer for a single line. If a line is longer than this
17/// length it will be split up into multiple messages.
18const MAX_LINE_BUFFER_LENGTH: usize = 4096;
19
20/// Error returned by this library.
21#[derive(Debug, PartialEq, Eq, Error, Clone)]
22pub enum LoggerError {
23    #[error("cannot create socket: {:?}", _0)]
24    CreateSocket(zx::Status),
25
26    #[error("cannot duplicate socket: {:?}", _0)]
27    DuplicateSocket(zx::Status),
28
29    #[error("invalid socket: {:?}", _0)]
30    InvalidSocket(zx::Status),
31}
32
33/// Error returned from draining LoggerStream or writing to LogWriter.
34#[derive(Debug, Error)]
35pub enum LogError {
36    /// Error encountered when draining LoggerStream.
37    #[error("can't get logs: {:?}", _0)]
38    Read(std::io::Error),
39
40    /// Error encountered when writing to LogWriter.
41    #[error("can't write logs: {:?}", _0)]
42    Write(std::io::Error),
43}
44
45/// Creates a combined socket handle for stdout and stderr and hooks them to same socket.
46/// It also wraps the socket into stream and returns it back.
47pub fn create_std_combined_log_stream()
48-> Result<(LoggerStream, zx::NullableHandle, zx::NullableHandle), LoggerError> {
49    let (client, log) = zx::Socket::create_stream();
50
51    let stream = LoggerStream::new(client).map_err(LoggerError::InvalidSocket)?;
52    let clone =
53        log.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(LoggerError::DuplicateSocket)?;
54
55    Ok((stream, log.into_handle(), clone.into_handle()))
56}
57
58/// Creates a socket handle for stdout/stderr and hooks it to a file handle.
59/// It also wraps the socket into stream and returns it back.
60pub fn create_log_stream() -> Result<(LoggerStream, zx::NullableHandle), LoggerError> {
61    let (client, log) = zx::Socket::create_stream();
62
63    let stream = LoggerStream::new(client).map_err(LoggerError::InvalidSocket)?;
64
65    Ok((stream, log.into_handle()))
66}
67/// Collects logs in background and gives a way to collect those logs.
68pub struct LogStreamReader {
69    fut: future::RemoteHandle<Result<Vec<u8>, LogError>>,
70}
71
72impl LogStreamReader {
73    pub fn new(logger: LoggerStream) -> Self {
74        let (logger_handle, logger_fut) = logger.read_to_end().remote_handle();
75        fasync::Task::spawn(logger_handle).detach();
76        Self { fut: logger_fut }
77    }
78
79    /// Retrieve all logs.
80    pub async fn get_logs(self) -> Result<Vec<u8>, LogError> {
81        self.fut.await
82    }
83}
84
85/// A stream bound to a socket where a source stream is captured.
86/// For example, stdout and stderr streams can be redirected to the contained
87/// socket and captured.
88pub struct LoggerStream {
89    socket: fasync::Socket,
90}
91
92impl Unpin for LoggerStream {}
93
94impl LoggerStream {
95    /// Create a LoggerStream from the provided zx::Socket. The `socket` object
96    /// should be bound to its intended source stream (e.g. "stdout").
97    pub fn new(socket: zx::Socket) -> Result<LoggerStream, zx::Status> {
98        let l = LoggerStream { socket: fasync::Socket::from_socket(socket) };
99        Ok(l)
100    }
101
102    /// Reads all bytes from socket.
103    pub async fn read_to_end(mut self) -> Result<Vec<u8>, LogError> {
104        let mut buffer: Vec<u8> = Vec::new();
105        let _bytes_read = self.socket.read_to_end(&mut buffer).await.map_err(LogError::Read)?;
106        Ok(buffer)
107    }
108
109    /// Drain the `stream` and write all of its contents to `writer`. Bytes are
110    /// delimited by newline and each line will be passed to `writer.write` individually.
111    /// An optional `peek_fn` may be specified which is passed a reference to each line before
112    /// it is written.
113    pub async fn buffer_drain_and_peek(
114        mut self,
115        writer: &mut SocketLogWriter,
116        peek_fn: Option<impl Fn(&[u8])>,
117    ) -> Result<(), LogError> {
118        let mut line_buffer: Vec<u8> = Vec::with_capacity(MAX_LINE_BUFFER_LENGTH);
119        let mut socket_buffer: Vec<u8> = vec![0; SOCKET_BUFFER_SIZE];
120
121        while let Some(bytes_read) = NonZeroUsize::new(
122            self.socket.read(&mut socket_buffer[..]).await.map_err(LogError::Read)?,
123        ) {
124            let bytes_read = bytes_read.get();
125
126            let newline_iter = socket_buffer[..bytes_read]
127                .iter()
128                .enumerate()
129                .filter_map(|(i, &b)| if b == b'\n' { Some(i) } else { None });
130
131            let mut prev_offset = 0;
132            for idx in newline_iter {
133                let line = &socket_buffer[prev_offset..idx + 1];
134                if !line_buffer.is_empty() {
135                    writer.write(line_buffer.drain(..).as_slice()).await?;
136                }
137                if let Some(peek) = &peek_fn {
138                    peek(line);
139                }
140                writer.write(line).await?;
141                prev_offset = idx + 1;
142            }
143            if prev_offset != bytes_read {
144                line_buffer.extend_from_slice(&socket_buffer[prev_offset..bytes_read]);
145            }
146
147            if line_buffer.len() > MAX_LINE_BUFFER_LENGTH {
148                let bytes = &line_buffer[..MAX_LINE_BUFFER_LENGTH];
149                if let Some(peek) = &peek_fn {
150                    peek(bytes);
151                }
152                writer.write(bytes).await?;
153                line_buffer.drain(..MAX_LINE_BUFFER_LENGTH);
154            }
155        }
156
157        if !line_buffer.is_empty() {
158            let bytes = &line_buffer[..];
159            if let Some(peek) = &peek_fn {
160                peek(bytes);
161            }
162            writer.write(bytes).await?;
163        }
164
165        Ok(())
166    }
167
168    /// Convenience function for buffer_drain_and_peek without a peek function.
169    pub async fn buffer_and_drain(self, writer: &mut SocketLogWriter) -> Result<(), LogError> {
170        self.buffer_drain_and_peek(writer, None::<fn(&[u8])>).await
171    }
172
173    /// Take the underlying socket of this object.
174    pub fn take_socket(self) -> fasync::Socket {
175        self.socket
176    }
177}
178
179/// Utility struct to write to socket asynchrously.
180pub struct SocketLogWriter {
181    logger: fasync::Socket,
182}
183
184impl SocketLogWriter {
185    pub fn new(logger: fasync::Socket) -> Self {
186        Self { logger }
187    }
188
189    pub async fn write_str(&mut self, s: &str) -> Result<(), LogError> {
190        self.write(s.as_bytes()).await
191    }
192
193    pub async fn write(&mut self, bytes: &[u8]) -> Result<(), LogError> {
194        self.logger.write_all(bytes).await.map_err(LogError::Write)
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use anyhow::{Context as _, Error, format_err};
202    use assert_matches::assert_matches;
203    use futures::{TryStreamExt as _, try_join};
204    use rand::distr::{Alphanumeric, SampleString as _};
205    use rand::rng;
206    use std::sync::mpsc;
207    use test_case::test_case;
208
209    #[fuchsia_async::run_singlethreaded(test)]
210    async fn log_writer_reader_work() {
211        let (sock1, sock2) = zx::Socket::create_stream();
212        let mut log_writer = SocketLogWriter::new(fasync::Socket::from_socket(sock1));
213
214        let reader = LoggerStream::new(sock2).unwrap();
215        let reader = LogStreamReader::new(reader);
216
217        log_writer.write_str("this is string one.").await.unwrap();
218        log_writer.write_str("this is string two.").await.unwrap();
219        drop(log_writer);
220
221        let actual = reader.get_logs().await.unwrap();
222        let actual = std::str::from_utf8(&actual).unwrap();
223        assert_eq!(actual, "this is string one.this is string two.".to_owned());
224    }
225
226    #[test_case(String::from("Hello World!") ; "consumes_simple_msg")]
227    #[test_case(get_random_string(10000) ; "consumes_large_msg")]
228    #[fasync::run_singlethreaded(test)]
229    async fn logger_stream_read_to_end(msg: String) -> Result<(), Error> {
230        let (stream, tx) = create_logger_stream()?;
231
232        let () = take_and_write_to_socket(tx, &msg)?;
233        let result = stream.read_to_end().await.context("Failed to read from socket")?;
234        let actual = std::str::from_utf8(&result).context("Failed to parse bytes")?.to_owned();
235
236        assert_eq!(actual, msg);
237        Ok(())
238    }
239
240    #[fasync::run_singlethreaded(test)]
241    async fn logger_stream_read_to_end_consumes_concat_msgs() -> Result<(), Error> {
242        let (stream, tx) = create_logger_stream()?;
243        let msgs =
244            vec!["Hello World!".to_owned(), "Hola Mundo!".to_owned(), "你好,世界!".to_owned()];
245
246        for msg in msgs.iter() {
247            let () = write_to_socket(&tx, &msg)?;
248        }
249        std::mem::drop(tx);
250        let result = stream.read_to_end().await.context("Failed to read from socket")?;
251        let actual = std::str::from_utf8(&result).context("Failed to parse bytes")?.to_owned();
252
253        assert_eq!(actual, msgs.join(""));
254        Ok(())
255    }
256
257    #[fasync::run_singlethreaded(test)]
258    async fn buffer_and_drain_reads_each_line_as_a_new_message() -> Result<(), Error> {
259        let (stream, tx) = create_logger_stream()?;
260        let (mut logger, rx) = create_datagram_logger()?;
261        let msg = "Hello World\nHola Mundo!\n你好,世界!";
262
263        let (tx_peeks, rx_peeks) = mpsc::channel();
264
265        let () = take_and_write_to_socket(tx, msg)?;
266        let (actual, ()) = try_join!(read_all_messages(rx), async move {
267            stream
268                .buffer_drain_and_peek(
269                    &mut logger,
270                    Some(move |line: &[u8]| tx_peeks.send(line.len()).unwrap()),
271                )
272                .await
273                .context("Failed to drain stream")
274        },)?;
275
276        let expected = vec![
277            "Hello World\n".to_string(),
278            "Hola Mundo!\n".to_string(),
279            "你好,世界!".to_string(),
280        ];
281        assert_eq!(actual, expected);
282
283        let lengths = rx_peeks.iter().collect::<Vec<_>>();
284
285        assert_eq!(lengths, expected.iter().map(|v| v.len()).collect::<Vec<_>>());
286
287        Ok(())
288    }
289
290    #[fasync::run_singlethreaded(test)]
291    async fn buffer_and_drain_does_not_buffer_past_maximum_size() -> Result<(), Error> {
292        let msg = get_random_string(MAX_LINE_BUFFER_LENGTH + 10);
293        let (stream, tx) = create_logger_stream()?;
294        let (mut logger, rx) = create_datagram_logger()?;
295
296        let (tx_peeks, rx_peeks) = mpsc::channel();
297
298        let () = take_and_write_to_socket(tx, &msg)?;
299        let (actual, ()) = try_join!(read_all_messages(rx), async move {
300            stream
301                .buffer_drain_and_peek(
302                    &mut logger,
303                    Some(move |line: &[u8]| {
304                        tx_peeks.send(line.len()).unwrap();
305                    }),
306                )
307                .await
308                .context("Failed to drain stream")
309        },)?;
310
311        let lengths = rx_peeks.iter().collect::<Vec<_>>();
312
313        assert_eq!(actual.len(), 2);
314        assert_eq!(actual[0], msg[0..MAX_LINE_BUFFER_LENGTH]);
315        assert_eq!(actual[1], msg[MAX_LINE_BUFFER_LENGTH..]);
316
317        assert_eq!(lengths, vec![MAX_LINE_BUFFER_LENGTH, 10]);
318
319        Ok(())
320    }
321
322    #[fasync::run_singlethreaded(test)]
323    async fn buffer_and_drain_dumps_full_buffer_if_no_newline_seen() -> Result<(), Error> {
324        let (stream, tx) = create_logger_stream()?;
325        let (mut logger, rx) = create_datagram_logger()?;
326
327        let ((), ()) = try_join!(
328            async move {
329                let msg = get_random_string(SOCKET_BUFFER_SIZE);
330                // First write up to (SOCKET_BUFFER_SIZE - 1) so that we can
331                // assert that buffer isn't drained prematurely.
332                let () = write_to_socket(&tx, &msg[..SOCKET_BUFFER_SIZE - 1])?;
333
334                // Temporarily convert fasync::Socket back to zx::Socket so that
335                // we can use non-blocking `read` call.
336                let rx = rx.into_zx_socket();
337                let mut buffer = vec![0u8; SOCKET_BUFFER_SIZE];
338                let maybe_bytes_read = rx.read(&mut buffer);
339                assert_eq!(maybe_bytes_read, Err(zx::Status::SHOULD_WAIT));
340
341                // Write last byte
342                let () = write_to_socket(&tx, &msg[SOCKET_BUFFER_SIZE - 1..SOCKET_BUFFER_SIZE])?;
343
344                // Confirm we still didn't write, waiting for newline.
345                let maybe_bytes_read = rx.read(&mut buffer);
346                assert_eq!(maybe_bytes_read, Err(zx::Status::SHOULD_WAIT));
347
348                // Drop socket to unblock the read routine.
349                std::mem::drop(tx);
350
351                // Convert zx::Socket back to fasync::Socket.
352                let mut rx = fasync::Socket::from_socket(rx);
353                let bytes_read =
354                    rx.read(&mut buffer).await.context("Failed to read from socket")?;
355                let msg_written = std::str::from_utf8(&buffer).context("Failed to parse bytes")?;
356
357                assert_eq!(bytes_read, SOCKET_BUFFER_SIZE);
358                assert_eq!(msg_written, msg);
359
360                Ok(())
361            },
362            async move { stream.buffer_and_drain(&mut logger).await.context("Failed to drain stream") },
363        )?;
364
365        Ok(())
366    }
367
368    #[fasync::run_singlethreaded(test)]
369    async fn buffer_and_drain_return_error_if_stream_polls_err() -> Result<(), Error> {
370        let (_tx, rx) = zx::Socket::create_stream();
371        // If we don't have read rights the socket returns an error when polled.
372        let rx = rx.duplicate_handle(zx::Rights::BASIC).expect("duplicate");
373        let stream = LoggerStream::new(rx).context("Failed to create LoggerStream")?;
374        let (mut logger, _rx) = create_datagram_logger()?;
375
376        let result = stream.buffer_and_drain(&mut logger).await;
377
378        assert_matches!(result, Err(LogError::Read(_)));
379        Ok(())
380    }
381
382    async fn read_all_messages(socket: fasync::Socket) -> Result<Vec<String>, Error> {
383        let mut results = Vec::new();
384        let mut stream = socket.into_datagram_stream();
385        while let Some(bytes) = stream.try_next().await.context("Failed to read socket stream")? {
386            results.push(
387                std::str::from_utf8(&bytes).context("Failed to parse bytes into utf8")?.to_owned(),
388            );
389        }
390
391        Ok(results)
392    }
393
394    fn take_and_write_to_socket(socket: zx::Socket, message: &str) -> Result<(), Error> {
395        write_to_socket(&socket, &message)
396    }
397
398    fn write_to_socket(socket: &zx::Socket, message: &str) -> Result<(), Error> {
399        let bytes_written =
400            socket.write(message.as_bytes()).context("Failed to write to socket")?;
401        match bytes_written == message.len() {
402            true => Ok(()),
403            false => Err(format_err!(
404                "Bytes written to socket doesn't match len of message. Message len = {}. Bytes written = {}",
405                message.len(),
406                bytes_written
407            )),
408        }
409    }
410
411    fn create_datagram_logger() -> Result<(SocketLogWriter, fasync::Socket), Error> {
412        let (tx, rx) = zx::Socket::create_datagram();
413        let logger = SocketLogWriter::new(fasync::Socket::from_socket(tx));
414        let rx = fasync::Socket::from_socket(rx);
415        Ok((logger, rx))
416    }
417
418    fn create_logger_stream() -> Result<(LoggerStream, zx::Socket), Error> {
419        let (tx, rx) = zx::Socket::create_stream();
420        let stream = LoggerStream::new(rx).context("Failed to create LoggerStream")?;
421        Ok((stream, tx))
422    }
423
424    fn get_random_string(size: usize) -> String {
425        Alphanumeric.sample_string(&mut rng(), size)
426    }
427}