88
99#include <stdio.h>
1010
11+ VALUE IO_Event_Profile = Qnil ;
12+
1113void IO_Event_Profile_Call_initialize (struct IO_Event_Profile_Call * call ) {
1214 call -> enter_time .tv_sec = 0 ;
1315 call -> enter_time .tv_nsec = 0 ;
@@ -29,6 +31,50 @@ void IO_Event_Profile_Call_free(struct IO_Event_Profile_Call *call) {
2931 }
3032}
3133
34+ static void IO_Event_Profile_mark (void * ptr ) {
35+ struct IO_Event_Profile * profile = (struct IO_Event_Profile * )ptr ;
36+
37+ // If `klass` is stored as a VALUE in calls, we need to mark them here:
38+ for (size_t i = 0 ; i < profile -> calls .limit ; i += 1 ) {
39+ struct IO_Event_Profile_Call * call = profile -> calls .base [i ];
40+ rb_gc_mark (call -> klass );
41+ }
42+ }
43+
44+ static void IO_Event_Profile_free (void * ptr ) {
45+ struct IO_Event_Profile * profile = (struct IO_Event_Profile * )ptr ;
46+
47+ IO_Event_Array_free (& profile -> calls );
48+
49+ free (profile );
50+ }
51+
52+ const rb_data_type_t IO_Event_Profile_Type = {
53+ .wrap_struct_name = "IO_Event_Profile" ,
54+ .function = {
55+ .dmark = IO_Event_Profile_mark ,
56+ .dfree = IO_Event_Profile_free ,
57+ },
58+ .flags = RUBY_TYPED_FREE_IMMEDIATELY ,
59+ };
60+
61+ VALUE IO_Event_Profile_allocate (VALUE klass ) {
62+ struct IO_Event_Profile * profile = ALLOC (struct IO_Event_Profile );
63+
64+ profile -> calls .element_initialize = (void (* )(void * ))IO_Event_Profile_Call_initialize ;
65+ profile -> calls .element_free = (void (* )(void * ))IO_Event_Profile_Call_free ;
66+
67+ IO_Event_Array_initialize (& profile -> calls , 0 , sizeof (struct IO_Event_Profile_Call ));
68+
69+ return TypedData_Wrap_Struct (klass , & IO_Event_Profile_Type , profile );
70+ }
71+
72+ struct IO_Event_Profile * IO_Event_Profile_get (VALUE self ) {
73+ struct IO_Event_Profile * profile ;
74+ TypedData_Get_Struct (self , struct IO_Event_Profile , & IO_Event_Profile_Type , profile );
75+ return profile ;
76+ }
77+
3278int event_flag_call_p (rb_event_flag_t event_flags ) {
3379 return event_flags & (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL );
3480}
@@ -38,7 +84,7 @@ int event_flag_return_p(rb_event_flag_t event_flags) {
3884}
3985
4086static void profile_event_callback (rb_event_flag_t event_flag , VALUE data , VALUE self , ID id , VALUE klass ) {
41- struct IO_Event_Profile * profile = ( struct IO_Event_Profile * ) data ;
87+ struct IO_Event_Profile * profile = IO_Event_Profile_get ( data ) ;
4288
4389 if (event_flag_call_p (event_flag )) {
4490 struct IO_Event_Profile_Call * call = IO_Event_Array_push (& profile -> calls );
@@ -77,39 +123,38 @@ static void profile_event_callback(rb_event_flag_t event_flag, VALUE data, VALUE
77123 }
78124}
79125
80- void IO_Event_Profile_initialize (struct IO_Event_Profile * profile , VALUE fiber ) {
81- profile -> fiber = fiber ;
82-
83- profile -> calls .element_initialize = (void (* )(void * ))IO_Event_Profile_Call_initialize ;
84- profile -> calls .element_free = (void (* )(void * ))IO_Event_Profile_Call_free ;
126+ void IO_Event_Profile_start (VALUE self , int track_calls ) {
127+ struct IO_Event_Profile * profile = IO_Event_Profile_get (self );
85128
86- IO_Event_Array_initialize (& profile -> calls , 0 , sizeof (struct IO_Event_Profile_Call ));
87- }
88-
89- void IO_Event_Profile_start (struct IO_Event_Profile * profile ) {
90129 IO_Event_Time_current (& profile -> start_time );
91130 profile -> nesting = 0 ;
92131 profile -> current = NULL ;
93132
133+ profile -> track_calls = track_calls ;
134+
94135 // Since fibers are currently limited to a single thread, we use this in the hope that it's a little more efficient:
95- VALUE thread = rb_thread_current ();
96- rb_thread_add_event_hook (thread , profile_event_callback , RUBY_EVENT_CALL | RUBY_EVENT_C_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN , (VALUE )profile );
136+ if (profile -> track_calls ) {
137+ VALUE thread = rb_thread_current ();
138+ rb_thread_add_event_hook (thread , profile_event_callback , RUBY_EVENT_CALL | RUBY_EVENT_C_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN , self );
139+ }
97140}
98141
99- void IO_Event_Profile_stop (struct IO_Event_Profile * profile ) {
142+ void IO_Event_Profile_stop (VALUE self ) {
143+ struct IO_Event_Profile * profile = IO_Event_Profile_get (self );
144+
100145 IO_Event_Time_current (& profile -> stop_time );
101146
102- VALUE thread = rb_thread_current ();
103- rb_thread_remove_event_hook_with_data (thread , profile_event_callback , (VALUE )profile );
104- }
105-
106- void IO_Event_Profile_free (struct IO_Event_Profile * profile ) {
107- IO_Event_Array_free (& profile -> calls );
147+ if (profile -> track_calls ) {
148+ VALUE thread = rb_thread_current ();
149+ rb_thread_remove_event_hook_with_data (thread , profile_event_callback , self );
150+ }
108151}
109152
110153static const float IO_EVENT_PROFILE_PRINT_MINIMUM_PROPORTION = 0.01 ;
111154
112- void IO_Event_Profile_print (FILE * restrict stream , struct IO_Event_Profile * profile ) {
155+ void IO_Event_Profile_print (VALUE self , FILE * restrict stream ) {
156+ struct IO_Event_Profile * profile = IO_Event_Profile_get (self );
157+
113158 struct timespec total_duration = {};
114159 IO_Event_Time_elapsed (& profile -> start_time , & profile -> stop_time , & total_duration );
115160
@@ -132,11 +177,18 @@ void IO_Event_Profile_print(FILE *restrict stream, struct IO_Event_Profile *prof
132177 fputc ('\t' , stream );
133178 }
134179
180+ VALUE class_inspect = rb_inspect (call -> klass );
135181 const char * name = rb_id2name (call -> id );
136- fprintf (stream , "\t%s:%d in '%s#%s' (" IO_EVENT_TIME_PRINTF_TIMESPEC "s)\n" , call -> path , call -> line , RSTRING_PTR (rb_inspect (call -> klass )), name , IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS (duration ));
182+
183+ fprintf (stream , "\t%s:%d in '%s#%s' (" IO_EVENT_TIME_PRINTF_TIMESPEC "s)\n" , call -> path , call -> line , RSTRING_PTR (class_inspect ), name , IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS (duration ));
137184 }
138185
139186 if (skipped > 0 ) {
140187 fprintf (stream , "Skipped %zu calls that were too short to be meaningful.\n" , skipped );
141188 }
142189}
190+
191+ void Init_IO_Event_Profile (VALUE IO_Event ) {
192+ IO_Event_Profile = rb_define_class_under (IO_Event , "Profile" , rb_cObject );
193+ rb_define_alloc_func (IO_Event_Profile , IO_Event_Profile_allocate );
194+ }
0 commit comments