Skip to content

Commit a3c79dc

Browse files
committed
Batch allocate all trampolines
Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com>
1 parent 4358ab8 commit a3c79dc

1 file changed

Lines changed: 50 additions & 41 deletions

File tree

profiling/src/wall_time.rs

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
141141
mod 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

Comments
 (0)