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    constants, ArgType, Argument, Header, Metatag, RawSeverity, Record, Value, MAX_SIZE_WORDS,
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        self.set_position(self.position() + n as u64);
714    }
715
716    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
717        let this = &mut self.get_mut().0;
718        if offset < this.len() {
719            let available = this.len() - offset;
720
721            // Copy the elements that fit into the buffer.
722            let min = available.min(to_put.len());
723            let dest = &mut this[offset..(offset + min)];
724            dest.copy_from_slice(&to_put[..min]);
725
726            // If we couldn't fit all elements, then extend the buffer with the remaining elements.
727            if available < to_put.len() {
728                this.extend_from_slice(&to_put[available..]);
729            }
730        } else {
731            // If the offset is bigger than the length, fill with zeros up to the offset and then
732            // write the slice.
733            this.resize(offset, 0);
734            this.extend_from_slice(to_put);
735        }
736    }
737}
738
739impl<T: MutableBuffer + ?Sized> MutableBuffer for &mut T {
740    fn has_remaining(&self, num_bytes: usize) -> bool {
741        (**self).has_remaining(num_bytes)
742    }
743    fn capacity(&self) -> usize {
744        (**self).capacity()
745    }
746
747    fn cursor(&self) -> usize {
748        (**self).cursor()
749    }
750
751    unsafe fn advance_cursor(&mut self, n: usize) {
752        (**self).advance_cursor(n);
753    }
754
755    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
756        (**self).put_slice_at(to_put, offset);
757    }
758}
759
760impl<T: MutableBuffer + ?Sized> MutableBuffer for Box<T> {
761    fn has_remaining(&self, num_bytes: usize) -> bool {
762        (**self).has_remaining(num_bytes)
763    }
764    fn capacity(&self) -> usize {
765        (**self).capacity()
766    }
767
768    fn cursor(&self) -> usize {
769        (**self).cursor()
770    }
771
772    unsafe fn advance_cursor(&mut self, n: usize) {
773        (**self).advance_cursor(n);
774    }
775
776    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
777        (**self).put_slice_at(to_put, offset);
778    }
779}
780
781impl MutableBuffer for Cursor<Vec<u8>> {
782    fn has_remaining(&self, num_bytes: usize) -> bool {
783        (self.cursor() + num_bytes) <= self.capacity()
784    }
785
786    fn capacity(&self) -> usize {
787        self.get_ref().len()
788    }
789
790    fn cursor(&self) -> usize {
791        self.position() as usize
792    }
793
794    unsafe fn advance_cursor(&mut self, n: usize) {
795        self.set_position(self.position() + n as u64);
796    }
797
798    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
799        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
800        dest.copy_from_slice(to_put);
801    }
802}
803
804impl MutableBuffer for Cursor<&mut [u8]> {
805    fn has_remaining(&self, num_bytes: usize) -> bool {
806        (self.cursor() + num_bytes) <= self.capacity()
807    }
808
809    fn capacity(&self) -> usize {
810        self.get_ref().len()
811    }
812
813    fn cursor(&self) -> usize {
814        self.position() as usize
815    }
816
817    unsafe fn advance_cursor(&mut self, n: usize) {
818        self.set_position(self.position() + n as u64);
819    }
820
821    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
822        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
823        dest.copy_from_slice(to_put);
824    }
825}
826
827impl<const N: usize> MutableBuffer for Cursor<[u8; N]> {
828    fn has_remaining(&self, num_bytes: usize) -> bool {
829        (self.cursor() + num_bytes) <= self.capacity()
830    }
831    fn capacity(&self) -> usize {
832        self.get_ref().len()
833    }
834
835    fn cursor(&self) -> usize {
836        self.position() as usize
837    }
838
839    unsafe fn advance_cursor(&mut self, n: usize) {
840        self.set_position(self.position() + n as u64);
841    }
842
843    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
844        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
845        dest.copy_from_slice(to_put);
846    }
847}
848
849/// An error that occurred while encoding data to the stream format.
850#[derive(Debug, Error)]
851pub enum EncodingError {
852    /// The provided buffer is too small.
853    #[error("buffer is too small")]
854    BufferTooSmall,
855
856    /// We attempted to encode values which are not yet supported by this implementation of
857    /// the Fuchsia Tracing format.
858    #[error("unsupported value type")]
859    Unsupported,
860
861    /// We attempted to write to a buffer with no remaining capacity.
862    #[error("the buffer has no remaining capacity")]
863    NoCapacity,
864
865    /// Some other error happened. Useful for integrating with this crate, but providing custom
866    /// errors.
867    #[error(transparent)]
868    Other(Box<dyn std::error::Error + Send + Sync>),
869}
870
871impl EncodingError {
872    /// Treat a custom error as an encoding error.
873    pub fn other<E>(err: E) -> Self
874    where
875        E: std::error::Error + Send + Sync + 'static,
876    {
877        Self::Other(err.into())
878    }
879}
880
881impl From<TryFromSliceError> for EncodingError {
882    fn from(_: TryFromSliceError) -> Self {
883        EncodingError::BufferTooSmall
884    }
885}
886
887/// Adds `count` to the dropped count for the message.  Returns `true` if successful.
888pub fn add_dropped_count(message: &mut Vec<u8>, count: u64) -> bool {
889    const DROPPED_HEADER_SIZE_WORDS: u16 = 4; // Header (1), name (2), value (1)
890    const DROPPED_HEADER: Header = Header(
891        4                                                     // arg type U64
892        | (DROPPED_HEADER_SIZE_WORDS as u64) << 4             // size words
893        | (string_mask(constants::NUM_DROPPED) as u64) << 16, // string ref
894    );
895
896    if message.len() < 16 {
897        return false;
898    }
899
900    // See if the message already has a dropped argument.
901    let mut argument = &mut message[16..];
902    while !argument.is_empty() {
903        let Ok((header, _)) = Header::read_from_prefix(argument) else {
904            return false;
905        };
906        let arg_len = header.size_words() as usize * 8;
907        if arg_len == 0 || arg_len > argument.len() {
908            return false;
909        }
910        if header.0 == DROPPED_HEADER.0
911            && &argument[8..8 + constants::NUM_DROPPED.len()] == constants::NUM_DROPPED.as_bytes()
912        {
913            let value = u64::mut_from_bytes(&mut argument[24..32]).unwrap();
914            *value = value.saturating_add(count);
915            return true;
916        }
917        argument = &mut argument[arg_len..];
918    }
919
920    let message_header = Header::mut_from_bytes(&mut message[..8]).unwrap();
921    let new_size = message_header.size_words() + DROPPED_HEADER_SIZE_WORDS;
922    if new_size > MAX_SIZE_WORDS {
923        return false;
924    }
925    message_header.set_size_words(new_size);
926
927    // Append the dropped argument.
928    message.extend(DROPPED_HEADER.0.as_bytes());
929    message.extend(constants::NUM_DROPPED.as_bytes());
930    message.extend(std::iter::repeat_n(0, 16 - constants::NUM_DROPPED.len()));
931    message.extend(count.as_bytes());
932
933    true
934}
935
936#[doc(hidden)]
937pub struct LogEvent<'a> {
938    record: &'a log::Record<'a>,
939    timestamp: zx::BootInstant,
940}
941
942impl<'a> LogEvent<'a> {
943    pub fn new(record: &'a log::Record<'a>) -> Self {
944        Self { record, timestamp: zx::BootInstant::get() }
945    }
946}
947
948impl RecordEvent for LogEvent<'_> {
949    fn raw_severity(&self) -> RawSeverity {
950        diagnostics_log_types::Severity::from(self.record.metadata().level()) as RawSeverity
951    }
952
953    fn file(&self) -> Option<&str> {
954        self.record.file()
955    }
956
957    fn line(&self) -> Option<u32> {
958        self.record.line()
959    }
960
961    fn target(&self) -> &str {
962        self.record.target()
963    }
964
965    fn timestamp(&self) -> zx::BootInstant {
966        self.timestamp
967    }
968
969    fn write_arguments<B: MutableBuffer>(
970        self,
971        writer: &mut Encoder<B>,
972    ) -> Result<(), EncodingError> {
973        let args = self.record.args();
974        let message =
975            args.as_str().map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(args.to_string()));
976        writer.write_argument(Argument::message(message))?;
977        self.record
978            .key_values()
979            .visit(&mut KeyValuesVisitor(writer))
980            .map_err(EncodingError::other)?;
981        Ok(())
982    }
983}
984
985struct KeyValuesVisitor<'a, B>(&'a mut Encoder<B>);
986
987impl<B: MutableBuffer> log::kv::VisitSource<'_> for KeyValuesVisitor<'_, B> {
988    fn visit_pair(
989        &mut self,
990        key: log::kv::Key<'_>,
991        value: log::kv::Value<'_>,
992    ) -> Result<(), log::kv::Error> {
993        value.visit(ValueVisitor { encoder: self.0, key: key.as_str() })
994    }
995}
996
997struct ValueVisitor<'a, B> {
998    encoder: &'a mut Encoder<B>,
999    key: &'a str,
1000}
1001
1002impl<B: MutableBuffer> log::kv::VisitValue<'_> for ValueVisitor<'_, B> {
1003    fn visit_any(&mut self, value: log::kv::Value<'_>) -> Result<(), log::kv::Error> {
1004        self.encoder
1005            .write_raw_argument(self.key, format!("{value}"))
1006            .map_err(log::kv::Error::boxed)?;
1007        Ok(())
1008    }
1009
1010    fn visit_null(&mut self) -> Result<(), log::kv::Error> {
1011        self.encoder.write_raw_argument(self.key, "null").map_err(log::kv::Error::boxed)?;
1012        Ok(())
1013    }
1014
1015    fn visit_u64(&mut self, value: u64) -> Result<(), log::kv::Error> {
1016        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1017        Ok(())
1018    }
1019
1020    fn visit_i64(&mut self, value: i64) -> 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_f64(&mut self, value: f64) -> 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_bool(&mut self, value: bool) -> 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_str(&mut self, value: &str) -> Result<(), log::kv::Error> {
1036        self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1037        Ok(())
1038    }
1039
1040    // TODO(https://fxbug.dev/360919323): when we enable kv_std we must support visit_error and
1041    // visit_borrowed_error.
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046    use super::*;
1047    use crate::parse::parse_record;
1048
1049    #[fuchsia::test]
1050    fn build_basic_record() {
1051        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1052        encoder
1053            .write_event(WriteEventParams::<_, &str, _> {
1054                event: TestRecord {
1055                    severity: Severity::Info.into_primitive(),
1056                    timestamp: zx::BootInstant::from_nanos(12345),
1057                    file: None,
1058                    line: None,
1059                    record_arguments: vec![],
1060                },
1061                tags: &[],
1062                metatags: std::iter::empty(),
1063                pid: zx::Koid::from_raw(0),
1064                tid: zx::Koid::from_raw(0),
1065                dropped: 0,
1066            })
1067            .expect("wrote event");
1068        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1069        assert_eq!(
1070            record,
1071            Record {
1072                timestamp: zx::BootInstant::from_nanos(12345),
1073                severity: Severity::Info.into_primitive(),
1074                arguments: vec![
1075                    Argument::pid(zx::Koid::from_raw(0)),
1076                    Argument::tid(zx::Koid::from_raw(0)),
1077                ]
1078            }
1079        );
1080    }
1081
1082    #[fuchsia::test]
1083    fn build_records_with_location() {
1084        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1085        encoder
1086            .write_event(WriteEventParams::<_, &str, _> {
1087                event: TestRecord {
1088                    severity: Severity::Error.into_primitive(),
1089                    timestamp: zx::BootInstant::from_nanos(12345),
1090                    file: Some("foo.rs"),
1091                    line: Some(10),
1092                    record_arguments: vec![],
1093                },
1094                tags: &[],
1095                metatags: std::iter::empty(),
1096                pid: zx::Koid::from_raw(0),
1097                tid: zx::Koid::from_raw(0),
1098                dropped: 0,
1099            })
1100            .expect("wrote event");
1101        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1102        assert_eq!(
1103            record,
1104            Record {
1105                timestamp: zx::BootInstant::from_nanos(12345),
1106                severity: Severity::Error.into_primitive(),
1107                arguments: vec![
1108                    Argument::pid(zx::Koid::from_raw(0)),
1109                    Argument::tid(zx::Koid::from_raw(0)),
1110                    Argument::file("foo.rs"),
1111                    Argument::line(10),
1112                ]
1113            }
1114        );
1115    }
1116
1117    #[fuchsia::test]
1118    fn build_record_with_dropped_count() {
1119        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1120        encoder
1121            .write_event(WriteEventParams::<_, &str, _> {
1122                event: TestRecord {
1123                    severity: Severity::Warn.into_primitive(),
1124                    timestamp: zx::BootInstant::from_nanos(12345),
1125                    file: None,
1126                    line: None,
1127                    record_arguments: vec![],
1128                },
1129                tags: &[],
1130                metatags: std::iter::empty(),
1131                pid: zx::Koid::from_raw(0),
1132                tid: zx::Koid::from_raw(0),
1133                dropped: 7,
1134            })
1135            .expect("wrote event");
1136        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1137        assert_eq!(
1138            record,
1139            Record {
1140                timestamp: zx::BootInstant::from_nanos(12345),
1141                severity: Severity::Warn.into_primitive(),
1142                arguments: vec![
1143                    Argument::pid(zx::Koid::from_raw(0)),
1144                    Argument::tid(zx::Koid::from_raw(0)),
1145                    Argument::dropped(7),
1146                ]
1147            }
1148        );
1149    }
1150
1151    #[test]
1152    fn resizable_vec_mutable_buffer() {
1153        // Putting a slice at offset=len is equivalent to concatenating.
1154        let mut vec = Cursor::new(ResizableBuffer(vec![1u8, 2, 3]));
1155        unsafe {
1156            vec.put_slice_at(&[4, 5, 6], 3);
1157        }
1158        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 4, 5, 6]);
1159
1160        // Putting a slice at an offset inside the buffer, is equivalent to replacing the items
1161        // there.
1162        let mut vec = Cursor::new(ResizableBuffer(vec![1, 3, 7, 9, 11, 13, 15]));
1163        unsafe {
1164            vec.put_slice_at(&[2, 4, 6], 2);
1165        }
1166        assert_eq!(vec.get_ref().0, vec![1, 3, 2, 4, 6, 13, 15]);
1167
1168        // Putting a slice at an index in range replaces all the items and extends if needed.
1169        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1170        unsafe {
1171            vec.put_slice_at(&[4, 5, 6, 7], 0);
1172        }
1173        assert_eq!(vec.get_ref().0, vec![4, 5, 6, 7]);
1174
1175        // Putting a slice at an offset beyond the buffer, fills with zeros the items in between.
1176        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1177        unsafe {
1178            vec.put_slice_at(&[4, 5, 6], 5);
1179        }
1180        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 0, 0, 4, 5, 6]);
1181    }
1182
1183    #[test]
1184    fn add_dropped_count_to_existing_count() {
1185        const INITIAL_DROPPED: u64 = 7;
1186        const EXTRA_DROPPED: u64 = 53;
1187
1188        let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1189        encoder
1190            .write_event(WriteEventParams::<_, &str, _> {
1191                event: TestRecord {
1192                    severity: Severity::Error.into_primitive(),
1193                    timestamp: zx::BootInstant::from_nanos(12345),
1194                    file: None,
1195                    line: Some(123),
1196                    record_arguments: vec![],
1197                },
1198                tags: &[],
1199                metatags: std::iter::empty(),
1200                pid: zx::Koid::from_raw(0),
1201                tid: zx::Koid::from_raw(0),
1202                dropped: INITIAL_DROPPED,
1203            })
1204            .expect("wrote event");
1205        let cursor = encoder.take();
1206        let position = cursor.position();
1207        let mut buffer = cursor.into_inner();
1208        buffer.truncate(position as usize);
1209        assert!(add_dropped_count(&mut buffer, EXTRA_DROPPED));
1210        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1211        assert_eq!(
1212            record,
1213            Record {
1214                timestamp: zx::BootInstant::from_nanos(12345),
1215                severity: Severity::Error.into_primitive(),
1216                arguments: vec![
1217                    Argument::pid(zx::Koid::from_raw(0)),
1218                    Argument::tid(zx::Koid::from_raw(0)),
1219                    Argument::dropped(INITIAL_DROPPED + EXTRA_DROPPED),
1220                    Argument::Line(123),
1221                ]
1222            }
1223        );
1224    }
1225
1226    #[test]
1227    fn add_dropped_count_when_none_exists() {
1228        const DROPPED: u64 = 53;
1229
1230        let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1231        encoder
1232            .write_event(WriteEventParams::<_, &str, _> {
1233                event: TestRecord {
1234                    severity: Severity::Error.into_primitive(),
1235                    timestamp: zx::BootInstant::from_nanos(12345),
1236                    file: None,
1237                    line: Some(123),
1238                    record_arguments: vec![],
1239                },
1240                tags: &[],
1241                metatags: std::iter::empty(),
1242                pid: zx::Koid::from_raw(0),
1243                tid: zx::Koid::from_raw(0),
1244                dropped: 0,
1245            })
1246            .expect("wrote event");
1247        let cursor = encoder.take();
1248        let position = cursor.position();
1249        let mut buffer = cursor.into_inner();
1250        buffer.truncate(position as usize);
1251        assert!(add_dropped_count(&mut buffer, DROPPED));
1252        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1253        assert_eq!(
1254            record,
1255            Record {
1256                timestamp: zx::BootInstant::from_nanos(12345),
1257                severity: Severity::Error.into_primitive(),
1258                arguments: vec![
1259                    Argument::pid(zx::Koid::from_raw(0)),
1260                    Argument::tid(zx::Koid::from_raw(0)),
1261                    Argument::Line(123),
1262                    Argument::dropped(DROPPED),
1263                ]
1264            }
1265        );
1266    }
1267
1268    #[test]
1269    fn add_dropped_count_when_just_enough_room() {
1270        const DROPPED: u64 = 53;
1271
1272        let mut encoder =
1273            Encoder::new(Cursor::new(vec![0; MAX_SIZE_WORDS as usize * 8]), EncoderOpts::default());
1274        // We want the argument to be just big enough so that there is only just enough room for
1275        // the dropped count:
1276        //
1277        //   Header    :    1
1278        //   Timestamp :    1
1279        //   Pid       :    3
1280        //   Tid       :    3
1281        //   Line      :    3
1282        //   Foo       : 4080
1283        //   Dropped   :    4
1284        //               ====
1285        //               4095
1286        let foo_arg = Argument::new("foo", String::from_iter(std::iter::repeat_n('x', 4078 * 8)));
1287        encoder
1288            .write_event(WriteEventParams::<_, &str, _> {
1289                event: TestRecord {
1290                    severity: Severity::Error.into_primitive(),
1291                    timestamp: zx::BootInstant::from_nanos(12345),
1292                    file: None,
1293                    line: Some(123),
1294                    record_arguments: vec![foo_arg.clone()],
1295                },
1296                tags: &[],
1297                metatags: std::iter::empty(),
1298                pid: zx::Koid::from_raw(0),
1299                tid: zx::Koid::from_raw(0),
1300                dropped: 0,
1301            })
1302            .expect("wrote event");
1303        let cursor = encoder.take();
1304        let position = cursor.position();
1305        let mut buffer = cursor.into_inner();
1306        buffer.truncate(position as usize);
1307        assert!(add_dropped_count(&mut buffer, DROPPED));
1308        let (record, _) = parse_record(&buffer).expect("wrote valid record");
1309        assert_eq!(
1310            record,
1311            Record {
1312                timestamp: zx::BootInstant::from_nanos(12345),
1313                severity: Severity::Error.into_primitive(),
1314                arguments: vec![
1315                    Argument::pid(zx::Koid::from_raw(0)),
1316                    Argument::tid(zx::Koid::from_raw(0)),
1317                    Argument::Line(123),
1318                    foo_arg,
1319                    Argument::dropped(DROPPED),
1320                ]
1321            }
1322        );
1323    }
1324
1325    #[test]
1326    fn add_dropped_count_invalid_message() {
1327        // Message too small.
1328        assert!(!add_dropped_count(&mut vec![1, 2, 3], 5));
1329
1330        // Argument too small.
1331        assert!(!add_dropped_count(&mut vec![0; 17], 5));
1332
1333        // Zero argument len.
1334        assert!(!add_dropped_count(&mut vec![0; 24], 5));
1335
1336        // Argument too big.
1337        let mut message = vec![0; 16];
1338        let mut arg_header = Header(0);
1339        arg_header.set_size_words(2);
1340        message.extend(arg_header.0.as_bytes());
1341        assert!(!add_dropped_count(&mut message, 5));
1342
1343        // Message too too big to accept another argument.
1344        let mut message = Vec::new();
1345        let mut header = Header(0);
1346        header.set_size_words(MAX_SIZE_WORDS);
1347        message.extend(header.0.as_bytes());
1348        message.extend([0; 8]); // timestamp
1349        let mut arg_header = Header(0);
1350        arg_header.set_size_words(4093);
1351        message.extend(arg_header.0.as_bytes());
1352        message.resize(4095 * 8, 0);
1353        assert!(!add_dropped_count(&mut message, 5));
1354    }
1355}