Skip to main content

diagnostics_log_encoding/
encode.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//! Encoding diagnostic records using the Fuchsia Tracing format.
6
7use crate::{
8    ArgType, Argument, Header, MAX_SIZE_WORDS, Metatag, RawSeverity, Record, Value, constants,
9};
10use std::array::TryFromSliceError;
11use std::borrow::{Borrow, Cow};
12use std::fmt::Debug;
13use std::io::Cursor;
14use std::ops::Deref;
15use thiserror::Error;
16use zerocopy::{FromBytes, IntoBytes};
17
18#[cfg(fuchsia_api_level_less_than = "27")]
19use fidl_fuchsia_diagnostics::Severity;
20#[cfg(fuchsia_api_level_at_least = "27")]
21use fidl_fuchsia_diagnostics_types::Severity;
22
23/// An `Encoder` wraps any value implementing `MutableBuffer` and writes diagnostic stream records
24/// into it.
25pub struct Encoder<B> {
26    pub(crate) buf: B,
27    /// Encoder options
28    options: EncoderOpts,
29}
30
31/// Options for the encoder
32#[derive(Default)]
33pub struct EncoderOpts {
34    /// Whether or not to always log the line/file information
35    /// Defaults to false. If false, the line/file information
36    /// will only be logged for ERROR and above.
37    pub always_log_file_line: bool,
38}
39
40/// Parameters for `Encoder/write_event`.
41pub struct WriteEventParams<'a, E, T, MS> {
42    /// The event to write as a record.
43    pub event: E,
44    /// Tags associated with the log event.
45    pub tags: &'a [T],
46    /// Metatags associated with the log event.
47    pub metatags: MS,
48    /// The process that emitted the log.
49    pub pid: zx::Koid,
50    /// The thread that emitted the log.
51    pub tid: zx::Koid,
52    /// Number of events that were dropped before this one.
53    pub dropped: u64,
54}
55
56impl<B> Encoder<B>
57where
58    B: MutableBuffer,
59{
60    /// Create a new `Encoder` from the provided buffer.
61    pub fn new(buf: B, options: EncoderOpts) -> Self {
62        Self { buf, options }
63    }
64
65    /// Returns a reference to the underlying buffer being used for encoding.
66    pub fn inner(&self) -> &B {
67        &self.buf
68    }
69
70    /// Returns a reference to the underlying buffer being used for encoding.
71    pub fn take(self) -> B {
72        self.buf
73    }
74
75    /// Writes an event to to the buffer as a record.
76    ///
77    /// Fails if there is insufficient space in the buffer for encoding.
78    pub fn write_event<'a, E, MS, T>(
79        &mut self,
80        params: WriteEventParams<'a, E, T, MS>,
81    ) -> Result<(), EncodingError>
82    where
83        E: RecordEvent,
84        MS: Iterator<Item = &'a Metatag>,
85        T: AsRef<str>,
86    {
87        let WriteEventParams { event, tags, metatags, pid, tid, dropped } = params;
88        let severity = event.raw_severity();
89        self.write_inner(event.timestamp(), severity, |this| {
90            this.write_raw_argument(constants::PID, pid.raw_koid())?;
91            this.write_raw_argument(constants::TID, tid.raw_koid())?;
92            if dropped > 0 {
93                this.write_raw_argument(constants::NUM_DROPPED, dropped)?;
94            }
95            if this.options.always_log_file_line || severity >= Severity::Error.into_primitive() {
96                // If the severity is ERROR or higher, we add the file and line information.
97                if let Some(mut file) = event.file() {
98                    let split = file.split("../");
99                    file = split.last().unwrap();
100                    this.write_raw_argument(constants::FILE, Value::Text(Cow::Borrowed(file)))?;
101                }
102
103                if let Some(line) = event.line() {
104                    this.write_raw_argument(constants::LINE, line as u64)?;
105                }
106            }
107
108            // Write the metatags as tags (if any were given)
109            for metatag in metatags {
110                match metatag {
111                    Metatag::Target => this.write_raw_argument(constants::TAG, event.target())?,
112                }
113            }
114
115            event.write_arguments(this)?;
116
117            for tag in tags {
118                this.write_raw_argument(constants::TAG, tag.as_ref())?;
119            }
120            Ok(())
121        })?;
122        Ok(())
123    }
124
125    /// Writes a Record to the buffer.
126    pub fn write_record<R>(&mut self, record: R) -> Result<(), EncodingError>
127    where
128        R: RecordFields,
129    {
130        self.write_inner(record.timestamp(), record.raw_severity(), |this| {
131            record.write_arguments(this)
132        })
133    }
134
135    fn write_inner<F>(
136        &mut self,
137        timestamp: zx::BootInstant,
138        severity: RawSeverity,
139        write_args: F,
140    ) -> Result<(), EncodingError>
141    where
142        F: FnOnce(&mut Self) -> Result<(), EncodingError>,
143    {
144        // TODO(https://fxbug.dev/42138121): on failure, zero out the region we were using
145        let starting_idx = self.buf.cursor();
146        // Prepare the header, we'll finish writing once we know the full size of the record.
147        let header_slot = self.buf.put_slot(std::mem::size_of::<u64>())?;
148        self.write_i64(timestamp.into_nanos())?;
149
150        write_args(self)?;
151
152        let mut header = Header(0);
153        header.set_type(crate::TRACING_FORMAT_LOG_RECORD_TYPE);
154        header.set_severity(severity);
155
156        let length = self.buf.cursor() - starting_idx;
157        header.set_len(length);
158
159        assert_eq!(length % 8, 0, "all records must be written 8-byte aligned");
160        self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
161        Ok(())
162    }
163
164    /// Writes an argument with this encoder with the given name and value.
165    pub fn write_raw_argument(
166        &mut self,
167        name: &str,
168        value: impl WriteArgumentValue<B>,
169    ) -> Result<(), EncodingError> {
170        self.inner_write_argument(move |header, encoder| {
171            encoder.write_argument_name(header, name)?;
172            value.write_value(header, encoder)?;
173            Ok(())
174        })
175    }
176
177    /// Writes an argument with this encoder.
178    pub fn write_argument<'a>(
179        &mut self,
180        argument: impl Borrow<Argument<'a>>,
181    ) -> Result<(), EncodingError> {
182        let argument = argument.borrow();
183        self.inner_write_argument(move |header, encoder| {
184            encoder.write_argument_name(header, argument.name())?;
185            argument.write_value(header, encoder)?;
186            Ok(())
187        })
188    }
189
190    fn write_argument_name(
191        &mut self,
192        header: &mut Header,
193        name: &str,
194    ) -> Result<(), EncodingError> {
195        self.write_string(name)?;
196        header.set_name_ref(string_mask(name));
197        Ok(())
198    }
199
200    fn inner_write_argument(
201        &mut self,
202        cb: impl FnOnce(&mut Header, &mut Self) -> Result<(), EncodingError>,
203    ) -> Result<(), EncodingError> {
204        let starting_idx = self.buf.cursor();
205        let header_slot = self.buf.put_slot(std::mem::size_of::<Header>())?;
206
207        let mut header = Header(0);
208        cb(&mut header, self)?;
209
210        let record_len = self.buf.cursor() - starting_idx;
211        assert_eq!(record_len % 8, 0, "arguments must be 8-byte aligned");
212
213        header.set_size_words((record_len / 8) as u16);
214        self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
215
216        Ok(())
217    }
218
219    /// Write an unsigned integer.
220    fn write_u64(&mut self, n: u64) -> Result<(), EncodingError> {
221        self.buf.put_u64_le(n).map_err(|_| EncodingError::BufferTooSmall)
222    }
223
224    /// Write a signed integer.
225    fn write_i64(&mut self, n: i64) -> Result<(), EncodingError> {
226        self.buf.put_i64_le(n).map_err(|_| EncodingError::BufferTooSmall)
227    }
228
229    /// Write a floating-point number.
230    fn write_f64(&mut self, n: f64) -> Result<(), EncodingError> {
231        self.buf.put_f64(n).map_err(|_| EncodingError::BufferTooSmall)
232    }
233
234    /// Write a string padded to 8-byte alignment.
235    fn write_string(&mut self, src: &str) -> Result<(), EncodingError> {
236        self.write_bytes(src.as_bytes())
237    }
238
239    /// Write bytes padded to 8-byte alignment.
240    #[doc(hidden)]
241    pub fn write_bytes(&mut self, src: &[u8]) -> Result<(), EncodingError> {
242        self.buf.put_slice(src).map_err(|_| EncodingError::BufferTooSmall)?;
243        unsafe {
244            let align = std::mem::size_of::<u64>();
245            let num_padding_bytes = (align - src.len() % align) % align;
246            // TODO(https://fxbug.dev/42138122) need to enforce that the buffer is zeroed
247            self.buf.advance_cursor(num_padding_bytes);
248        }
249        Ok(())
250    }
251}
252
253mod private {
254    use super::*;
255
256    pub trait Sealed {}
257    impl Sealed for Value<'_> {}
258    impl Sealed for Argument<'_> {}
259    impl Sealed for u64 {}
260    impl Sealed for f64 {}
261    impl Sealed for i64 {}
262    impl Sealed for bool {}
263    impl Sealed for String {}
264    impl Sealed for &str {}
265    impl Sealed for Cow<'_, str> {}
266}
267
268/// Trait implemented by types which can be written to the encoder.
269pub trait WriteArgumentValue<B>: private::Sealed {
270    /// Writes the value of the argument.
271    fn write_value(
272        &self,
273        header: &mut Header,
274        encoder: &mut Encoder<B>,
275    ) -> Result<(), EncodingError>;
276}
277
278impl<B: MutableBuffer> WriteArgumentValue<B> for Argument<'_> {
279    fn write_value(
280        &self,
281        header: &mut Header,
282        encoder: &mut Encoder<B>,
283    ) -> Result<(), EncodingError> {
284        match self {
285            Self::Pid(value) | Self::Tid(value) => value.raw_koid().write_value(header, encoder),
286            Self::Line(value) | Self::Dropped(value) => value.write_value(header, encoder),
287            Self::Tag(value) | Self::File(value) | Self::Message(value) => {
288                value.write_value(header, encoder)
289            }
290            Self::Other { value, .. } => value.write_value(header, encoder),
291        }
292    }
293}
294
295impl<B: MutableBuffer> WriteArgumentValue<B> for i64 {
296    fn write_value(
297        &self,
298        header: &mut Header,
299        encoder: &mut Encoder<B>,
300    ) -> Result<(), EncodingError> {
301        header.set_type(ArgType::I64 as u8);
302        encoder.write_i64(*self)
303    }
304}
305
306impl<B: MutableBuffer> WriteArgumentValue<B> for u64 {
307    fn write_value(
308        &self,
309        header: &mut Header,
310        encoder: &mut Encoder<B>,
311    ) -> Result<(), EncodingError> {
312        header.set_type(ArgType::U64 as u8);
313        encoder.write_u64(*self)
314    }
315}
316
317impl<B: MutableBuffer> WriteArgumentValue<B> for f64 {
318    fn write_value(
319        &self,
320        header: &mut Header,
321        encoder: &mut Encoder<B>,
322    ) -> Result<(), EncodingError> {
323        header.set_type(ArgType::F64 as u8);
324        encoder.write_f64(*self)
325    }
326}
327
328impl<B: MutableBuffer> WriteArgumentValue<B> for bool {
329    fn write_value(
330        &self,
331        header: &mut Header,
332        _encoder: &mut Encoder<B>,
333    ) -> Result<(), EncodingError> {
334        header.set_type(ArgType::Bool as u8);
335        header.set_bool_val(*self);
336        Ok(())
337    }
338}
339
340impl<B: MutableBuffer> WriteArgumentValue<B> for &str {
341    fn write_value(
342        &self,
343        header: &mut Header,
344        encoder: &mut Encoder<B>,
345    ) -> Result<(), EncodingError> {
346        header.set_type(ArgType::String as u8);
347        header.set_value_ref(string_mask(self));
348        encoder.write_string(self)
349    }
350}
351
352impl<B: MutableBuffer> WriteArgumentValue<B> for String {
353    fn write_value(
354        &self,
355        header: &mut Header,
356        encoder: &mut Encoder<B>,
357    ) -> Result<(), EncodingError> {
358        self.as_str().write_value(header, encoder)
359    }
360}
361
362impl<B: MutableBuffer> WriteArgumentValue<B> for Cow<'_, str> {
363    fn write_value(
364        &self,
365        header: &mut Header,
366        encoder: &mut Encoder<B>,
367    ) -> Result<(), EncodingError> {
368        self.as_ref().write_value(header, encoder)
369    }
370}
371
372impl<B: MutableBuffer> WriteArgumentValue<B> for Value<'_> {
373    fn write_value(
374        &self,
375        header: &mut Header,
376        encoder: &mut Encoder<B>,
377    ) -> Result<(), EncodingError> {
378        match self {
379            Value::SignedInt(s) => s.write_value(header, encoder),
380            Value::UnsignedInt(u) => u.write_value(header, encoder),
381            Value::Floating(f) => f.write_value(header, encoder),
382            Value::Text(t) => t.write_value(header, encoder),
383            Value::Boolean(b) => b.write_value(header, encoder),
384        }
385    }
386}
387
388const fn string_mask(s: &str) -> u16 {
389    let len = s.len();
390    if len == 0 {
391        return 0;
392    }
393    (len as u16) | (1 << 15)
394}
395
396/// Trait implemented by types which can be written by the Encoder.
397pub trait RecordEvent {
398    /// Returns the record severity.
399    fn raw_severity(&self) -> RawSeverity;
400    /// Returns the name of the file where the record was emitted.
401    fn file(&self) -> Option<&str>;
402    /// Returns the number of the line in the file where the record was emitted.
403    fn line(&self) -> Option<u32>;
404    /// Returns the target of the record.
405    fn target(&self) -> &str;
406    /// Consumes this type and writes all the arguments.
407    fn write_arguments<B: MutableBuffer>(
408        self,
409        writer: &mut Encoder<B>,
410    ) -> Result<(), EncodingError>;
411    /// Returns the timestamp associated to this record.
412    fn timestamp(&self) -> zx::BootInstant;
413}
414
415/// Trait implemented by complete Records.
416pub trait RecordFields {
417    /// Returns the record severity.
418    fn raw_severity(&self) -> RawSeverity;
419
420    /// Returns the timestamp associated to this record.
421    fn timestamp(&self) -> zx::BootInstant;
422
423    /// Consumes this type and writes all the arguments.
424    fn write_arguments<B: MutableBuffer>(
425        self,
426        writer: &mut Encoder<B>,
427    ) -> Result<(), EncodingError>;
428}
429
430/// Arguments to create a record for testing purposes.
431pub struct TestRecord<'a> {
432    /// Severity of the log
433    pub severity: RawSeverity,
434    /// Timestamp of the test record.
435    pub timestamp: zx::BootInstant,
436    /// File that emitted the log.
437    pub file: Option<&'a str>,
438    /// Line in the file that emitted the log.
439    pub line: Option<u32>,
440    /// Additional record arguments.
441    pub record_arguments: Vec<Argument<'a>>,
442}
443
444impl TestRecord<'_> {
445    /// Creates a test record from a record.
446    pub fn from<'a>(file: &'a str, line: u32, record: &'a Record<'a>) -> TestRecord<'a> {
447        TestRecord {
448            severity: record.severity,
449            timestamp: record.timestamp,
450            file: Some(file),
451            line: Some(line),
452            record_arguments: record.arguments.clone(),
453        }
454    }
455}
456
457impl RecordEvent for TestRecord<'_> {
458    fn raw_severity(&self) -> RawSeverity {
459        self.severity
460    }
461
462    fn file(&self) -> Option<&str> {
463        self.file
464    }
465
466    fn line(&self) -> Option<u32> {
467        self.line
468    }
469
470    fn target(&self) -> &str {
471        unimplemented!("Unused at the moment");
472    }
473
474    fn timestamp(&self) -> zx::BootInstant {
475        self.timestamp
476    }
477
478    fn write_arguments<B: MutableBuffer>(
479        self,
480        writer: &mut Encoder<B>,
481    ) -> Result<(), EncodingError> {
482        for argument in self.record_arguments {
483            writer.write_argument(argument)?;
484        }
485        Ok(())
486    }
487}
488
489impl RecordFields for Record<'_> {
490    fn raw_severity(&self) -> RawSeverity {
491        self.severity
492    }
493
494    fn write_arguments<B: MutableBuffer>(
495        self,
496        writer: &mut Encoder<B>,
497    ) -> Result<(), EncodingError> {
498        for arg in self.arguments {
499            writer.write_argument(arg)?;
500        }
501        Ok(())
502    }
503
504    fn timestamp(&self) -> zx::BootInstant {
505        self.timestamp
506    }
507}
508
509#[cfg(test)]
510impl RecordFields for &Record<'_> {
511    fn raw_severity(&self) -> RawSeverity {
512        self.severity
513    }
514
515    fn write_arguments<B: MutableBuffer>(
516        self,
517        writer: &mut Encoder<B>,
518    ) -> Result<(), EncodingError> {
519        for arg in &self.arguments {
520            writer.write_argument(arg)?;
521        }
522        Ok(())
523    }
524
525    fn timestamp(&self) -> zx::BootInstant {
526        self.timestamp
527    }
528}
529
530/// Analogous to `bytes::BufMut` with some additions to be able to write at specific offsets.
531pub trait MutableBuffer {
532    /// Returns the number of total bytes this container can store. Shared memory buffers are not
533    /// expected to resize and this should return the same value during the entire lifetime of the
534    /// buffer.
535    fn capacity(&self) -> usize;
536
537    /// Returns the current position into which the next write is expected.
538    fn cursor(&self) -> usize;
539
540    /// Advance the write cursor by `n` bytes.
541    ///
542    /// # Safety
543    ///
544    /// This is marked unsafe because a malformed caller may
545    /// cause a subsequent out-of-bounds write.
546    unsafe fn advance_cursor(&mut self, n: usize);
547
548    /// Write a copy of the `src` slice into the buffer, starting at the provided offset.
549    ///
550    /// # Safety
551    ///
552    /// Implementations are not expected to bounds check the requested copy, although they may do
553    /// so and still satisfy this trait's contract.
554    unsafe fn put_slice_at(&mut self, src: &[u8], offset: usize);
555
556    /// Returns whether the buffer has sufficient remaining capacity to write an incoming value.
557    fn has_remaining(&self, num_bytes: usize) -> bool;
558
559    /// Advances the write cursor without immediately writing any bytes to the buffer. The returned
560    /// struct offers the ability to later write to the provided portion of the buffer.
561    fn put_slot(&mut self, width: usize) -> Result<WriteSlot, EncodingError> {
562        if self.has_remaining(width) {
563            let slot = WriteSlot { range: self.cursor()..(self.cursor() + width) };
564            unsafe {
565                self.advance_cursor(width);
566            }
567            Ok(slot)
568        } else {
569            Err(EncodingError::BufferTooSmall)
570        }
571    }
572
573    /// Write `src` into the provided slot that was created at a previous point in the stream.
574    fn fill_slot(&mut self, slot: WriteSlot, src: &[u8]) {
575        assert_eq!(
576            src.len(),
577            slot.range.end - slot.range.start,
578            "WriteSlots can only insert exactly-sized content into the buffer"
579        );
580        unsafe {
581            self.put_slice_at(src, slot.range.start);
582        }
583    }
584
585    /// Writes the contents of the `src` buffer to `self`, starting at `self.cursor()` and
586    /// advancing the cursor by `src.len()`.
587    ///
588    /// # Panics
589    ///
590    /// This function panics if there is not enough remaining capacity in `self`.
591    fn put_slice(&mut self, src: &[u8]) -> Result<(), EncodingError> {
592        if self.has_remaining(src.len()) {
593            unsafe {
594                self.put_slice_at(src, self.cursor());
595                self.advance_cursor(src.len());
596            }
597            Ok(())
598        } else {
599            Err(EncodingError::NoCapacity)
600        }
601    }
602
603    /// Writes an unsigned 64 bit integer to `self` in little-endian byte order.
604    ///
605    /// Advances the cursor by 8 bytes.
606    ///
607    /// # Examples
608    ///
609    /// ```
610    /// use bytes::BufMut;
611    ///
612    /// let mut buf = vec![0; 8];
613    /// buf.put_u64_le_at(0x0102030405060708, 0);
614    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
615    /// ```
616    ///
617    /// # Panics
618    ///
619    /// This function panics if there is not enough remaining capacity in `self`.
620    fn put_u64_le(&mut self, n: u64) -> Result<(), EncodingError> {
621        self.put_slice(&n.to_le_bytes())
622    }
623
624    /// Writes a signed 64 bit integer to `self` in little-endian byte order.
625    ///
626    /// The cursor position is advanced by 8.
627    ///
628    /// # Examples
629    ///
630    /// ```
631    /// use bytes::BufMut;
632    ///
633    /// let mut buf = vec![0; 8];
634    /// buf.put_i64_le_at(0x0102030405060708, 0);
635    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
636    /// ```
637    ///
638    /// # Panics
639    ///
640    /// This function panics if there is not enough remaining capacity in `self`.
641    fn put_i64_le(&mut self, n: i64) -> Result<(), EncodingError> {
642        self.put_slice(&n.to_le_bytes())
643    }
644
645    /// Writes a double-precision IEEE 754 floating point number to `self`.
646    ///
647    /// The cursor position is advanced by 8.
648    ///
649    /// # Examples
650    ///
651    /// ```
652    /// use bytes::BufMut;
653    ///
654    /// let mut buf = vec![];
655    /// buf.put_i64_le(0x0102030405060708);
656    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
657    /// ```
658    ///
659    /// # Panics
660    ///
661    /// This function panics if there is not enough remaining capacity in `self`.
662    fn put_f64(&mut self, n: f64) -> Result<(), EncodingError> {
663        self.put_slice(&n.to_bits().to_ne_bytes())
664    }
665}
666
667/// A region of the buffer which was advanced past and can later be filled in.
668#[must_use]
669pub struct WriteSlot {
670    range: std::ops::Range<usize>,
671}
672
673/// Wrapper for a vector that allows us to implement necessary traits.
674#[derive(Debug, Default)]
675pub struct ResizableBuffer(Vec<u8>);
676
677impl From<Vec<u8>> for ResizableBuffer {
678    fn from(buf: Vec<u8>) -> Self {
679        Self(buf)
680    }
681}
682
683impl Deref for ResizableBuffer {
684    type Target = Vec<u8>;
685
686    // Required method
687    fn deref(&self) -> &Self::Target {
688        &self.0
689    }
690}
691
692impl ResizableBuffer {
693    /// Return the inner vector.
694    pub fn into_inner(self) -> Vec<u8> {
695        self.0
696    }
697}
698
699impl MutableBuffer for Cursor<ResizableBuffer> {
700    fn capacity(&self) -> usize {
701        self.get_ref().0.len()
702    }
703
704    fn cursor(&self) -> usize {
705        self.position() as usize
706    }
707
708    fn has_remaining(&self, _num_bytes: usize) -> bool {
709        true
710    }
711
712    unsafe fn advance_cursor(&mut self, n: usize) {
713        let new_pos = self.position() as usize + n;
714        let vec = &mut self.get_mut().0;
715        if new_pos > vec.len() {
716            vec.resize(new_pos, 0);
717        }
718        self.set_position(new_pos as u64);
719    }
720
721    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
722        let this = &mut self.get_mut().0;
723        if offset < this.len() {
724            let available = this.len() - offset;
725
726            // Copy the elements that fit into the buffer.
727            let min = available.min(to_put.len());
728            let dest = &mut this[offset..(offset + min)];
729            dest.copy_from_slice(&to_put[..min]);
730
731            // If we couldn't fit all elements, then extend the buffer with the remaining elements.
732            if available < to_put.len() {
733                this.extend_from_slice(&to_put[available..]);
734            }
735        } else {
736            // If the offset is bigger than the length, fill with zeros up to the offset and then
737            // write the slice.
738            this.resize(offset, 0);
739            this.extend_from_slice(to_put);
740        }
741    }
742}
743
744impl<T: MutableBuffer + ?Sized> MutableBuffer for &mut T {
745    fn has_remaining(&self, num_bytes: usize) -> bool {
746        (**self).has_remaining(num_bytes)
747    }
748    fn capacity(&self) -> usize {
749        (**self).capacity()
750    }
751
752    fn cursor(&self) -> usize {
753        (**self).cursor()
754    }
755
756    unsafe fn advance_cursor(&mut self, n: usize) {
757        unsafe { (**self).advance_cursor(n) };
758    }
759
760    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
761        unsafe { (**self).put_slice_at(to_put, offset) };
762    }
763}
764
765impl<T: MutableBuffer + ?Sized> MutableBuffer for Box<T> {
766    fn has_remaining(&self, num_bytes: usize) -> bool {
767        (**self).has_remaining(num_bytes)
768    }
769    fn capacity(&self) -> usize {
770        (**self).capacity()
771    }
772
773    fn cursor(&self) -> usize {
774        (**self).cursor()
775    }
776
777    unsafe fn advance_cursor(&mut self, n: usize) {
778        unsafe { (**self).advance_cursor(n) };
779    }
780
781    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
782        unsafe { (**self).put_slice_at(to_put, offset) };
783    }
784}
785
786impl MutableBuffer for Cursor<Vec<u8>> {
787    fn has_remaining(&self, num_bytes: usize) -> bool {
788        (self.cursor() + num_bytes) <= self.capacity()
789    }
790
791    fn capacity(&self) -> usize {
792        self.get_ref().len()
793    }
794
795    fn cursor(&self) -> usize {
796        self.position() as usize
797    }
798
799    unsafe fn advance_cursor(&mut self, n: usize) {
800        self.set_position(self.position() + n as u64);
801    }
802
803    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
804        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
805        dest.copy_from_slice(to_put);
806    }
807}
808
809impl MutableBuffer for Cursor<&mut [u8]> {
810    fn has_remaining(&self, num_bytes: usize) -> bool {
811        (self.cursor() + num_bytes) <= self.capacity()
812    }
813
814    fn capacity(&self) -> usize {
815        self.get_ref().len()
816    }
817
818    fn cursor(&self) -> usize {
819        self.position() as usize
820    }
821
822    unsafe fn advance_cursor(&mut self, n: usize) {
823        self.set_position(self.position() + n as u64);
824    }
825
826    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
827        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
828        dest.copy_from_slice(to_put);
829    }
830}
831
832impl<const N: usize> MutableBuffer for Cursor<[u8; N]> {
833    fn has_remaining(&self, num_bytes: usize) -> bool {
834        (self.cursor() + num_bytes) <= self.capacity()
835    }
836    fn capacity(&self) -> usize {
837        self.get_ref().len()
838    }
839
840    fn cursor(&self) -> usize {
841        self.position() as usize
842    }
843
844    unsafe fn advance_cursor(&mut self, n: usize) {
845        self.set_position(self.position() + n as u64);
846    }
847
848    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
849        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
850        dest.copy_from_slice(to_put);
851    }
852}
853
854/// An error that occurred while encoding data to the stream format.
855#[derive(Debug, Error)]
856pub enum EncodingError {
857    /// The provided buffer is too small.
858    #[error("buffer is too small")]
859    BufferTooSmall,
860
861    /// We attempted to encode values which are not yet supported by this implementation of
862    /// the Fuchsia Tracing format.
863    #[error("unsupported value type")]
864    Unsupported,
865
866    /// We attempted to write to a buffer with no remaining capacity.
867    #[error("the buffer has no remaining capacity")]
868    NoCapacity,
869
870    /// Some other error happened. Useful for integrating with this crate, but providing custom
871    /// errors.
872    #[error(transparent)]
873    Other(Box<dyn std::error::Error + Send + Sync>),
874}
875
876impl EncodingError {
877    /// Treat a custom error as an encoding error.
878    pub fn other<E>(err: E) -> Self
879    where
880        E: std::error::Error + Send + Sync + 'static,
881    {
882        Self::Other(err.into())
883    }
884}
885
886impl From<TryFromSliceError> for EncodingError {
887    fn from(_: TryFromSliceError) -> Self {
888        EncodingError::BufferTooSmall
889    }
890}
891
892/// Adds `count` to the dropped count for the message.  Returns `true` if successful.
893pub fn add_dropped_count(message: &mut Vec<u8>, count: u64) -> bool {
894    const DROPPED_HEADER_SIZE_WORDS: u16 = 4; // Header (1), name (2), value (1)
895    const DROPPED_HEADER: Header = Header(
896        4                                                     // arg type U64
897        | (DROPPED_HEADER_SIZE_WORDS as u64) << 4             // size words
898        | (string_mask(constants::NUM_DROPPED) as u64) << 16, // string ref
899    );
900
901    if message.len() < 16 {
902        return false;
903    }
904
905    // See if the message already has a dropped argument.
906    let mut argument = &mut message[16..];
907    while !argument.is_empty() {
908        let Ok((header, _)) = Header::read_from_prefix(argument) else {
909            return false;
910        };
911        let arg_len = header.size_words() as usize * 8;
912        if arg_len == 0 || arg_len > argument.len() {
913            return false;
914        }
915        if header.0 == DROPPED_HEADER.0
916            && &argument[8..8 + constants::NUM_DROPPED.len()] == constants::NUM_DROPPED.as_bytes()
917        {
918            let value = u64::mut_from_bytes(&mut argument[24..32]).unwrap();
919            *value = value.saturating_add(count);
920            return true;
921        }
922        argument = &mut argument[arg_len..];
923    }
924
925    let message_header = Header::mut_from_bytes(&mut message[..8]).unwrap();
926    let new_size = message_header.size_words() + DROPPED_HEADER_SIZE_WORDS;
927    if new_size > MAX_SIZE_WORDS {
928        return false;
929    }
930    message_header.set_size_words(new_size);
931
932    // Append the dropped argument.
933    message.extend(DROPPED_HEADER.0.as_bytes());
934    message.extend(constants::NUM_DROPPED.as_bytes());
935    message.extend(std::iter::repeat_n(0, 16 - constants::NUM_DROPPED.len()));
936    message.extend(count.as_bytes());
937
938    true
939}
940
941#[doc(hidden)]
942pub struct LogEvent<'a> {
943    record: &'a log::Record<'a>,
944    timestamp: zx::BootInstant,
945}
946
947impl<'a> LogEvent<'a> {
948    pub fn new(record: &'a log::Record<'a>) -> Self {
949        Self { record, timestamp: zx::BootInstant::get() }
950    }
951}
952
953impl RecordEvent for LogEvent<'_> {
954    fn raw_severity(&self) -> RawSeverity {
955        diagnostics_log_types::Severity::from(self.record.metadata().level()) as RawSeverity
956    }
957
958    fn file(&self) -> Option<&str> {
959        self.record.file()
960    }
961
962    fn line(&self) -> Option<u32> {
963        self.record.line()
964    }
965
966    fn target(&self) -> &str {
967        self.record.target()
968    }
969
970    fn timestamp(&self) -> zx::BootInstant {
971        self.timestamp
972    }
973
974    fn write_arguments<B: MutableBuffer>(
975        self,
976        writer: &mut Encoder<B>,
977    ) -> Result<(), EncodingError> {
978        let args = self.record.args();
979        let message =
980            args.as_str().map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(args.to_string()));
981        writer.write_argument(Argument::message(message))?;
982        self.record
983            .key_values()
984            .visit(&mut KeyValuesVisitor(writer))
985            .map_err(EncodingError::other)?;
986        Ok(())
987    }
988}
989
990struct KeyValuesVisitor<'a, B>(&'a mut Encoder<B>);
991
992impl<B: MutableBuffer> log::kv::VisitSource<'_> for KeyValuesVisitor<'_, B> {
993    fn visit_pair(
994        &mut self,
995        key: log::kv::Key<'_>,
996        value: log::kv::Value<'_>,
997    ) -> Result<(), log::kv::Error> {
998        value.visit(ValueVisitor { encoder: self.0, key: key.as_str() })
999    }
1000}
1001
1002struct ValueVisitor<'a, B> {
1003    encoder: &'a mut Encoder<B>,
1004    key: &'a str,
1005}
1006
1007impl<B: MutableBuffer> log::kv::VisitValue<'_> for ValueVisitor<'_, B> {
1008    fn visit_any(&mut self, value: log::kv::Value<'_>) -> Result<(), log::kv::Error> {
1009        self.encoder
1010            .write_raw_argument(self.key, format!("{value}"))
1011            .map_err(log::kv::Error::boxed)?;
1012        Ok(())
1013    }
1014
1015    fn visit_null(&mut self) -> Result<(), log::kv::Error> {
1016        self.encoder.write_raw_argument(self.key, "null").map_err(log::kv::Error::boxed)?;
1017        Ok(())
1018    }
1019
1020    fn visit_u64(&mut self, value: u64) -> Result<(), log::kv::Error> {
1021        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1022        Ok(())
1023    }
1024
1025    fn visit_i64(&mut self, value: i64) -> Result<(), log::kv::Error> {
1026        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1027        Ok(())
1028    }
1029
1030    fn visit_f64(&mut self, value: f64) -> Result<(), log::kv::Error> {
1031        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1032        Ok(())
1033    }
1034
1035    fn visit_bool(&mut self, value: bool) -> Result<(), log::kv::Error> {
1036        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1037        Ok(())
1038    }
1039
1040    fn visit_str(&mut self, value: &str) -> Result<(), log::kv::Error> {
1041        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1042        Ok(())
1043    }
1044
1045    // TODO(https://fxbug.dev/360919323): when we enable kv_std we must support visit_error and
1046    // visit_borrowed_error.
1047}
1048
1049#[cfg(test)]
1050mod tests {
1051    use super::*;
1052    use crate::parse::parse_record;
1053
1054    #[fuchsia::test]
1055    fn build_basic_record() {
1056        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1057        encoder
1058            .write_event(WriteEventParams::<_, &str, _> {
1059                event: TestRecord {
1060                    severity: Severity::Info.into_primitive(),
1061                    timestamp: zx::BootInstant::from_nanos(12345),
1062                    file: None,
1063                    line: None,
1064                    record_arguments: vec![],
1065                },
1066                tags: &[],
1067                metatags: std::iter::empty(),
1068                pid: zx::Koid::from_raw(0),
1069                tid: zx::Koid::from_raw(0),
1070                dropped: 0,
1071            })
1072            .expect("wrote event");
1073        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1074        assert_eq!(
1075            record,
1076            Record {
1077                timestamp: zx::BootInstant::from_nanos(12345),
1078                severity: Severity::Info.into_primitive(),
1079                arguments: vec![
1080                    Argument::pid(zx::Koid::from_raw(0)),
1081                    Argument::tid(zx::Koid::from_raw(0)),
1082                ]
1083            }
1084        );
1085    }
1086
1087    #[fuchsia::test]
1088    fn build_records_with_location() {
1089        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1090        encoder
1091            .write_event(WriteEventParams::<_, &str, _> {
1092                event: TestRecord {
1093                    severity: Severity::Error.into_primitive(),
1094                    timestamp: zx::BootInstant::from_nanos(12345),
1095                    file: Some("foo.rs"),
1096                    line: Some(10),
1097                    record_arguments: vec![],
1098                },
1099                tags: &[],
1100                metatags: std::iter::empty(),
1101                pid: zx::Koid::from_raw(0),
1102                tid: zx::Koid::from_raw(0),
1103                dropped: 0,
1104            })
1105            .expect("wrote event");
1106        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1107        assert_eq!(
1108            record,
1109            Record {
1110                timestamp: zx::BootInstant::from_nanos(12345),
1111                severity: Severity::Error.into_primitive(),
1112                arguments: vec![
1113                    Argument::pid(zx::Koid::from_raw(0)),
1114                    Argument::tid(zx::Koid::from_raw(0)),
1115                    Argument::file("foo.rs"),
1116                    Argument::line(10),
1117                ]
1118            }
1119        );
1120    }
1121
1122    #[fuchsia::test]
1123    fn build_record_with_dropped_count() {
1124        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1125        encoder
1126            .write_event(WriteEventParams::<_, &str, _> {
1127                event: TestRecord {
1128                    severity: Severity::Warn.into_primitive(),
1129                    timestamp: zx::BootInstant::from_nanos(12345),
1130                    file: None,
1131                    line: None,
1132                    record_arguments: vec![],
1133                },
1134                tags: &[],
1135                metatags: std::iter::empty(),
1136                pid: zx::Koid::from_raw(0),
1137                tid: zx::Koid::from_raw(0),
1138                dropped: 7,
1139            })
1140            .expect("wrote event");
1141        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1142        assert_eq!(
1143            record,
1144            Record {
1145                timestamp: zx::BootInstant::from_nanos(12345),
1146                severity: Severity::Warn.into_primitive(),
1147                arguments: vec![
1148                    Argument::pid(zx::Koid::from_raw(0)),
1149                    Argument::tid(zx::Koid::from_raw(0)),
1150                    Argument::dropped(7),
1151                ]
1152            }
1153        );
1154    }
1155
1156    #[test]
1157    fn resizable_vec_mutable_buffer() {
1158        // Putting a slice at offset=len is equivalent to concatenating.
1159        let mut vec = Cursor::new(ResizableBuffer(vec![1u8, 2, 3]));
1160        unsafe {
1161            vec.put_slice_at(&[4, 5, 6], 3);
1162        }
1163        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 4, 5, 6]);
1164
1165        // Putting a slice at an offset inside the buffer, is equivalent to replacing the items
1166        // there.
1167        let mut vec = Cursor::new(ResizableBuffer(vec![1, 3, 7, 9, 11, 13, 15]));
1168        unsafe {
1169            vec.put_slice_at(&[2, 4, 6], 2);
1170        }
1171        assert_eq!(vec.get_ref().0, vec![1, 3, 2, 4, 6, 13, 15]);
1172
1173        // Putting a slice at an index in range replaces all the items and extends if needed.
1174        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1175        unsafe {
1176            vec.put_slice_at(&[4, 5, 6, 7], 0);
1177        }
1178        assert_eq!(vec.get_ref().0, vec![4, 5, 6, 7]);
1179
1180        // Putting a slice at an offset beyond the buffer, fills with zeros the items in between.
1181        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1182        unsafe {
1183            vec.put_slice_at(&[4, 5, 6], 5);
1184        }
1185        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 0, 0, 4, 5, 6]);
1186    }
1187
1188    #[test]
1189    fn add_dropped_count_to_existing_count() {
1190        const INITIAL_DROPPED: u64 = 7;
1191        const EXTRA_DROPPED: u64 = 53;
1192
1193        let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1194        encoder
1195            .write_event(WriteEventParams::<_, &str, _> {
1196                event: TestRecord {
1197                    severity: Severity::Error.into_primitive(),
1198                    timestamp: zx::BootInstant::from_nanos(12345),
1199                    file: None,
1200                    line: Some(123),
1201                    record_arguments: vec![],
1202                },
1203                tags: &[],
1204                metatags: std::iter::empty(),
1205                pid: zx::Koid::from_raw(0),
1206                tid: zx::Koid::from_raw(0),
1207                dropped: INITIAL_DROPPED,
1208            })
1209            .expect("wrote event");
1210        let cursor = encoder.take();
1211        let position = cursor.position();
1212        let mut buffer = cursor.into_inner();
1213        buffer.truncate(position as usize);
1214        assert!(add_dropped_count(&mut buffer, EXTRA_DROPPED));
1215        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1216        assert_eq!(
1217            record,
1218            Record {
1219                timestamp: zx::BootInstant::from_nanos(12345),
1220                severity: Severity::Error.into_primitive(),
1221                arguments: vec![
1222                    Argument::pid(zx::Koid::from_raw(0)),
1223                    Argument::tid(zx::Koid::from_raw(0)),
1224                    Argument::dropped(INITIAL_DROPPED + EXTRA_DROPPED),
1225                    Argument::Line(123),
1226                ]
1227            }
1228        );
1229    }
1230
1231    #[test]
1232    fn add_dropped_count_when_none_exists() {
1233        const DROPPED: u64 = 53;
1234
1235        let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1236        encoder
1237            .write_event(WriteEventParams::<_, &str, _> {
1238                event: TestRecord {
1239                    severity: Severity::Error.into_primitive(),
1240                    timestamp: zx::BootInstant::from_nanos(12345),
1241                    file: None,
1242                    line: Some(123),
1243                    record_arguments: vec![],
1244                },
1245                tags: &[],
1246                metatags: std::iter::empty(),
1247                pid: zx::Koid::from_raw(0),
1248                tid: zx::Koid::from_raw(0),
1249                dropped: 0,
1250            })
1251            .expect("wrote event");
1252        let cursor = encoder.take();
1253        let position = cursor.position();
1254        let mut buffer = cursor.into_inner();
1255        buffer.truncate(position as usize);
1256        assert!(add_dropped_count(&mut buffer, DROPPED));
1257        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1258        assert_eq!(
1259            record,
1260            Record {
1261                timestamp: zx::BootInstant::from_nanos(12345),
1262                severity: Severity::Error.into_primitive(),
1263                arguments: vec![
1264                    Argument::pid(zx::Koid::from_raw(0)),
1265                    Argument::tid(zx::Koid::from_raw(0)),
1266                    Argument::Line(123),
1267                    Argument::dropped(DROPPED),
1268                ]
1269            }
1270        );
1271    }
1272
1273    #[test]
1274    fn add_dropped_count_when_just_enough_room() {
1275        const DROPPED: u64 = 53;
1276
1277        let mut encoder =
1278            Encoder::new(Cursor::new(vec![0; MAX_SIZE_WORDS as usize * 8]), EncoderOpts::default());
1279        // We want the argument to be just big enough so that there is only just enough room for
1280        // the dropped count:
1281        //
1282        //   Header    :    1
1283        //   Timestamp :    1
1284        //   Pid       :    3
1285        //   Tid       :    3
1286        //   Line      :    3
1287        //   Foo       : 4080
1288        //   Dropped   :    4
1289        //               ====
1290        //               4095
1291        let foo_arg = Argument::new("foo", String::from_iter(std::iter::repeat_n('x', 4078 * 8)));
1292        encoder
1293            .write_event(WriteEventParams::<_, &str, _> {
1294                event: TestRecord {
1295                    severity: Severity::Error.into_primitive(),
1296                    timestamp: zx::BootInstant::from_nanos(12345),
1297                    file: None,
1298                    line: Some(123),
1299                    record_arguments: vec![foo_arg.clone()],
1300                },
1301                tags: &[],
1302                metatags: std::iter::empty(),
1303                pid: zx::Koid::from_raw(0),
1304                tid: zx::Koid::from_raw(0),
1305                dropped: 0,
1306            })
1307            .expect("wrote event");
1308        let cursor = encoder.take();
1309        let position = cursor.position();
1310        let mut buffer = cursor.into_inner();
1311        buffer.truncate(position as usize);
1312        assert!(add_dropped_count(&mut buffer, DROPPED));
1313        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1314        assert_eq!(
1315            record,
1316            Record {
1317                timestamp: zx::BootInstant::from_nanos(12345),
1318                severity: Severity::Error.into_primitive(),
1319                arguments: vec![
1320                    Argument::pid(zx::Koid::from_raw(0)),
1321                    Argument::tid(zx::Koid::from_raw(0)),
1322                    Argument::Line(123),
1323                    foo_arg,
1324                    Argument::dropped(DROPPED),
1325                ]
1326            }
1327        );
1328    }
1329
1330    #[test]
1331    fn add_dropped_count_invalid_message() {
1332        // Message too small.
1333        assert!(!add_dropped_count(&mut vec![1, 2, 3], 5));
1334
1335        // Argument too small.
1336        assert!(!add_dropped_count(&mut vec![0; 17], 5));
1337
1338        // Zero argument len.
1339        assert!(!add_dropped_count(&mut vec![0; 24], 5));
1340
1341        // Argument too big.
1342        let mut message = vec![0; 16];
1343        let mut arg_header = Header(0);
1344        arg_header.set_size_words(2);
1345        message.extend(arg_header.0.as_bytes());
1346        assert!(!add_dropped_count(&mut message, 5));
1347
1348        // Message too too big to accept another argument.
1349        let mut message = Vec::new();
1350        let mut header = Header(0);
1351        header.set_size_words(MAX_SIZE_WORDS);
1352        message.extend(header.0.as_bytes());
1353        message.extend([0; 8]); // timestamp
1354        let mut arg_header = Header(0);
1355        arg_header.set_size_words(4093);
1356        message.extend(arg_header.0.as_bytes());
1357        message.resize(4095 * 8, 0);
1358        assert!(!add_dropped_count(&mut message, 5));
1359    }
1360}