1#![no_std]
6
7use arch_rs::{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 kstring::interned_category::InternedCategory;
14use kstring::{declare_interned_category, declare_interned_string};
15use platform_rs::timer_current_boot_ticks;
16use spsc_buffer::{Buffer, NoOpAllocator, Reservation};
17use zx_status::Status;
18
19#[repr(C)]
21struct KTraceState {
22 categories_bitmask: AtomicU32,
23 writes_enabled: AtomicBool,
24}
25const _: () = assert!(size_of::<KTraceState>() == 8);
26declare_interned_category!(META_CAT, "kernel:meta", extern);
29declare_interned_string!(DROP_STATS_REF, "drop_stats", extern);
30declare_interned_string!(NUM_RECORDS_REF, "num_records", extern);
31declare_interned_string!(NUM_BYTES_REF, "num_bytes", extern);
32
33#[repr(C)]
35struct DroppedRecordDurationEvent {
36 header: u64,
37 start: i64,
38 process_id: u64,
39 thread_id: u64,
40 num_dropped_arg: u64,
41 bytes_dropped_arg: u64,
42 end: i64,
43}
44const _: () = assert!(size_of::<DroppedRecordDurationEvent>() == 56);
45#[repr(C)]
53#[derive(Default, Debug, Clone)]
54struct DroppedRecordStats {
55 first_dropped: i64,
56 last_dropped: i64,
57
58 num_dropped: u32,
61 bytes_dropped: u32,
62 has_dropped: bool,
63}
64const _: () = assert!(size_of::<DroppedRecordStats>() == 32);
65impl DroppedRecordStats {
68 fn reset(&mut self) {
69 self.first_dropped = 0;
70 self.last_dropped = 0;
71 self.num_dropped = 0;
72 self.bytes_dropped = 0;
73 self.has_dropped = false;
74 }
75
76 fn track(&mut self, now: i64, size: u32) {
77 if !self.has_dropped {
78 self.first_dropped = now;
79 self.has_dropped = true;
80 }
81 self.last_dropped = now;
82 self.num_dropped = self.num_dropped.wrapping_add(1);
83 self.bytes_dropped = self.bytes_dropped.wrapping_add(size);
84 }
85
86 fn has_dropped(&self) -> bool {
87 self.has_dropped
88 }
89}
90
91pub struct KTraceBuffer {
94 buffer: &'static mut Buffer<NoOpAllocator>,
95 drop_stats: &'static mut DroppedRecordStats,
96 size: u32,
97 cpu_ref_header_entry: u16,
98 process_koid: u64,
99 thread_koid: u64,
100}
101
102unsafe impl Send for KTraceBuffer {}
105unsafe impl Sync for KTraceBuffer {}
106
107impl KTraceBuffer {
108 fn new(
111 buffer: &'static mut Buffer<NoOpAllocator>,
112 drop_stats: &'static mut DroppedRecordStats,
113 cpu_ref_header_entry: u16,
114 process_koid: u64,
115 thread_koid: u64,
116 ) -> Self {
117 let size = buffer.size();
118 Self { buffer, drop_stats, size, cpu_ref_header_entry, process_koid, thread_koid }
119 }
120
121 pub fn size(&self) -> u32 {
123 self.size
124 }
125
126 pub fn drain(&self) -> Result<(), Status> {
128 self.buffer.drain()
129 }
130
131 pub fn read<F>(&self, copy_fn: F, len: u32) -> Result<u32, Status>
133 where
134 F: FnMut(u32, &[u8]) -> Result<(), Status>,
135 {
136 self.buffer.read(copy_fn, len)
137 }
138
139 pub fn reserve(&mut self, header: u64) -> Result<KTraceReservation<'_>, Status> {
142 debug_assert!(ints_disabled());
143 let record_type = (header & 0xf) as u32;
145 let num_words = if record_type == 15 {
146 ((header >> 4) & 0xffffffff) as u32
148 } else {
149 ((header >> 4) & 0xfff) as u32
151 };
152 let size = num_words * 8;
153
154 let mut total_size = size;
155 let event = if self.drop_stats.has_dropped() {
156 total_size += size_of::<DroppedRecordDurationEvent>() as u32;
157 Some(self.serialize_drop_stats())
158 } else {
159 None
160 };
161
162 match self.buffer.reserve(total_size) {
163 Err(status) => {
164 let now = timer_current_boot_ticks();
165 self.drop_stats.track(now, size);
166 Err(status)
167 }
168 Ok(mut res) => {
169 if let Some(event) = event {
170 let event_bytes = unsafe {
173 slice::from_raw_parts(
174 ptr::from_ref(&event).cast::<u8>(),
175 size_of::<DroppedRecordDurationEvent>(),
176 )
177 };
178 res.write(event_bytes)?;
179 self.drop_stats.reset();
180 }
181 Ok(KTraceReservation::new(res, header))
182 }
183 }
184 }
185
186 pub fn emit_drop_stats(&mut self) -> Result<(), Status> {
188 debug_assert!(ints_disabled());
189 if !self.drop_stats.has_dropped() {
190 return Ok(());
191 }
192
193 let event = self.serialize_drop_stats();
195
196 let mut res = self.buffer.reserve(size_of::<DroppedRecordDurationEvent>() as u32)?;
197 let event_bytes = unsafe {
199 slice::from_raw_parts(
200 ptr::from_ref(&event).cast::<u8>(),
201 size_of::<DroppedRecordDurationEvent>(),
202 )
203 };
204 res.write(event_bytes)?;
205 res.commit()?;
206
207 self.drop_stats.reset();
210 Ok(())
211 }
212
213 pub fn reset_drop_stats(&mut self) {
215 self.drop_stats.reset();
216 }
217
218 fn serialize_drop_stats(&self) -> DroppedRecordDurationEvent {
219 let mut header = 4u64; let record_size_words = (size_of::<DroppedRecordDurationEvent>() / 8) as u64;
221 header |= record_size_words << 4; header |= 4u64 << 16; header |= 2u64 << 20; header |= (self.cpu_ref_header_entry as u64) << 24;
225 header |= (META_CAT.label().id() as u64) << 32;
226 header |= (DROP_STATS_REF.id() as u64) << 48;
227
228 let mut num_dropped_arg = 2u64;
235 num_dropped_arg |= 1u64 << 4;
236 num_dropped_arg |= (NUM_RECORDS_REF.id() as u64) << 16;
237 num_dropped_arg |= (self.drop_stats.num_dropped as u64) << 32;
238
239 let mut bytes_dropped_arg = 2u64;
240 bytes_dropped_arg |= 1u64 << 4;
241 bytes_dropped_arg |= (NUM_BYTES_REF.id() as u64) << 16;
242 bytes_dropped_arg |= (self.drop_stats.bytes_dropped as u64) << 32;
243
244 DroppedRecordDurationEvent {
245 header,
246 start: self.drop_stats.first_dropped,
247 process_id: self.process_koid,
248 thread_id: self.thread_koid,
249 num_dropped_arg,
250 bytes_dropped_arg,
251 end: self.drop_stats.last_dropped,
252 }
253 }
254}
255
256#[derive(Debug)]
258pub struct KTraceReservation<'a> {
259 reservation: Reservation<'a>,
260}
261
262impl<'a> KTraceReservation<'a> {
263 fn new(reservation: Reservation<'a>, header: u64) -> Self {
264 let mut this = Self { reservation };
265 let _ = this.write_word(header);
266 this
267 }
268
269 pub fn write_word(&mut self, word: u64) -> Result<(), Status> {
271 debug_assert!(ints_disabled());
272 self.reservation.write(&word.to_ne_bytes())
273 }
274
275 pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), Status> {
277 debug_assert!(ints_disabled());
278 self.reservation.write(bytes)?;
279 let num_bytes = bytes.len();
280 let aligned_bytes = (num_bytes + 7) & !7;
281 let num_zeros_to_write = aligned_bytes - num_bytes;
282 if num_zeros_to_write > 0 {
283 let zero = [0u8; 8];
284 self.reservation.write(&zero[..num_zeros_to_write])?;
285 }
286 Ok(())
287 }
288
289 pub fn commit(self) -> Result<(), Status> {
291 debug_assert!(ints_disabled());
292 self.reservation.commit()
293 }
294}
295
296pub struct KTrace {
298 state: &'static KTraceState,
302
303 buffers: Box<[AtomicPtr<KTraceBuffer>]>,
306}
307
308unsafe impl Sync for KTrace {}
310unsafe impl Send for KTrace {}
311
312#[repr(transparent)]
313struct KTraceSingleton(UnsafeCell<MaybeUninit<KTrace>>);
314
315unsafe impl Sync for KTraceSingleton {}
316unsafe impl Send for KTraceSingleton {}
317
318static INSTANCE: KTraceSingleton = KTraceSingleton(UnsafeCell::new(MaybeUninit::uninit()));
319
320impl KTrace {
321 pub fn get_instance() -> &'static Self {
323 unsafe { &*INSTANCE.0.get().cast::<KTrace>() }
325 }
326
327 pub unsafe fn reserve(&self, header: u64) -> Result<KTraceReservation<'_>, Status> {
333 debug_assert!(ints_disabled());
334 if !self.writes_enabled() {
335 return Err(Status::BAD_STATE);
336 }
337
338 let ptr = self.buffers[curr_cpu_num() as usize].load(Ordering::Acquire);
340 if ptr.is_null() {
341 return Err(Status::BAD_STATE);
342 }
343
344 let buf = unsafe { &mut *ptr };
345 buf.reserve(header)
346 }
347
348 pub fn writes_enabled(&self) -> bool {
350 self.state.writes_enabled.load(Ordering::Acquire)
351 }
352
353 pub fn categories_bitmask(&self) -> u32 {
355 self.state.categories_bitmask.load(Ordering::Acquire)
356 }
357
358 pub fn is_category_enabled(&self, category: &InternedCategory) -> bool {
360 let category_index = category.index();
361 if category_index == InternedCategory::INVALID_INDEX {
362 return false;
363 }
364 let bitmask = self.categories_bitmask();
365 (bitmask & (1 << category_index)) != 0
366 }
367}
368
369#[unsafe(no_mangle)]
375pub unsafe extern "C" fn rust_ktrace_init(num_buffers: u32, state_ptr: *mut ffi::c_void) -> i32 {
376 if num_buffers == 0 || state_ptr.is_null() {
377 return Status::INVALID_ARGS.into_raw();
378 }
379
380 let state = unsafe { &*state_ptr.cast::<KTraceState>() };
384
385 let buffers = match Box::<[AtomicPtr<KTraceBuffer>]>::try_new_zeroed_slice(num_buffers as usize)
386 {
387 Ok(b) => b,
388 Err(_) => return Status::NO_MEMORY.into_raw(),
389 };
390
391 let ktrace = KTrace { state, buffers };
392
393 unsafe {
394 let slot = INSTANCE.0.get();
395 ptr::write(slot.cast::<KTrace>(), ktrace);
396 }
397
398 Status::OK.into_raw()
399}
400
401#[unsafe(no_mangle)]
408pub unsafe extern "C" fn rust_ktrace_init_cpu_buffer(
409 cpu_num: u32,
410 spsc_buffer_ptr: *mut ffi::c_void,
411 drop_stats_ptr: *mut ffi::c_void,
412 process_koid: u64,
413 thread_koid: u64,
414 cpu_ref_header_entry: u16,
415) -> i32 {
416 let ktrace = KTrace::get_instance();
417 if cpu_num >= ktrace.buffers.len() as u32 {
418 return Status::INVALID_ARGS.into_raw();
419 }
420
421 let (buffer, drop_stats) = unsafe {
424 (
425 &mut *spsc_buffer_ptr.cast::<Buffer<NoOpAllocator>>(),
426 &mut *drop_stats_ptr.cast::<DroppedRecordStats>(),
427 )
428 };
429
430 let buf =
432 KTraceBuffer::new(buffer, drop_stats, cpu_ref_header_entry, process_koid, thread_koid);
433
434 let boxed_buf = match Box::try_new(buf) {
435 Ok(b) => b,
436 Err(_) => return Status::NO_MEMORY.into_raw(),
437 };
438
439 let raw_ptr = Box::into_raw(boxed_buf);
440
441 let old_ptr = ktrace.buffers[cpu_num as usize].swap(raw_ptr, Ordering::AcqRel);
443 if !old_ptr.is_null() {
444 unsafe {
446 let _ = Box::from_raw(old_ptr);
447 }
448 }
449
450 Status::OK.into_raw()
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456 use arch_rs::{InterruptDisableGuard, max_num_cpus};
457
458 declare_interned_category!(META_CAT, "kernel:meta", extern);
459 declare_interned_category!(MEMORY_CAT, "kernel:memory", extern);
460 declare_interned_category!(SCHED_CAT, "kernel:sched", extern);
461 declare_interned_category!(CONTENTION_CAT, "kernel:contention", extern);
462 declare_interned_category!(IPC_CAT, "kernel:ipc", extern);
463 declare_interned_category!(IRQ_CAT, "kernel:irq", extern);
464 declare_interned_string!(DROP_STATS_REF, "drop_stats", extern);
465 declare_interned_string!(NUM_RECORDS_REF, "num_records", extern);
466 declare_interned_string!(NUM_BYTES_REF, "num_bytes", extern);
467
468 #[unsafe(no_mangle)]
478 pub unsafe extern "C" fn rust_ktrace_test_interop(header: u64, val: u64) -> i32 {
479 let ktrace = KTrace::get_instance();
480 if let Ok(mut res) = unsafe { ktrace.reserve(header) } {
482 let _ = res.write_word(val);
483 let _ = res.commit();
484 0
485 } else {
486 -1
487 }
488 }
489
490 #[unsafe(no_mangle)]
492 pub extern "C" fn rust_ktrace_test_init_and_size() -> bool {
493 let mut storage = [0u8; 256];
494 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
495 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
496 let mut stats = DroppedRecordStats::default();
497 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
498 let kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
499
500 if kbuf.size() != 256 {
501 return false;
502 }
503 if kbuf.process_koid != 100 {
504 return false;
505 }
506 if kbuf.thread_koid != 200 {
507 return false;
508 }
509 true
510 }
511
512 #[unsafe(no_mangle)]
514 pub extern "C" fn rust_ktrace_test_write() -> bool {
515 let _guard = InterruptDisableGuard::new();
516 let mut storage = [0u8; 256];
517 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
518 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
519 let mut stats = DroppedRecordStats::default();
520 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
521 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
522
523 let header = 4u64 | (2u64 << 4);
525 let mut res = match kbuf.reserve(header) {
526 Ok(r) => r,
527 Err(_) => return false,
528 };
529
530 if res.write_word(0xabcdef0123456789).is_err() {
532 return false;
533 }
534 if res.commit().is_err() {
535 return false;
536 }
537
538 let mut read_bytes = [0u8; 16];
540 let read_len = match kbuf.read(
541 |offset, src| {
542 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
543 Ok(())
544 },
545 16,
546 ) {
547 Ok(l) => l,
548 Err(_) => return false,
549 };
550
551 if read_len != 16 {
552 return false;
553 }
554 if u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap()) != header {
555 return false;
556 }
557 if u64::from_ne_bytes(read_bytes[8..16].try_into().unwrap()) != 0xabcdef0123456789 {
558 return false;
559 }
560 true
561 }
562
563 #[unsafe(no_mangle)]
566 pub extern "C" fn rust_ktrace_test_dropped_record_tracking() -> bool {
567 let _guard = InterruptDisableGuard::new();
568 let mut storage = [0u8; 128]; let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
570 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
571 let mut stats = DroppedRecordStats::default();
572 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
573 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
574
575 let header = 4u64 | (12u64 << 4);
578 let mut res = match kbuf.reserve(header) {
579 Ok(r) => r,
580 Err(_) => return false,
581 };
582 if res.write_bytes(&[0u8; 88]).is_err() {
583 return false;
584 }
585 if res.commit().is_err() {
586 return false;
587 }
588
589 let header2 = 4u64 | (8u64 << 4);
592 if kbuf.reserve(header2).err() != Some(Status::NO_SPACE) {
593 return false;
594 }
595
596 if !kbuf.drop_stats.has_dropped() {
598 return false;
599 }
600 if kbuf.drop_stats.num_dropped != 1 {
601 return false;
602 }
603 if kbuf.drop_stats.bytes_dropped != 64 {
604 return false;
605 }
606
607 if kbuf.drain().is_err() {
609 return false;
610 }
611
612 let header3 = 4u64 | (2u64 << 4);
616 let mut res3 = match kbuf.reserve(header3) {
617 Ok(r) => r,
618 Err(_) => return false,
619 };
620 if res3.write_bytes(&[0u8; 8]).is_err() {
621 return false;
622 }
623 if res3.commit().is_err() {
624 return false;
625 }
626
627 if kbuf.drop_stats.has_dropped() {
629 return false;
630 }
631 if kbuf.drop_stats.num_dropped != 0 {
632 return false;
633 }
634 if kbuf.drop_stats.bytes_dropped != 0 {
635 return false;
636 }
637
638 let mut read_bytes = [0u8; 72];
640 let read_len = match kbuf.read(
641 |offset, src| {
642 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
643 Ok(())
644 },
645 72,
646 ) {
647 Ok(l) => l,
648 Err(_) => return false,
649 };
650
651 if read_len != 72 {
652 return false;
653 }
654
655 let event_header = u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap());
657 if (event_header & 0xf) != 4 {
658 return false;
659 }
660 if ((event_header >> 4) & 0xfff) != 7 {
661 return false;
662 }
663 if ((event_header >> 16) & 0xf) != 4 {
664 return false;
665 }
666 if ((event_header >> 20) & 0xf) != 2 {
667 return false;
668 }
669 if ((event_header >> 24) & 0xff) != 1 {
670 return false;
671 }
672 if ((event_header >> 32) & 0xffff) != u64::from(META_CAT.label().id()) {
673 return false;
674 }
675 if ((event_header >> 48) & 0xffff) != u64::from(DROP_STATS_REF.id()) {
676 return false;
677 }
678
679 if u64::from_ne_bytes(read_bytes[16..24].try_into().unwrap()) != 100 {
681 return false;
682 }
683 if u64::from_ne_bytes(read_bytes[24..32].try_into().unwrap()) != 200 {
684 return false;
685 }
686
687 let num_dropped_arg = u64::from_ne_bytes(read_bytes[32..40].try_into().unwrap());
689 if (num_dropped_arg & 0xf) != 2 {
690 return false;
691 }
692 if ((num_dropped_arg >> 4) & 0xfff) != 1 {
693 return false;
694 }
695 if ((num_dropped_arg >> 16) & 0xffff) != u64::from(NUM_RECORDS_REF.id()) {
696 return false;
697 }
698 if ((num_dropped_arg >> 32) & 0xffffffff) != 1 {
699 return false;
700 }
701
702 let bytes_dropped_arg = u64::from_ne_bytes(read_bytes[40..48].try_into().unwrap());
703 if (bytes_dropped_arg & 0xf) != 2 {
704 return false;
705 }
706 if ((bytes_dropped_arg >> 16) & 0xffff) != u64::from(NUM_BYTES_REF.id()) {
707 return false;
708 }
709 if ((bytes_dropped_arg >> 32) & 0xffffffff) != 64 {
710 return false;
711 }
712
713 let res_header = u64::from_ne_bytes(read_bytes[56..64].try_into().unwrap());
715 if res_header != header3 {
716 return false;
717 }
718
719 true
720 }
721
722 #[unsafe(no_mangle)]
724 pub extern "C" fn rust_ktrace_test_emit_drop_stats() -> bool {
725 let _guard = InterruptDisableGuard::new();
726 let mut storage = [0u8; 128];
727 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
728 let leaked_ref = unsafe { &mut *ptr::from_mut(&mut inner_buf) };
729 let mut stats = DroppedRecordStats::default();
730 let leaked_stats = unsafe { &mut *ptr::from_mut(&mut stats) };
731 let mut kbuf = KTraceBuffer::new(leaked_ref, leaked_stats, 1, 100, 200);
732
733 let header = 4u64 | (32u64 << 4);
735 if kbuf.reserve(header).err() != Some(Status::NO_SPACE) {
736 return false;
737 }
738
739 if !kbuf.drop_stats.has_dropped() {
740 return false;
741 }
742 if kbuf.drop_stats.num_dropped != 1 {
743 return false;
744 }
745 if kbuf.drop_stats.bytes_dropped != 256 {
746 return false;
747 }
748
749 if kbuf.emit_drop_stats().is_err() {
751 return false;
752 }
753
754 if kbuf.drop_stats.has_dropped() {
755 return false;
756 }
757 if kbuf.drop_stats.num_dropped != 0 {
758 return false;
759 }
760 if kbuf.drop_stats.bytes_dropped != 0 {
761 return false;
762 }
763
764 let mut read_bytes = [0u8; 56];
766 let read_len = match kbuf.read(
767 |offset, src| {
768 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
769 Ok(())
770 },
771 56,
772 ) {
773 Ok(l) => l,
774 Err(_) => return false,
775 };
776
777 if read_len != 56 {
778 return false;
779 }
780
781 let event_header = u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap());
782 if (event_header & 0xf) != 4 {
783 return false;
784 }
785 if ((event_header >> 4) & 0xfff) != 7 {
786 return false;
787 }
788 if ((event_header >> 16) & 0xf) != 4 {
789 return false;
790 }
791 if ((event_header >> 20) & 0xf) != 2 {
792 return false;
793 }
794 if ((event_header >> 24) & 0xff) != 1 {
795 return false;
796 }
797 if ((event_header >> 32) & 0xffff) != u64::from(META_CAT.label().id()) {
798 return false;
799 }
800 if ((event_header >> 48) & 0xffff) != u64::from(DROP_STATS_REF.id()) {
801 return false;
802 }
803
804 if u64::from_ne_bytes(read_bytes[16..24].try_into().unwrap()) != 100 {
805 return false;
806 }
807 if u64::from_ne_bytes(read_bytes[24..32].try_into().unwrap()) != 200 {
808 return false;
809 }
810
811 let num_dropped_arg = u64::from_ne_bytes(read_bytes[32..40].try_into().unwrap());
812 if (num_dropped_arg & 0xf) != 2 {
813 return false;
814 }
815 if ((num_dropped_arg >> 16) & 0xffff) != u64::from(NUM_RECORDS_REF.id()) {
816 return false;
817 }
818 if ((num_dropped_arg >> 32) & 0xffffffff) != 1 {
819 return false;
820 }
821
822 let bytes_dropped_arg = u64::from_ne_bytes(read_bytes[40..48].try_into().unwrap());
823 if (bytes_dropped_arg & 0xf) != 2 {
824 return false;
825 }
826 if ((bytes_dropped_arg >> 16) & 0xffff) != u64::from(NUM_BYTES_REF.id()) {
827 return false;
828 }
829 if ((bytes_dropped_arg >> 32) & 0xffffffff) != 256 {
830 return false;
831 }
832
833 true
834 }
835
836 #[unsafe(no_mangle)]
839 pub extern "C" fn rust_ktrace_test_global_lifecycle() -> bool {
840 let _guard = InterruptDisableGuard::new();
841 META_CAT.set_index(0, InternedCategory::INVALID_INDEX);
843 MEMORY_CAT.set_index(1, InternedCategory::INVALID_INDEX);
844 SCHED_CAT.set_index(2, InternedCategory::INVALID_INDEX);
845 CONTENTION_CAT.set_index(3, InternedCategory::INVALID_INDEX);
846 IPC_CAT.set_index(4, InternedCategory::INVALID_INDEX);
847 IRQ_CAT.set_index(5, InternedCategory::INVALID_INDEX);
848
849 let num_cpus = max_num_cpus();
852 let mut local_state = KTraceState {
853 categories_bitmask: AtomicU32::new(0),
854 writes_enabled: AtomicBool::new(false),
855 };
856 let local_state_ptr = ptr::from_mut(&mut local_state).cast::<ffi::c_void>();
857
858 let status = unsafe { rust_ktrace_init(num_cpus, local_state_ptr) };
859 if status != 0 {
860 return false;
861 }
862
863 let ktrace = KTrace::get_instance();
864
865 if ktrace.writes_enabled() {
867 return false;
868 }
869 if ktrace.categories_bitmask() != 0 {
870 return false;
871 }
872 if ktrace.is_category_enabled(&META_CAT) {
873 return false;
874 }
875 if ktrace.is_category_enabled(&IRQ_CAT) {
876 return false;
877 }
878
879 local_state.writes_enabled.store(true, Ordering::Release);
881 if !ktrace.writes_enabled() {
882 return false;
883 }
884 local_state.writes_enabled.store(false, Ordering::Release);
885 if ktrace.writes_enabled() {
886 return false;
887 }
888
889 let mask = (1 << MEMORY_CAT.index()) | (1 << CONTENTION_CAT.index());
891 local_state.categories_bitmask.store(mask, Ordering::Release);
892 if ktrace.categories_bitmask() != mask {
893 return false;
894 }
895 if ktrace.is_category_enabled(&META_CAT) {
896 return false;
897 }
898 if !ktrace.is_category_enabled(&MEMORY_CAT) {
899 return false;
900 }
901 if ktrace.is_category_enabled(&SCHED_CAT) {
902 return false;
903 }
904 if !ktrace.is_category_enabled(&CONTENTION_CAT) {
905 return false;
906 }
907 if ktrace.is_category_enabled(&IPC_CAT) {
908 return false;
909 }
910
911 let mut storage = [0u8; 256];
913 let mut inner_buf = unsafe { Buffer::from_raw_parts(storage.as_mut_ptr(), storage.len()) };
914 let inner_buf_ptr = ptr::from_mut(&mut inner_buf).cast::<ffi::c_void>();
915 let mut stats = DroppedRecordStats::default();
916 let stats_ptr = ptr::from_mut(&mut stats).cast::<ffi::c_void>();
917
918 let cpu = curr_cpu_num();
920 let init_status = unsafe {
921 rust_ktrace_init_cpu_buffer(
922 cpu, inner_buf_ptr,
924 stats_ptr,
925 100, 200, 1, )
929 };
930 if init_status != 0 {
931 return false;
932 }
933
934 let header = 4u64 | (2u64 << 4); if unsafe { ktrace.reserve(header) }.err() != Some(Status::BAD_STATE) {
937 return false;
938 }
939
940 local_state.writes_enabled.store(true, Ordering::Release);
942
943 let mut res = match unsafe { ktrace.reserve(header) } {
945 Ok(r) => r,
946 Err(_) => return false,
947 };
948 if res.write_word(0x1234567890abcdef).is_err() {
949 return false;
950 }
951 if res.commit().is_err() {
952 return false;
953 }
954
955 let ptr = ktrace.buffers[cpu as usize].load(Ordering::Acquire);
957 if ptr.is_null() {
958 return false;
959 }
960 let buf = unsafe { &*ptr };
961
962 let mut read_bytes = [0u8; 16];
963 let read_len = match buf.read(
964 |offset, src| {
965 read_bytes[offset as usize..offset as usize + src.len()].copy_from_slice(src);
966 Ok(())
967 },
968 16,
969 ) {
970 Ok(l) => l,
971 Err(_) => return false,
972 };
973
974 if read_len != 16 {
975 return false;
976 }
977 if u64::from_ne_bytes(read_bytes[0..8].try_into().unwrap()) != header {
978 return false;
979 }
980 if u64::from_ne_bytes(read_bytes[8..16].try_into().unwrap()) != 0x1234567890abcdef {
981 return false;
982 }
983
984 true
985 }
986}