fxt/
thread.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::{trace_header, ParseError, ParseResult, THREAD_RECORD_TYPE};
6use nom::combinator::all_consuming;
7use nom::number::complete::le_u64;
8use nom::Parser;
9use std::num::NonZeroU8;
10
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
12pub struct ProcessKoid(pub u64);
13
14impl From<u64> for ProcessKoid {
15    fn from(n: u64) -> Self {
16        Self(n)
17    }
18}
19
20impl PartialEq<u64> for ProcessKoid {
21    fn eq(&self, other: &u64) -> bool {
22        self.0.eq(other)
23    }
24}
25
26impl std::fmt::Display for ProcessKoid {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(f, "{}", self.0)
29    }
30}
31
32#[derive(Clone, Debug, PartialEq)]
33pub enum ProcessRef {
34    Index(NonZeroU8),
35    Inline(ProcessKoid),
36}
37
38impl ProcessRef {
39    pub(crate) fn parse<'a>(thread_ref: u8, buf: &'a [u8]) -> ParseResult<'a, Self> {
40        Ok(if let Some(index) = NonZeroU8::new(thread_ref) {
41            (buf, Self::Index(index))
42        } else {
43            let (buf, koid) = le_u64(buf)?;
44            (buf, Self::Inline(ProcessKoid(koid)))
45        })
46    }
47}
48
49#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
50pub struct ThreadKoid(pub u64);
51
52impl From<u64> for ThreadKoid {
53    fn from(n: u64) -> Self {
54        Self(n)
55    }
56}
57
58impl PartialEq<u64> for ThreadKoid {
59    fn eq(&self, other: &u64) -> bool {
60        self.0.eq(other)
61    }
62}
63
64impl std::fmt::Display for ThreadKoid {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(f, "{}", self.0)
67    }
68}
69
70#[derive(Clone, Debug, PartialEq)]
71pub enum ThreadRef {
72    Index(NonZeroU8),
73    Inline(ThreadKoid),
74}
75
76impl ThreadRef {
77    pub(crate) fn parse<'a>(thread_ref: u8, buf: &'a [u8]) -> ParseResult<'a, Self> {
78        Ok(if let Some(index) = NonZeroU8::new(thread_ref) {
79            (buf, Self::Index(index))
80        } else {
81            let (buf, koid) = le_u64(buf)?;
82            (buf, Self::Inline(ThreadKoid(koid)))
83        })
84    }
85}
86
87#[derive(Debug, PartialEq)]
88pub(crate) struct ThreadRecord {
89    pub index: NonZeroU8,
90    pub process_koid: ProcessKoid,
91    pub thread_koid: ThreadKoid,
92}
93
94impl ThreadRecord {
95    pub(super) fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
96        let (buf, header) = ThreadHeader::parse(buf)?;
97        let (rem, payload) = header.take_payload(buf)?;
98        let (payload, process_koid) = nom::combinator::map(le_u64, ProcessKoid).parse(payload)?;
99        let (empty, thread_koid) =
100            all_consuming(nom::combinator::map(le_u64, ThreadKoid)).parse(payload)?;
101        assert!(empty.is_empty(), "all_consuming must not return any remaining buffer");
102        let index =
103            NonZeroU8::new(header.thread_index()).ok_or(nom::Err::Error(ParseError::InvalidRef))?;
104        Ok((rem, Self { index, process_koid, thread_koid }))
105    }
106}
107
108trace_header! {
109    ThreadHeader (THREAD_RECORD_TYPE) {
110        u8, thread_index: 16, 23;
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::RawTraceRecord;
118
119    #[test]
120    fn process_ref_index() {
121        let (trailing, parsed) = ProcessRef::parse(11, &[1, 1, 1, 1]).unwrap();
122        assert_eq!(parsed, ProcessRef::Index(NonZeroU8::new(11).unwrap()));
123        assert_eq!(trailing, [1, 1, 1, 1],);
124    }
125
126    #[test]
127    fn process_ref_inline() {
128        let mut buf = 52u64.to_le_bytes().to_vec(); // value
129        buf.extend([1, 1, 1, 1]); // trailing
130
131        let (trailing, parsed) = ProcessRef::parse(0, &buf).unwrap();
132        assert_eq!(parsed, ProcessRef::Inline(ProcessKoid(52)));
133        assert_eq!(trailing, [1, 1, 1, 1]);
134    }
135
136    #[test]
137    fn thread_ref_index() {
138        let (trailing, parsed) = ThreadRef::parse(14, &[1, 1, 1, 1]).unwrap();
139        assert_eq!(parsed, ThreadRef::Index(NonZeroU8::new(14).unwrap()));
140        assert_eq!(trailing, [1, 1, 1, 1]);
141    }
142
143    #[test]
144    fn thread_ref_inline() {
145        let mut buf = 54u64.to_le_bytes().to_vec(); // value
146        buf.extend([1, 1, 1, 1]); // trailing
147
148        let (trailing, parsed) = ThreadRef::parse(0, &buf).unwrap();
149        assert_eq!(parsed, ThreadRef::Inline(ThreadKoid(54)));
150        assert_eq!(trailing, [1, 1, 1, 1]);
151    }
152
153    #[test]
154    fn thread_record() {
155        let mut header = ThreadHeader::empty();
156        header.set_thread_index(10);
157        header.set_size_words(3); // header, process koid, thread koid
158
159        let mut buf = header.0.to_le_bytes().to_vec(); // header
160        buf.extend(52u64.to_le_bytes()); // process
161        buf.extend(54u64.to_le_bytes()); // thread
162        buf.extend([1, 1, 1, 1]); // trailing
163
164        let (trailing, parsed) = RawTraceRecord::parse(&buf).unwrap();
165        assert_eq!(
166            parsed.parsed,
167            RawTraceRecord::Thread(ThreadRecord {
168                index: NonZeroU8::new(10).unwrap(),
169                process_koid: ProcessKoid(52),
170                thread_koid: ThreadKoid(54)
171            })
172        );
173        assert_eq!(trailing, [1, 1, 1, 1]);
174    }
175}