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