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