1use std::alloc::GlobalAlloc;
2
3unsafe extern "C" {
4 fn sftrace_setup(
5 entry_slot: unsafe extern "C" fn(),
6 exit_slot: unsafe extern "C" fn(),
7 tailcall_slot: unsafe extern "C" fn(),
8 );
9
10 fn sftrace_alloc_event(
11 kind: u8,
12 size: usize,
13 align: usize,
14 ptr: *mut u8
15 );
16}
17
18#[cfg(target_arch = "x86_64")]
19macro_rules! build_slot {
20 ( $( $name:ident );* $( ; )? ) => {
21 $(
22 build_slot!(@$name);
23 )*
24 };
25 ( @ $name:ident ) => {
26 #[unsafe(naked)]
27 unsafe extern "C" fn $name() {
28 std::arch::naked_asm!(
30 "ret",
31 "nop",
32 "nop",
33 "nop",
34 "nop",
35 "nop",
36 "nop",
37 "nop",
38 "nop",
39 "nop",
40 "nop",
41 "nop",
42 "nop",
43 "nop",
44 "nop",
45 "nop",
46 );
47 }
48 };
49}
50
51#[cfg(target_arch = "aarch64")]
52macro_rules! build_slot {
53 ( $( $name:ident );* $( ; )? ) => {
54 $(
55 build_slot!(@$name);
56 )*
57 };
58 ( @ $name:ident ) => {
59 unsafe extern "C" fn $name() {
60 }
62 };
63}
64
65build_slot! {
66 sftrace_entry_slot;
67 sftrace_exit_slot;
68 sftrace_tailcall_slot;
69}
70
71#[inline(always)]
72pub unsafe fn setup() {
73 if std::env::var_os("SFTRACE_OUTPUT_FILE").is_none() {
74 return;
76 }
77
78 ENABLE_ALLOCATOR_HOOK.store(true, std::sync::atomic::Ordering::Relaxed);
79
80 unsafe {
81 sftrace_setup(
82 sftrace_entry_slot,
83 sftrace_exit_slot,
84 sftrace_tailcall_slot
85 );
86 }
87}
88
89static ENABLE_ALLOCATOR_HOOK: std::sync::atomic::AtomicBool =
90 std::sync::atomic::AtomicBool::new(false);
91
92pub struct SftraceAllocator<A: GlobalAlloc>(pub A);
93
94unsafe impl<A: GlobalAlloc> GlobalAlloc for SftraceAllocator<A> {
95 #[inline]
96 unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
97 let enable = ENABLE_ALLOCATOR_HOOK.load(std::sync::atomic::Ordering::Relaxed);
98
99 unsafe {
100 let v = std::alloc::System.alloc(layout);
101 if enable {
102 sftrace_alloc_event(1, layout.size(), layout.align(), v);
103 }
104 v
105 }
106 }
107
108 #[inline]
109 unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
110 let enable = ENABLE_ALLOCATOR_HOOK.load(std::sync::atomic::Ordering::Relaxed);
111
112 unsafe {
113 if enable {
114 sftrace_alloc_event(2, layout.size(), layout.align(), ptr);
115 }
116
117 std::alloc::System.dealloc(ptr, layout);
118 }
119 }
120
121 #[inline]
122 unsafe fn alloc_zeroed(&self, layout: std::alloc::Layout) -> *mut u8 {
123 let enable = ENABLE_ALLOCATOR_HOOK.load(std::sync::atomic::Ordering::Relaxed);
124
125 unsafe {
126 let v = std::alloc::System.alloc_zeroed(layout);
127 if enable {
128 sftrace_alloc_event(1, layout.size(), layout.align(), v);
129 }
130 v
131 }
132 }
133
134 #[inline]
135 unsafe fn realloc(&self, ptr: *mut u8, layout: std::alloc::Layout, new_size: usize) -> *mut u8 {
136 let enable = ENABLE_ALLOCATOR_HOOK.load(std::sync::atomic::Ordering::Relaxed);
137 unsafe {
138 if enable {
139 sftrace_alloc_event(4, layout.size(), layout.align(), ptr);
140 }
141 let v = std::alloc::System.realloc(ptr, layout, new_size);
142 if enable {
143 sftrace_alloc_event(3, new_size, layout.align(), v);
144 }
145 v
146 }
147 }
148}