fxt/
log.rs

1// Copyright 2023 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
5use crate::init::Ticks;
6use crate::session::ResolveCtx;
7use crate::string::parse_padded_string;
8use crate::thread::{ProcessKoid, ProcessRef, ThreadKoid, ThreadRef};
9use crate::{trace_header, ParseResult, Provider, LOG_RECORD_TYPE};
10use flyweights::FlyStr;
11use nom::combinator::all_consuming;
12use nom::Parser;
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct LogRecord {
16    pub provider: Option<Provider>,
17    pub timestamp: i64,
18    pub process: ProcessKoid,
19    pub thread: ThreadKoid,
20    pub message: FlyStr,
21}
22
23impl LogRecord {
24    pub(super) fn resolve(ctx: &mut ResolveCtx, raw: RawLogRecord<'_>) -> Self {
25        Self {
26            provider: ctx.current_provider(),
27            timestamp: ctx.resolve_ticks(raw.ticks),
28            process: ctx.resolve_process(raw.process),
29            thread: ctx.resolve_thread(raw.thread),
30            message: raw.message.into(),
31        }
32    }
33}
34
35#[derive(Debug, PartialEq)]
36pub(super) struct RawLogRecord<'a> {
37    ticks: Ticks,
38    process: ProcessRef,
39    thread: ThreadRef,
40    message: &'a str,
41}
42
43impl<'a> RawLogRecord<'a> {
44    pub(super) fn parse(buf: &'a [u8]) -> ParseResult<'a, Self> {
45        let (buf, header) = LogHeader::parse(buf)?;
46        let (rem, payload) = header.take_payload(buf)?;
47        let (payload, ticks) = Ticks::parse(payload)?;
48        let (payload, process) = ProcessRef::parse(header.thread_ref(), payload)?;
49        let (payload, thread) = ThreadRef::parse(header.thread_ref(), payload)?;
50        let (empty, message) =
51            all_consuming(|p| parse_padded_string(header.message_len() as usize, p))
52                .parse(payload)?;
53        assert!(empty.is_empty(), "all_consuming must not return any remaining buffer");
54        Ok((rem, Self { ticks, process, thread, message }))
55    }
56}
57
58trace_header! {
59    LogHeader (LOG_RECORD_TYPE) {
60        u16, message_len: 16, 30;
61        u8, thread_ref: 32, 39;
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::fxt_builder::FxtBuilder;
69    use crate::RawTraceRecord;
70    use std::num::NonZeroU8;
71
72    #[test]
73    fn log_record_index_thread() {
74        let message = "hello, world!";
75        let mut header = LogHeader::empty();
76        header.set_message_len(message.len() as u16);
77        header.set_thread_ref(16);
78
79        assert_parses_to_record!(
80            FxtBuilder::new(header).atom(1024u64.to_le_bytes()).atom(message).build(),
81            RawTraceRecord::Log(RawLogRecord {
82                ticks: Ticks(1024),
83                process: ProcessRef::Index(NonZeroU8::new(16).unwrap()),
84                thread: ThreadRef::Index(NonZeroU8::new(16).unwrap()),
85                message,
86            }),
87        );
88    }
89
90    #[test]
91    fn log_record_inline_thread() {
92        let message = "hello, world!";
93        let mut header = LogHeader::empty();
94        header.set_message_len(message.len() as u16);
95
96        assert_parses_to_record!(
97            FxtBuilder::new(header)
98                .atom(1024u64.to_le_bytes())
99                .atom(24u64.to_le_bytes())
100                .atom(26u64.to_le_bytes())
101                .atom(message)
102                .build(),
103            RawTraceRecord::Log(RawLogRecord {
104                ticks: Ticks(1024),
105                process: ProcessRef::Inline(ProcessKoid(24)),
106                thread: ThreadRef::Inline(ThreadKoid(26)),
107                message,
108            }),
109        );
110    }
111}