fiber-profiler 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - checksums.yaml.gz.sig +0 -0
 - data/ext/fiber/profiler/capture.c +248 -194
 - data/ext/fiber/profiler/capture.h +0 -4
 - data/lib/fiber/profiler/version.rb +1 -1
 - data.tar.gz.sig +0 -0
 - metadata +8 -3
 - metadata.gz.sig +0 -0
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 763fd680dafd595bcfdb69ffcac3161ab2c21ffe4ddeb54110d30b26af78c645
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 7619a44d197a88b4ecb2f24e3e48a4d4e8a660f7fc9bd1d6cc0482bad45df92f
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 968cc396e79c7030a7fa2d384b21dd0491f4d4ea03c2173cfe1c1081d982cc6976ba32c023a676deab5dfc748b1dbc320d9f1087f41532249484f647a62b1f94
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 1b68a1bfccf03defd375fe90da755b33fd9da19155bfac3b7b03208a664075c3469518077c0270102f36f07b44b4016ce8fd96dd1969701370f14ccd526816b3
         
     | 
    
        checksums.yaml.gz.sig
    CHANGED
    
    | 
         Binary file 
     | 
| 
         @@ -19,6 +19,7 @@ enum { 
     | 
|
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            int Fiber_Profiler_capture_p = 0;
         
     | 
| 
       21 
21 
     | 
    
         
             
            double Fiber_Profiler_Capture_stall_threshold = 0.01;
         
     | 
| 
      
 22 
     | 
    
         
            +
            double Fiber_Profiler_Capture_filter_threshold = 0.001;
         
     | 
| 
       22 
23 
     | 
    
         
             
            int Fiber_Profiler_Capture_track_calls = 1;
         
     | 
| 
       23 
24 
     | 
    
         
             
            double Fiber_Profiler_Capture_sample_rate = 1;
         
     | 
| 
       24 
25 
     | 
    
         | 
| 
         @@ -65,7 +66,7 @@ void Fiber_Profiler_Stream_free(struct Fiber_Profiler_Stream *stream) { 
     | 
|
| 
       65 
66 
     | 
    
         
             
            }
         
     | 
| 
       66 
67 
     | 
    
         | 
| 
       67 
68 
     | 
    
         
             
            struct Fiber_Profiler_Capture;
         
     | 
| 
       68 
     | 
    
         
            -
            typedef void(*Fiber_Profiler_Stream_Print)(struct Fiber_Profiler_Capture*, FILE* restrict);
         
     | 
| 
      
 69 
     | 
    
         
            +
            typedef void(*Fiber_Profiler_Stream_Print)(struct Fiber_Profiler_Capture*, FILE* restrict, double duration);
         
     | 
| 
       69 
70 
     | 
    
         | 
| 
       70 
71 
     | 
    
         
             
            struct Fiber_Profiler_Capture {
         
     | 
| 
       71 
72 
     | 
    
         
             
            	// The threshold in seconds, which determines when a fiber is considered to have stalled the event loop.
         
     | 
| 
         @@ -74,7 +75,7 @@ struct Fiber_Profiler_Capture { 
     | 
|
| 
       74 
75 
     | 
    
         
             
            	// Whether or not to track calls.
         
     | 
| 
       75 
76 
     | 
    
         
             
            	int track_calls;
         
     | 
| 
       76 
77 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
            	// The sample rate of the  
     | 
| 
      
 78 
     | 
    
         
            +
            	// The sample rate of the capture, as a fraction of 1.0, which controls how often the profiler will sample between fiber context switches.
         
     | 
| 
       78 
79 
     | 
    
         
             
            	double sample_rate;
         
     | 
| 
       79 
80 
     | 
    
         | 
| 
       80 
81 
     | 
    
         
             
            	// Calls that are shorter than this filter threshold will be ignored.
         
     | 
| 
         @@ -89,6 +90,15 @@ struct Fiber_Profiler_Capture { 
     | 
|
| 
       89 
90 
     | 
    
         
             
            	// The stream buffer used for printing.
         
     | 
| 
       90 
91 
     | 
    
         
             
            	struct Fiber_Profiler_Stream stream;
         
     | 
| 
       91 
92 
     | 
    
         | 
| 
      
 93 
     | 
    
         
            +
            	// How many fiber context switches have been encountered. Not all of them will be sampled, based on the sample rate.
         
     | 
| 
      
 94 
     | 
    
         
            +
            	size_t switches;
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            	// How many samples have been taken, not all of them will be stalls, based on the stall threshold.
         
     | 
| 
      
 97 
     | 
    
         
            +
            	size_t samples;
         
     | 
| 
      
 98 
     | 
    
         
            +
            	
         
     | 
| 
      
 99 
     | 
    
         
            +
            	// The number of stalls encountered and printed.
         
     | 
| 
      
 100 
     | 
    
         
            +
            	size_t stalls;
         
     | 
| 
      
 101 
     | 
    
         
            +
            	
         
     | 
| 
       92 
102 
     | 
    
         
             
            	// Whether or not the profiler is currently running.
         
     | 
| 
       93 
103 
     | 
    
         
             
            	int running;
         
     | 
| 
       94 
104 
     | 
    
         | 
| 
         @@ -98,14 +108,11 @@ struct Fiber_Profiler_Capture { 
     | 
|
| 
       98 
108 
     | 
    
         
             
            	// Whether or not to capture call data.
         
     | 
| 
       99 
109 
     | 
    
         
             
            	int capture;
         
     | 
| 
       100 
110 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
            	// The number of stalls encountered.
         
     | 
| 
       102 
     | 
    
         
            -
            	size_t stalls;
         
     | 
| 
       103 
     | 
    
         
            -
            	
         
     | 
| 
       104 
111 
     | 
    
         
             
            	// The start time of the profile.
         
     | 
| 
       105 
112 
     | 
    
         
             
            	struct timespec start_time;
         
     | 
| 
       106 
113 
     | 
    
         | 
| 
       107 
     | 
    
         
            -
            	// The  
     | 
| 
       108 
     | 
    
         
            -
            	struct timespec  
     | 
| 
      
 114 
     | 
    
         
            +
            	// The time of the last fiber switch that was sampled.
         
     | 
| 
      
 115 
     | 
    
         
            +
            	struct timespec switch_time;
         
     | 
| 
       109 
116 
     | 
    
         | 
| 
       110 
117 
     | 
    
         
             
            	// The depth of the call stack (can be negative).
         
     | 
| 
       111 
118 
     | 
    
         
             
            	int nesting;
         
     | 
| 
         @@ -120,13 +127,6 @@ struct Fiber_Profiler_Capture { 
     | 
|
| 
       120 
127 
     | 
    
         
             
            	struct Fiber_Profiler_Deque calls;
         
     | 
| 
       121 
128 
     | 
    
         
             
            };
         
     | 
| 
       122 
129 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
            void Fiber_Profiler_Capture_reset(struct Fiber_Profiler_Capture *profiler) {
         
     | 
| 
       124 
     | 
    
         
            -
            	profiler->nesting = 0;
         
     | 
| 
       125 
     | 
    
         
            -
            	profiler->nesting_minimum = 0;
         
     | 
| 
       126 
     | 
    
         
            -
            	profiler->current = NULL;
         
     | 
| 
       127 
     | 
    
         
            -
            	Fiber_Profiler_Deque_truncate(&profiler->calls);
         
     | 
| 
       128 
     | 
    
         
            -
            }
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
130 
     | 
    
         
             
            void Fiber_Profiler_Capture_Call_initialize(void *element) {
         
     | 
| 
       131 
131 
     | 
    
         
             
            	struct Fiber_Profiler_Capture_Call *call = element;
         
     | 
| 
       132 
132 
     | 
    
         | 
| 
         @@ -155,43 +155,43 @@ void Fiber_Profiler_Capture_Call_free(void *element) { 
     | 
|
| 
       155 
155 
     | 
    
         
             
            }
         
     | 
| 
       156 
156 
     | 
    
         | 
| 
       157 
157 
     | 
    
         
             
            static void Fiber_Profiler_Capture_mark(void *ptr) {
         
     | 
| 
       158 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 158 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = (struct Fiber_Profiler_Capture*)ptr;
         
     | 
| 
       159 
159 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
            	rb_gc_mark_movable( 
     | 
| 
       161 
     | 
    
         
            -
            	rb_gc_mark_movable( 
     | 
| 
      
 160 
     | 
    
         
            +
            	rb_gc_mark_movable(capture->thread);
         
     | 
| 
      
 161 
     | 
    
         
            +
            	rb_gc_mark_movable(capture->output);
         
     | 
| 
       162 
162 
     | 
    
         | 
| 
       163 
163 
     | 
    
         
             
            	// If `klass` is stored as a VALUE in calls, we need to mark them here:
         
     | 
| 
       164 
     | 
    
         
            -
            	Fiber_Profiler_Deque_each(& 
     | 
| 
      
 164 
     | 
    
         
            +
            	Fiber_Profiler_Deque_each(&capture->calls, struct Fiber_Profiler_Capture_Call, call) {
         
     | 
| 
       165 
165 
     | 
    
         
             
            		rb_gc_mark_movable(call->klass);
         
     | 
| 
       166 
166 
     | 
    
         
             
            	}
         
     | 
| 
       167 
167 
     | 
    
         
             
            }
         
     | 
| 
       168 
168 
     | 
    
         | 
| 
       169 
169 
     | 
    
         
             
            static void Fiber_Profiler_Capture_compact(void *ptr) {
         
     | 
| 
       170 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 170 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = (struct Fiber_Profiler_Capture*)ptr;
         
     | 
| 
       171 
171 
     | 
    
         | 
| 
       172 
     | 
    
         
            -
            	 
     | 
| 
       173 
     | 
    
         
            -
            	 
     | 
| 
      
 172 
     | 
    
         
            +
            	capture->thread = rb_gc_location(capture->thread);
         
     | 
| 
      
 173 
     | 
    
         
            +
            	capture->output = rb_gc_location(capture->output);
         
     | 
| 
       174 
174 
     | 
    
         | 
| 
       175 
175 
     | 
    
         
             
            	// If `klass` is stored as a VALUE in calls, we need to update their locations here:
         
     | 
| 
       176 
     | 
    
         
            -
            	Fiber_Profiler_Deque_each(& 
     | 
| 
      
 176 
     | 
    
         
            +
            	Fiber_Profiler_Deque_each(&capture->calls, struct Fiber_Profiler_Capture_Call, call) {
         
     | 
| 
       177 
177 
     | 
    
         
             
            		call->klass = rb_gc_location(call->klass);
         
     | 
| 
       178 
178 
     | 
    
         
             
            	}
         
     | 
| 
       179 
179 
     | 
    
         
             
            }
         
     | 
| 
       180 
180 
     | 
    
         | 
| 
       181 
181 
     | 
    
         
             
            static void Fiber_Profiler_Capture_free(void *ptr) {
         
     | 
| 
       182 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 182 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = (struct Fiber_Profiler_Capture*)ptr;
         
     | 
| 
       183 
183 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
            	RUBY_ASSERT( 
     | 
| 
      
 184 
     | 
    
         
            +
            	RUBY_ASSERT(capture->running == 0);
         
     | 
| 
       185 
185 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
            	Fiber_Profiler_Stream_free(& 
     | 
| 
       187 
     | 
    
         
            -
            	Fiber_Profiler_Deque_free(& 
     | 
| 
      
 186 
     | 
    
         
            +
            	Fiber_Profiler_Stream_free(&capture->stream);
         
     | 
| 
      
 187 
     | 
    
         
            +
            	Fiber_Profiler_Deque_free(&capture->calls);
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
       189 
     | 
    
         
            -
            	free( 
     | 
| 
      
 189 
     | 
    
         
            +
            	free(capture);
         
     | 
| 
       190 
190 
     | 
    
         
             
            }
         
     | 
| 
       191 
191 
     | 
    
         | 
| 
       192 
192 
     | 
    
         
             
            static size_t Fiber_Profiler_Capture_memsize(const void *ptr) {
         
     | 
| 
       193 
     | 
    
         
            -
            	const struct Fiber_Profiler_Capture * 
     | 
| 
       194 
     | 
    
         
            -
            	return sizeof(* 
     | 
| 
      
 193 
     | 
    
         
            +
            	const struct Fiber_Profiler_Capture *capture = (const struct Fiber_Profiler_Capture*)ptr;
         
     | 
| 
      
 194 
     | 
    
         
            +
            	return sizeof(*capture) + Fiber_Profiler_Deque_memory_size(&capture->calls);
         
     | 
| 
       195 
195 
     | 
    
         
             
            }
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
       197 
197 
     | 
    
         
             
            const rb_data_type_t Fiber_Profiler_Capture_Type = {
         
     | 
| 
         @@ -206,9 +206,9 @@ const rb_data_type_t Fiber_Profiler_Capture_Type = { 
     | 
|
| 
       206 
206 
     | 
    
         
             
            };
         
     | 
| 
       207 
207 
     | 
    
         | 
| 
       208 
208 
     | 
    
         
             
            struct Fiber_Profiler_Capture *Fiber_Profiler_Capture_get(VALUE self) {
         
     | 
| 
       209 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
       210 
     | 
    
         
            -
            	TypedData_Get_Struct(self, struct Fiber_Profiler_Capture, &Fiber_Profiler_Capture_Type,  
     | 
| 
       211 
     | 
    
         
            -
            	return  
     | 
| 
      
 209 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture;
         
     | 
| 
      
 210 
     | 
    
         
            +
            	TypedData_Get_Struct(self, struct Fiber_Profiler_Capture, &Fiber_Profiler_Capture_Type, capture);
         
     | 
| 
      
 211 
     | 
    
         
            +
            	return capture;
         
     | 
| 
       212 
212 
     | 
    
         
             
            }
         
     | 
| 
       213 
213 
     | 
    
         | 
| 
       214 
214 
     | 
    
         
             
            int IO_istty(VALUE io) {
         
     | 
| 
         @@ -220,78 +220,83 @@ int IO_istty(VALUE io) { 
     | 
|
| 
       220 
220 
     | 
    
         
             
            	return 0;
         
     | 
| 
       221 
221 
     | 
    
         
             
            }
         
     | 
| 
       222 
222 
     | 
    
         | 
| 
       223 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture * 
     | 
| 
       224 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture * 
     | 
| 
      
 223 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *capture, FILE *restrict stream, double duration);
         
     | 
| 
      
 224 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *capture, FILE *restrict stream, double duration);
         
     | 
| 
       225 
225 
     | 
    
         | 
| 
       226 
     | 
    
         
            -
            static void Fiber_Profiler_Capture_output_set(struct Fiber_Profiler_Capture * 
     | 
| 
       227 
     | 
    
         
            -
            	 
     | 
| 
      
 226 
     | 
    
         
            +
            static void Fiber_Profiler_Capture_output_set(struct Fiber_Profiler_Capture *capture, VALUE output) {
         
     | 
| 
      
 227 
     | 
    
         
            +
            	capture->output = output;
         
     | 
| 
       228 
228 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
            	if (IO_istty( 
     | 
| 
       230 
     | 
    
         
            -
            		 
     | 
| 
      
 229 
     | 
    
         
            +
            	if (IO_istty(capture->output)) {
         
     | 
| 
      
 230 
     | 
    
         
            +
            		capture->print = &Fiber_Profiler_Capture_print_tty;
         
     | 
| 
       231 
231 
     | 
    
         
             
            	} else {
         
     | 
| 
       232 
     | 
    
         
            -
            		 
     | 
| 
      
 232 
     | 
    
         
            +
            		capture->print = &Fiber_Profiler_Capture_print_json;
         
     | 
| 
       233 
233 
     | 
    
         
             
            	}
         
     | 
| 
       234 
234 
     | 
    
         
             
            }
         
     | 
| 
       235 
235 
     | 
    
         | 
| 
       236 
236 
     | 
    
         
             
            VALUE Fiber_Profiler_Capture_allocate(VALUE klass) {
         
     | 
| 
       237 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 237 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = ALLOC(struct Fiber_Profiler_Capture);
         
     | 
| 
       238 
238 
     | 
    
         | 
| 
       239 
239 
     | 
    
         
             
            	// Initialize the profiler state:
         
     | 
| 
       240 
     | 
    
         
            -
            	Fiber_Profiler_Stream_initialize(& 
     | 
| 
       241 
     | 
    
         
            -
            	 
     | 
| 
      
 240 
     | 
    
         
            +
            	Fiber_Profiler_Stream_initialize(&capture->stream);
         
     | 
| 
      
 241 
     | 
    
         
            +
            	capture->output = Qnil;
         
     | 
| 
       242 
242 
     | 
    
         | 
| 
       243 
     | 
    
         
            -
            	 
     | 
| 
       244 
     | 
    
         
            -
            	 
     | 
| 
      
 243 
     | 
    
         
            +
            	capture->switches = 0;
         
     | 
| 
      
 244 
     | 
    
         
            +
            	capture->samples = 0;
         
     | 
| 
      
 245 
     | 
    
         
            +
            	capture->stalls = 0;
         
     | 
| 
       245 
246 
     | 
    
         | 
| 
       246 
     | 
    
         
            -
            	 
     | 
| 
       247 
     | 
    
         
            -
            	 
     | 
| 
       248 
     | 
    
         
            -
            	profiler->nesting = 0;
         
     | 
| 
       249 
     | 
    
         
            -
            	profiler->nesting_minimum = 0;
         
     | 
| 
       250 
     | 
    
         
            -
            	profiler->current = NULL;
         
     | 
| 
      
 247 
     | 
    
         
            +
            	capture->running = 0;
         
     | 
| 
      
 248 
     | 
    
         
            +
            	capture->thread = Qnil;
         
     | 
| 
       251 
249 
     | 
    
         | 
| 
       252 
     | 
    
         
            -
            	 
     | 
| 
       253 
     | 
    
         
            -
            	 
     | 
| 
       254 
     | 
    
         
            -
            	 
     | 
| 
      
 250 
     | 
    
         
            +
            	capture->capture = 0;
         
     | 
| 
      
 251 
     | 
    
         
            +
            	capture->nesting = 0;
         
     | 
| 
      
 252 
     | 
    
         
            +
            	capture->nesting_minimum = 0;
         
     | 
| 
      
 253 
     | 
    
         
            +
            	capture->current = NULL;
         
     | 
| 
       255 
254 
     | 
    
         | 
| 
       256 
     | 
    
         
            -
            	 
     | 
| 
       257 
     | 
    
         
            -
            	 
     | 
| 
      
 255 
     | 
    
         
            +
            	capture->stall_threshold = Fiber_Profiler_Capture_stall_threshold;
         
     | 
| 
      
 256 
     | 
    
         
            +
            	capture->filter_threshold = Fiber_Profiler_Capture_filter_threshold;
         
     | 
| 
      
 257 
     | 
    
         
            +
            	capture->track_calls = Fiber_Profiler_Capture_track_calls;
         
     | 
| 
      
 258 
     | 
    
         
            +
            	capture->sample_rate = Fiber_Profiler_Capture_sample_rate;
         
     | 
| 
       258 
259 
     | 
    
         | 
| 
       259 
     | 
    
         
            -
            	 
     | 
| 
       260 
     | 
    
         
            -
            	 
     | 
| 
      
 260 
     | 
    
         
            +
            	capture->calls.element_initialize = (void (*)(void*))Fiber_Profiler_Capture_Call_initialize;
         
     | 
| 
      
 261 
     | 
    
         
            +
            	capture->calls.element_free = (void (*)(void*))Fiber_Profiler_Capture_Call_free;
         
     | 
| 
       261 
262 
     | 
    
         | 
| 
       262 
     | 
    
         
            -
            	Fiber_Profiler_Deque_initialize(& 
     | 
| 
       263 
     | 
    
         
            -
            	Fiber_Profiler_Deque_reserve_default(& 
     | 
| 
      
 263 
     | 
    
         
            +
            	Fiber_Profiler_Deque_initialize(&capture->calls, sizeof(struct Fiber_Profiler_Capture_Call));
         
     | 
| 
      
 264 
     | 
    
         
            +
            	Fiber_Profiler_Deque_reserve_default(&capture->calls);
         
     | 
| 
       264 
265 
     | 
    
         | 
| 
       265 
     | 
    
         
            -
            	return TypedData_Wrap_Struct(klass, &Fiber_Profiler_Capture_Type,  
     | 
| 
      
 266 
     | 
    
         
            +
            	return TypedData_Wrap_Struct(klass, &Fiber_Profiler_Capture_Type, capture);
         
     | 
| 
       266 
267 
     | 
    
         
             
            }
         
     | 
| 
       267 
268 
     | 
    
         | 
| 
       268 
     | 
    
         
            -
            ID Fiber_Profiler_Capture_initialize_options[ 
     | 
| 
      
 269 
     | 
    
         
            +
            ID Fiber_Profiler_Capture_initialize_options[5];
         
     | 
| 
       269 
270 
     | 
    
         | 
| 
       270 
271 
     | 
    
         
             
            VALUE Fiber_Profiler_Capture_initialize(int argc, VALUE *argv, VALUE self) {
         
     | 
| 
       271 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 272 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       272 
273 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
            	VALUE arguments[ 
     | 
| 
      
 274 
     | 
    
         
            +
            	VALUE arguments[5] = {0};
         
     | 
| 
       274 
275 
     | 
    
         
             
            	VALUE options = Qnil;
         
     | 
| 
       275 
276 
     | 
    
         
             
            	rb_scan_args(argc, argv, ":", &options);
         
     | 
| 
       276 
     | 
    
         
            -
            	rb_get_kwargs(options, Fiber_Profiler_Capture_initialize_options, 0,  
     | 
| 
      
 277 
     | 
    
         
            +
            	rb_get_kwargs(options, Fiber_Profiler_Capture_initialize_options, 0, 5, arguments);
         
     | 
| 
       277 
278 
     | 
    
         | 
| 
       278 
279 
     | 
    
         
             
            	if (arguments[0] != Qundef) {
         
     | 
| 
       279 
     | 
    
         
            -
            		 
     | 
| 
      
 280 
     | 
    
         
            +
            		capture->stall_threshold = NUM2DBL(arguments[0]);
         
     | 
| 
       280 
281 
     | 
    
         
             
            	}
         
     | 
| 
       281 
282 
     | 
    
         | 
| 
       282 
283 
     | 
    
         
             
            	if (arguments[1] != Qundef) {
         
     | 
| 
       283 
     | 
    
         
            -
            		 
     | 
| 
      
 284 
     | 
    
         
            +
            		capture->filter_threshold = NUM2DBL(arguments[1]);
         
     | 
| 
       284 
285 
     | 
    
         
             
            	}
         
     | 
| 
       285 
286 
     | 
    
         | 
| 
       286 
287 
     | 
    
         
             
            	if (arguments[2] != Qundef) {
         
     | 
| 
       287 
     | 
    
         
            -
            		 
     | 
| 
      
 288 
     | 
    
         
            +
            		capture->track_calls = RB_TEST(arguments[2]);
         
     | 
| 
       288 
289 
     | 
    
         
             
            	}
         
     | 
| 
       289 
290 
     | 
    
         | 
| 
       290 
291 
     | 
    
         
             
            	if (arguments[3] != Qundef) {
         
     | 
| 
       291 
     | 
    
         
            -
            		 
     | 
| 
      
 292 
     | 
    
         
            +
            		capture->sample_rate = NUM2DBL(arguments[3]);
         
     | 
| 
      
 293 
     | 
    
         
            +
            	}
         
     | 
| 
      
 294 
     | 
    
         
            +
            	
         
     | 
| 
      
 295 
     | 
    
         
            +
            	if (arguments[4] != Qundef) {
         
     | 
| 
      
 296 
     | 
    
         
            +
            		Fiber_Profiler_Capture_output_set(capture, arguments[4]);
         
     | 
| 
       292 
297 
     | 
    
         
             
            	} else {
         
     | 
| 
       293 
298 
     | 
    
         
             
            		// Initialize the profiler output - we dup `rb_stderr` because the profiler may otherwise run into synchronization issues with other uses of `rb_stderr`:
         
     | 
| 
       294 
     | 
    
         
            -
            		Fiber_Profiler_Capture_output_set( 
     | 
| 
      
 299 
     | 
    
         
            +
            		Fiber_Profiler_Capture_output_set(capture, rb_obj_dup(rb_stderr));
         
     | 
| 
       295 
300 
     | 
    
         
             
            	}
         
     | 
| 
       296 
301 
     | 
    
         | 
| 
       297 
302 
     | 
    
         
             
            	return self;
         
     | 
| 
         @@ -302,10 +307,10 @@ VALUE Fiber_Profiler_Capture_default(VALUE klass) { 
     | 
|
| 
       302 
307 
     | 
    
         
             
            		return Qnil;
         
     | 
| 
       303 
308 
     | 
    
         
             
            	}
         
     | 
| 
       304 
309 
     | 
    
         | 
| 
       305 
     | 
    
         
            -
            	VALUE  
     | 
| 
       306 
     | 
    
         
            -
            	Fiber_Profiler_Capture_initialize(0, NULL,  
     | 
| 
      
 310 
     | 
    
         
            +
            	VALUE capture = Fiber_Profiler_Capture_allocate(klass);
         
     | 
| 
      
 311 
     | 
    
         
            +
            	Fiber_Profiler_Capture_initialize(0, NULL, capture);
         
     | 
| 
       307 
312 
     | 
    
         | 
| 
       308 
     | 
    
         
            -
            	return  
     | 
| 
      
 313 
     | 
    
         
            +
            	return capture;
         
     | 
| 
       309 
314 
     | 
    
         
             
            }
         
     | 
| 
       310 
315 
     | 
    
         | 
| 
       311 
316 
     | 
    
         
             
            int event_flag_call_p(rb_event_flag_t event_flags) {
         
     | 
| 
         @@ -332,19 +337,19 @@ const char *event_flag_name(rb_event_flag_t event_flag) { 
     | 
|
| 
       332 
337 
     | 
    
         
             
            	}
         
     | 
| 
       333 
338 
     | 
    
         
             
            }
         
     | 
| 
       334 
339 
     | 
    
         | 
| 
       335 
     | 
    
         
            -
            static struct Fiber_Profiler_Capture_Call* Fiber_Profiler_Capture_Call_new(struct Fiber_Profiler_Capture * 
     | 
| 
       336 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Deque_push(& 
     | 
| 
      
 340 
     | 
    
         
            +
            static struct Fiber_Profiler_Capture_Call* Fiber_Profiler_Capture_Call_new(struct Fiber_Profiler_Capture *capture, rb_event_flag_t event_flag, ID id, VALUE klass) {
         
     | 
| 
      
 341 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Deque_push(&capture->calls);
         
     | 
| 
       337 
342 
     | 
    
         | 
| 
       338 
343 
     | 
    
         
             
            	call->event_flag = event_flag;
         
     | 
| 
       339 
344 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
            	call->parent =  
     | 
| 
      
 345 
     | 
    
         
            +
            	call->parent = capture->current;
         
     | 
| 
       341 
346 
     | 
    
         
             
            	if (call->parent) {
         
     | 
| 
       342 
347 
     | 
    
         
             
            		call->parent->children += 1;
         
     | 
| 
       343 
348 
     | 
    
         
             
            	}
         
     | 
| 
       344 
349 
     | 
    
         | 
| 
       345 
     | 
    
         
            -
            	 
     | 
| 
      
 350 
     | 
    
         
            +
            	capture->current = call;
         
     | 
| 
       346 
351 
     | 
    
         | 
| 
       347 
     | 
    
         
            -
            	call->nesting =  
     | 
| 
      
 352 
     | 
    
         
            +
            	call->nesting = capture->nesting;
         
     | 
| 
       348 
353 
     | 
    
         | 
| 
       349 
354 
     | 
    
         
             
            	if (id) {
         
     | 
| 
       350 
355 
     | 
    
         
             
            		call->id = id;
         
     | 
| 
         @@ -363,15 +368,20 @@ static struct Fiber_Profiler_Capture_Call* Fiber_Profiler_Capture_Call_new(struc 
     | 
|
| 
       363 
368 
     | 
    
         
             
            }
         
     | 
| 
       364 
369 
     | 
    
         | 
| 
       365 
370 
     | 
    
         
             
            // Finish the call by calculating the duration and filtering it if necessary.
         
     | 
| 
       366 
     | 
    
         
            -
            int Fiber_Profiler_Capture_Call_finish(struct Fiber_Profiler_Capture * 
     | 
| 
      
 371 
     | 
    
         
            +
            int Fiber_Profiler_Capture_Call_finish(struct Fiber_Profiler_Capture *capture, struct Fiber_Profiler_Capture_Call *call) {
         
     | 
| 
       367 
372 
     | 
    
         
             
            	// Don't filter calls if we're not running:
         
     | 
| 
       368 
373 
     | 
    
         
             
            	if (DEBUG_FILTERED) return 0;
         
     | 
| 
       369 
374 
     | 
    
         | 
| 
       370 
     | 
    
         
            -
            	if (call-> 
     | 
| 
      
 375 
     | 
    
         
            +
            	if (event_flag_return_p(call->event_flag)) {
         
     | 
| 
      
 376 
     | 
    
         
            +
            		// We don't filter return statements, as they are always part of the call stack:
         
     | 
| 
      
 377 
     | 
    
         
            +
            		return 0;
         
     | 
| 
      
 378 
     | 
    
         
            +
            	}
         
     | 
| 
      
 379 
     | 
    
         
            +
            	
         
     | 
| 
      
 380 
     | 
    
         
            +
            	if (call->duration < capture->filter_threshold) {
         
     | 
| 
       371 
381 
     | 
    
         
             
            		// We can only remove calls from the end of the deque, otherwise they might be referenced by other calls:
         
     | 
| 
       372 
     | 
    
         
            -
            		if (call == Fiber_Profiler_Deque_last(& 
     | 
| 
       373 
     | 
    
         
            -
            			if ( 
     | 
| 
       374 
     | 
    
         
            -
            				 
     | 
| 
      
 382 
     | 
    
         
            +
            		if (call == Fiber_Profiler_Deque_last(&capture->calls)) {
         
     | 
| 
      
 383 
     | 
    
         
            +
            			if (capture->current == call) {
         
     | 
| 
      
 384 
     | 
    
         
            +
            				capture->current = call->parent;
         
     | 
| 
       375 
385 
     | 
    
         
             
            			}
         
     | 
| 
       376 
386 
     | 
    
         | 
| 
       377 
387 
     | 
    
         
             
            			if (call->parent) {
         
     | 
| 
         @@ -380,7 +390,7 @@ int Fiber_Profiler_Capture_Call_finish(struct Fiber_Profiler_Capture *profiler, 
     | 
|
| 
       380 
390 
     | 
    
         
             
            				call->parent = NULL;
         
     | 
| 
       381 
391 
     | 
    
         
             
            			}
         
     | 
| 
       382 
392 
     | 
    
         | 
| 
       383 
     | 
    
         
            -
            			Fiber_Profiler_Deque_pop(& 
     | 
| 
      
 393 
     | 
    
         
            +
            			Fiber_Profiler_Deque_pop(&capture->calls);
         
     | 
| 
       384 
394 
     | 
    
         | 
| 
       385 
395 
     | 
    
         
             
            			return 1;
         
     | 
| 
       386 
396 
     | 
    
         
             
            		}
         
     | 
| 
         @@ -389,10 +399,11 @@ int Fiber_Profiler_Capture_Call_finish(struct Fiber_Profiler_Capture *profiler, 
     | 
|
| 
       389 
399 
     | 
    
         
             
            	return 0;
         
     | 
| 
       390 
400 
     | 
    
         
             
            }
         
     | 
| 
       391 
401 
     | 
    
         | 
| 
      
 402 
     | 
    
         
            +
            // Whether to highlight a call as expensive. This is purely cosmetic.
         
     | 
| 
       392 
403 
     | 
    
         
             
            static const double Fiber_Profiler_Capture_Call_EXPENSIVE_THRESHOLD = 0.2;
         
     | 
| 
       393 
404 
     | 
    
         | 
| 
       394 
     | 
    
         
            -
            int Fiber_Profiler_Capture_Call_expensive_p(struct Fiber_Profiler_Capture_Call *call, double  
     | 
| 
       395 
     | 
    
         
            -
            	if (call->duration >  
     | 
| 
      
 405 
     | 
    
         
            +
            int Fiber_Profiler_Capture_Call_expensive_p(struct Fiber_Profiler_Capture *capture, struct Fiber_Profiler_Capture_Call *call, double duration) {
         
     | 
| 
      
 406 
     | 
    
         
            +
            	if (call->duration > duration * Fiber_Profiler_Capture_Call_EXPENSIVE_THRESHOLD) {
         
     | 
| 
       396 
407 
     | 
    
         
             
            		return 1;
         
     | 
| 
       397 
408 
     | 
    
         
             
            	}
         
     | 
| 
       398 
409 
     | 
    
         | 
| 
         @@ -400,57 +411,63 @@ int Fiber_Profiler_Capture_Call_expensive_p(struct Fiber_Profiler_Capture_Call * 
     | 
|
| 
       400 
411 
     | 
    
         
             
            }
         
     | 
| 
       401 
412 
     | 
    
         | 
| 
       402 
413 
     | 
    
         
             
            static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
         
     | 
| 
       403 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 414 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(data);
         
     | 
| 
       404 
415 
     | 
    
         | 
| 
       405 
416 
     | 
    
         
             
            	// We don't want to capture data if we're not running:
         
     | 
| 
       406 
     | 
    
         
            -
            	if (! 
     | 
| 
      
 417 
     | 
    
         
            +
            	if (!capture->capture) return;
         
     | 
| 
       407 
418 
     | 
    
         | 
| 
       408 
419 
     | 
    
         
             
            	if (event_flag_call_p(event_flag)) {
         
     | 
| 
       409 
     | 
    
         
            -
            		struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new( 
     | 
| 
      
 420 
     | 
    
         
            +
            		struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new(capture, event_flag, id, klass);
         
     | 
| 
       410 
421 
     | 
    
         | 
| 
       411 
     | 
    
         
            -
            		 
     | 
| 
      
 422 
     | 
    
         
            +
            		capture->nesting += 1;
         
     | 
| 
       412 
423 
     | 
    
         | 
| 
       413 
424 
     | 
    
         
             
            		Fiber_Profiler_Time_current(&call->enter_time);
         
     | 
| 
       414 
425 
     | 
    
         
             
            	}
         
     | 
| 
       415 
426 
     | 
    
         | 
| 
       416 
427 
     | 
    
         
             
            	else if (event_flag_return_p(event_flag)) {
         
     | 
| 
       417 
     | 
    
         
            -
            		struct Fiber_Profiler_Capture_Call *call =  
     | 
| 
      
 428 
     | 
    
         
            +
            		struct Fiber_Profiler_Capture_Call *call = capture->current;
         
     | 
| 
       418 
429 
     | 
    
         | 
| 
       419 
430 
     | 
    
         
             
            		// We may encounter returns without a preceeding call. This isn't an error, but we should pretend like the call started at the beginning of the profiling session:
         
     | 
| 
       420 
431 
     | 
    
         
             
            		if (call == NULL) {
         
     | 
| 
       421 
     | 
    
         
            -
            			struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(& 
     | 
| 
       422 
     | 
    
         
            -
            			call = Fiber_Profiler_Capture_Call_new( 
     | 
| 
      
 432 
     | 
    
         
            +
            			struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(&capture->calls);
         
     | 
| 
      
 433 
     | 
    
         
            +
            			call = Fiber_Profiler_Capture_Call_new(capture, event_flag, id, klass);
         
     | 
| 
      
 434 
     | 
    
         
            +
            			
         
     | 
| 
      
 435 
     | 
    
         
            +
            			struct timespec call_time;
         
     | 
| 
       423 
436 
     | 
    
         | 
| 
       424 
437 
     | 
    
         
             
            			if (last_call) {
         
     | 
| 
       425 
     | 
    
         
            -
            				 
     | 
| 
      
 438 
     | 
    
         
            +
            				call_time = last_call->enter_time;
         
     | 
| 
       426 
439 
     | 
    
         
             
            			} else {
         
     | 
| 
       427 
     | 
    
         
            -
            				 
     | 
| 
      
 440 
     | 
    
         
            +
            				call_time = capture->switch_time;
         
     | 
| 
       428 
441 
     | 
    
         
             
            			}
         
     | 
| 
      
 442 
     | 
    
         
            +
            			
         
     | 
| 
      
 443 
     | 
    
         
            +
            			// For return statements, we record the current time as the enter time:
         
     | 
| 
      
 444 
     | 
    
         
            +
            			Fiber_Profiler_Time_current(&call->enter_time);
         
     | 
| 
      
 445 
     | 
    
         
            +
            			call->duration = Fiber_Profiler_Time_delta(&call_time, &call->enter_time);
         
     | 
| 
      
 446 
     | 
    
         
            +
            		} else {
         
     | 
| 
      
 447 
     | 
    
         
            +
            			call->duration = Fiber_Profiler_Time_delta_current(&call->enter_time);
         
     | 
| 
       429 
448 
     | 
    
         
             
            		}
         
     | 
| 
       430 
449 
     | 
    
         | 
| 
       431 
     | 
    
         
            -
            		 
     | 
| 
       432 
     | 
    
         
            -
            		
         
     | 
| 
       433 
     | 
    
         
            -
            		profiler->current = call->parent;
         
     | 
| 
      
 450 
     | 
    
         
            +
            		capture->current = call->parent;
         
     | 
| 
       434 
451 
     | 
    
         | 
| 
       435 
452 
     | 
    
         
             
            		// We may encounter returns without a preceeding call.
         
     | 
| 
       436 
     | 
    
         
            -
            		 
     | 
| 
      
 453 
     | 
    
         
            +
            		capture->nesting -= 1;
         
     | 
| 
       437 
454 
     | 
    
         | 
| 
       438 
455 
     | 
    
         
             
            		// We need to keep track of how deep the call stack goes:
         
     | 
| 
       439 
     | 
    
         
            -
            		if ( 
     | 
| 
       440 
     | 
    
         
            -
            			 
     | 
| 
      
 456 
     | 
    
         
            +
            		if (capture->nesting < capture->nesting_minimum) {
         
     | 
| 
      
 457 
     | 
    
         
            +
            			capture->nesting_minimum = capture->nesting;
         
     | 
| 
       441 
458 
     | 
    
         
             
            		}
         
     | 
| 
       442 
459 
     | 
    
         | 
| 
       443 
     | 
    
         
            -
            		Fiber_Profiler_Capture_Call_finish( 
     | 
| 
      
 460 
     | 
    
         
            +
            		Fiber_Profiler_Capture_Call_finish(capture, call);
         
     | 
| 
       444 
461 
     | 
    
         
             
            	}
         
     | 
| 
       445 
462 
     | 
    
         | 
| 
       446 
463 
     | 
    
         
             
            	else {
         
     | 
| 
       447 
     | 
    
         
            -
            		struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(& 
     | 
| 
       448 
     | 
    
         
            -
            		struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new( 
     | 
| 
      
 464 
     | 
    
         
            +
            		struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(&capture->calls);
         
     | 
| 
      
 465 
     | 
    
         
            +
            		struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new(capture, event_flag, id, klass);
         
     | 
| 
       449 
466 
     | 
    
         | 
| 
       450 
467 
     | 
    
         
             
            		if (last_call) {
         
     | 
| 
       451 
468 
     | 
    
         
             
            			call->enter_time = last_call->enter_time;
         
     | 
| 
       452 
469 
     | 
    
         
             
            		} else {
         
     | 
| 
       453 
     | 
    
         
            -
            			call->enter_time =  
     | 
| 
      
 470 
     | 
    
         
            +
            			call->enter_time = capture->switch_time;
         
     | 
| 
       454 
471 
     | 
    
         
             
            		}
         
     | 
| 
       455 
472 
     | 
    
         | 
| 
       456 
473 
     | 
    
         
             
            		call->duration = Fiber_Profiler_Time_delta_current(&call->enter_time);
         
     | 
| 
         @@ -458,35 +475,35 @@ static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE da 
     | 
|
| 
       458 
475 
     | 
    
         
             
            }
         
     | 
| 
       459 
476 
     | 
    
         | 
| 
       460 
477 
     | 
    
         
             
            void Fiber_Profiler_Capture_pause(VALUE self) {
         
     | 
| 
       461 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 478 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       462 
479 
     | 
    
         | 
| 
       463 
     | 
    
         
            -
            	if (! 
     | 
| 
      
 480 
     | 
    
         
            +
            	if (!capture->capture) return;
         
     | 
| 
      
 481 
     | 
    
         
            +
            	capture->capture = 0;
         
     | 
| 
       464 
482 
     | 
    
         | 
| 
       465 
     | 
    
         
            -
            	 
     | 
| 
       466 
     | 
    
         
            -
             
     | 
| 
       467 
     | 
    
         
            -
            	 
     | 
| 
      
 483 
     | 
    
         
            +
            	if (capture->track_calls) {
         
     | 
| 
      
 484 
     | 
    
         
            +
            		rb_thread_remove_event_hook_with_data(capture->thread, Fiber_Profiler_Capture_callback, self);
         
     | 
| 
      
 485 
     | 
    
         
            +
            	}
         
     | 
| 
       468 
486 
     | 
    
         
             
            }
         
     | 
| 
       469 
487 
     | 
    
         | 
| 
       470 
488 
     | 
    
         
             
            void Fiber_Profiler_Capture_resume(VALUE self) {
         
     | 
| 
       471 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
       472 
     | 
    
         
            -
            	
         
     | 
| 
       473 
     | 
    
         
            -
            	if (profiler->capture) return;
         
     | 
| 
      
 489 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       474 
490 
     | 
    
         | 
| 
       475 
     | 
    
         
            -
            	 
     | 
| 
      
 491 
     | 
    
         
            +
            	if (capture->capture) return;
         
     | 
| 
      
 492 
     | 
    
         
            +
            	capture->capture = 1;
         
     | 
| 
      
 493 
     | 
    
         
            +
            	capture->samples += 1;
         
     | 
| 
       476 
494 
     | 
    
         | 
| 
       477 
     | 
    
         
            -
            	 
     | 
| 
       478 
     | 
    
         
            -
             
     | 
| 
       479 
     | 
    
         
            -
            	if (profiler->track_calls) {
         
     | 
| 
       480 
     | 
    
         
            -
            		// event_flags |= RUBY_EVENT_LINE;
         
     | 
| 
      
 495 
     | 
    
         
            +
            	if (capture->track_calls) {
         
     | 
| 
      
 496 
     | 
    
         
            +
            		rb_event_flag_t event_flags = 0;
         
     | 
| 
       481 
497 
     | 
    
         | 
| 
      
 498 
     | 
    
         
            +
            		// event_flags |= RUBY_EVENT_LINE;
         
     | 
| 
       482 
499 
     | 
    
         
             
            		event_flags |= RUBY_EVENT_CALL | RUBY_EVENT_RETURN;
         
     | 
| 
       483 
500 
     | 
    
         
             
            		event_flags |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
         
     | 
| 
       484 
501 
     | 
    
         
             
            		event_flags |= RUBY_EVENT_B_CALL | RUBY_EVENT_B_RETURN;
         
     | 
| 
      
 502 
     | 
    
         
            +
            		
         
     | 
| 
      
 503 
     | 
    
         
            +
            		// CRuby will raise an exception if you try to add "INTERNAL_EVENT" hooks at the same time as other hooks, so we do it in two calls:
         
     | 
| 
      
 504 
     | 
    
         
            +
            		rb_thread_add_event_hook(capture->thread, Fiber_Profiler_Capture_callback, event_flags, self);
         
     | 
| 
      
 505 
     | 
    
         
            +
            		rb_thread_add_event_hook(capture->thread, Fiber_Profiler_Capture_callback, RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_SWEEP, self);
         
     | 
| 
       485 
506 
     | 
    
         
             
            	}
         
     | 
| 
       486 
     | 
    
         
            -
            	
         
     | 
| 
       487 
     | 
    
         
            -
            	// CRuby will raise an exception if you try to add "INTERNAL_EVENT" hooks at the same time as other hooks, so we do it in two calls:
         
     | 
| 
       488 
     | 
    
         
            -
            	rb_thread_add_event_hook(profiler->thread, Fiber_Profiler_Capture_callback, event_flags, self);
         
     | 
| 
       489 
     | 
    
         
            -
            	rb_thread_add_event_hook(profiler->thread, Fiber_Profiler_Capture_callback, RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_SWEEP, self);
         
     | 
| 
       490 
507 
     | 
    
         
             
            }
         
     | 
| 
       491 
508 
     | 
    
         | 
| 
       492 
509 
     | 
    
         
             
            void Fiber_Profiler_Capture_fiber_switch(VALUE self);
         
     | 
| 
         @@ -495,68 +512,70 @@ void Fiber_Profiler_Capture_fiber_switch_callback(rb_event_flag_t event_flag, VA 
     | 
|
| 
       495 
512 
     | 
    
         
             
            	Fiber_Profiler_Capture_fiber_switch(data);
         
     | 
| 
       496 
513 
     | 
    
         
             
            }
         
     | 
| 
       497 
514 
     | 
    
         | 
| 
      
 515 
     | 
    
         
            +
            // Reset the sample state, and truncate the call log.
         
     | 
| 
      
 516 
     | 
    
         
            +
            void Fiber_Profiler_Capture_reset(struct Fiber_Profiler_Capture *capture) {
         
     | 
| 
      
 517 
     | 
    
         
            +
            	capture->nesting = 0;
         
     | 
| 
      
 518 
     | 
    
         
            +
            	capture->nesting_minimum = 0;
         
     | 
| 
      
 519 
     | 
    
         
            +
            	capture->current = NULL;
         
     | 
| 
      
 520 
     | 
    
         
            +
            	Fiber_Profiler_Deque_truncate(&capture->calls);
         
     | 
| 
      
 521 
     | 
    
         
            +
            }
         
     | 
| 
      
 522 
     | 
    
         
            +
             
     | 
| 
       498 
523 
     | 
    
         
             
            VALUE Fiber_Profiler_Capture_start(VALUE self) {
         
     | 
| 
       499 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 524 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       500 
525 
     | 
    
         | 
| 
       501 
     | 
    
         
            -
            	if ( 
     | 
| 
      
 526 
     | 
    
         
            +
            	if (capture->running) return Qfalse;
         
     | 
| 
       502 
527 
     | 
    
         | 
| 
       503 
     | 
    
         
            -
            	 
     | 
| 
       504 
     | 
    
         
            -
            	 
     | 
| 
      
 528 
     | 
    
         
            +
            	capture->running = 1;
         
     | 
| 
      
 529 
     | 
    
         
            +
            	capture->thread = rb_thread_current();
         
     | 
| 
       505 
530 
     | 
    
         | 
| 
       506 
     | 
    
         
            -
            	Fiber_Profiler_Capture_reset( 
     | 
| 
       507 
     | 
    
         
            -
            	Fiber_Profiler_Time_current(& 
     | 
| 
      
 531 
     | 
    
         
            +
            	Fiber_Profiler_Capture_reset(capture);
         
     | 
| 
      
 532 
     | 
    
         
            +
            	Fiber_Profiler_Time_current(&capture->start_time);
         
     | 
| 
       508 
533 
     | 
    
         | 
| 
       509 
     | 
    
         
            -
            	rb_thread_add_event_hook( 
     | 
| 
      
 534 
     | 
    
         
            +
            	rb_thread_add_event_hook(capture->thread, Fiber_Profiler_Capture_fiber_switch_callback, RUBY_EVENT_FIBER_SWITCH, self);
         
     | 
| 
       510 
535 
     | 
    
         | 
| 
       511 
536 
     | 
    
         
             
            	return self;
         
     | 
| 
       512 
537 
     | 
    
         
             
            }
         
     | 
| 
       513 
538 
     | 
    
         | 
| 
       514 
539 
     | 
    
         
             
            VALUE Fiber_Profiler_Capture_stop(VALUE self) {
         
     | 
| 
       515 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 540 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       516 
541 
     | 
    
         | 
| 
       517 
     | 
    
         
            -
            	if (! 
     | 
| 
      
 542 
     | 
    
         
            +
            	if (!capture->running) return Qfalse;
         
     | 
| 
       518 
543 
     | 
    
         | 
| 
       519 
544 
     | 
    
         
             
            	Fiber_Profiler_Capture_pause(self);
         
     | 
| 
       520 
545 
     | 
    
         | 
| 
       521 
     | 
    
         
            -
            	rb_thread_remove_event_hook_with_data( 
     | 
| 
      
 546 
     | 
    
         
            +
            	rb_thread_remove_event_hook_with_data(capture->thread, Fiber_Profiler_Capture_fiber_switch_callback, self);
         
     | 
| 
       522 
547 
     | 
    
         | 
| 
       523 
     | 
    
         
            -
            	 
     | 
| 
       524 
     | 
    
         
            -
            	 
     | 
| 
       525 
     | 
    
         
            -
             
     | 
| 
       526 
     | 
    
         
            -
            	 
     | 
| 
       527 
     | 
    
         
            -
            	Fiber_Profiler_Capture_reset(profiler);
         
     | 
| 
      
 548 
     | 
    
         
            +
            	capture->running = 0;
         
     | 
| 
      
 549 
     | 
    
         
            +
            	capture->thread = Qnil;
         
     | 
| 
      
 550 
     | 
    
         
            +
            	
         
     | 
| 
      
 551 
     | 
    
         
            +
            	Fiber_Profiler_Capture_reset(capture);
         
     | 
| 
       528 
552 
     | 
    
         | 
| 
       529 
553 
     | 
    
         
             
            	return self;
         
     | 
| 
       530 
554 
     | 
    
         
             
            }
         
     | 
| 
       531 
555 
     | 
    
         | 
| 
       532 
     | 
    
         
            -
            void Fiber_Profiler_Capture_finish(struct Fiber_Profiler_Capture * 
     | 
| 
       533 
     | 
    
         
            -
            	 
     | 
| 
       534 
     | 
    
         
            -
            	
         
     | 
| 
       535 
     | 
    
         
            -
            	struct timespec stop_time;
         
     | 
| 
       536 
     | 
    
         
            -
            	Fiber_Profiler_Time_current(&stop_time);
         
     | 
| 
       537 
     | 
    
         
            -
            	
         
     | 
| 
       538 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture_Call *current = profiler->current;
         
     | 
| 
      
 556 
     | 
    
         
            +
            void Fiber_Profiler_Capture_finish(struct Fiber_Profiler_Capture *capture, struct timespec switch_time) {
         
     | 
| 
      
 557 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture_Call *current = capture->current;
         
     | 
| 
       539 
558 
     | 
    
         
             
            	while (current) {
         
     | 
| 
       540 
559 
     | 
    
         
             
            		struct Fiber_Profiler_Capture_Call *parent = current->parent;
         
     | 
| 
       541 
560 
     | 
    
         | 
| 
       542 
     | 
    
         
            -
            		current->duration = Fiber_Profiler_Time_delta(¤t->enter_time, & 
     | 
| 
      
 561 
     | 
    
         
            +
            		current->duration = Fiber_Profiler_Time_delta(¤t->enter_time, &switch_time);
         
     | 
| 
       543 
562 
     | 
    
         | 
| 
       544 
     | 
    
         
            -
            		Fiber_Profiler_Capture_Call_finish( 
     | 
| 
      
 563 
     | 
    
         
            +
            		Fiber_Profiler_Capture_Call_finish(capture, current);
         
     | 
| 
       545 
564 
     | 
    
         | 
| 
       546 
565 
     | 
    
         
             
            		current = parent;
         
     | 
| 
       547 
566 
     | 
    
         
             
            	}
         
     | 
| 
       548 
567 
     | 
    
         
             
            }
         
     | 
| 
       549 
568 
     | 
    
         | 
| 
       550 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print(struct Fiber_Profiler_Capture * 
     | 
| 
      
 569 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print(struct Fiber_Profiler_Capture *capture, double duration);
         
     | 
| 
       551 
570 
     | 
    
         | 
| 
       552 
     | 
    
         
            -
            int Fiber_Profiler_Capture_sample(struct Fiber_Profiler_Capture * 
     | 
| 
      
 571 
     | 
    
         
            +
            int Fiber_Profiler_Capture_sample(struct Fiber_Profiler_Capture *capture) {
         
     | 
| 
       553 
572 
     | 
    
         
             
            	VALUE fiber = Fiber_Profiler_Fiber_current();
         
     | 
| 
       554 
573 
     | 
    
         | 
| 
       555 
574 
     | 
    
         
             
            	// We don't want to capture data from blocking fibers:
         
     | 
| 
       556 
575 
     | 
    
         
             
            	if (Fiber_Profiler_Fiber_blocking(fiber)) return 0;
         
     | 
| 
       557 
576 
     | 
    
         | 
| 
       558 
     | 
    
         
            -
            	if ( 
     | 
| 
       559 
     | 
    
         
            -
            		return rand() < (RAND_MAX *  
     | 
| 
      
 577 
     | 
    
         
            +
            	if (capture->sample_rate < 1) {
         
     | 
| 
      
 578 
     | 
    
         
            +
            		return rand() < (RAND_MAX * capture->sample_rate);
         
     | 
| 
       560 
579 
     | 
    
         
             
            	} else {
         
     | 
| 
       561 
580 
     | 
    
         
             
            		return 1;
         
     | 
| 
       562 
581 
     | 
    
         
             
            	}
         
     | 
| 
         @@ -564,47 +583,58 @@ int Fiber_Profiler_Capture_sample(struct Fiber_Profiler_Capture *profiler) { 
     | 
|
| 
       564 
583 
     | 
    
         | 
| 
       565 
584 
     | 
    
         
             
            void Fiber_Profiler_Capture_fiber_switch(VALUE self)
         
     | 
| 
       566 
585 
     | 
    
         
             
            {
         
     | 
| 
       567 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
       568 
     | 
    
         
            -
            	 
     | 
| 
       569 
     | 
    
         
            -
            	double duration = Fiber_Profiler_Time_delta(&profiler->start_time, &profiler->stop_time);
         
     | 
| 
      
 586 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
      
 587 
     | 
    
         
            +
            	capture->switches += 1;
         
     | 
| 
       570 
588 
     | 
    
         | 
| 
       571 
     | 
    
         
            -
            	 
     | 
| 
       572 
     | 
    
         
            -
             
     | 
| 
      
 589 
     | 
    
         
            +
            	// The time of the switch:
         
     | 
| 
      
 590 
     | 
    
         
            +
            	struct timespec switch_time;
         
     | 
| 
      
 591 
     | 
    
         
            +
            	Fiber_Profiler_Time_current(&switch_time);
         
     | 
| 
      
 592 
     | 
    
         
            +
            	
         
     | 
| 
      
 593 
     | 
    
         
            +
            	if (capture->capture) {
         
     | 
| 
      
 594 
     | 
    
         
            +
            		// The duration of the sample:
         
     | 
| 
      
 595 
     | 
    
         
            +
            		double duration = Fiber_Profiler_Time_delta(&capture->switch_time, &switch_time);
         
     | 
| 
       573 
596 
     | 
    
         | 
| 
       574 
     | 
    
         
            -
            		 
     | 
| 
      
 597 
     | 
    
         
            +
            		// Finish the current sample:
         
     | 
| 
      
 598 
     | 
    
         
            +
            		Fiber_Profiler_Capture_pause(self);
         
     | 
| 
      
 599 
     | 
    
         
            +
            		Fiber_Profiler_Capture_finish(capture, switch_time);
         
     | 
| 
       575 
600 
     | 
    
         | 
| 
       576 
     | 
    
         
            -
            		 
     | 
| 
       577 
     | 
    
         
            -
             
     | 
| 
       578 
     | 
    
         
            -
            			 
     | 
| 
      
 601 
     | 
    
         
            +
            		// If the duration of the sample is greater than the stall threshold, we consider it a stall:
         
     | 
| 
      
 602 
     | 
    
         
            +
            		if (duration > capture->stall_threshold) {
         
     | 
| 
      
 603 
     | 
    
         
            +
            			capture->stalls += 1;
         
     | 
| 
      
 604 
     | 
    
         
            +
            			
         
     | 
| 
      
 605 
     | 
    
         
            +
            			// Print the sample:
         
     | 
| 
      
 606 
     | 
    
         
            +
            			Fiber_Profiler_Capture_print(capture, duration);
         
     | 
| 
       579 
607 
     | 
    
         
             
            		}
         
     | 
| 
       580 
608 
     | 
    
         | 
| 
       581 
     | 
    
         
            -
            		 
     | 
| 
      
 609 
     | 
    
         
            +
            		// Reset the capture state:
         
     | 
| 
      
 610 
     | 
    
         
            +
            		Fiber_Profiler_Capture_reset(capture);
         
     | 
| 
       582 
611 
     | 
    
         
             
            	}
         
     | 
| 
       583 
612 
     | 
    
         | 
| 
       584 
     | 
    
         
            -
            	if (Fiber_Profiler_Capture_sample( 
     | 
| 
       585 
     | 
    
         
            -
            		//  
     | 
| 
       586 
     | 
    
         
            -
            		 
     | 
| 
      
 613 
     | 
    
         
            +
            	if (Fiber_Profiler_Capture_sample(capture)) {
         
     | 
| 
      
 614 
     | 
    
         
            +
            		// Save the time of the switch for the next sample:
         
     | 
| 
      
 615 
     | 
    
         
            +
            		capture->switch_time = switch_time;
         
     | 
| 
       587 
616 
     | 
    
         | 
| 
      
 617 
     | 
    
         
            +
            		// Start capturing data again:
         
     | 
| 
       588 
618 
     | 
    
         
             
            		Fiber_Profiler_Capture_resume(self);
         
     | 
| 
       589 
619 
     | 
    
         
             
            	}
         
     | 
| 
       590 
620 
     | 
    
         
             
            }
         
     | 
| 
       591 
621 
     | 
    
         | 
| 
       592 
622 
     | 
    
         
             
            // When sampling a fiber, we may encounter returns without a preceeding call. This isn't an error, and we should correctly visualize the call stack. We track both the relative nesting (which can be negative) and the minimum nesting level encountered during the profiling session, and use that to determine the absolute nesting level of each call when printing the call stack.
         
     | 
| 
       593 
     | 
    
         
            -
            static size_t Fiber_Profiler_Capture_absolute_nesting(struct Fiber_Profiler_Capture * 
     | 
| 
       594 
     | 
    
         
            -
            	return call->nesting -  
     | 
| 
      
 623 
     | 
    
         
            +
            static size_t Fiber_Profiler_Capture_absolute_nesting(struct Fiber_Profiler_Capture *capture, struct Fiber_Profiler_Capture_Call *call) {
         
     | 
| 
      
 624 
     | 
    
         
            +
            	return call->nesting - capture->nesting_minimum;
         
     | 
| 
       595 
625 
     | 
    
         
             
            }
         
     | 
| 
       596 
626 
     | 
    
         | 
| 
       597 
627 
     | 
    
         
             
            // If a call is within this threshold of the parent call, it will be skipped when printing the call stack - it's considered inconsequential to the performance of the parent call.
         
     | 
| 
       598 
628 
     | 
    
         
             
            static const double Fiber_Profiler_Capture_SKIP_THRESHOLD = 0.98;
         
     | 
| 
       599 
629 
     | 
    
         | 
| 
       600 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture * 
     | 
| 
       601 
     | 
    
         
            -
            	double  
     | 
| 
      
 630 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *capture, FILE *restrict stream, double duration) {
         
     | 
| 
      
 631 
     | 
    
         
            +
            	double start_time = Fiber_Profiler_Time_delta(&capture->start_time, &capture->switch_time);
         
     | 
| 
       602 
632 
     | 
    
         | 
| 
       603 
     | 
    
         
            -
            	fprintf(stderr, "## Fiber stalled for %.3f seconds  
     | 
| 
      
 633 
     | 
    
         
            +
            	fprintf(stderr, "## Fiber stalled for %.3f seconds (switches=%zu, samples=%zu, stalls=%zu, T+%0.3fs)\n", duration, capture->switches, capture->samples, capture->stalls, start_time);
         
     | 
| 
       604 
634 
     | 
    
         | 
| 
       605 
635 
     | 
    
         
             
            	size_t skipped = 0;
         
     | 
| 
       606 
636 
     | 
    
         | 
| 
       607 
     | 
    
         
            -
            	Fiber_Profiler_Deque_each(& 
     | 
| 
      
 637 
     | 
    
         
            +
            	Fiber_Profiler_Deque_each(&capture->calls, struct Fiber_Profiler_Capture_Call, call) {
         
     | 
| 
       608 
638 
     | 
    
         
             
            		if (call->children) {
         
     | 
| 
       609 
639 
     | 
    
         
             
            			if (call->parent && call->parent->children == 1) {
         
     | 
| 
       610 
640 
     | 
    
         
             
            				if (call->duration > call->parent->duration * Fiber_Profiler_Capture_SKIP_THRESHOLD) {
         
     | 
| 
         @@ -627,7 +657,7 @@ void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, F 
     | 
|
| 
       627 
657 
     | 
    
         
             
            		if (skipped) {
         
     | 
| 
       628 
658 
     | 
    
         
             
            			fprintf(stream, "\e[2m");
         
     | 
| 
       629 
659 
     | 
    
         | 
| 
       630 
     | 
    
         
            -
            			size_t nesting = Fiber_Profiler_Capture_absolute_nesting( 
     | 
| 
      
 660 
     | 
    
         
            +
            			size_t nesting = Fiber_Profiler_Capture_absolute_nesting(capture, call);
         
     | 
| 
       631 
661 
     | 
    
         
             
            			for (size_t i = 0; i < nesting; i += 1) {
         
     | 
| 
       632 
662 
     | 
    
         
             
            				fputc('\t', stream);
         
     | 
| 
       633 
663 
     | 
    
         
             
            			}
         
     | 
| 
         @@ -638,12 +668,12 @@ void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, F 
     | 
|
| 
       638 
668 
     | 
    
         
             
            			call->nesting += 1;
         
     | 
| 
       639 
669 
     | 
    
         
             
            		}
         
     | 
| 
       640 
670 
     | 
    
         | 
| 
       641 
     | 
    
         
            -
            		size_t nesting = Fiber_Profiler_Capture_absolute_nesting( 
     | 
| 
      
 671 
     | 
    
         
            +
            		size_t nesting = Fiber_Profiler_Capture_absolute_nesting(capture, call);
         
     | 
| 
       642 
672 
     | 
    
         
             
            		for (size_t i = 0; i < nesting; i += 1) {
         
     | 
| 
       643 
673 
     | 
    
         
             
            			fputc('\t', stream);
         
     | 
| 
       644 
674 
     | 
    
         
             
            		}
         
     | 
| 
       645 
675 
     | 
    
         | 
| 
       646 
     | 
    
         
            -
            		if (Fiber_Profiler_Capture_Call_expensive_p(call,  
     | 
| 
      
 676 
     | 
    
         
            +
            		if (Fiber_Profiler_Capture_Call_expensive_p(capture, call, duration)) {
         
     | 
| 
       647 
677 
     | 
    
         
             
            			fprintf(stream, "\e[31m");
         
     | 
| 
       648 
678 
     | 
    
         
             
            		}
         
     | 
| 
       649 
679 
     | 
    
         | 
| 
         @@ -651,7 +681,7 @@ void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, F 
     | 
|
| 
       651 
681 
     | 
    
         
             
            		const char *name = rb_id2name(call->id);
         
     | 
| 
       652 
682 
     | 
    
         | 
| 
       653 
683 
     | 
    
         
             
            		struct timespec offset;
         
     | 
| 
       654 
     | 
    
         
            -
            		Fiber_Profiler_Time_elapsed(& 
     | 
| 
      
 684 
     | 
    
         
            +
            		Fiber_Profiler_Time_elapsed(&capture->switch_time, &call->enter_time, &offset);
         
     | 
| 
       655 
685 
     | 
    
         | 
| 
       656 
686 
     | 
    
         
             
            		fprintf(stream, "%s:%d in %s '%s#%s' (%0.4fs, T+" Fiber_Profiler_TIME_PRINTF_TIMESPEC ")\n", call->path, call->line, event_flag_name(call->event_flag), RSTRING_PTR(class_inspect), name, call->duration, Fiber_Profiler_TIME_PRINTF_TIMESPEC_ARGUMENTS(offset));
         
     | 
| 
       657 
687 
     | 
    
         | 
| 
         @@ -667,21 +697,25 @@ void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, F 
     | 
|
| 
       667 
697 
     | 
    
         
             
            			fprintf(stream, "... filtered %zu direct calls ...\e[0m\n", call->filtered);
         
     | 
| 
       668 
698 
     | 
    
         
             
            		}
         
     | 
| 
       669 
699 
     | 
    
         
             
            	}
         
     | 
| 
      
 700 
     | 
    
         
            +
            	
         
     | 
| 
      
 701 
     | 
    
         
            +
            	if (skipped) {
         
     | 
| 
      
 702 
     | 
    
         
            +
            		fprintf(stream, "\e[2m... skipped %zu calls ...\e[0m\n", skipped);
         
     | 
| 
      
 703 
     | 
    
         
            +
            	}
         
     | 
| 
       670 
704 
     | 
    
         
             
            }
         
     | 
| 
       671 
705 
     | 
    
         | 
| 
       672 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture * 
     | 
| 
       673 
     | 
    
         
            -
            	double  
     | 
| 
      
 706 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *capture, FILE *restrict stream, double duration) {
         
     | 
| 
      
 707 
     | 
    
         
            +
            	double start_time = Fiber_Profiler_Time_delta(&capture->start_time, &capture->switch_time);
         
     | 
| 
       674 
708 
     | 
    
         | 
| 
       675 
709 
     | 
    
         
             
            	fputc('{', stream);
         
     | 
| 
       676 
710 
     | 
    
         | 
| 
       677 
     | 
    
         
            -
            	fprintf(stream, "\"duration\":%0.6f",  
     | 
| 
      
 711 
     | 
    
         
            +
            	fprintf(stream, "\"start_time\":%0.3f,\"duration\":%0.6f", start_time, duration);
         
     | 
| 
       678 
712 
     | 
    
         | 
| 
       679 
713 
     | 
    
         
             
            	size_t skipped = 0;
         
     | 
| 
       680 
714 
     | 
    
         | 
| 
       681 
715 
     | 
    
         
             
            	fprintf(stream, ",\"calls\":[");
         
     | 
| 
       682 
716 
     | 
    
         
             
            	int first = 1;
         
     | 
| 
       683 
717 
     | 
    
         | 
| 
       684 
     | 
    
         
            -
            	Fiber_Profiler_Deque_each(& 
     | 
| 
      
 718 
     | 
    
         
            +
            	Fiber_Profiler_Deque_each(&capture->calls, struct Fiber_Profiler_Capture_Call, call) {
         
     | 
| 
       685 
719 
     | 
    
         
             
            		if (call->children) {
         
     | 
| 
       686 
720 
     | 
    
         
             
            			if (call->parent && call->parent->children == 1) {
         
     | 
| 
       687 
721 
     | 
    
         
             
            				if (call->duration > call->parent->duration * Fiber_Profiler_Capture_SKIP_THRESHOLD) {
         
     | 
| 
         @@ -700,10 +734,10 @@ void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler, 
     | 
|
| 
       700 
734 
     | 
    
         
             
            		VALUE class_inspect = rb_inspect(call->klass);
         
     | 
| 
       701 
735 
     | 
    
         
             
            		const char *name = rb_id2name(call->id);
         
     | 
| 
       702 
736 
     | 
    
         | 
| 
       703 
     | 
    
         
            -
            		size_t nesting = Fiber_Profiler_Capture_absolute_nesting( 
     | 
| 
      
 737 
     | 
    
         
            +
            		size_t nesting = Fiber_Profiler_Capture_absolute_nesting(capture, call);
         
     | 
| 
       704 
738 
     | 
    
         | 
| 
       705 
739 
     | 
    
         
             
            		struct timespec offset;
         
     | 
| 
       706 
     | 
    
         
            -
            		Fiber_Profiler_Time_elapsed(& 
     | 
| 
      
 740 
     | 
    
         
            +
            		Fiber_Profiler_Time_elapsed(&capture->switch_time, &call->enter_time, &offset);
         
     | 
| 
       707 
741 
     | 
    
         | 
| 
       708 
742 
     | 
    
         
             
            		fprintf(stream, "%s{\"path\":\"%s\",\"line\":%d,\"class\":\"%s\",\"method\":\"%s\",\"duration\":%0.6f,\"offset\":" Fiber_Profiler_TIME_PRINTF_TIMESPEC ",\"nesting\":%zu,\"skipped\":%zu,\"filtered\":%zu}", first ? "" : ",", call->path, call->line, RSTRING_PTR(class_inspect), name, call->duration, Fiber_Profiler_TIME_PRINTF_TIMESPEC_ARGUMENTS(offset), nesting, skipped, call->filtered);
         
     | 
| 
       709 
743 
     | 
    
         | 
| 
         @@ -717,17 +751,17 @@ void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler, 
     | 
|
| 
       717 
751 
     | 
    
         
             
            		fprintf(stream, ",\"skipped\":%zu", skipped);
         
     | 
| 
       718 
752 
     | 
    
         
             
            	}
         
     | 
| 
       719 
753 
     | 
    
         | 
| 
       720 
     | 
    
         
            -
            	fprintf(stream, "}\n");
         
     | 
| 
      
 754 
     | 
    
         
            +
            	fprintf(stream, ",\"switches\":%zu,\"samples\":%zu,\"stalls\":%zu}\n", capture->switches, capture->samples, capture->stalls);
         
     | 
| 
       721 
755 
     | 
    
         
             
            }
         
     | 
| 
       722 
756 
     | 
    
         | 
| 
       723 
     | 
    
         
            -
            void Fiber_Profiler_Capture_print(struct Fiber_Profiler_Capture * 
     | 
| 
       724 
     | 
    
         
            -
            	if ( 
     | 
| 
      
 757 
     | 
    
         
            +
            void Fiber_Profiler_Capture_print(struct Fiber_Profiler_Capture *capture, double duration) {
         
     | 
| 
      
 758 
     | 
    
         
            +
            	if (capture->output == Qnil) return;
         
     | 
| 
       725 
759 
     | 
    
         | 
| 
       726 
     | 
    
         
            -
            	FILE *stream =  
     | 
| 
       727 
     | 
    
         
            -
            	 
     | 
| 
      
 760 
     | 
    
         
            +
            	FILE *stream = capture->stream.file;
         
     | 
| 
      
 761 
     | 
    
         
            +
            	capture->print(capture, stream, duration);
         
     | 
| 
       728 
762 
     | 
    
         
             
            	fflush(stream);
         
     | 
| 
       729 
763 
     | 
    
         | 
| 
       730 
     | 
    
         
            -
            	rb_io_write( 
     | 
| 
      
 764 
     | 
    
         
            +
            	rb_io_write(capture->output, rb_str_new_static(capture->stream.buffer, capture->stream.size));
         
     | 
| 
       731 
765 
     | 
    
         | 
| 
       732 
766 
     | 
    
         
             
            	fseek(stream, 0, SEEK_SET);
         
     | 
| 
       733 
767 
     | 
    
         
             
            }
         
     | 
| 
         @@ -735,27 +769,33 @@ void Fiber_Profiler_Capture_print(struct Fiber_Profiler_Capture *profiler) { 
     | 
|
| 
       735 
769 
     | 
    
         
             
            #pragma mark - Accessors
         
     | 
| 
       736 
770 
     | 
    
         | 
| 
       737 
771 
     | 
    
         
             
            static VALUE Fiber_Profiler_Capture_stall_threshold_get(VALUE self) {
         
     | 
| 
       738 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 772 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
      
 773 
     | 
    
         
            +
            	
         
     | 
| 
      
 774 
     | 
    
         
            +
            	return DBL2NUM(capture->stall_threshold);
         
     | 
| 
      
 775 
     | 
    
         
            +
            }
         
     | 
| 
      
 776 
     | 
    
         
            +
             
     | 
| 
      
 777 
     | 
    
         
            +
            static VALUE Fiber_Profiler_Capture_filter_threshold_get(VALUE self) {
         
     | 
| 
      
 778 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       739 
779 
     | 
    
         | 
| 
       740 
     | 
    
         
            -
            	return DBL2NUM( 
     | 
| 
      
 780 
     | 
    
         
            +
            	return DBL2NUM(capture->filter_threshold);
         
     | 
| 
       741 
781 
     | 
    
         
             
            }
         
     | 
| 
       742 
782 
     | 
    
         | 
| 
       743 
783 
     | 
    
         
             
            static VALUE Fiber_Profiler_Capture_track_calls_get(VALUE self) {
         
     | 
| 
       744 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 784 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       745 
785 
     | 
    
         | 
| 
       746 
     | 
    
         
            -
            	return  
     | 
| 
      
 786 
     | 
    
         
            +
            	return capture->track_calls ? Qtrue : Qfalse;
         
     | 
| 
       747 
787 
     | 
    
         
             
            }
         
     | 
| 
       748 
788 
     | 
    
         | 
| 
       749 
789 
     | 
    
         
             
            static VALUE Fiber_Profiler_Capture_stalls_get(VALUE self) {
         
     | 
| 
       750 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 790 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       751 
791 
     | 
    
         | 
| 
       752 
     | 
    
         
            -
            	return SIZET2NUM( 
     | 
| 
      
 792 
     | 
    
         
            +
            	return SIZET2NUM(capture->stalls);
         
     | 
| 
       753 
793 
     | 
    
         
             
            }
         
     | 
| 
       754 
794 
     | 
    
         | 
| 
       755 
795 
     | 
    
         
             
            static VALUE Fiber_Profiler_Capture_sample_rate_get(VALUE self) {
         
     | 
| 
       756 
     | 
    
         
            -
            	struct Fiber_Profiler_Capture * 
     | 
| 
      
 796 
     | 
    
         
            +
            	struct Fiber_Profiler_Capture *capture = Fiber_Profiler_Capture_get(self);
         
     | 
| 
       757 
797 
     | 
    
         | 
| 
       758 
     | 
    
         
            -
            	return DBL2NUM( 
     | 
| 
      
 798 
     | 
    
         
            +
            	return DBL2NUM(capture->sample_rate);
         
     | 
| 
       759 
799 
     | 
    
         
             
            }
         
     | 
| 
       760 
800 
     | 
    
         | 
| 
       761 
801 
     | 
    
         
             
            #pragma mark - Environment Variables
         
     | 
| 
         @@ -800,18 +840,31 @@ static double FIBER_PROFILER_CAPTURE_SAMPLE_RATE(void) { 
     | 
|
| 
       800 
840 
     | 
    
         
             
            	}
         
     | 
| 
       801 
841 
     | 
    
         
             
            }
         
     | 
| 
       802 
842 
     | 
    
         | 
| 
      
 843 
     | 
    
         
            +
            static double FIBER_PROFILER_CAPTURE_FILTER_THRESHOLD(void) {
         
     | 
| 
      
 844 
     | 
    
         
            +
            	const char *value = getenv("FIBER_PROFILER_CAPTURE_FILTER_THRESHOLD");
         
     | 
| 
      
 845 
     | 
    
         
            +
            	
         
     | 
| 
      
 846 
     | 
    
         
            +
            	if (value) {
         
     | 
| 
      
 847 
     | 
    
         
            +
            		return atof(value);
         
     | 
| 
      
 848 
     | 
    
         
            +
            	} else {
         
     | 
| 
      
 849 
     | 
    
         
            +
            		// We use 10% of the stall threshold as the default filter threshold:
         
     | 
| 
      
 850 
     | 
    
         
            +
            		return Fiber_Profiler_Capture_stall_threshold * 0.1;
         
     | 
| 
      
 851 
     | 
    
         
            +
            	}
         
     | 
| 
      
 852 
     | 
    
         
            +
            }
         
     | 
| 
      
 853 
     | 
    
         
            +
             
     | 
| 
       803 
854 
     | 
    
         
             
            #pragma mark - Initialization
         
     | 
| 
       804 
855 
     | 
    
         | 
| 
       805 
856 
     | 
    
         
             
            void Init_Fiber_Profiler_Capture(VALUE Fiber_Profiler) {
         
     | 
| 
       806 
857 
     | 
    
         
             
            	Fiber_Profiler_capture_p = FIBER_PROFILER_CAPTURE();
         
     | 
| 
       807 
858 
     | 
    
         
             
            	Fiber_Profiler_Capture_stall_threshold = FIBER_PROFILER_CAPTURE_STALL_THRESHOLD();
         
     | 
| 
      
 859 
     | 
    
         
            +
            	Fiber_Profiler_Capture_filter_threshold = FIBER_PROFILER_CAPTURE_FILTER_THRESHOLD();
         
     | 
| 
       808 
860 
     | 
    
         
             
            	Fiber_Profiler_Capture_track_calls = FIBER_PROFILER_CAPTURE_TRACK_CALLS();
         
     | 
| 
       809 
861 
     | 
    
         
             
            	Fiber_Profiler_Capture_sample_rate = FIBER_PROFILER_CAPTURE_SAMPLE_RATE();
         
     | 
| 
       810 
862 
     | 
    
         | 
| 
       811 
863 
     | 
    
         
             
            	Fiber_Profiler_Capture_initialize_options[0] = rb_intern("stall_threshold");
         
     | 
| 
       812 
     | 
    
         
            -
            	Fiber_Profiler_Capture_initialize_options[1] = rb_intern(" 
     | 
| 
       813 
     | 
    
         
            -
            	Fiber_Profiler_Capture_initialize_options[2] = rb_intern(" 
     | 
| 
       814 
     | 
    
         
            -
            	Fiber_Profiler_Capture_initialize_options[3] = rb_intern(" 
     | 
| 
      
 864 
     | 
    
         
            +
            	Fiber_Profiler_Capture_initialize_options[1] = rb_intern("filter_threshold");
         
     | 
| 
      
 865 
     | 
    
         
            +
            	Fiber_Profiler_Capture_initialize_options[2] = rb_intern("track_calls");
         
     | 
| 
      
 866 
     | 
    
         
            +
            	Fiber_Profiler_Capture_initialize_options[3] = rb_intern("sample_rate");
         
     | 
| 
      
 867 
     | 
    
         
            +
            	Fiber_Profiler_Capture_initialize_options[4] = rb_intern("output");
         
     | 
| 
       815 
868 
     | 
    
         | 
| 
       816 
869 
     | 
    
         
             
            	Fiber_Profiler_Capture = rb_define_class_under(Fiber_Profiler, "Capture", rb_cObject);
         
     | 
| 
       817 
870 
     | 
    
         
             
            	rb_define_alloc_func(Fiber_Profiler_Capture, Fiber_Profiler_Capture_allocate);
         
     | 
| 
         @@ -824,6 +877,7 @@ void Init_Fiber_Profiler_Capture(VALUE Fiber_Profiler) { 
     | 
|
| 
       824 
877 
     | 
    
         
             
            	rb_define_method(Fiber_Profiler_Capture, "stop", Fiber_Profiler_Capture_stop, 0);
         
     | 
| 
       825 
878 
     | 
    
         | 
| 
       826 
879 
     | 
    
         
             
            	rb_define_method(Fiber_Profiler_Capture, "stall_threshold", Fiber_Profiler_Capture_stall_threshold_get, 0);
         
     | 
| 
      
 880 
     | 
    
         
            +
            	rb_define_method(Fiber_Profiler_Capture, "filter_threshold", Fiber_Profiler_Capture_filter_threshold_get, 0);
         
     | 
| 
       827 
881 
     | 
    
         
             
            	rb_define_method(Fiber_Profiler_Capture, "track_calls", Fiber_Profiler_Capture_track_calls_get, 0);
         
     | 
| 
       828 
882 
     | 
    
         
             
            	rb_define_method(Fiber_Profiler_Capture, "sample_rate", Fiber_Profiler_Capture_sample_rate_get, 0);
         
     | 
| 
       829 
883 
     | 
    
         | 
    
        data.tar.gz.sig
    CHANGED
    
    | 
         Binary file 
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,10 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: fiber-profiler
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.3.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Samuel Williams
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire:
         
     | 
| 
       8 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       9 
10 
     | 
    
         
             
            cert_chain:
         
     | 
| 
       10 
11 
     | 
    
         
             
            - |
         
     | 
| 
         @@ -36,8 +37,10 @@ cert_chain: 
     | 
|
| 
       36 
37 
     | 
    
         
             
              Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
         
     | 
| 
       37 
38 
     | 
    
         
             
              voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
         
     | 
| 
       38 
39 
     | 
    
         
             
              -----END CERTIFICATE-----
         
     | 
| 
       39 
     | 
    
         
            -
            date: 2025-02- 
     | 
| 
      
 40 
     | 
    
         
            +
            date: 2025-02-15 00:00:00.000000000 Z
         
     | 
| 
       40 
41 
     | 
    
         
             
            dependencies: []
         
     | 
| 
      
 42 
     | 
    
         
            +
            description:
         
     | 
| 
      
 43 
     | 
    
         
            +
            email:
         
     | 
| 
       41 
44 
     | 
    
         
             
            executables: []
         
     | 
| 
       42 
45 
     | 
    
         
             
            extensions:
         
     | 
| 
       43 
46 
     | 
    
         
             
            - ext/extconf.rb
         
     | 
| 
         @@ -66,6 +69,7 @@ licenses: 
     | 
|
| 
       66 
69 
     | 
    
         
             
            metadata:
         
     | 
| 
       67 
70 
     | 
    
         
             
              documentation_uri: https://socketry.github.io/fiber-profiler/
         
     | 
| 
       68 
71 
     | 
    
         
             
              source_code_uri: https://github.com/socketry/fiber-profiler.git
         
     | 
| 
      
 72 
     | 
    
         
            +
            post_install_message:
         
     | 
| 
       69 
73 
     | 
    
         
             
            rdoc_options: []
         
     | 
| 
       70 
74 
     | 
    
         
             
            require_paths:
         
     | 
| 
       71 
75 
     | 
    
         
             
            - lib
         
     | 
| 
         @@ -80,7 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       80 
84 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       81 
85 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       82 
86 
     | 
    
         
             
            requirements: []
         
     | 
| 
       83 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 87 
     | 
    
         
            +
            rubygems_version: 3.5.16
         
     | 
| 
      
 88 
     | 
    
         
            +
            signing_key:
         
     | 
| 
       84 
89 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       85 
90 
     | 
    
         
             
            summary: A fiber stall profiler.
         
     | 
| 
       86 
91 
     | 
    
         
             
            test_files: []
         
     | 
    
        metadata.gz.sig
    CHANGED
    
    | 
         Binary file 
     |