1#![no_std]
6
7use arch_rs::{InterruptDisableGuard, curr_cpu_num, ints_disabled};
8use core::cell::UnsafeCell;
9use core::mem::{MaybeUninit, size_of};
10use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering};
11use core::{ffi, ptr, slice};
12use kalloc::Box;
13use kernel::thread::{FxtRef, ThreadPtr};
14pub use kstring::declare_interned_category;
15use kstring::declare_interned_string;
16use kstring::interned_category::InternedCategory;
17pub use kstring::interned_string::InternedString;
18pub use platform_rs::{InstantBootTicks, timer_current_boot_ticks};
19use spsc_buffer::{Buffer, NoOpAllocator, Reservation};
20use zx_status::Status;
21
22pub use ktrace_macro::*;
24#[allow(unused_extern_crates)]
25extern crate self as ktrace_rs;
26
27#[repr(C)]
29pub struct KTraceState {
30 pub categories_bitmask: AtomicU32,
31 pub writes_enabled: AtomicBool,
32}
33const _: () = assert!(size_of::<KTraceState>() == 8);
34declare_interned_category!(META_CAT, "kernel:meta", extern);
37declare_interned_string!(DROP_STATS_REF, "drop_stats", extern);
38declare_interned_string!(NUM_RECORDS_REF, "num_records", extern);
39declare_interned_string!(NUM_BYTES_REF, "num_bytes", extern);
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Context {
43 Thread = 0,
44 Cpu = 1,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum EventType {
49 Instant = 0,
50 Counter = 1,
51 DurationBegin = 2,
52 DurationEnd = 3,
53 DurationComplete = 4,
54 FlowBegin = 8,
55 FlowStep = 9,
56 FlowEnd = 10,
57}
58
59pub enum ArgValue<'a> {
61 Null,
62 Bool(bool),
63 Int32(i32),
64 Uint32(u32),
65 Int64(i64),
66 Uint64(u64),
67 Double(f64),
68 String(&'a str),
69 Pointer(usize),
70 Koid(u64),
71}
72
73impl<'a> From<bool> for ArgValue<'a> {
74 fn from(v: bool) -> Self {
75 ArgValue::Bool(v)
76 }
77}
78impl<'a> From<i32> for ArgValue<'a> {
79 fn from(v: i32) -> Self {
80 ArgValue::Int32(v)
81 }
82}
83impl<'a> From<u32> for ArgValue<'a> {
84 fn from(v: u32) -> Self {
85 ArgValue::Uint32(v)
86 }
87}
88impl<'a> From<i64> for ArgValue<'a> {
89 fn from(v: i64) -> Self {
90 ArgValue::Int64(v)
91 }
92}
93impl<'a> From<u64> for ArgValue<'a> {
94 fn from(v: u64) -> Self {
95 ArgValue::Uint64(v)
96 }
97}
98impl<'a> From<f64> for ArgValue<'a> {
99 fn from(v: f64) -> Self {
100 ArgValue::Double(v)
101 }
102}
103impl<'a> From<&'a str> for ArgValue<'a> {
104 fn from(v: &'a str) -> Self {
105 ArgValue::String(v)
106 }
107}
108
109pub struct Argument<'a> {
110 name: &'static InternedString,
111 value: ArgValue<'a>,
112}
113
114impl<'a> Argument<'a> {
115 pub fn new(name: &'static InternedString, value: impl Into<ArgValue<'a>>) -> Self {
116 Self { name, value: value.into() }
117 }
118
119 fn size_words(&self) -> usize {
121 match &self.value {
122 ArgValue::Null | ArgValue::Bool(_) | ArgValue::Int32(_) | ArgValue::Uint32(_) => 1,
123 ArgValue::Int64(_)
124 | ArgValue::Uint64(_)
125 | ArgValue::Double(_)
126 | ArgValue::Pointer(_)
127 | ArgValue::Koid(_) => 2,
128 ArgValue::String(s) => 1 + (s.len() + 7) / 8,
129 }
130 }
131
132 fn write(&self, res: &mut KTraceReservation<'_>) -> Result<(), Status> {
133 let name_id = self.name.id() as u64;
134 let mut header = 0u64;
135 header |= (name_id & 0xffff) << 16; match &self.value {
138 ArgValue::Null => {
139 header |= 0u64; res.write_word(header)?;
141 }
142 ArgValue::Int32(v) => {
143 header |= 1u64; header |= ((*v as u32) as u64) << 32;
145 res.write_word(header)?;
146 }
147 ArgValue::Uint32(v) => {
148 header |= 2u64; header |= (*v as u64) << 32;
150 res.write_word(header)?;
151 }
152 ArgValue::Int64(v) => {
153 header |= 3u64; res.write_word(header)?;
155 res.write_word(*v as u64)?;
156 }
157 ArgValue::Uint64(v) => {
158 header |= 4u64; res.write_word(header)?;
160 res.write_word(*v)?;
161 }
162 ArgValue::Double(v) => {
163 header |= 5u64; res.write_word(header)?;
165 res.write_word(v.to_bits())?;
166 }
167 ArgValue::String(s) => {
168 header |= 6u64; let string_len = s.len();
170 header |= (((string_len + 7) / 8) as u64) << 4; header |= (string_len as u64) << 32; res.write_word(header)?;
173 res.write_bytes(s.as_bytes())?;
174 }
175 ArgValue::Pointer(v) => {
176 header |= 7u64; res.write_word(header)?;
178 res.write_word(*v as u64)?;
179 }
180 ArgValue::Koid(v) => {
181 header |= 8u64; res.write_word(header)?;
183 res.write_word(*v)?;
184 }
185 ArgValue::Bool(v) => {
186 header |= 9u64; header |= ((*v as u64) & 1) << 32;
188 res.write_word(header)?;
189 }
190 }
191 Ok(())
192 }
193}
194
195pub struct KTraceScope<'a> {
196 category: &'static InternedCategory,
197 name: &'static InternedString,
198 timestamp: InstantBootTicks,
199 context: Context,
200 args: &'a [Argument<'a>],
201}
202
203impl<'a> KTraceScope<'a> {
204 #[inline(never)]
205 #[cold]
206 pub fn begin(
207 category: &'static InternedCategory,
208 name: &'static InternedString,
209 context: Context,
210 args: &'a [Argument<'a>],
211 ) -> Self {
212 let timestamp = timer_current_boot_ticks();
213 Self { category, name, timestamp, context, args }
214 }
215}
216
217impl<'a> Drop for KTraceScope<'a> {
218 #[inline(never)]
219 #[cold]
220 fn drop(&mut self) {
221 let end_time = timer_current_boot_ticks();
222 let ktrace = KTrace::get_instance();
223 ktrace.emit_event(
224 EventType::DurationComplete,
225 self.category,
226 self.name,
227 self.timestamp,
228 self.context,
229 Some(end_time.0 as u64),
230 self.args,
231 );
232 }
233}
234
235#[repr(C)]
237struct DroppedRecordDurationEvent {
238 header: u64,
239 start: InstantBootTicks,
240 process_id: u64,
241 thread_id: u64,
242 num_dropped_arg: u64,
243 bytes_dropped_arg: u64,
244 end: InstantBootTicks,
245}
246const _: () = assert!(size_of::<DroppedRecordDurationEvent>() == 56);
247#[repr(C)]
255#[derive(Default, Debug, Clone)]
256pub struct DroppedRecordStats {
257 pub first_dropped: InstantBootTicks,
258 pub last_dropped: InstantBootTicks,
259
260 pub num_dropped: u32,
263 pub bytes_dropped: u32,
264 pub has_dropped: bool,
265}
266const _: () = assert!(size_of::<DroppedRecordStats>() == 32);
267impl DroppedRecordStats {
270 pub fn reset(&mut self) {
271 self.first_dropped = InstantBootTicks(0);
272 self.last_dropped = InstantBootTicks(0);
273 self.num_dropped = 0;
274 self.bytes_dropped = 0;
275 self.has_dropped = false;
276 }
277
278 pub fn track(&mut self, now: InstantBootTicks, size: u32) {
279 if !self.has_dropped {
280 self.first_dropped = now;
281 self.has_dropped = true;
282 }
283 self.last_dropped = now;
284 self.num_dropped = self.num_dropped.wrapping_add(1);
285 self.bytes_dropped = self.bytes_dropped.wrapping_add(size);
286 }
287
288 pub fn has_dropped(&self) -> bool {
289 self.has_dropped
290 }
291}
292
293pub struct KTraceBuffer {
296 buffer: &'static mut Buffer<NoOpAllocator>,
297 pub drop_stats: &'static mut DroppedRecordStats,
298 size: u32,
299 cpu_ref_header_entry: u16,
300 pub process_koid: u64,
301 pub thread_koid: u64,
302}
303
304unsafe impl Send for KTraceBuffer {}
307unsafe impl Sync for KTraceBuffer {}
308
309impl KTraceBuffer {
310 pub fn new(
313 buffer: &'static mut Buffer<NoOpAllocator>,
314 drop_stats: &'static mut DroppedRecordStats,
315 cpu_ref_header_entry: u16,
316 process_koid: u64,
317 thread_koid: u64,
318 ) -> Self {
319 let size = buffer.size();
320 Self { buffer, drop_stats, size, cpu_ref_header_entry, process_koid, thread_koid }
321 }
322
323 pub fn size(&self) -> u32 {
325 self.size
326 }
327
328 pub fn drain(&self) -> Result<(), Status> {
330 self.buffer.drain()
331 }
332
333 pub fn read<F>(&self, copy_fn: F, len: u32) -> Result<u32, Status>
335 where
336 F: FnMut(u32, &[u8]) -> Result<(), Status>,
337 {
338 self.buffer.read(copy_fn, len)
339 }
340
341 pub fn reserve(&mut self, header: u64) -> Result<KTraceReservation<'_>, Status> {
344 debug_assert!(ints_disabled());
345 let record_type = (header & 0xf) as u32;
347 let num_words = if record_type == 15 {
348 ((header >> 4) & 0xffffffff) as u32
350 } else {
351 ((header >> 4) & 0xfff) as u32
353 };
354 let size = num_words * 8;
355
356 let mut total_size = size;
357 let event = if self.drop_stats.has_dropped() {
358 total_size += size_of::<DroppedRecordDurationEvent>() as u32;
359 Some(self.serialize_drop_stats())
360 } else {
361 None
362 };
363
364 match self.buffer.reserve(total_size) {
365 Err(status) => {
366 let now = timer_current_boot_ticks();
367 self.drop_stats.track(now, size);
368 Err(status)
369 }
370 Ok(mut res) => {
371 if let Some(event) = event {
372 let event_bytes = unsafe {
375 slice::from_raw_parts(
376 ptr::from_ref(&event).cast::<u8>(),
377 size_of::<DroppedRecordDurationEvent>(),
378 )
379 };
380 res.write(event_bytes)?;
381 self.drop_stats.reset();
382 }
383 Ok(KTraceReservation::new(res, header))
384 }
385 }
386 }
387
388 pub fn emit_drop_stats(&mut self) -> Result<(), Status> {
390 debug_assert!(ints_disabled());
391 if !self.drop_stats.has_dropped() {
392 return Ok(());
393 }
394
395 let event = self.serialize_drop_stats();
397
398 let mut res = self.buffer.reserve(size_of::<DroppedRecordDurationEvent>() as u32)?;
399 let event_bytes = unsafe {
401 slice::from_raw_parts(
402 ptr::from_ref(&event).cast::<u8>(),
403 size_of::<DroppedRecordDurationEvent>(),
404 )
405 };
406 res.write(event_bytes)?;
407 res.commit()?;
408
409 self.drop_stats.reset();
412 Ok(())
413 }
414
415 pub fn reset_drop_stats(&mut self) {
417 self.drop_stats.reset();
418 }
419
420 fn serialize_drop_stats(&self) -> DroppedRecordDurationEvent {
421 let mut header = 4u64; let record_size_words = (size_of::<DroppedRecordDurationEvent>() / 8) as u64;
423 header |= record_size_words << 4; header |= 4u64 << 16; header |= 2u64 << 20; header |= (self.cpu_ref_header_entry as u64) << 24;
427 header |= (META_CAT.label().id() as u64) << 32;
428 header |= (DROP_STATS_REF.id() as u64) << 48;
429
430 let mut num_dropped_arg = 2u64;
437 num_dropped_arg |= 1u64 << 4;
438 num_dropped_arg |= (NUM_RECORDS_REF.id() as u64) << 16;
439 num_dropped_arg |= (self.drop_stats.num_dropped as u64) << 32;
440
441 let mut bytes_dropped_arg = 2u64;
442 bytes_dropped_arg |= 1u64 << 4;
443 bytes_dropped_arg |= (NUM_BYTES_REF.id() as u64) << 16;
444 bytes_dropped_arg |= (self.drop_stats.bytes_dropped as u64) << 32;
445
446 DroppedRecordDurationEvent {
447 header,
448 start: self.drop_stats.first_dropped,
449 process_id: self.process_koid,
450 thread_id: self.thread_koid,
451 num_dropped_arg,
452 bytes_dropped_arg,
453 end: self.drop_stats.last_dropped,
454 }
455 }
456}
457
458#[derive(Debug)]
460pub struct KTraceReservation<'a> {
461 reservation: Reservation<'a>,
462}
463
464impl<'a> KTraceReservation<'a> {
465 fn new(reservation: Reservation<'a>, header: u64) -> Self {
466 let mut this = Self { reservation };
467 let _ = this.write_word(header);
468 this
469 }
470
471 pub fn write_word(&mut self, word: u64) -> Result<(), Status> {
473 debug_assert!(ints_disabled());
474 self.reservation.write(&word.to_ne_bytes())
475 }
476
477 pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Status> {
479 debug_assert!(ints_disabled());
480 self.reservation.write(bytes)?;
481 let num_bytes = bytes.len();
482 let aligned_bytes = (num_bytes + 7) & !7;
483 let num_zeros_to_write = aligned_bytes - num_bytes;
484 if num_zeros_to_write > 0 {
485 let zero = [0u8; 8];
486 self.reservation.write(&zero[..num_zeros_to_write])?;
487 }
488 Ok(())
489 }
490
491 pub fn commit(self) -> Result<(), Status> {
493 debug_assert!(ints_disabled());
494 self.reservation.commit()
495 }
496}
497
498pub struct KTrace {
500 state: &'static KTraceState,
504
505 buffers: Box<[AtomicPtr<KTraceBuffer>]>,
508}
509
510unsafe impl Sync for KTrace {}
512unsafe impl Send for KTrace {}
513
514#[repr(transparent)]
515struct KTraceSingleton(UnsafeCell<MaybeUninit<KTrace>>);
516
517unsafe impl Sync for KTraceSingleton {}
518unsafe impl Send for KTraceSingleton {}
519
520static INSTANCE: KTraceSingleton = KTraceSingleton(UnsafeCell::new(MaybeUninit::uninit()));
521
522impl KTrace {
523 pub fn get_instance() -> &'static Self {
525 unsafe { &*INSTANCE.0.get().cast::<KTrace>() }
527 }
528
529 pub fn get_buffer(&self, cpu: usize) -> *mut KTraceBuffer {
531 self.buffers[cpu].load(Ordering::Acquire)
532 }
533
534 pub unsafe fn reserve(&self, header: u64) -> Result<KTraceReservation<'_>, Status> {
540 debug_assert!(ints_disabled());
541 if !self.writes_enabled() {
542 return Err(Status::BAD_STATE);
543 }
544
545 let ptr = self.buffers[curr_cpu_num() as usize].load(Ordering::Acquire);
547 if ptr.is_null() {
548 return Err(Status::BAD_STATE);
549 }
550
551 let buf = unsafe { &mut *ptr };
552 buf.reserve(header)
553 }
554
555 pub fn writes_enabled(&self) -> bool {
557 self.state.writes_enabled.load(Ordering::Acquire)
558 }
559
560 pub fn categories_bitmask(&self) -> u32 {
562 self.state.categories_bitmask.load(Ordering::Acquire)
563 }
564
565 pub fn is_category_enabled(&self, category: &InternedCategory) -> bool {
567 let category_index = category.index();
568 if category_index == InternedCategory::INVALID_INDEX {
569 return false;
570 }
571 let bitmask = self.categories_bitmask();
572 (bitmask & (1 << category_index)) != 0
573 }
574
575 #[inline(never)]
579 #[cold]
580 pub fn emit_kernel_object_outlined(
581 &self,
582 koid: u64,
583 obj_type: u32,
584 name: &InternedString,
585 args: &[Argument<'_>],
586 ) {
587 let _guard = InterruptDisableGuard::new();
588 if !self.writes_enabled() {
589 return;
590 }
591
592 let base_size = 2; let args_size: usize = args.iter().map(|a| a.size_words()).sum();
594 let total_size_words = base_size + args_size;
595
596 if total_size_words > 0xfff {
597 return;
598 }
599
600 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) } {
607 let _ = res.write_word(koid);
608 for arg in args {
609 let _ = arg.write(&mut res);
610 }
611 let _ = res.commit();
612 }
613 }
614
615 #[inline(never)]
619 #[cold]
620 pub fn emit_event(
621 &self,
622 event_type: EventType,
623 category: &InternedCategory,
624 name: &InternedString,
625 timestamp: InstantBootTicks,
626 context: Context,
627 content: Option<u64>,
628 args: &[Argument<'_>],
629 ) {
630 let _guard = InterruptDisableGuard::new();
631 if !self.writes_enabled() {
632 return;
633 }
634
635 let (process_koid, thread_koid) = match context {
637 Context::Thread => {
638 let FxtRef { pid, tid } = unsafe { ThreadPtr::current().fxt_ref() };
640 (pid, tid)
641 }
642 Context::Cpu => {
643 let cpu = curr_cpu_num() as usize;
644 let ptr = self.buffers[cpu].load(Ordering::Acquire);
645 if ptr.is_null() {
646 return;
647 }
648 let buf = unsafe { &*ptr };
649 (buf.process_koid, buf.thread_koid)
650 }
651 };
652
653 let base_size = 4; let content_size = if content.is_some() { 1 } else { 0 };
656 let args_size: usize = args.iter().map(|a| a.size_words()).sum();
657 let total_size_words = base_size + content_size + args_size;
658
659 if total_size_words > 0xfff {
660 return;
661 }
662
663 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) } {
673 let _ = res.write_word(timestamp.0 as u64);
674 let _ = res.write_word(process_koid);
675 let _ = res.write_word(thread_koid);
676
677 for arg in args {
678 let _ = arg.write(&mut res);
679 }
680
681 if let Some(c) = content {
682 let _ = res.write_word(c);
683 }
684
685 let _ = res.commit();
686 }
687 }
688}
689
690#[unsafe(no_mangle)]
696pub unsafe extern "C" fn rust_ktrace_init(num_buffers: u32, state_ptr: *mut ffi::c_void) -> i32 {
697 if num_buffers == 0 || state_ptr.is_null() {
698 return Status::INVALID_ARGS.into_raw();
699 }
700
701 let state = unsafe { &*state_ptr.cast::<KTraceState>() };
705
706 let buffers = match Box::<[AtomicPtr<KTraceBuffer>]>::try_new_zeroed_slice(num_buffers as usize)
707 {
708 Ok(b) => b,
709 Err(_) => return Status::NO_MEMORY.into_raw(),
710 };
711
712 let ktrace = KTrace { state, buffers };
713
714 unsafe {
715 let slot = INSTANCE.0.get();
716 ptr::write(slot.cast::<KTrace>(), ktrace);
717 }
718
719 Status::OK.into_raw()
720}
721
722#[unsafe(no_mangle)]
729pub unsafe extern "C" fn rust_ktrace_init_cpu_buffer(
730 cpu_num: u32,
731 spsc_buffer_ptr: *mut ffi::c_void,
732 drop_stats_ptr: *mut ffi::c_void,
733 process_koid: u64,
734 thread_koid: u64,
735 cpu_ref_header_entry: u16,
736) -> i32 {
737 let ktrace = KTrace::get_instance();
738 if cpu_num >= ktrace.buffers.len() as u32 {
739 return Status::INVALID_ARGS.into_raw();
740 }
741
742 let (buffer, drop_stats) = unsafe {
745 (
746 &mut *spsc_buffer_ptr.cast::<Buffer<NoOpAllocator>>(),
747 &mut *drop_stats_ptr.cast::<DroppedRecordStats>(),
748 )
749 };
750
751 let buf =
753 KTraceBuffer::new(buffer, drop_stats, cpu_ref_header_entry, process_koid, thread_koid);
754
755 let boxed_buf = match Box::try_new(buf) {
756 Ok(b) => b,
757 Err(_) => return Status::NO_MEMORY.into_raw(),
758 };
759
760 let raw_ptr = Box::into_raw(boxed_buf);
761
762 let old_ptr = ktrace.buffers[cpu_num as usize].swap(raw_ptr, Ordering::AcqRel);
764 if !old_ptr.is_null() {
765 unsafe {
767 let _ = Box::from_raw(old_ptr);
768 }
769 }
770
771 Status::OK.into_raw()
772}