1use 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}