1#![no_std]
8
9use unittest as _;
10
11use arch_rs::{InterruptDisableGuard, curr_cpu_num, ints_disabled};
12use core::cell::UnsafeCell;
13use core::mem::{MaybeUninit, size_of};
14use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering};
15use core::{ffi, ptr, slice};
16use kalloc::Box;
17use kernel::thread::{FxtRef, ThreadPtr};
18pub use kstring::declare_interned_category;
19use kstring::declare_interned_string;
20use kstring::interned_category::InternedCategory;
21pub use kstring::interned_string::InternedString;
22pub use platform_rs::{InstantBootTicks, timer_current_boot_ticks};
23use spsc_buffer::{Buffer, NoOpAllocator, Reservation};
24use zx_status::Status;
25
26pub use ktrace_macro::*;
28#[allow(unused_extern_crates)]
29extern crate self as ktrace_rs;
30
31#[repr(C)]
33pub struct KTraceState {
34 pub categories_bitmask: AtomicU32,
35 pub writes_enabled: AtomicBool,
36}
37const _: () = assert!(size_of::<KTraceState>() == 8);
38declare_interned_category!(META_CAT, "kernel:meta", extern);
41declare_interned_string!(DROP_STATS_REF, "drop_stats", extern);
42declare_interned_string!(NUM_RECORDS_REF, "num_records", extern);
43declare_interned_string!(NUM_BYTES_REF, "num_bytes", extern);
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Context {
47 Thread = 0,
48 Cpu = 1,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum EventType {
53 Instant = 0,
54 Counter = 1,
55 DurationBegin = 2,
56 DurationEnd = 3,
57 DurationComplete = 4,
58 FlowBegin = 8,
59 FlowStep = 9,
60 FlowEnd = 10,
61}
62
63pub enum ArgValue<'a> {
65 Null,
66 Bool(bool),
67 Int32(i32),
68 Uint32(u32),
69 Int64(i64),
70 Uint64(u64),
71 Double(f64),
72 String(&'a str),
73 Pointer(usize),
74 Koid(u64),
75}
76
77impl<'a> From<bool> for ArgValue<'a> {
78 fn from(v: bool) -> Self {
79 ArgValue::Bool(v)
80 }
81}
82impl<'a> From<i32> for ArgValue<'a> {
83 fn from(v: i32) -> Self {
84 ArgValue::Int32(v)
85 }
86}
87impl<'a> From<u32> for ArgValue<'a> {
88 fn from(v: u32) -> Self {
89 ArgValue::Uint32(v)
90 }
91}
92impl<'a> From<i64> for ArgValue<'a> {
93 fn from(v: i64) -> Self {
94 ArgValue::Int64(v)
95 }
96}
97impl<'a> From<u64> for ArgValue<'a> {
98 fn from(v: u64) -> Self {
99 ArgValue::Uint64(v)
100 }
101}
102impl<'a> From<f64> for ArgValue<'a> {
103 fn from(v: f64) -> Self {
104 ArgValue::Double(v)
105 }
106}
107impl<'a> From<&'a str> for ArgValue<'a> {
108 fn from(v: &'a str) -> Self {
109 ArgValue::String(v)
110 }
111}
112
113pub struct Argument<'a> {
114 name: &'static InternedString,
115 value: ArgValue<'a>,
116}
117
118impl<'a> Argument<'a> {
119 pub fn new(name: &'static InternedString, value: impl Into<ArgValue<'a>>) -> Self {
120 Self { name, value: value.into() }
121 }
122
123 fn size_words(&self) -> usize {
125 match &self.value {
126 ArgValue::Null | ArgValue::Bool(_) | ArgValue::Int32(_) | ArgValue::Uint32(_) => 1,
127 ArgValue::Int64(_)
128 | ArgValue::Uint64(_)
129 | ArgValue::Double(_)
130 | ArgValue::Pointer(_)
131 | ArgValue::Koid(_) => 2,
132 ArgValue::String(s) => 1 + (s.len() + 7) / 8,
133 }
134 }
135
136 fn write(&self, res: &mut KTraceReservation<'_>) -> Result<(), Status> {
137 let name_id = self.name.id() as u64;
138 let mut header = 0u64;
139 header |= (name_id & 0xffff) << 16; match &self.value {
142 ArgValue::Null => {
143 header |= 0u64; res.write_word(header)?;
145 }
146 ArgValue::Int32(v) => {
147 header |= 1u64; header |= ((*v as u32) as u64) << 32;
149 res.write_word(header)?;
150 }
151 ArgValue::Uint32(v) => {
152 header |= 2u64; header |= (*v as u64) << 32;
154 res.write_word(header)?;
155 }
156 ArgValue::Int64(v) => {
157 header |= 3u64; res.write_word(header)?;
159 res.write_word(*v as u64)?;
160 }
161 ArgValue::Uint64(v) => {
162 header |= 4u64; res.write_word(header)?;
164 res.write_word(*v)?;
165 }
166 ArgValue::Double(v) => {
167 header |= 5u64; res.write_word(header)?;
169 res.write_word(v.to_bits())?;
170 }
171 ArgValue::String(s) => {
172 header |= 6u64; let string_len = s.len();
174 header |= (((string_len + 7) / 8) as u64) << 4; header |= (string_len as u64) << 32; res.write_word(header)?;
177 res.write_bytes(s.as_bytes())?;
178 }
179 ArgValue::Pointer(v) => {
180 header |= 7u64; res.write_word(header)?;
182 res.write_word(*v as u64)?;
183 }
184 ArgValue::Koid(v) => {
185 header |= 8u64; res.write_word(header)?;
187 res.write_word(*v)?;
188 }
189 ArgValue::Bool(v) => {
190 header |= 9u64; header |= ((*v as u64) & 1) << 32;
192 res.write_word(header)?;
193 }
194 }
195 Ok(())
196 }
197}
198
199pub struct KTraceScope<'a> {
200 category: &'static InternedCategory,
201 name: &'static InternedString,
202 timestamp: InstantBootTicks,
203 context: Context,
204 args: &'a [Argument<'a>],
205}
206
207impl<'a> KTraceScope<'a> {
208 #[inline(never)]
209 #[cold]
210 pub fn begin(
211 category: &'static InternedCategory,
212 name: &'static InternedString,
213 context: Context,
214 args: &'a [Argument<'a>],
215 ) -> Self {
216 let timestamp = timer_current_boot_ticks();
217 Self { category, name, timestamp, context, args }
218 }
219}
220
221impl<'a> Drop for KTraceScope<'a> {
222 #[inline(never)]
223 #[cold]
224 fn drop(&mut self) {
225 let end_time = timer_current_boot_ticks();
226 let ktrace = KTrace::get_instance();
227 ktrace.emit_event(
228 EventType::DurationComplete,
229 self.category,
230 self.name,
231 self.timestamp,
232 self.context,
233 Some(end_time.0 as u64),
234 self.args,
235 );
236 }
237}
238
239#[repr(C)]
241struct DroppedRecordDurationEvent {
242 header: u64,
243 start: InstantBootTicks,
244 process_id: u64,
245 thread_id: u64,
246 num_dropped_arg: u64,
247 bytes_dropped_arg: u64,
248 end: InstantBootTicks,
249}
250const _: () = assert!(size_of::<DroppedRecordDurationEvent>() == 56);
251#[repr(C)]
259#[derive(Default, Debug, Clone)]
260pub struct DroppedRecordStats {
261 pub first_dropped: InstantBootTicks,
262 pub last_dropped: InstantBootTicks,
263
264 pub num_dropped: u32,
267 pub bytes_dropped: u32,
268 pub has_dropped: bool,
269}
270const _: () = assert!(size_of::<DroppedRecordStats>() == 32);
271impl DroppedRecordStats {
274 pub fn reset(&mut self) {
275 self.first_dropped = InstantBootTicks(0);
276 self.last_dropped = InstantBootTicks(0);
277 self.num_dropped = 0;
278 self.bytes_dropped = 0;
279 self.has_dropped = false;
280 }
281
282 pub fn track(&mut self, now: InstantBootTicks, size: u32) {
283 if !self.has_dropped {
284 self.first_dropped = now;
285 self.has_dropped = true;
286 }
287 self.last_dropped = now;
288 self.num_dropped = self.num_dropped.wrapping_add(1);
289 self.bytes_dropped = self.bytes_dropped.wrapping_add(size);
290 }
291
292 pub fn has_dropped(&self) -> bool {
293 self.has_dropped
294 }
295}
296
297pub struct KTraceBuffer {
300 buffer: &'static mut Buffer<NoOpAllocator>,
301 pub drop_stats: &'static mut DroppedRecordStats,
302 size: u32,
303 cpu_ref_header_entry: u16,
304 pub process_koid: u64,
305 pub thread_koid: u64,
306}
307
308unsafe impl Send for KTraceBuffer {}
311unsafe impl Sync for KTraceBuffer {}
312
313impl KTraceBuffer {
314 pub fn new(
317 buffer: &'static mut Buffer<NoOpAllocator>,
318 drop_stats: &'static mut DroppedRecordStats,
319 cpu_ref_header_entry: u16,
320 process_koid: u64,
321 thread_koid: u64,
322 ) -> Self {
323 let size = buffer.size();
324 Self { buffer, drop_stats, size, cpu_ref_header_entry, process_koid, thread_koid }
325 }
326
327 pub fn size(&self) -> u32 {
329 self.size
330 }
331
332 pub fn drain(&self) -> Result<(), Status> {
334 self.buffer.drain()
335 }
336
337 pub fn read<F>(&self, copy_fn: F, len: u32) -> Result<u32, Status>
339 where
340 F: FnMut(u32, &[u8]) -> Result<(), Status>,
341 {
342 self.buffer.read(copy_fn, len)
343 }
344
345 pub fn reserve(&mut self, header: u64) -> Result<KTraceReservation<'_>, Status> {
348 debug_assert!(ints_disabled());
349 let record_type = (header & 0xf) as u32;
351 let num_words = if record_type == 15 {
352 ((header >> 4) & 0xffffffff) as u32
354 } else {
355 ((header >> 4) & 0xfff) as u32
357 };
358 let size = num_words * 8;
359
360 let mut total_size = size;
361 let event = if self.drop_stats.has_dropped() {
362 total_size += size_of::<DroppedRecordDurationEvent>() as u32;
363 Some(self.serialize_drop_stats())
364 } else {
365 None
366 };
367
368 match self.buffer.reserve(total_size) {
369 Err(status) => {
370 let now = timer_current_boot_ticks();
371 self.drop_stats.track(now, size);
372 Err(status)
373 }
374 Ok(mut res) => {
375 if let Some(event) = event {
376 let event_bytes = unsafe {
379 slice::from_raw_parts(
380 ptr::from_ref(&event).cast::<u8>(),
381 size_of::<DroppedRecordDurationEvent>(),
382 )
383 };
384 res.write(event_bytes)?;
385 self.drop_stats.reset();
386 }
387 Ok(KTraceReservation::new(res, header))
388 }
389 }
390 }
391
392 pub fn emit_drop_stats(&mut self) -> Result<(), Status> {
394 debug_assert!(ints_disabled());
395 if !self.drop_stats.has_dropped() {
396 return Ok(());
397 }
398
399 let event = self.serialize_drop_stats();
401
402 let mut res = self.buffer.reserve(size_of::<DroppedRecordDurationEvent>() as u32)?;
403 let event_bytes = unsafe {
405 slice::from_raw_parts(
406 ptr::from_ref(&event).cast::<u8>(),
407 size_of::<DroppedRecordDurationEvent>(),
408 )
409 };
410 res.write(event_bytes)?;
411 res.commit()?;
412
413 self.drop_stats.reset();
416 Ok(())
417 }
418
419 pub fn reset_drop_stats(&mut self) {
421 self.drop_stats.reset();
422 }
423
424 fn serialize_drop_stats(&self) -> DroppedRecordDurationEvent {
425 let mut header = 4u64; let record_size_words = (size_of::<DroppedRecordDurationEvent>() / 8) as u64;
427 header |= record_size_words << 4; header |= 4u64 << 16; header |= 2u64 << 20; header |= (self.cpu_ref_header_entry as u64) << 24;
431 header |= (META_CAT.label().id() as u64) << 32;
432 header |= (DROP_STATS_REF.id() as u64) << 48;
433
434 let mut num_dropped_arg = 2u64;
441 num_dropped_arg |= 1u64 << 4;
442 num_dropped_arg |= (NUM_RECORDS_REF.id() as u64) << 16;
443 num_dropped_arg |= (self.drop_stats.num_dropped as u64) << 32;
444
445 let mut bytes_dropped_arg = 2u64;
446 bytes_dropped_arg |= 1u64 << 4;
447 bytes_dropped_arg |= (NUM_BYTES_REF.id() as u64) << 16;
448 bytes_dropped_arg |= (self.drop_stats.bytes_dropped as u64) << 32;
449
450 DroppedRecordDurationEvent {
451 header,
452 start: self.drop_stats.first_dropped,
453 process_id: self.process_koid,
454 thread_id: self.thread_koid,
455 num_dropped_arg,
456 bytes_dropped_arg,
457 end: self.drop_stats.last_dropped,
458 }
459 }
460}
461
462#[derive(Debug)]
464pub struct KTraceReservation<'a> {
465 reservation: Reservation<'a>,
466}
467
468impl<'a> KTraceReservation<'a> {
469 fn new(reservation: Reservation<'a>, header: u64) -> Self {
470 let mut this = Self { reservation };
471 let _ = this.write_word(header);
472 this
473 }
474
475 pub fn write_word(&mut self, word: u64) -> Result<(), Status> {
477 debug_assert!(ints_disabled());
478 self.reservation.write(&word.to_ne_bytes())
479 }
480
481 pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Status> {
483 debug_assert!(ints_disabled());
484 self.reservation.write(bytes)?;
485 let num_bytes = bytes.len();
486 let aligned_bytes = (num_bytes + 7) & !7;
487 let num_zeros_to_write = aligned_bytes - num_bytes;
488 if num_zeros_to_write > 0 {
489 let zero = [0u8; 8];
490 self.reservation.write(&zero[..num_zeros_to_write])?;
491 }
492 Ok(())
493 }
494
495 pub fn commit(self) -> Result<(), Status> {
497 debug_assert!(ints_disabled());
498 self.reservation.commit()
499 }
500}
501
502pub struct KTrace {
504 state: &'static KTraceState,
508
509 buffers: Box<[AtomicPtr<KTraceBuffer>]>,
512}
513
514unsafe impl Sync for KTrace {}
516unsafe impl Send for KTrace {}
517
518#[repr(transparent)]
519struct KTraceSingleton(UnsafeCell<MaybeUninit<KTrace>>);
520
521unsafe impl Sync for KTraceSingleton {}
522unsafe impl Send for KTraceSingleton {}
523
524static INSTANCE: KTraceSingleton = KTraceSingleton(UnsafeCell::new(MaybeUninit::uninit()));
525
526impl KTrace {
527 pub fn get_instance() -> &'static Self {
529 unsafe { &*INSTANCE.0.get().cast::<KTrace>() }
531 }
532
533 pub fn get_buffer(&self, cpu: usize) -> *mut KTraceBuffer {
535 self.buffers[cpu].load(Ordering::Acquire)
536 }
537
538 pub unsafe fn reserve(&self, header: u64) -> Result<KTraceReservation<'_>, Status> {
544 debug_assert!(ints_disabled());
545 if !self.writes_enabled() {
546 return Err(Status::BAD_STATE);
547 }
548
549 let ptr = self.buffers[curr_cpu_num() as usize].load(Ordering::Acquire);
551 if ptr.is_null() {
552 return Err(Status::BAD_STATE);
553 }
554
555 let buf = unsafe { &mut *ptr };
556 buf.reserve(header)
557 }
558
559 pub fn writes_enabled(&self) -> bool {
561 self.state.writes_enabled.load(Ordering::Acquire)
562 }
563
564 pub fn categories_bitmask(&self) -> u32 {
566 self.state.categories_bitmask.load(Ordering::Acquire)
567 }
568
569 pub fn is_category_enabled(&self, category: &InternedCategory) -> bool {
571 let category_index = category.index();
572 if category_index == InternedCategory::INVALID_INDEX {
573 return false;
574 }
575 let bitmask = self.categories_bitmask();
576 (bitmask & (1 << category_index)) != 0
577 }
578
579 #[inline(never)]
583 #[cold]
584 pub fn emit_kernel_object_outlined(
585 &self,
586 koid: u64,
587 obj_type: u32,
588 name: &InternedString,
589 args: &[Argument<'_>],
590 ) {
591 let _guard = InterruptDisableGuard::new();
592 if !self.writes_enabled() {
593 return;
594 }
595
596 let base_size = 2; let args_size: usize = args.iter().map(|a| a.size_words()).sum();
598 let total_size_words = base_size + args_size;
599
600 if total_size_words > 0xfff {
601 return;
602 }
603
604 let mut header = 7u64; header |= (total_size_words as u64) << 4; header |= (obj_type as u64) << 16; header |= (name.id() as u64) << 24; header |= (args.len() as u64) << 40; if let Ok(mut res) = unsafe { self.reserve(header) } {
611 let _ = res.write_word(koid);
612 for arg in args {
613 let _ = arg.write(&mut res);
614 }
615 let _ = res.commit();
616 }
617 }
618
619 #[inline(never)]
623 #[cold]
624 pub fn emit_event(
625 &self,
626 event_type: EventType,
627 category: &InternedCategory,
628 name: &InternedString,
629 timestamp: InstantBootTicks,
630 context: Context,
631 content: Option<u64>,
632 args: &[Argument<'_>],
633 ) {
634 let _guard = InterruptDisableGuard::new();
635 if !self.writes_enabled() {
636 return;
637 }
638
639 let (process_koid, thread_koid) = match context {
641 Context::Thread => {
642 let FxtRef { pid, tid } = unsafe { ThreadPtr::current().fxt_ref() };
644 (pid, tid)
645 }
646 Context::Cpu => {
647 let cpu = curr_cpu_num() as usize;
648 let ptr = self.buffers[cpu].load(Ordering::Acquire);
649 if ptr.is_null() {
650 return;
651 }
652 let buf = unsafe { &*ptr };
653 (buf.process_koid, buf.thread_koid)
654 }
655 };
656
657 let base_size = 4; let content_size = if content.is_some() { 1 } else { 0 };
660 let args_size: usize = args.iter().map(|a| a.size_words()).sum();
661 let total_size_words = base_size + content_size + args_size;
662
663 if total_size_words > 0xfff {
664 return;
665 }
666
667 let mut header = 4u64; header |= (total_size_words as u64) << 4; header |= (event_type as u32 as u64) << 16; header |= (args.len() as u64) << 20; header |= (category.label().id() as u64) << 32; header |= (name.id() as u64) << 48; if let Ok(mut res) = unsafe { self.reserve(header) } {
677 let _ = res.write_word(timestamp.0 as u64);
678 let _ = res.write_word(process_koid);
679 let _ = res.write_word(thread_koid);
680
681 for arg in args {
682 let _ = arg.write(&mut res);
683 }
684
685 if let Some(c) = content {
686 let _ = res.write_word(c);
687 }
688
689 let _ = res.commit();
690 }
691 }
692}
693
694#[unsafe(no_mangle)]
700pub unsafe extern "C" fn rust_ktrace_init(num_buffers: u32, state_ptr: *mut ffi::c_void) -> i32 {
701 if num_buffers == 0 || state_ptr.is_null() {
702 return Status::INVALID_ARGS.into_raw();
703 }
704
705 let state = unsafe { &*state_ptr.cast::<KTraceState>() };
709
710 let buffers = match Box::<[AtomicPtr<KTraceBuffer>]>::try_new_zeroed_slice(num_buffers as usize)
711 {
712 Ok(b) => b,
713 Err(_) => return Status::NO_MEMORY.into_raw(),
714 };
715
716 let ktrace = KTrace { state, buffers };
717
718 unsafe {
719 let slot = INSTANCE.0.get();
720 ptr::write(slot.cast::<KTrace>(), ktrace);
721 }
722
723 Status::OK.into_raw()
724}
725
726#[unsafe(no_mangle)]
733pub unsafe extern "C" fn rust_ktrace_init_cpu_buffer(
734 cpu_num: u32,
735 spsc_buffer_ptr: *mut ffi::c_void,
736 drop_stats_ptr: *mut ffi::c_void,
737 process_koid: u64,
738 thread_koid: u64,
739 cpu_ref_header_entry: u16,
740) -> i32 {
741 let ktrace = KTrace::get_instance();
742 if cpu_num >= ktrace.buffers.len() as u32 {
743 return Status::INVALID_ARGS.into_raw();
744 }
745
746 let (buffer, drop_stats) = unsafe {
749 (
750 &mut *spsc_buffer_ptr.cast::<Buffer<NoOpAllocator>>(),
751 &mut *drop_stats_ptr.cast::<DroppedRecordStats>(),
752 )
753 };
754
755 let buf =
757 KTraceBuffer::new(buffer, drop_stats, cpu_ref_header_entry, process_koid, thread_koid);
758
759 let boxed_buf = match Box::try_new(buf) {
760 Ok(b) => b,
761 Err(_) => return Status::NO_MEMORY.into_raw(),
762 };
763
764 let raw_ptr = Box::into_raw(boxed_buf);
765
766 let old_ptr = ktrace.buffers[cpu_num as usize].swap(raw_ptr, Ordering::AcqRel);
768 if !old_ptr.is_null() {
769 unsafe {
771 let _ = Box::from_raw(old_ptr);
772 }
773 }
774
775 Status::OK.into_raw()
776}
777
778#[cfg(all(not(gcc), ktest))]
780#[unittest::test_suite(name = "ktrace_rust")]
781mod tests {
782 use arch_rs::{InterruptDisableGuard, curr_cpu_num, max_num_cpus};
783 use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
784 use core::{ffi, ptr};
785 use kernel::thread::{FxtRef, ThreadPtr};
786 use spsc_buffer::Buffer;
787 use unittest::{
788 assert_eq, assert_ok, expect_eq, expect_false, expect_ne, expect_true, unwrap_ok,
789 };
790
791 declare_interned_category!(MEMORY_CAT, "kernel:memory", extern);
792 declare_interned_category!(SCHED_CAT, "kernel:sched", extern);
793 declare_interned_category!(CONTENTION_CAT, "kernel:contention", extern);
794 declare_interned_category!(IPC_CAT, "kernel:ipc", extern);
795 declare_interned_category!(IRQ_CAT, "kernel:irq", extern);
796
797 #[test]
799 fn init_and_size() {
800 let mut storage = [0u8; 256];
801 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
802 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
803 let mut stats = DroppedRecordStats::default();
804 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
805 let kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
806
807 expect_eq!(kbuf.size(), 256);
808 expect_eq!(kbuf.process_koid, 100);
809 expect_eq!(kbuf.thread_koid, 200);
810 }
811
812 #[test]
814 fn test_write() {
815 let _guard = InterruptDisableGuard::new();
816 let mut storage = [0u8; 256];
817 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
818 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
819 let mut stats = DroppedRecordStats::default();
820 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
821 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
822
823 let header = 4u64 | (2u64 << 4);
825 let mut res = unwrap_ok!(kbuf.reserve(header));
826
827 assert_ok!(res.write_word(0xabcdef0123456789));
829 assert_ok!(res.commit());
830
831 let mut read_bytes = [0u8; 16];
833 let read_len = unwrap_ok!(kbuf.read(
834 |offset, src| {
835 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
836 Ok(())
837 },
838 16,
839 ));
840
841 assert_eq!(read_len, 16);
842 expect_eq!(u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap()), header);
843 expect_eq!(u64::from_ne_bytes(read_bytes[8..16].try_into().unwrap()), 0xabcdef0123456789);
844 }
845
846 #[test]
851 fn dropped_record_tracking() {
852 let _guard = InterruptDisableGuard::new();
853 let mut storage = [0u8; 128]; let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
855 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
856 let mut stats = DroppedRecordStats::default();
857 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
858 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
859
860 let header = 4u64 | (12u64 << 4);
863 let mut res = unwrap_ok!(kbuf.reserve(header));
864 assert_ok!(res.write_bytes(&[0u8; 88]));
865 assert_ok!(res.commit());
866
867 let header2 = 4u64 | (8u64 << 4);
870 expect_true!(kbuf.reserve(header2).err() == Some(Status::NO_SPACE));
871
872 expect_true!(kbuf.drop_stats.has_dropped());
874 expect_eq!(kbuf.drop_stats.num_dropped, 1);
875 expect_eq!(kbuf.drop_stats.bytes_dropped, 64);
876
877 assert_ok!(kbuf.drain());
879
880 let header3 = 4u64 | (2u64 << 4);
884 let mut res3 = unwrap_ok!(kbuf.reserve(header3));
885 assert_ok!(res3.write_bytes(&[0u8; 8]));
886 assert_ok!(res3.commit());
887
888 expect_false!(kbuf.drop_stats.has_dropped());
890 expect_eq!(kbuf.drop_stats.num_dropped, 0);
891 expect_eq!(kbuf.drop_stats.bytes_dropped, 0);
892
893 let mut read_bytes = [0u8; 72];
895 let read_len = unwrap_ok!(kbuf.read(
896 |offset, src| {
897 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
898 Ok(())
899 },
900 72,
901 ));
902 expect_eq!(read_len, 72);
903
904 let event_header = u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap());
906
907 expect_eq!(event_header & 0xf, 4);
908 expect_eq!((event_header >> 4) & 0xfff, 7);
909 expect_eq!((event_header >> 16) & 0xf, 4);
910 expect_eq!((event_header >> 20) & 0xf, 2);
911 expect_eq!((event_header >> 24) & 0xff, 1);
912 expect_eq!((event_header >> 32) & 0xffff, u64::from(META_CAT.label().id()));
913 expect_eq!((event_header >> 48) & 0xffff, u64::from(DROP_STATS_REF.id()));
914
915 expect_eq!(u64::from_ne_bytes(read_bytes[16..24].try_into().unwrap()), 100);
917 expect_eq!(u64::from_ne_bytes(read_bytes[24..32].try_into().unwrap()), 200);
918
919 let num_dropped_arg = u64::from_ne_bytes(read_bytes[32..40].try_into().unwrap());
921 expect_eq!(num_dropped_arg & 0xf, 2);
922 expect_eq!((num_dropped_arg >> 4) & 0xfff, 1);
923 expect_eq!((num_dropped_arg >> 16) & 0xffff, u64::from(NUM_RECORDS_REF.id()));
924 expect_eq!((num_dropped_arg >> 32) & 0xffffffff, 1);
925
926 let bytes_dropped_arg = u64::from_ne_bytes(read_bytes[40..48].try_into().unwrap());
927 expect_eq!(bytes_dropped_arg & 0xf, 2);
928 expect_eq!((bytes_dropped_arg >> 16) & 0xffff, u64::from(NUM_BYTES_REF.id()));
929 expect_eq!((bytes_dropped_arg >> 32) & 0xffffffff, 64);
930
931 let res_header = u64::from_ne_bytes(read_bytes[56..64].try_into().unwrap());
933 expect_eq!(res_header, header3);
934 }
935
936 #[test]
938 fn emit_drop_stats() {
939 let _guard = InterruptDisableGuard::new();
940 let mut storage = [0u8; 128];
941 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
942 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
943 let mut stats = DroppedRecordStats::default();
944 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
945 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
946
947 let header = 4u64 | (32u64 << 4);
949 expect_true!(kbuf.reserve(header).err() == Some(Status::NO_SPACE));
950
951 expect_true!(kbuf.drop_stats.has_dropped());
952 expect_eq!(kbuf.drop_stats.num_dropped, 1);
953 expect_eq!(kbuf.drop_stats.bytes_dropped, 256);
954
955 assert_ok!(kbuf.emit_drop_stats());
957
958 expect_false!(kbuf.drop_stats.has_dropped());
959 expect_eq!(kbuf.drop_stats.num_dropped, 0);
960 expect_eq!(kbuf.drop_stats.bytes_dropped, 0);
961
962 let mut read_bytes = [0u8; 56];
964 let read_len = unwrap_ok!(kbuf.read(
965 |offset, src| {
966 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
967 Ok(())
968 },
969 56,
970 ));
971
972 expect_eq!(read_len, 56);
973
974 let event_header = u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap());
975 expect_eq!(event_header & 0xf, 4);
976 expect_eq!((event_header >> 4) & 0xfff, 7);
977 expect_eq!((event_header >> 16) & 0xf, 4);
978 expect_eq!((event_header >> 20) & 0xf, 2);
979 expect_eq!((event_header >> 24) & 0xff, 1);
980 expect_eq!((event_header >> 32) & 0xffff, u64::from(META_CAT.label().id()));
981 expect_eq!((event_header >> 48) & 0xffff, u64::from(DROP_STATS_REF.id()));
982
983 expect_eq!(u64::from_ne_bytes(read_bytes[16..24].try_into().unwrap()), 100);
984 expect_eq!(u64::from_ne_bytes(read_bytes[24..32].try_into().unwrap()), 200);
985
986 let num_dropped_arg = u64::from_ne_bytes(read_bytes[32..40].try_into().unwrap());
987 expect_eq!(num_dropped_arg & 0xf, 2);
988 expect_eq!((num_dropped_arg >> 16) & 0xffff, u64::from(NUM_RECORDS_REF.id()));
989 expect_eq!((num_dropped_arg >> 32) & 0xffffffff, 1);
990
991 let bytes_dropped_arg = u64::from_ne_bytes(read_bytes[40..48].try_into().unwrap());
992 expect_eq!(bytes_dropped_arg & 0xf, 2);
993 expect_eq!((bytes_dropped_arg >> 16) & 0xffff, u64::from(NUM_BYTES_REF.id()));
994 expect_eq!((bytes_dropped_arg >> 32) & 0xffffffff, 256);
995 }
996
997 #[test]
1002 fn global_lifecycle() {
1003 let _guard = InterruptDisableGuard::new();
1004 META_CAT.set_index(0, kstring::interned_category::InternedCategory::INVALID_INDEX);
1006 MEMORY_CAT.set_index(1, kstring::interned_category::InternedCategory::INVALID_INDEX);
1007 SCHED_CAT.set_index(2, kstring::interned_category::InternedCategory::INVALID_INDEX);
1008 CONTENTION_CAT.set_index(3, kstring::interned_category::InternedCategory::INVALID_INDEX);
1009 IPC_CAT.set_index(4, kstring::interned_category::InternedCategory::INVALID_INDEX);
1010 IRQ_CAT.set_index(5, kstring::interned_category::InternedCategory::INVALID_INDEX);
1011
1012 let num_cpus = max_num_cpus();
1015 let mut local_state = KTraceState {
1016 categories_bitmask: AtomicU32::new(0),
1017 writes_enabled: AtomicBool::new(false),
1018 };
1019 let local_state_ptr = ptr::from_mut(&mut local_state).cast::<ffi::c_void>();
1020
1021 let status = unsafe { rust_ktrace_init(num_cpus, local_state_ptr) };
1022 expect_eq!(status, 0);
1023
1024 let ktrace = KTrace::get_instance();
1025
1026 expect_false!(ktrace.writes_enabled());
1028 expect_eq!(ktrace.categories_bitmask(), 0);
1029 expect_false!(ktrace.is_category_enabled(&META_CAT));
1030 expect_false!(ktrace.is_category_enabled(&IRQ_CAT));
1031
1032 local_state.writes_enabled.store(true, Ordering::Release);
1034 expect_true!(ktrace.writes_enabled());
1035 local_state.writes_enabled.store(false, Ordering::Release);
1036 expect_false!(ktrace.writes_enabled());
1037
1038 let mask = (1 << MEMORY_CAT.index()) | (1 << CONTENTION_CAT.index());
1040 local_state.categories_bitmask.store(mask, Ordering::Release);
1041 expect_eq!(ktrace.categories_bitmask(), mask);
1042 expect_false!(ktrace.is_category_enabled(&META_CAT));
1043 expect_true!(ktrace.is_category_enabled(&MEMORY_CAT));
1044 expect_false!(ktrace.is_category_enabled(&SCHED_CAT));
1045 expect_true!(ktrace.is_category_enabled(&CONTENTION_CAT));
1046 expect_false!(ktrace.is_category_enabled(&IPC_CAT));
1047
1048 let mut storage = [0u8; 256];
1050 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
1051 let inner_buf_ptr = ptr::from_mut(&mut inner_buf).cast::<ffi::c_void>();
1052 let mut stats = DroppedRecordStats::default();
1053 let stats_ptr = ptr::from_mut(&mut stats).cast::<ffi::c_void>();
1054
1055 let cpu = curr_cpu_num();
1057 let init_status = unsafe {
1058 rust_ktrace_init_cpu_buffer(
1059 cpu, inner_buf_ptr,
1061 stats_ptr,
1062 100, 200, 1, )
1066 };
1067 expect_eq!(init_status, 0);
1068
1069 let header = 4u64 | (2u64 << 4); expect_true!(unsafe { ktrace.reserve(header) }.err() == Some(Status::BAD_STATE));
1072
1073 local_state.writes_enabled.store(true, Ordering::Release);
1075
1076 let mut res = unwrap_ok!(unsafe { ktrace.reserve(header) });
1078 assert_ok!(res.write_word(0x1234567890abcdef));
1079 assert_ok!(res.commit());
1080
1081 let ptr = ktrace.get_buffer(cpu as usize);
1083 expect_false!(ptr.is_null());
1084 let buf = unsafe { &*ptr };
1085
1086 let mut read_bytes = [0u8; 16];
1087 let read_len = unwrap_ok!(buf.read(
1088 |offset, src| {
1089 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
1090 Ok(())
1091 },
1092 16,
1093 ));
1094
1095 expect_eq!(read_len, 16);
1096 expect_eq!(u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap()), header);
1097 expect_eq!(u64::from_ne_bytes(read_bytes[8..16].try_into().unwrap()), 0x1234567890abcdef);
1098
1099 let mask = (1 << META_CAT.index()) | (1 << MEMORY_CAT.index());
1101 local_state.categories_bitmask.store(mask, Ordering::Release);
1102 expect_true!(ktrace.is_category_enabled(&META_CAT));
1103
1104 instant!(META_CAT, "my_event", "arg1" => 42i32, "arg2" => "hello");
1106
1107 let mut read_bytes = [0u8; 128];
1109 let read_len = unwrap_ok!(buf.read(
1110 |offset, src| {
1111 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
1112 Ok(())
1113 },
1114 56,
1115 ));
1116
1117 expect_eq!(read_len, 56);
1118
1119 let header_word = u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap());
1121 expect_eq!(header_word & 0xf, 4);
1122 expect_eq!((header_word >> 4) & 0xfff, 7);
1123 expect_eq!((header_word >> 16) & 0xf, 0);
1124 expect_eq!((header_word >> 20) & 0xf, 2);
1125 expect_eq!((header_word >> 32) & 0xffff, u64::from(META_CAT.label().id()));
1126 let name_id = (header_word >> 48) & 0xffff;
1127 expect_ne!(name_id, 0);
1128
1129 let timestamp_word = u64::from_ne_bytes(read_bytes[8..16].try_into().unwrap());
1131 expect_ne!(timestamp_word, 0);
1132
1133 let process_koid = u64::from_ne_bytes(read_bytes[16..24].try_into().unwrap());
1135 let thread_koid = u64::from_ne_bytes(read_bytes[24..32].try_into().unwrap());
1136 let FxtRef { pid: expected_proc, tid: expected_thread } =
1138 unsafe { ThreadPtr::current().fxt_ref() };
1139 expect_eq!(process_koid, expected_proc);
1140 expect_eq!(thread_koid, expected_thread);
1141
1142 let arg1_header = u64::from_ne_bytes(read_bytes[32..40].try_into().unwrap());
1144 expect_eq!(arg1_header & 0xf, 1);
1145 expect_eq!((arg1_header >> 32) & 0xffffffff, 42);
1146 let arg1_name_id = (arg1_header >> 16) & 0xffff;
1147 expect_ne!(arg1_name_id, 0);
1148
1149 let arg2_header = u64::from_ne_bytes(read_bytes[40..48].try_into().unwrap());
1151 expect_eq!(arg2_header & 0xf, 6);
1152 expect_eq!((arg2_header >> 4) & 0xf, 1);
1153 expect_eq!((arg2_header >> 32) & 0xffffffff, 5);
1154 let arg2_name_id = (arg2_header >> 16) & 0xffff;
1155 expect_ne!(arg2_name_id, 0);
1156
1157 let arg2_val = &read_bytes[48..56];
1158 expect_true!(&arg2_val[0..5] == b"hello");
1159 expect_true!(&arg2_val[5..8] == &[0, 0, 0]);
1160 }
1161}
1162
1163#[cfg(ktest)]
1173#[unsafe(no_mangle)]
1174pub unsafe extern "C" fn rust_ktrace_test_interop(header: u64, val: u64) -> i32 {
1175 let ktrace = KTrace::get_instance();
1176 if let Ok(mut res) = unsafe { ktrace.reserve(header) } {
1178 let _ = res.write_word(val);
1179 let _ = res.commit();
1180 0
1181 } else {
1182 -1
1183 }
1184}
1185
1186#[cfg(ktest)]
1192#[unsafe(no_mangle)]
1193pub unsafe extern "C" fn rust_ktrace_test_macros() {
1194 instant!("kernel:meta", "rust_instant", Context::Thread, "val" => 101u32);
1196 duration_begin!("kernel:meta", "rust_duration", Context::Thread, "val" => 102u32);
1197 duration_end!("kernel:meta", "rust_duration", Context::Thread, "val" => 103u32);
1198 counter!("kernel:meta", "rust_counter", 104u64, "val" => 105u32);
1199 flow_begin!("kernel:meta", "rust_flow", 106u64, "val" => 107u32);
1200 flow_step!("kernel:meta", "rust_flow", 106u64, "val" => 108u32);
1201 flow_end!("kernel:meta", "rust_flow", 106u64, "val" => 109u32);
1202 complete!("kernel:meta", "rust_complete", InstantBootTicks(110i64), "val" => 111u32);
1203 kernel_object!("kernel:meta", 112u64, 1u32, "rust_kernel_obj", "val" => 113u32);
1204 kernel_object_always!(114u64, 2u32, "rust_kernel_obj_always", "val" => 115u32);
1205}