1use 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
23pub struct Encoder<B> {
26 pub(crate) buf: B,
27 options: EncoderOpts,
29}
30
31#[derive(Default)]
33pub struct EncoderOpts {
34 pub always_log_file_line: bool,
38}
39
40pub struct WriteEventParams<'a, E, T, MS> {
42 pub event: E,
44 pub tags: &'a [T],
46 pub metatags: MS,
48 pub pid: zx::Koid,
50 pub tid: zx::Koid,
52 pub dropped: u64,
54}
55
56impl<B> Encoder<B>
57where
58 B: MutableBuffer,
59{
60 pub fn new(buf: B, options: EncoderOpts) -> Self {
62 Self { buf, options }
63 }
64
65 pub fn inner(&self) -> &B {
67 &self.buf
68 }
69
70 pub fn take(self) -> B {
72 self.buf
73 }
74
75 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 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 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 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 let starting_idx = self.buf.cursor();
146 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 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 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 fn write_u64(&mut self, n: u64) -> Result<(), EncodingError> {
221 self.buf.put_u64_le(n).map_err(|_| EncodingError::BufferTooSmall)
222 }
223
224 fn write_i64(&mut self, n: i64) -> Result<(), EncodingError> {
226 self.buf.put_i64_le(n).map_err(|_| EncodingError::BufferTooSmall)
227 }
228
229 fn write_f64(&mut self, n: f64) -> Result<(), EncodingError> {
231 self.buf.put_f64(n).map_err(|_| EncodingError::BufferTooSmall)
232 }
233
234 fn write_string(&mut self, src: &str) -> Result<(), EncodingError> {
236 self.write_bytes(src.as_bytes())
237 }
238
239 #[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 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
268pub trait WriteArgumentValue<B>: private::Sealed {
270 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
396pub trait RecordEvent {
398 fn raw_severity(&self) -> RawSeverity;
400 fn file(&self) -> Option<&str>;
402 fn line(&self) -> Option<u32>;
404 fn target(&self) -> &str;
406 fn write_arguments<B: MutableBuffer>(
408 self,
409 writer: &mut Encoder<B>,
410 ) -> Result<(), EncodingError>;
411 fn timestamp(&self) -> zx::BootInstant;
413}
414
415pub trait RecordFields {
417 fn raw_severity(&self) -> RawSeverity;
419
420 fn timestamp(&self) -> zx::BootInstant;
422
423 fn write_arguments<B: MutableBuffer>(
425 self,
426 writer: &mut Encoder<B>,
427 ) -> Result<(), EncodingError>;
428}
429
430pub struct TestRecord<'a> {
432 pub severity: RawSeverity,
434 pub timestamp: zx::BootInstant,
436 pub file: Option<&'a str>,
438 pub line: Option<u32>,
440 pub record_arguments: Vec<Argument<'a>>,
442}
443
444impl TestRecord<'_> {
445 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
530pub trait MutableBuffer {
532 fn capacity(&self) -> usize;
536
537 fn cursor(&self) -> usize;
539
540 unsafe fn advance_cursor(&mut self, n: usize);
547
548 unsafe fn put_slice_at(&mut self, src: &[u8], offset: usize);
555
556 fn has_remaining(&self, num_bytes: usize) -> bool;
558
559 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 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 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 fn put_u64_le(&mut self, n: u64) -> Result<(), EncodingError> {
621 self.put_slice(&n.to_le_bytes())
622 }
623
624 fn put_i64_le(&mut self, n: i64) -> Result<(), EncodingError> {
642 self.put_slice(&n.to_le_bytes())
643 }
644
645 fn put_f64(&mut self, n: f64) -> Result<(), EncodingError> {
661 self.put_slice(&n.to_bits().to_le_bytes())
662 }
663}
664
665#[must_use]
667pub struct WriteSlot {
668 range: std::ops::Range<usize>,
669}
670
671#[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 fn deref(&self) -> &Self::Target {
686 &self.0
687 }
688}
689
690impl ResizableBuffer {
691 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 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 available < to_put.len() {
731 this.extend_from_slice(&to_put[available..]);
732 }
733 } else {
734 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#[derive(Debug, Error)]
854pub enum EncodingError {
855 #[error("buffer is too small")]
857 BufferTooSmall,
858
859 #[error("unsupported value type")]
862 Unsupported,
863
864 #[error("the buffer has no remaining capacity")]
866 NoCapacity,
867
868 #[error(transparent)]
871 Other(Box<dyn std::error::Error + Send + Sync>),
872}
873
874impl EncodingError {
875 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
890pub fn add_dropped_count(message: &mut Vec<u8>, count: u64) -> bool {
892 const DROPPED_HEADER_SIZE_WORDS: u16 = 4; const DROPPED_HEADER: Header = Header(
894 4 | (DROPPED_HEADER_SIZE_WORDS as u64) << 4 | (string_mask(constants::NUM_DROPPED) as u64) << 16, );
898
899 if message.len() < 16 {
900 return false;
901 }
902
903 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 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 }
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 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 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 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 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 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 assert!(!add_dropped_count(&mut vec![1, 2, 3], 5));
1332
1333 assert!(!add_dropped_count(&mut vec![0; 17], 5));
1335
1336 assert!(!add_dropped_count(&mut vec![0; 24], 5));
1338
1339 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 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]); 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}