@@ -137,10 +137,9 @@ pub extern "C" fn ddog_php_prof_interrupt_function(execute_data: *mut zend_execu
137137 }
138138}
139139
140- #[ cfg( php_frameless) ]
140+ // #[cfg(php_frameless)]
141141mod frameless {
142142 #[ cfg( any( target_arch = "x86_64" , target_arch = "aarch64" ) ) ]
143- use crate :: bindings:: { zend_flf_functions, zend_flf_handlers} ;
144143
145144 #[ cfg( any( target_arch = "x86_64" , target_arch = "aarch64" ) ) ]
146145 mod trampoline {
@@ -151,51 +150,48 @@ mod frameless {
151150 use dynasmrt:: { dynasm, DynasmApi } ;
152151 use std:: ffi:: c_void;
153152 use super :: super :: ddog_php_prof_interrupt_function;
153+ use crate :: bindings:: { zend_flf_functions, zend_flf_handlers} ;
154154 use crate :: zend;
155155
156- pub unsafe fn generate_wrapper ( original : * mut c_void ) -> * mut c_void {
157- // Calls original function, then calls interrupt function.
158- let mut assembler = Assembler :: new ( ) . unwrap ( ) ;
159- let interrupt_addr = ddog_php_prof_icall_trampoline_target as * const ( ) ;
160- #[ cfg( target_arch = "aarch64" ) ]
161- dynasm ! ( assembler
162- ; mov x16, original as u64
163- ; blr x16
164- ; mov x16, interrupt_addr as u64
165- ; br x16 // tail call
166- ) ;
167- #[ cfg( target_arch = "x86_64" ) ]
168- dynasm ! ( assembler
169- ; mov rax, QWORD original as i64
170- ; call rax
171- ; mov rax, QWORD interrupt_addr as i64
172- ; jmp rax // tail call
173- ) ;
174- let buffer = assembler. finalize ( ) . unwrap ( ) ;
175- let ptr = buffer. as_ptr ( ) as * mut c_void ;
176- std:: mem:: forget ( buffer) ; // TODO: leaks memory
177- ptr
178- }
179-
180- #[ no_mangle]
181- #[ inline( never) ]
182- pub unsafe extern "C" fn ddog_php_prof_icall_trampoline_target ( ) {
183- // TODO: First check for REQUEST_LOCALS.interrupt_count before fetching execute data to make this less expensive
184- ddog_php_prof_interrupt_function ( zend:: ddog_php_prof_get_current_execute_data ( ) ) ;
185- }
186- }
187-
188- #[ no_mangle]
189- pub unsafe extern "C" fn ddog_php_prof_post_startup ( ) {
190- #[ cfg( any( target_arch = "x86_64" , target_arch = "aarch64" ) ) ]
191- {
156+ pub unsafe fn install ( ) {
157+ // Collect frameless functions ahead of time to batch-process them.
158+ // Otherwise we get a new memory page per function.
159+ let mut originals = Vec :: new ( ) ;
192160 let mut i = 0 ;
193161 loop {
194162 let original = * zend_flf_handlers. add ( i) ;
195163 if original. is_null ( ) {
196164 break ;
197165 }
198- let wrapper = trampoline:: generate_wrapper ( original) ;
166+ originals. push ( original) ;
167+ i += 1 ;
168+ }
169+
170+ let mut assembler = Assembler :: new ( ) . unwrap ( ) ;
171+ let interrupt_addr = ddog_php_prof_icall_trampoline_target as * const ( ) ;
172+ let mut offsets = Vec :: new ( ) ; // keep function offsets
173+ for orig in originals. iter ( ) {
174+ offsets. push ( assembler. offset ( ) ) ;
175+ // Calls original function, then calls interrupt function.
176+ #[ cfg( target_arch = "aarch64" ) ]
177+ dynasm ! ( assembler
178+ ; mov x16, * orig as u64
179+ ; blr x16
180+ ; mov x16, interrupt_addr as u64
181+ ; br x16 // tail call
182+ ) ;
183+ #[ cfg( target_arch = "x86_64" ) ]
184+ dynasm ! ( assembler
185+ ; mov rax, QWORD * orig as i64
186+ ; call rax
187+ ; mov rax, QWORD interrupt_addr as i64
188+ ; jmp rax // tail call
189+ ) ;
190+ }
191+
192+ let buffer = assembler. finalize ( ) . unwrap ( ) ;
193+ for ( i, offset) in offsets. iter ( ) . enumerate ( ) {
194+ let wrapper = buffer. as_ptr ( ) . add ( offset. 0 ) as * mut c_void ;
199195 * zend_flf_handlers. add ( i) = wrapper;
200196 let func = & mut * * zend_flf_functions. add ( i) ;
201197
@@ -212,16 +208,29 @@ mod frameless {
212208 ptr = ptr. add ( 1 ) ;
213209 }
214210 for info in infos. iter_mut ( ) {
215- if info. handler == original {
211+ if info. handler == originals [ i ] {
216212 info. handler = wrapper;
217213 }
218214 }
219215 let new_infos = infos. into_boxed_slice ( ) ;
220216 func. internal_function . frameless_function_infos = new_infos. as_ptr ( ) as * mut _ ;
221217 std:: mem:: forget ( new_infos) ; // TODO: leaks memory
222- i += 1 ;
223218 }
219+ std:: mem:: forget ( buffer) ; // TODO: leaks memory
224220 }
221+
222+ #[ no_mangle]
223+ #[ inline( never) ]
224+ pub unsafe extern "C" fn ddog_php_prof_icall_trampoline_target ( ) {
225+ // TODO: First check for REQUEST_LOCALS.interrupt_count before fetching execute data to make this less expensive
226+ ddog_php_prof_interrupt_function ( zend:: ddog_php_prof_get_current_execute_data ( ) ) ;
227+ }
228+ }
229+
230+ #[ no_mangle]
231+ pub unsafe extern "C" fn ddog_php_prof_post_startup ( ) {
232+ #[ cfg( any( target_arch = "x86_64" , target_arch = "aarch64" ) ) ]
233+ trampoline:: install ( ) ;
225234 }
226235}
227236
0 commit comments