use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use cairo_lang_sierra::program::Program;
use starknet_types_core::felt::Felt;
use crate::error::{Error, Result};
use crate::execution_result::ContractExecutionResult;
use crate::executor::{AotContractExecutor, ArcProgram};
use crate::metadata::profiler::{Profile, ProfilerBinding, ProfilerImpl, LIBFUNC_PROFILE};
use crate::starknet::StarknetSyscallHandler;
use crate::utils::BuiltinCosts;
#[derive(Debug)]
pub struct AotWithProgram {
pub executor: AotContractExecutor,
pub program: ArcProgram,
}
impl AotWithProgram {
pub fn run(
&self,
selector: Felt,
args: &[Felt],
gas: u64,
builtin_costs: Option<BuiltinCosts>,
syscall_handler: impl StarknetSyscallHandler,
) -> Result<ContractExecutionResult> {
self.executor.run_with_libfunc_profile(
&self.program,
selector,
args,
gas,
builtin_costs,
syscall_handler,
|_profile| {},
)
}
pub fn run_with_profile<H, F>(
&self,
selector: Felt,
args: &[Felt],
gas: u64,
builtin_costs: Option<BuiltinCosts>,
syscall_handler: H,
on_profile: F,
) -> Result<ContractExecutionResult>
where
H: StarknetSyscallHandler,
F: FnOnce(Profile, ArcProgram),
{
let program_for_cb = Arc::clone(&self.program);
self.executor.run_with_libfunc_profile(
&self.program,
selector,
args,
gas,
builtin_costs,
syscall_handler,
move |profile| on_profile(profile, program_for_cb),
)
}
}
static PROFILE_LOCK: Mutex<()> = Mutex::new(());
thread_local! {
static PROFILE_DEPTH: Cell<usize> = const { Cell::new(0) };
}
impl AotContractExecutor {
#[allow(clippy::too_many_arguments)]
pub fn run_with_libfunc_profile<H, F>(
&self,
program: &Arc<Program>,
selector: Felt,
args: &[Felt],
gas: u64,
builtin_costs: Option<BuiltinCosts>,
syscall_handler: H,
on_profile: F,
) -> Result<ContractExecutionResult>
where
H: StarknetSyscallHandler,
F: FnOnce(Profile),
{
let _profile_lock = PROFILE_DEPTH
.with(|depth| depth.get() == 0)
.then(|| PROFILE_LOCK.lock().unwrap_or_else(|e| e.into_inner()));
PROFILE_DEPTH.with(|depth| depth.set(depth.get() + 1));
let _depth_guard = ProfileDepthGuard;
let trace_id_ptr = self
.find_symbol_ptr(ProfilerBinding::ProfileId.symbol())
.ok_or_else(|| {
Error::UnexpectedValue(format!(
"AOT executor missing libfunc-profiling symbol `{}`; \
was the program compiled with libfunc-profiling enabled?",
ProfilerBinding::ProfileId.symbol()
))
})?
.cast::<u64>();
static COUNTER: AtomicU64 = AtomicU64::new(0);
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
LIBFUNC_PROFILE
.lock()
.unwrap_or_else(|e| e.into_inner())
.insert(counter, ProfilerImpl::new());
let old_trace_id = unsafe { *trace_id_ptr };
unsafe {
*trace_id_ptr = counter;
}
let _guard = ProfilerGuard {
trace_id_ptr,
old_trace_id,
counter,
};
let result = self.run(selector, args, gas, builtin_costs, syscall_handler);
let drained = LIBFUNC_PROFILE
.lock()
.unwrap_or_else(|e| e.into_inner())
.remove(&counter);
if let (Some(profiler), Ok(_)) = (drained, &result) {
on_profile(profiler.get_profile(program));
}
result
}
}
struct ProfilerGuard {
trace_id_ptr: *mut u64,
old_trace_id: u64,
counter: u64,
}
impl Drop for ProfilerGuard {
fn drop(&mut self) {
unsafe {
*self.trace_id_ptr = self.old_trace_id;
}
if let Ok(mut profile) = LIBFUNC_PROFILE.lock() {
profile.remove(&self.counter);
}
}
}
struct ProfileDepthGuard;
impl Drop for ProfileDepthGuard {
fn drop(&mut self) {
PROFILE_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
}
}