11#![ cfg( feature = "with-libfunc-counter" ) ]
22//! The libfunc counter feature is used to generate information counting how many time a libfunc has been called.
33//!
4- //! When this feature is used, the compiler will call three important methods :
4+ //! When this feature is used, the compiler will call one main method :
55//!
6- //! 1. `count_libfunc`: called before every libfunc execution.
6+ //! 1. `count_libfunc`: called before every libfunc execution. This method will handle the counting. Given a the index
7+ //! of a libfunc (relative the its declaration order), it accesses the array of counters and updates the counter.
78//!
8- //! 2. `build_array_counter`: called only once to build the array of counters (one for each libfuncs). The order of
9- //! is based on the libfuncs' declaration order.
9+ //! In the context of Starknet contracts, we need to add support for building the arrays of counters for multiple executions.
10+ //! To do so, we need one important element, which must be set before every contract execution:
1011//!
11- //! 3. `store_array_counter`: called before finishing each entrypoint execution. It transforms the MLIR array into a
12- //! Rust vector which is then stored in `LIBFUNC_COUNTER`, a static variable that registers the array of counters by
13- //! execution, along with its `counter_id` (which is relative to the execution).
12+ //! * A counter to track the ID of the current array of counter, which gets updated every time we switch to another
13+ //! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution.
1414//!
15- //! In the context of Starknet contracts, we need to add support for building
16- //! the arrays of counters for multiple executions. To do so, we need one important element, which must be set before every contract
17- //! execution:
18- //!
19- //! A counter to track the ID of the current array of counter, which gets updated every time we switch to another
20- //! contract. Since a contract can call other contracts, we need a way of restoring the counter after every execution.
15+ //! * An array-of-counter guard. Every time a new entrypoint is executed, a new array of counters needs to be created in order to isolate
16+ //! each of them. The guard keeps the last array that was used to restore it once the inner entrypoint execution has finished.
2117//!
2218//! See `cairo-native-run` for an example on how to do it.
2319use std:: collections:: HashSet ;
@@ -48,15 +44,15 @@ impl LibfuncCounterBinding {
4844 pub const fn symbol ( self ) -> & ' static str {
4945 match self {
5046 LibfuncCounterBinding :: CounterId => "cairo_native__counter_id" ,
51- LibfuncCounterBinding :: GetCounterArray => "cairo_native__get_counter_array " ,
47+ LibfuncCounterBinding :: GetCounterArray => "cairo_native__get_counters_array " ,
5248 }
5349 }
5450
5551 pub const fn function_ptr ( self ) -> * const ( ) {
5652 match self {
5753 LibfuncCounterBinding :: CounterId => std:: ptr:: null ( ) ,
5854 LibfuncCounterBinding :: GetCounterArray => {
59- libfunc_counter_runtime:: get_counter_array as * const ( )
55+ libfunc_counter_runtime:: get_counters_array as * const ( )
6056 }
6157 }
6258 }
@@ -77,7 +73,7 @@ impl LibfuncCounterMeta {
7773 /// Register the global for the given binding, if not yet registered, and return
7874 /// a pointer to the stored value.
7975 ///
80- /// For the function to be available, `setup_runtime` must be called before running the module
76+ /// For the function to be available, `setup_runtime` must be called before running the module.
8177 pub fn build_function < ' c , ' a > (
8278 & mut self ,
8379 context : & ' c Context ,
@@ -151,15 +147,16 @@ impl LibfuncCounterMeta {
151147 block. append_op_result ( memref:: load ( libfunc_counter_id_ptr, & [ ] , location) )
152148 }
153149
154- /// Build the array of counters
155- fn build_array_counter < ' c , ' a > (
150+ /// Returns the array of counters.
151+ fn get_array_counter < ' c , ' a > (
156152 & mut self ,
157153 context : & ' c Context ,
158154 module : & Module ,
159155 block : & ' a Block < ' c > ,
160156 location : Location < ' c > ,
161157 ) -> Result < Value < ' c , ' a > > {
162158 self . build_counter_id ( context, module, block, location) ?;
159+
163160 let function_ptr = self . build_function (
164161 context,
165162 module,
@@ -187,7 +184,7 @@ impl LibfuncCounterMeta {
187184 let u32_ty = IntegerType :: new ( context, 32 ) . into ( ) ;
188185 let k1 = block. const_int ( context, location, 1 , 32 ) ?;
189186
190- let array_counter_ptr = self . build_array_counter ( context, module, block, location) ?;
187+ let array_counter_ptr = self . get_array_counter ( context, module, block, location) ?;
191188
192189 let value_counter_ptr = block. gep (
193190 context,
@@ -233,9 +230,10 @@ pub mod libfunc_counter_runtime {
233230 use crate :: {
234231 error:: Result ,
235232 metadata:: { libfunc_counter:: LibfuncCounterMeta , MetadataStorage } ,
233+ utils:: libc_malloc,
236234 } ;
237235
238- /// Contains an array of vector for each execution completed
236+ /// Contains an array of vector for each execution completed.
239237 pub static LIBFUNC_COUNTER : LazyLock < Mutex < HashMap < u64 , Vec < u32 > > > > =
240238 LazyLock :: new ( || Mutex :: new ( HashMap :: new ( ) ) ) ;
241239
@@ -255,13 +253,13 @@ pub mod libfunc_counter_runtime {
255253 impl CountersArrayGuard {
256254 pub fn init ( libfuncs_amount : usize ) -> CountersArrayGuard {
257255 let u32_libfuncs_amount = libfuncs_amount * 4 ;
256+ let new_array: * mut u32 = unsafe { libc_malloc ( u32_libfuncs_amount) . cast ( ) } ;
258257
259- let new_array = unsafe { libc:: malloc ( u32_libfuncs_amount) as * mut u32 } ;
260-
258+ // All positions in the array must be initialized with 0. Since
259+ // some libfuncs declared may not be called, their respective counter
260+ // won't be updated.
261261 for i in 0 ..libfuncs_amount {
262- unsafe {
263- * ( new_array. add ( i) ) = 0
264- } ;
262+ unsafe { * ( new_array. add ( i) ) = 0 } ;
265263 }
266264
267265 Self ( COUNTERS_ARRAY . replace ( new_array) )
@@ -274,7 +272,7 @@ pub mod libfunc_counter_runtime {
274272 }
275273 }
276274
277- /// Increase the libfunc's counter given its index
275+ /// Update the libfunc's counter based on its index, relative to the order of declaration.
278276 pub fn count_libfunc (
279277 context : & Context ,
280278 module : & Module ,
@@ -288,21 +286,22 @@ pub mod libfunc_counter_runtime {
288286 libfunc_counter. count_libfunc ( context, module, block, location, libfunc_idx)
289287 }
290288
291- pub extern "C" fn get_counter_array ( ) -> * mut u32 {
289+ pub extern "C" fn get_counters_array ( ) -> * mut u32 {
292290 COUNTERS_ARRAY . with ( |x| x. get ( ) ) as * mut u32
293291 }
294292
293+ /// Converts the pointer to the counters into a Rust `Vec` and store it. Then, it frees the pointer.
294+ ///
295+ /// This method should be called at the end of an entrypoint execution.
295296 pub unsafe fn store_and_free_counters_array ( counter_id_ptr : * mut u64 , libfuncs_amount : usize ) {
296- let counter_array_ptr = get_counter_array ( ) ;
297+ let counter_array_ptr = get_counters_array ( ) ;
297298 let counters_vec = slice:: from_raw_parts ( counter_array_ptr, libfuncs_amount) . to_vec ( ) ;
298299
299300 LIBFUNC_COUNTER
300301 . lock ( )
301302 . unwrap ( )
302303 . insert ( * counter_id_ptr, counters_vec) ;
303304
304- if !( counter_array_ptr. is_null ( ) ) {
305- libc:: free ( counter_array_ptr as * mut libc:: c_void ) ;
306- }
305+ libc:: free ( counter_array_ptr as * mut libc:: c_void ) ;
307306 }
308307}
0 commit comments