1use crate::{
8 ArgType, Argument, Header, MAX_SIZE_WORDS, Metatag, RawSeverity, Record, Value, constants, zx,
9};
10use fidl_fuchsia_diagnostics_types::Severity;
11use std::array::TryFromSliceError;
12use std::borrow::{Borrow, Cow};
13use std::fmt::Debug;
14use std::io::Cursor;
15use std::ops::Deref;
16use thiserror::Error;
17use zerocopy::{FromBytes, IntoBytes};
18
19pub struct Encoder<B> {
22 pub(crate) buf: B,
23 options: EncoderOpts,
25}
26
27#[derive(Default)]
29pub struct EncoderOpts {
30 pub always_log_file_line: bool,
34}
35
36pub struct WriteEventParams<'a, E, T, MS> {
38 pub event: E,
40 pub tags: &'a [T],
42 pub metatags: MS,
44 pub pid: zx::Koid,
46 pub tid: zx::Koid,
48 pub dropped: u64,
50}
51
52impl<B> Encoder<B>
53where
54 B: MutableBuffer,
55{
56 pub fn new(buf: B, options: EncoderOpts) -> Self {
58 Self { buf, options }
59 }
60
61 pub fn inner(&self) -> &B {
63 &self.buf
64 }
65
66 pub fn take(self) -> B {
68 self.buf
69 }
70
71 pub fn write_event<'a, E, MS, T>(
75 &mut self,
76 params: WriteEventParams<'a, E, T, MS>,
77 ) -> Result<(), EncodingError>
78 where
79 E: RecordEvent,
80 MS: Iterator<Item = &'a Metatag>,
81 T: AsRef<str>,
82 {
83 let WriteEventParams { event, tags, metatags, pid, tid, dropped } = params;
84 let severity = event.raw_severity();
85 self.write_inner(event.timestamp(), severity, |this| {
86 this.write_raw_argument(constants::PID, pid.raw_koid())?;
87 this.write_raw_argument(constants::TID, tid.raw_koid())?;
88 if dropped > 0 {
89 this.write_raw_argument(constants::NUM_DROPPED, dropped)?;
90 }
91 if this.options.always_log_file_line || severity >= Severity::Error.into_primitive() {
92 if let Some(mut file) = event.file() {
94 let split = file.split("../");
95 file = split.last().unwrap();
96 this.write_raw_argument(constants::FILE, Value::Text(Cow::Borrowed(file)))?;
97 }
98
99 if let Some(line) = event.line() {
100 this.write_raw_argument(constants::LINE, line as u64)?;
101 }
102 }
103
104 for metatag in metatags {
106 match metatag {
107 Metatag::Target => this.write_raw_argument(constants::TAG, event.target())?,
108 }
109 }
110
111 event.write_arguments(this)?;
112
113 for tag in tags {
114 this.write_raw_argument(constants::TAG, tag.as_ref())?;
115 }
116 Ok(())
117 })?;
118 Ok(())
119 }
120
121 pub fn write_record<R>(&mut self, record: R) -> Result<(), EncodingError>
123 where
124 R: RecordFields,
125 {
126 self.write_inner(record.timestamp(), record.raw_severity(), |this| {
127 record.write_arguments(this)
128 })
129 }
130
131 fn write_inner<F>(
132 &mut self,
133 timestamp: zx::BootInstant,
134 severity: RawSeverity,
135 write_args: F,
136 ) -> Result<(), EncodingError>
137 where
138 F: FnOnce(&mut Self) -> Result<(), EncodingError>,
139 {
140 let starting_idx = self.buf.cursor();
142 let header_slot = self.buf.put_slot(std::mem::size_of::<u64>())?;
144 self.write_i64(timestamp.into_nanos())?;
145
146 write_args(self)?;
147
148 let mut header = Header(0);
149 header.set_type(crate::TRACING_FORMAT_LOG_RECORD_TYPE);
150 header.set_severity(severity);
151
152 let length = self.buf.cursor() - starting_idx;
153 header.set_len(length);
154
155 assert_eq!(length % 8, 0, "all records must be written 8-byte aligned");
156 self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
157 Ok(())
158 }
159
160 pub fn write_raw_argument(
162 &mut self,
163 name: &str,
164 value: impl WriteArgumentValue<B>,
165 ) -> Result<(), EncodingError> {
166 self.inner_write_argument(move |header, encoder| {
167 encoder.write_argument_name(header, name)?;
168 value.write_value(header, encoder)?;
169 Ok(())
170 })
171 }
172
173 pub fn write_argument<'a>(
175 &mut self,
176 argument: impl Borrow<Argument<'a>>,
177 ) -> Result<(), EncodingError> {
178 let argument = argument.borrow();
179 self.inner_write_argument(move |header, encoder| {
180 encoder.write_argument_name(header, argument.name())?;
181 argument.write_value(header, encoder)?;
182 Ok(())
183 })
184 }
185
186 fn write_argument_name(
187 &mut self,
188 header: &mut Header,
189 name: &str,
190 ) -> Result<(), EncodingError> {
191 self.write_string(name)?;
192 header.set_name_ref(string_mask(name));
193 Ok(())
194 }
195
196 fn inner_write_argument(
197 &mut self,
198 cb: impl FnOnce(&mut Header, &mut Self) -> Result<(), EncodingError>,
199 ) -> Result<(), EncodingError> {
200 let starting_idx = self.buf.cursor();
201 let header_slot = self.buf.put_slot(std::mem::size_of::<Header>())?;
202
203 let mut header = Header(0);
204 cb(&mut header, self)?;
205
206 let record_len = self.buf.cursor() - starting_idx;
207 assert_eq!(record_len % 8, 0, "arguments must be 8-byte aligned");
208
209 header.set_size_words((record_len / 8) as u16);
210 self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
211
212 Ok(())
213 }
214
215 fn write_u64(&mut self, n: u64) -> Result<(), EncodingError> {
217 self.buf.put_u64_le(n).map_err(|_| EncodingError::BufferTooSmall)
218 }
219
220 fn write_i64(&mut self, n: i64) -> Result<(), EncodingError> {
222 self.buf.put_i64_le(n).map_err(|_| EncodingError::BufferTooSmall)
223 }
224
225 fn write_f64(&mut self, n: f64) -> Result<(), EncodingError> {
227 self.buf.put_f64(n).map_err(|_| EncodingError::BufferTooSmall)
228 }
229
230 fn write_string(&mut self, src: &str) -> Result<(), EncodingError> {
232 self.write_bytes(src.as_bytes())
233 }
234
235 #[doc(hidden)]
237 pub fn write_bytes(&mut self, src: &[u8]) -> Result<(), EncodingError> {
238 self.buf.put_slice(src).map_err(|_| EncodingError::BufferTooSmall)?;
239 unsafe {
240 let align = std::mem::size_of::<u64>();
241 let num_padding_bytes = (align - src.len() % align) % align;
242 self.buf.advance_cursor(num_padding_bytes);
244 }
245 Ok(())
246 }
247}
248
249mod private {
250 use super::*;
251
252 pub trait Sealed {}
253 impl Sealed for Value<'_> {}
254 impl Sealed for Argument<'_> {}
255 impl Sealed for u64 {}
256 impl Sealed for f64 {}
257 impl Sealed for i64 {}
258 impl Sealed for bool {}
259 impl Sealed for String {}
260 impl Sealed for &str {}
261 impl Sealed for Cow<'_, str> {}
262}
263
264pub trait WriteArgumentValue<B>: private::Sealed {
266 fn write_value(
268 &self,
269 header: &mut Header,
270 encoder: &mut Encoder<B>,
271 ) -> Result<(), EncodingError>;
272}
273
274impl<B: MutableBuffer> WriteArgumentValue<B> for Argument<'_> {
275 fn write_value(
276 &self,
277 header: &mut Header,
278 encoder: &mut Encoder<B>,
279 ) -> Result<(), EncodingError> {
280 match self {
281 Self::Pid(value) | Self::Tid(value) => value.raw_koid().write_value(header, encoder),
282 Self::Line(value) | Self::Dropped(value) => value.write_value(header, encoder),
283 Self::Tag(value) | Self::File(value) | Self::Message(value) => {
284 value.write_value(header, encoder)
285 }
286 Self::Other { value, .. } => value.write_value(header, encoder),
287 }
288 }
289}
290
291impl<B: MutableBuffer> WriteArgumentValue<B> for i64 {
292 fn write_value(
293 &self,
294 header: &mut Header,
295 encoder: &mut Encoder<B>,
296 ) -> Result<(), EncodingError> {
297 header.set_type(ArgType::I64 as u8);
298 encoder.write_i64(*self)
299 }
300}
301
302impl<B: MutableBuffer> WriteArgumentValue<B> for u64 {
303 fn write_value(
304 &self,
305 header: &mut Header,
306 encoder: &mut Encoder<B>,
307 ) -> Result<(), EncodingError> {
308 header.set_type(ArgType::U64 as u8);
309 encoder.write_u64(*self)
310 }
311}
312
313impl<B: MutableBuffer> WriteArgumentValue<B> for f64 {
314 fn write_value(
315 &self,
316 header: &mut Header,
317 encoder: &mut Encoder<B>,
318 ) -> Result<(), EncodingError> {
319 header.set_type(ArgType::F64 as u8);
320 encoder.write_f64(*self)
321 }
322}
323
324impl<B: MutableBuffer> WriteArgumentValue<B> for bool {
325 fn write_value(
326 &self,
327 header: &mut Header,
328 _encoder: &mut Encoder<B>,
329 ) -> Result<(), EncodingError> {
330 header.set_type(ArgType::Bool as u8);
331 header.set_bool_val(*self);
332 Ok(())
333 }
334}
335
336impl<B: MutableBuffer> WriteArgumentValue<B> for &str {
337 fn write_value(
338 &self,
339 header: &mut Header,
340 encoder: &mut Encoder<B>,
341 ) -> Result<(), EncodingError> {
342 header.set_type(ArgType::String as u8);
343 header.set_value_ref(string_mask(self));
344 encoder.write_string(self)
345 }
346}
347
348impl<B: MutableBuffer> WriteArgumentValue<B> for String {
349 fn write_value(
350 &self,
351 header: &mut Header,
352 encoder: &mut Encoder<B>,
353 ) -> Result<(), EncodingError> {
354 self.as_str().write_value(header, encoder)
355 }
356}
357
358impl<B: MutableBuffer> WriteArgumentValue<B> for Cow<'_, str> {
359 fn write_value(
360 &self,
361 header: &mut Header,
362 encoder: &mut Encoder<B>,
363 ) -> Result<(), EncodingError> {
364 self.as_ref().write_value(header, encoder)
365 }
366}
367
368impl<B: MutableBuffer> WriteArgumentValue<B> for Value<'_> {
369 fn write_value(
370 &self,
371 header: &mut Header,
372 encoder: &mut Encoder<B>,
373 ) -> Result<(), EncodingError> {
374 match self {
375 Value::SignedInt(s) => s.write_value(header, encoder),
376 Value::UnsignedInt(u) => u.write_value(header, encoder),
377 Value::Floating(f) => f.write_value(header, encoder),
378 Value::Text(t) => t.write_value(header, encoder),
379 Value::Boolean(b) => b.write_value(header, encoder),
380 }
381 }
382}
383
384const fn string_mask(s: &str) -> u16 {
385 let len = s.len();
386 if len == 0 {
387 return 0;
388 }
389 (len as u16) | (1 << 15)
390}
391
392pub trait RecordEvent {
394 fn raw_severity(&self) -> RawSeverity;
396 fn file(&self) -> Option<&str>;
398 fn line(&self) -> Option<u32>;
400 fn target(&self) -> &str;
402 fn write_arguments<B: MutableBuffer>(
404 self,
405 writer: &mut Encoder<B>,
406 ) -> Result<(), EncodingError>;
407 fn timestamp(&self) -> zx::BootInstant;
409}
410
411pub trait RecordFields {
413 fn raw_severity(&self) -> RawSeverity;
415
416 fn timestamp(&self) -> zx::BootInstant;
418
419 fn write_arguments<B: MutableBuffer>(
421 self,
422 writer: &mut Encoder<B>,
423 ) -> Result<(), EncodingError>;
424}
425
426pub struct TestRecord<'a> {
428 pub severity: RawSeverity,
430 pub timestamp: zx::BootInstant,
432 pub file: Option<&'a str>,
434 pub line: Option<u32>,
436 pub record_arguments: Vec<Argument<'a>>,
438}
439
440impl TestRecord<'_> {
441 pub fn from<'a>(file: &'a str, line: u32, record: &'a Record<'a>) -> TestRecord<'a> {
443 TestRecord {
444 severity: record.severity,
445 timestamp: record.timestamp,
446 file: Some(file),
447 line: Some(line),
448 record_arguments: record.arguments.clone(),
449 }
450 }
451}
452
453impl RecordEvent for TestRecord<'_> {
454 fn raw_severity(&self) -> RawSeverity {
455 self.severity
456 }
457
458 fn file(&self) -> Option<&str> {
459 self.file
460 }
461
462 fn line(&self) -> Option<u32> {
463 self.line
464 }
465
466 fn target(&self) -> &str {
467 unimplemented!("Unused at the moment");
468 }
469
470 fn timestamp(&self) -> zx::BootInstant {
471 self.timestamp
472 }
473
474 fn write_arguments<B: MutableBuffer>(
475 self,
476 writer: &mut Encoder<B>,
477 ) -> Result<(), EncodingError> {
478 for argument in self.record_arguments {
479 writer.write_argument(argument)?;
480 }
481 Ok(())
482 }
483}
484
485impl RecordFields for Record<'_> {
486 fn raw_severity(&self) -> RawSeverity {
487 self.severity
488 }
489
490 fn write_arguments<B: MutableBuffer>(
491 self,
492 writer: &mut Encoder<B>,
493 ) -> Result<(), EncodingError> {
494 for arg in self.arguments {
495 writer.write_argument(arg)?;
496 }
497 Ok(())
498 }
499
500 fn timestamp(&self) -> zx::BootInstant {
501 self.timestamp
502 }
503}
504
505#[cfg(test)]
506impl RecordFields for &Record<'_> {
507 fn raw_severity(&self) -> RawSeverity {
508 self.severity
509 }
510
511 fn write_arguments<B: MutableBuffer>(
512 self,
513 writer: &mut Encoder<B>,
514 ) -> Result<(), EncodingError> {
515 for arg in &self.arguments {
516 writer.write_argument(arg)?;
517 }
518 Ok(())
519 }
520
521 fn timestamp(&self) -> zx::BootInstant {
522 self.timestamp
523 }
524}
525
526pub trait MutableBuffer {
528 fn capacity(&self) -> usize;
532
533 fn cursor(&self) -> usize;
535
536 unsafe fn advance_cursor(&mut self, n: usize);
543
544 unsafe fn put_slice_at(&mut self, src: &[u8], offset: usize);
551
552 fn has_remaining(&self, num_bytes: usize) -> bool;
554
555 fn put_slot(&mut self, width: usize) -> Result<WriteSlot, EncodingError> {
558 if self.has_remaining(width) {
559 let slot = WriteSlot { range: self.cursor()..(self.cursor() + width) };
560 unsafe {
561 self.advance_cursor(width);
562 }
563 Ok(slot)
564 } else {
565 Err(EncodingError::BufferTooSmall)
566 }
567 }
568
569 fn fill_slot(&mut self, slot: WriteSlot, src: &[u8]) {
571 assert_eq!(
572 src.len(),
573 slot.range.end - slot.range.start,
574 "WriteSlots can only insert exactly-sized content into the buffer"
575 );
576 unsafe {
577 self.put_slice_at(src, slot.range.start);
578 }
579 }
580
581 fn put_slice(&mut self, src: &[u8]) -> Result<(), EncodingError> {
588 if self.has_remaining(src.len()) {
589 unsafe {
590 self.put_slice_at(src, self.cursor());
591 self.advance_cursor(src.len());
592 }
593 Ok(())
594 } else {
595 Err(EncodingError::NoCapacity)
596 }
597 }
598
599 fn put_u64_le(&mut self, n: u64) -> Result<(), EncodingError> {
617 self.put_slice(&n.to_le_bytes())
618 }
619
620 fn put_i64_le(&mut self, n: i64) -> Result<(), EncodingError> {
638 self.put_slice(&n.to_le_bytes())
639 }
640
641 fn put_f64(&mut self, n: f64) -> Result<(), EncodingError> {
657 self.put_slice(&n.to_bits().to_le_bytes())
658 }
659}
660
661#[must_use]
663pub struct WriteSlot {
664 range: std::ops::Range<usize>,
665}
666
667#[derive(Debug, Default)]
669pub struct ResizableBuffer(Vec<u8>);
670
671impl From<Vec<u8>> for ResizableBuffer {
672 fn from(buf: Vec<u8>) -> Self {
673 Self(buf)
674 }
675}
676
677impl Deref for ResizableBuffer {
678 type Target = Vec<u8>;
679
680 fn deref(&self) -> &Self::Target {
682 &self.0
683 }
684}
685
686impl ResizableBuffer {
687 pub fn into_inner(self) -> Vec<u8> {
689 self.0
690 }
691}
692
693impl MutableBuffer for Cursor<ResizableBuffer> {
694 fn capacity(&self) -> usize {
695 self.get_ref().0.len()
696 }
697
698 fn cursor(&self) -> usize {
699 self.position() as usize
700 }
701
702 fn has_remaining(&self, _num_bytes: usize) -> bool {
703 true
704 }
705
706 unsafe fn advance_cursor(&mut self, n: usize) {
707 let new_pos = self.position() as usize + n;
708 let vec = &mut self.get_mut().0;
709 if new_pos > vec.len() {
710 vec.resize(new_pos, 0);
711 }
712 self.set_position(new_pos as u64);
713 }
714
715 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
716 let this = &mut self.get_mut().0;
717 if offset < this.len() {
718 let available = this.len() - offset;
719
720 let min = available.min(to_put.len());
722 let dest = &mut this[offset..(offset + min)];
723 dest.copy_from_slice(&to_put[..min]);
724
725 if available < to_put.len() {
727 this.extend_from_slice(&to_put[available..]);
728 }
729 } else {
730 this.resize(offset, 0);
733 this.extend_from_slice(to_put);
734 }
735 }
736}
737
738impl<T: MutableBuffer + ?Sized> MutableBuffer for &mut T {
739 fn has_remaining(&self, num_bytes: usize) -> bool {
740 (**self).has_remaining(num_bytes)
741 }
742 fn capacity(&self) -> usize {
743 (**self).capacity()
744 }
745
746 fn cursor(&self) -> usize {
747 (**self).cursor()
748 }
749
750 unsafe fn advance_cursor(&mut self, n: usize) {
751 unsafe { (**self).advance_cursor(n) };
752 }
753
754 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
755 unsafe { (**self).put_slice_at(to_put, offset) };
756 }
757}
758
759impl<T: MutableBuffer + ?Sized> MutableBuffer for Box<T> {
760 fn has_remaining(&self, num_bytes: usize) -> bool {
761 (**self).has_remaining(num_bytes)
762 }
763 fn capacity(&self) -> usize {
764 (**self).capacity()
765 }
766
767 fn cursor(&self) -> usize {
768 (**self).cursor()
769 }
770
771 unsafe fn advance_cursor(&mut self, n: usize) {
772 unsafe { (**self).advance_cursor(n) };
773 }
774
775 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
776 unsafe { (**self).put_slice_at(to_put, offset) };
777 }
778}
779
780impl MutableBuffer for Cursor<Vec<u8>> {
781 fn has_remaining(&self, num_bytes: usize) -> bool {
782 (self.cursor() + num_bytes) <= self.capacity()
783 }
784
785 fn capacity(&self) -> usize {
786 self.get_ref().len()
787 }
788
789 fn cursor(&self) -> usize {
790 self.position() as usize
791 }
792
793 unsafe fn advance_cursor(&mut self, n: usize) {
794 self.set_position(self.position() + n as u64);
795 }
796
797 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
798 let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
799 dest.copy_from_slice(to_put);
800 }
801}
802
803impl MutableBuffer for Cursor<&mut [u8]> {
804 fn has_remaining(&self, num_bytes: usize) -> bool {
805 (self.cursor() + num_bytes) <= self.capacity()
806 }
807
808 fn capacity(&self) -> usize {
809 self.get_ref().len()
810 }
811
812 fn cursor(&self) -> usize {
813 self.position() as usize
814 }
815
816 unsafe fn advance_cursor(&mut self, n: usize) {
817 self.set_position(self.position() + n as u64);
818 }
819
820 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
821 let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
822 dest.copy_from_slice(to_put);
823 }
824}
825
826impl<const N: usize> MutableBuffer for Cursor<[u8; N]> {
827 fn has_remaining(&self, num_bytes: usize) -> bool {
828 (self.cursor() + num_bytes) <= self.capacity()
829 }
830 fn capacity(&self) -> usize {
831 self.get_ref().len()
832 }
833
834 fn cursor(&self) -> usize {
835 self.position() as usize
836 }
837
838 unsafe fn advance_cursor(&mut self, n: usize) {
839 self.set_position(self.position() + n as u64);
840 }
841
842 unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
843 let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
844 dest.copy_from_slice(to_put);
845 }
846}
847
848#[derive(Debug, Error)]
850pub enum EncodingError {
851 #[error("buffer is too small")]
853 BufferTooSmall,
854
855 #[error("unsupported value type")]
858 Unsupported,
859
860 #[error("the buffer has no remaining capacity")]
862 NoCapacity,
863
864 #[error(transparent)]
867 Other(Box<dyn std::error::Error + Send + Sync>),
868}
869
870impl EncodingError {
871 pub fn other<E>(err: E) -> Self
873 where
874 E: std::error::Error + Send + Sync + 'static,
875 {
876 Self::Other(err.into())
877 }
878}
879
880impl From<TryFromSliceError> for EncodingError {
881 fn from(_: TryFromSliceError) -> Self {
882 EncodingError::BufferTooSmall
883 }
884}
885
886pub fn add_dropped_count(message: &mut Vec<u8>, count: u64) -> bool {
888 const DROPPED_HEADER_SIZE_WORDS: u16 = 4; const DROPPED_HEADER: Header = Header(
890 4 | (DROPPED_HEADER_SIZE_WORDS as u64) << 4 | (string_mask(constants::NUM_DROPPED) as u64) << 16, );
894
895 if message.len() < 16 {
896 return false;
897 }
898
899 let mut argument = &mut message[16..];
901 while !argument.is_empty() {
902 let Ok((header, _)) = Header::read_from_prefix(argument) else {
903 return false;
904 };
905 let arg_len = header.size_words() as usize * 8;
906 if arg_len == 0 || arg_len > argument.len() {
907 return false;
908 }
909 if header.0 == DROPPED_HEADER.0
910 && &argument[8..8 + constants::NUM_DROPPED.len()] == constants::NUM_DROPPED.as_bytes()
911 {
912 let value = u64::mut_from_bytes(&mut argument[24..32]).unwrap();
913 *value = value.saturating_add(count);
914 return true;
915 }
916 argument = &mut argument[arg_len..];
917 }
918
919 let message_header = Header::mut_from_bytes(&mut message[..8]).unwrap();
920 let new_size = message_header.size_words() + DROPPED_HEADER_SIZE_WORDS;
921 if new_size > MAX_SIZE_WORDS {
922 return false;
923 }
924 message_header.set_size_words(new_size);
925
926 message.extend(DROPPED_HEADER.0.as_bytes());
928 message.extend(constants::NUM_DROPPED.as_bytes());
929 message.extend(std::iter::repeat_n(0, 16 - constants::NUM_DROPPED.len()));
930 message.extend(count.as_bytes());
931
932 true
933}
934
935#[doc(hidden)]
936pub struct LogEvent<'a> {
937 record: &'a log::Record<'a>,
938 timestamp: zx::BootInstant,
939}
940
941impl<'a> LogEvent<'a> {
942 pub fn new(record: &'a log::Record<'a>) -> Self {
943 Self { record, timestamp: zx::BootInstant::get() }
944 }
945}
946
947impl RecordEvent for LogEvent<'_> {
948 fn raw_severity(&self) -> RawSeverity {
949 diagnostics_log_types::Severity::from(self.record.metadata().level()) as RawSeverity
950 }
951
952 fn file(&self) -> Option<&str> {
953 self.record.file()
954 }
955
956 fn line(&self) -> Option<u32> {
957 self.record.line()
958 }
959
960 fn target(&self) -> &str {
961 self.record.target()
962 }
963
964 fn timestamp(&self) -> zx::BootInstant {
965 self.timestamp
966 }
967
968 fn write_arguments<B: MutableBuffer>(
969 self,
970 writer: &mut Encoder<B>,
971 ) -> Result<(), EncodingError> {
972 let args = self.record.args();
973 let message =
974 args.as_str().map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(args.to_string()));
975 writer.write_argument(Argument::message(message))?;
976 self.record
977 .key_values()
978 .visit(&mut KeyValuesVisitor(writer))
979 .map_err(EncodingError::other)?;
980 Ok(())
981 }
982}
983
984struct KeyValuesVisitor<'a, B>(&'a mut Encoder<B>);
985
986impl<B: MutableBuffer> log::kv::VisitSource<'_> for KeyValuesVisitor<'_, B> {
987 fn visit_pair(
988 &mut self,
989 key: log::kv::Key<'_>,
990 value: log::kv::Value<'_>,
991 ) -> Result<(), log::kv::Error> {
992 value.visit(ValueVisitor { encoder: self.0, key: key.as_str() })
993 }
994}
995
996struct ValueVisitor<'a, B> {
997 encoder: &'a mut Encoder<B>,
998 key: &'a str,
999}
1000
1001impl<B: MutableBuffer> log::kv::VisitValue<'_> for ValueVisitor<'_, B> {
1002 fn visit_any(&mut self, value: log::kv::Value<'_>) -> Result<(), log::kv::Error> {
1003 self.encoder
1004 .write_raw_argument(self.key, format!("{value}"))
1005 .map_err(log::kv::Error::boxed)?;
1006 Ok(())
1007 }
1008
1009 fn visit_null(&mut self) -> Result<(), log::kv::Error> {
1010 self.encoder.write_raw_argument(self.key, "null").map_err(log::kv::Error::boxed)?;
1011 Ok(())
1012 }
1013
1014 fn visit_u64(&mut self, value: u64) -> Result<(), log::kv::Error> {
1015 self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1016 Ok(())
1017 }
1018
1019 fn visit_i64(&mut self, value: i64) -> Result<(), log::kv::Error> {
1020 self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1021 Ok(())
1022 }
1023
1024 fn visit_f64(&mut self, value: f64) -> Result<(), log::kv::Error> {
1025 self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1026 Ok(())
1027 }
1028
1029 fn visit_bool(&mut self, value: bool) -> Result<(), log::kv::Error> {
1030 self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1031 Ok(())
1032 }
1033
1034 fn visit_str(&mut self, value: &str) -> Result<(), log::kv::Error> {
1035 self.encoder.write_raw_argument(self.key, value).map_err(log::kv::Error::boxed)?;
1036 Ok(())
1037 }
1038
1039 }
1042
1043#[cfg(test)]
1044mod tests {
1045 use super::*;
1046 use crate::parse::parse_record;
1047
1048 #[fuchsia::test]
1049 fn build_basic_record() {
1050 let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1051 encoder
1052 .write_event(WriteEventParams::<_, &str, _> {
1053 event: TestRecord {
1054 severity: Severity::Info.into_primitive(),
1055 timestamp: zx::BootInstant::from_nanos(12345),
1056 file: None,
1057 line: None,
1058 record_arguments: vec![],
1059 },
1060 tags: &[],
1061 metatags: std::iter::empty(),
1062 pid: zx::Koid::from_raw(0),
1063 tid: zx::Koid::from_raw(0),
1064 dropped: 0,
1065 })
1066 .expect("wrote event");
1067 let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1068 assert_eq!(
1069 record,
1070 Record {
1071 timestamp: zx::BootInstant::from_nanos(12345),
1072 severity: Severity::Info.into_primitive(),
1073 arguments: vec![
1074 Argument::pid(zx::Koid::from_raw(0)),
1075 Argument::tid(zx::Koid::from_raw(0)),
1076 ]
1077 }
1078 );
1079 }
1080
1081 #[fuchsia::test]
1082 fn build_records_with_location() {
1083 let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1084 encoder
1085 .write_event(WriteEventParams::<_, &str, _> {
1086 event: TestRecord {
1087 severity: Severity::Error.into_primitive(),
1088 timestamp: zx::BootInstant::from_nanos(12345),
1089 file: Some("foo.rs"),
1090 line: Some(10),
1091 record_arguments: vec![],
1092 },
1093 tags: &[],
1094 metatags: std::iter::empty(),
1095 pid: zx::Koid::from_raw(0),
1096 tid: zx::Koid::from_raw(0),
1097 dropped: 0,
1098 })
1099 .expect("wrote event");
1100 let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1101 assert_eq!(
1102 record,
1103 Record {
1104 timestamp: zx::BootInstant::from_nanos(12345),
1105 severity: Severity::Error.into_primitive(),
1106 arguments: vec![
1107 Argument::pid(zx::Koid::from_raw(0)),
1108 Argument::tid(zx::Koid::from_raw(0)),
1109 Argument::file("foo.rs"),
1110 Argument::line(10),
1111 ]
1112 }
1113 );
1114 }
1115
1116 #[fuchsia::test]
1117 fn build_record_with_dropped_count() {
1118 let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
1119 encoder
1120 .write_event(WriteEventParams::<_, &str, _> {
1121 event: TestRecord {
1122 severity: Severity::Warn.into_primitive(),
1123 timestamp: zx::BootInstant::from_nanos(12345),
1124 file: None,
1125 line: None,
1126 record_arguments: vec![],
1127 },
1128 tags: &[],
1129 metatags: std::iter::empty(),
1130 pid: zx::Koid::from_raw(0),
1131 tid: zx::Koid::from_raw(0),
1132 dropped: 7,
1133 })
1134 .expect("wrote event");
1135 let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
1136 assert_eq!(
1137 record,
1138 Record {
1139 timestamp: zx::BootInstant::from_nanos(12345),
1140 severity: Severity::Warn.into_primitive(),
1141 arguments: vec![
1142 Argument::pid(zx::Koid::from_raw(0)),
1143 Argument::tid(zx::Koid::from_raw(0)),
1144 Argument::dropped(7),
1145 ]
1146 }
1147 );
1148 }
1149
1150 #[test]
1151 fn resizable_vec_mutable_buffer() {
1152 let mut vec = Cursor::new(ResizableBuffer(vec![1u8, 2, 3]));
1154 unsafe {
1155 vec.put_slice_at(&[4, 5, 6], 3);
1156 }
1157 assert_eq!(vec.get_ref().0, vec![1, 2, 3, 4, 5, 6]);
1158
1159 let mut vec = Cursor::new(ResizableBuffer(vec![1, 3, 7, 9, 11, 13, 15]));
1162 unsafe {
1163 vec.put_slice_at(&[2, 4, 6], 2);
1164 }
1165 assert_eq!(vec.get_ref().0, vec![1, 3, 2, 4, 6, 13, 15]);
1166
1167 let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1169 unsafe {
1170 vec.put_slice_at(&[4, 5, 6, 7], 0);
1171 }
1172 assert_eq!(vec.get_ref().0, vec![4, 5, 6, 7]);
1173
1174 let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1176 unsafe {
1177 vec.put_slice_at(&[4, 5, 6], 5);
1178 }
1179 assert_eq!(vec.get_ref().0, vec![1, 2, 3, 0, 0, 4, 5, 6]);
1180 }
1181
1182 #[test]
1183 fn add_dropped_count_to_existing_count() {
1184 const INITIAL_DROPPED: u64 = 7;
1185 const EXTRA_DROPPED: u64 = 53;
1186
1187 let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1188 encoder
1189 .write_event(WriteEventParams::<_, &str, _> {
1190 event: TestRecord {
1191 severity: Severity::Error.into_primitive(),
1192 timestamp: zx::BootInstant::from_nanos(12345),
1193 file: None,
1194 line: Some(123),
1195 record_arguments: vec![],
1196 },
1197 tags: &[],
1198 metatags: std::iter::empty(),
1199 pid: zx::Koid::from_raw(0),
1200 tid: zx::Koid::from_raw(0),
1201 dropped: INITIAL_DROPPED,
1202 })
1203 .expect("wrote event");
1204 let cursor = encoder.take();
1205 let position = cursor.position();
1206 let mut buffer = cursor.into_inner();
1207 buffer.truncate(position as usize);
1208 assert!(add_dropped_count(&mut buffer, EXTRA_DROPPED));
1209 let (record, _) = parse_record(&buffer).expect("wrote valid record");
1210 assert_eq!(
1211 record,
1212 Record {
1213 timestamp: zx::BootInstant::from_nanos(12345),
1214 severity: Severity::Error.into_primitive(),
1215 arguments: vec![
1216 Argument::pid(zx::Koid::from_raw(0)),
1217 Argument::tid(zx::Koid::from_raw(0)),
1218 Argument::dropped(INITIAL_DROPPED + EXTRA_DROPPED),
1219 Argument::Line(123),
1220 ]
1221 }
1222 );
1223 }
1224
1225 #[test]
1226 fn add_dropped_count_when_none_exists() {
1227 const DROPPED: u64 = 53;
1228
1229 let mut encoder = Encoder::new(Cursor::new(vec![0; 256]), EncoderOpts::default());
1230 encoder
1231 .write_event(WriteEventParams::<_, &str, _> {
1232 event: TestRecord {
1233 severity: Severity::Error.into_primitive(),
1234 timestamp: zx::BootInstant::from_nanos(12345),
1235 file: None,
1236 line: Some(123),
1237 record_arguments: vec![],
1238 },
1239 tags: &[],
1240 metatags: std::iter::empty(),
1241 pid: zx::Koid::from_raw(0),
1242 tid: zx::Koid::from_raw(0),
1243 dropped: 0,
1244 })
1245 .expect("wrote event");
1246 let cursor = encoder.take();
1247 let position = cursor.position();
1248 let mut buffer = cursor.into_inner();
1249 buffer.truncate(position as usize);
1250 assert!(add_dropped_count(&mut buffer, DROPPED));
1251 let (record, _) = parse_record(&buffer).expect("wrote valid record");
1252 assert_eq!(
1253 record,
1254 Record {
1255 timestamp: zx::BootInstant::from_nanos(12345),
1256 severity: Severity::Error.into_primitive(),
1257 arguments: vec![
1258 Argument::pid(zx::Koid::from_raw(0)),
1259 Argument::tid(zx::Koid::from_raw(0)),
1260 Argument::Line(123),
1261 Argument::dropped(DROPPED),
1262 ]
1263 }
1264 );
1265 }
1266
1267 #[test]
1268 fn add_dropped_count_when_just_enough_room() {
1269 const DROPPED: u64 = 53;
1270
1271 let mut encoder =
1272 Encoder::new(Cursor::new(vec![0; MAX_SIZE_WORDS as usize * 8]), EncoderOpts::default());
1273 let foo_arg = Argument::new("foo", String::from_iter(std::iter::repeat_n('x', 4078 * 8)));
1286 encoder
1287 .write_event(WriteEventParams::<_, &str, _> {
1288 event: TestRecord {
1289 severity: Severity::Error.into_primitive(),
1290 timestamp: zx::BootInstant::from_nanos(12345),
1291 file: None,
1292 line: Some(123),
1293 record_arguments: vec![foo_arg.clone()],
1294 },
1295 tags: &[],
1296 metatags: std::iter::empty(),
1297 pid: zx::Koid::from_raw(0),
1298 tid: zx::Koid::from_raw(0),
1299 dropped: 0,
1300 })
1301 .expect("wrote event");
1302 let cursor = encoder.take();
1303 let position = cursor.position();
1304 let mut buffer = cursor.into_inner();
1305 buffer.truncate(position as usize);
1306 assert!(add_dropped_count(&mut buffer, DROPPED));
1307 let (record, _) = parse_record(&buffer).expect("wrote valid record");
1308 assert_eq!(
1309 record,
1310 Record {
1311 timestamp: zx::BootInstant::from_nanos(12345),
1312 severity: Severity::Error.into_primitive(),
1313 arguments: vec![
1314 Argument::pid(zx::Koid::from_raw(0)),
1315 Argument::tid(zx::Koid::from_raw(0)),
1316 Argument::Line(123),
1317 foo_arg,
1318 Argument::dropped(DROPPED),
1319 ]
1320 }
1321 );
1322 }
1323
1324 #[test]
1325 fn add_dropped_count_invalid_message() {
1326 assert!(!add_dropped_count(&mut vec![1, 2, 3], 5));
1328
1329 assert!(!add_dropped_count(&mut vec![0; 17], 5));
1331
1332 assert!(!add_dropped_count(&mut vec![0; 24], 5));
1334
1335 let mut message = vec![0; 16];
1337 let mut arg_header = Header(0);
1338 arg_header.set_size_words(2);
1339 message.extend(arg_header.0.as_bytes());
1340 assert!(!add_dropped_count(&mut message, 5));
1341
1342 let mut message = Vec::new();
1344 let mut header = Header(0);
1345 header.set_size_words(MAX_SIZE_WORDS);
1346 message.extend(header.0.as_bytes());
1347 message.extend([0; 8]); let mut arg_header = Header(0);
1349 arg_header.set_size_words(4093);
1350 message.extend(arg_header.0.as_bytes());
1351 message.resize(4095 * 8, 0);
1352 assert!(!add_dropped_count(&mut message, 5));
1353 }
1354}