fiber-profiler 0.1.2 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/Fiber_Profiler.bundle +0 -0
- data/ext/Fiber_Profiler.bundle.dSYM/Contents/Resources/DWARF/Fiber_Profiler.bundle +0 -0
- data/ext/capture.o +0 -0
- data/ext/fiber/profiler/capture.c +253 -94
- data/ext/fiber/profiler/deque.h +48 -1
- data/ext/fiber/profiler/time.c +0 -20
- data/ext/fiber/profiler/time.h +27 -4
- data/ext/time.o +0 -0
- data/lib/fiber/profiler/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +2 -3
- metadata.gz.sig +0 -0
- data/ext/mkmf.log +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90d32f14c57f33c6c247a942a45a2cc56e3cfd673a67c32cfacdf780b21b3473
|
4
|
+
data.tar.gz: 7b666f890ca18c55d5baba91961bae7033d25fa052fc8b618eac13e28992199d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3a93685cad55774675df4db103cc3f2308cbe342a198eadd6ac83e216e409224faf65736b648f3849b1e3faa28b0ae9298faa6b4fb75e1ddcb5c05f6c86efef
|
7
|
+
data.tar.gz: 621ee83b498452ce5d3eab1501a115b6f086f62b04800b09829e9a85143bcbd8141ea6050ff34f3afe0c52a996ec6ce62893f9e4e998b63485522409a684415e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/ext/Fiber_Profiler.bundle
CHANGED
Binary file
|
Binary file
|
data/ext/capture.o
CHANGED
Binary file
|
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
enum {
|
16
16
|
DEBUG_SKIPPED = 0,
|
17
|
+
DEBUG_FILTERED = 0,
|
17
18
|
};
|
18
19
|
|
19
20
|
int Fiber_Profiler_capture_p = 0;
|
@@ -25,9 +26,11 @@ VALUE Fiber_Profiler_Capture = Qnil;
|
|
25
26
|
|
26
27
|
struct Fiber_Profiler_Capture_Call {
|
27
28
|
struct timespec enter_time;
|
28
|
-
|
29
|
+
double duration;
|
29
30
|
|
30
|
-
|
31
|
+
int nesting;
|
32
|
+
size_t children;
|
33
|
+
size_t filtered;
|
31
34
|
|
32
35
|
rb_event_flag_t event_flag;
|
33
36
|
ID id;
|
@@ -65,41 +68,61 @@ struct Fiber_Profiler_Capture;
|
|
65
68
|
typedef void(*Fiber_Profiler_Stream_Print)(struct Fiber_Profiler_Capture*, FILE* restrict);
|
66
69
|
|
67
70
|
struct Fiber_Profiler_Capture {
|
68
|
-
//
|
71
|
+
// The threshold in seconds, which determines when a fiber is considered to have stalled the event loop.
|
69
72
|
double stall_threshold;
|
73
|
+
|
74
|
+
// Whether or not to track calls.
|
70
75
|
int track_calls;
|
76
|
+
|
77
|
+
// The sample rate of the profiler, as a fraction of 1.0, which controls how often the profiler will sample between fiber context switches.
|
71
78
|
double sample_rate;
|
72
79
|
|
73
|
-
// Calls that are shorter than this filter threshold will be ignored
|
80
|
+
// Calls that are shorter than this filter threshold will be ignored.
|
74
81
|
double filter_threshold;
|
75
82
|
|
83
|
+
// The output object to write to.
|
76
84
|
VALUE output;
|
85
|
+
|
86
|
+
// The stream print function to use.
|
77
87
|
Fiber_Profiler_Stream_Print print;
|
88
|
+
|
89
|
+
// The stream buffer used for printing.
|
78
90
|
struct Fiber_Profiler_Stream stream;
|
79
91
|
|
80
|
-
// Whether or not the profiler is currently running
|
92
|
+
// Whether or not the profiler is currently running.
|
81
93
|
int running;
|
82
94
|
|
83
|
-
//
|
95
|
+
// The thread being profiled.
|
96
|
+
VALUE thread;
|
97
|
+
|
98
|
+
// Whether or not to capture call data.
|
84
99
|
int capture;
|
85
100
|
|
101
|
+
// The number of stalls encountered.
|
86
102
|
size_t stalls;
|
87
103
|
|
88
|
-
//
|
104
|
+
// The start time of the profile.
|
89
105
|
struct timespec start_time;
|
106
|
+
|
107
|
+
// The stop time of the profile.
|
90
108
|
struct timespec stop_time;
|
91
109
|
|
92
|
-
// The depth of the call stack
|
93
|
-
|
110
|
+
// The depth of the call stack (can be negative).
|
111
|
+
int nesting;
|
112
|
+
|
113
|
+
// The minimum nesting level encountered during the profiling session.
|
114
|
+
int nesting_minimum;
|
94
115
|
|
95
|
-
// The current call frame
|
116
|
+
// The current call frame.
|
96
117
|
struct Fiber_Profiler_Capture_Call *current;
|
97
118
|
|
119
|
+
// The call recorded during the profiling session.
|
98
120
|
struct Fiber_Profiler_Deque calls;
|
99
121
|
};
|
100
122
|
|
101
123
|
void Fiber_Profiler_Capture_reset(struct Fiber_Profiler_Capture *profiler) {
|
102
124
|
profiler->nesting = 0;
|
125
|
+
profiler->nesting_minimum = 0;
|
103
126
|
profiler->current = NULL;
|
104
127
|
Fiber_Profiler_Deque_truncate(&profiler->calls);
|
105
128
|
}
|
@@ -109,10 +132,11 @@ void Fiber_Profiler_Capture_Call_initialize(void *element) {
|
|
109
132
|
|
110
133
|
call->enter_time.tv_sec = 0;
|
111
134
|
call->enter_time.tv_nsec = 0;
|
112
|
-
call->
|
113
|
-
call->exit_time.tv_nsec = 0;
|
135
|
+
call->duration = 0;
|
114
136
|
|
115
137
|
call->nesting = 0;
|
138
|
+
call->children = 0;
|
139
|
+
call->filtered = 0;
|
116
140
|
|
117
141
|
call->event_flag = 0;
|
118
142
|
call->id = 0;
|
@@ -133,6 +157,7 @@ void Fiber_Profiler_Capture_Call_free(void *element) {
|
|
133
157
|
static void Fiber_Profiler_Capture_mark(void *ptr) {
|
134
158
|
struct Fiber_Profiler_Capture *profiler = (struct Fiber_Profiler_Capture*)ptr;
|
135
159
|
|
160
|
+
rb_gc_mark_movable(profiler->thread);
|
136
161
|
rb_gc_mark_movable(profiler->output);
|
137
162
|
|
138
163
|
// If `klass` is stored as a VALUE in calls, we need to mark them here:
|
@@ -144,6 +169,7 @@ static void Fiber_Profiler_Capture_mark(void *ptr) {
|
|
144
169
|
static void Fiber_Profiler_Capture_compact(void *ptr) {
|
145
170
|
struct Fiber_Profiler_Capture *profiler = (struct Fiber_Profiler_Capture*)ptr;
|
146
171
|
|
172
|
+
profiler->thread = rb_gc_location(profiler->thread);
|
147
173
|
profiler->output = rb_gc_location(profiler->output);
|
148
174
|
|
149
175
|
// If `klass` is stored as a VALUE in calls, we need to update their locations here:
|
@@ -213,22 +239,28 @@ VALUE Fiber_Profiler_Capture_allocate(VALUE klass) {
|
|
213
239
|
// Initialize the profiler state:
|
214
240
|
Fiber_Profiler_Stream_initialize(&profiler->stream);
|
215
241
|
profiler->output = Qnil;
|
242
|
+
|
216
243
|
profiler->running = 0;
|
244
|
+
profiler->thread = Qnil;
|
245
|
+
|
217
246
|
profiler->capture = 0;
|
218
247
|
profiler->stalls = 0;
|
219
248
|
profiler->nesting = 0;
|
249
|
+
profiler->nesting_minimum = 0;
|
220
250
|
profiler->current = NULL;
|
221
251
|
|
222
252
|
profiler->stall_threshold = Fiber_Profiler_Capture_stall_threshold;
|
223
253
|
profiler->track_calls = Fiber_Profiler_Capture_track_calls;
|
224
254
|
profiler->sample_rate = Fiber_Profiler_Capture_sample_rate;
|
225
255
|
|
226
|
-
// Filter calls that are less than
|
227
|
-
profiler->filter_threshold = profiler->stall_threshold * 0.
|
256
|
+
// Filter calls that are less than 10% of the stall threshold:
|
257
|
+
profiler->filter_threshold = profiler->stall_threshold * 0.1;
|
228
258
|
|
229
259
|
profiler->calls.element_initialize = (void (*)(void*))Fiber_Profiler_Capture_Call_initialize;
|
230
260
|
profiler->calls.element_free = (void (*)(void*))Fiber_Profiler_Capture_Call_free;
|
261
|
+
|
231
262
|
Fiber_Profiler_Deque_initialize(&profiler->calls, sizeof(struct Fiber_Profiler_Capture_Call));
|
263
|
+
Fiber_Profiler_Deque_reserve_default(&profiler->calls);
|
232
264
|
|
233
265
|
return TypedData_Wrap_Struct(klass, &Fiber_Profiler_Capture_Type, profiler);
|
234
266
|
}
|
@@ -295,20 +327,24 @@ const char *event_flag_name(rb_event_flag_t event_flag) {
|
|
295
327
|
case RUBY_INTERNAL_EVENT_GC_START: return "gc-start";
|
296
328
|
case RUBY_INTERNAL_EVENT_GC_END_MARK: return "gc-end-mark";
|
297
329
|
case RUBY_INTERNAL_EVENT_GC_END_SWEEP: return "gc-end-sweep";
|
330
|
+
case RUBY_EVENT_LINE: return "line";
|
298
331
|
default: return "unknown";
|
299
332
|
}
|
300
333
|
}
|
301
334
|
|
302
|
-
static struct Fiber_Profiler_Capture_Call*
|
335
|
+
static struct Fiber_Profiler_Capture_Call* Fiber_Profiler_Capture_Call_new(struct Fiber_Profiler_Capture *profiler, rb_event_flag_t event_flag, ID id, VALUE klass) {
|
303
336
|
struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Deque_push(&profiler->calls);
|
304
337
|
|
305
338
|
call->event_flag = event_flag;
|
306
339
|
|
307
340
|
call->parent = profiler->current;
|
341
|
+
if (call->parent) {
|
342
|
+
call->parent->children += 1;
|
343
|
+
}
|
344
|
+
|
308
345
|
profiler->current = call;
|
309
|
-
|
346
|
+
|
310
347
|
call->nesting = profiler->nesting;
|
311
|
-
profiler->nesting += 1;
|
312
348
|
|
313
349
|
if (id) {
|
314
350
|
call->id = id;
|
@@ -326,21 +362,54 @@ static struct Fiber_Profiler_Capture_Call* profiler_event_record_call(struct Fib
|
|
326
362
|
return call;
|
327
363
|
}
|
328
364
|
|
329
|
-
|
365
|
+
// Finish the call by calculating the duration and filtering it if necessary.
|
366
|
+
int Fiber_Profiler_Capture_Call_finish(struct Fiber_Profiler_Capture *profiler, struct Fiber_Profiler_Capture_Call *call) {
|
367
|
+
// Don't filter calls if we're not running:
|
368
|
+
if (DEBUG_FILTERED) return 0;
|
369
|
+
|
370
|
+
if (call->duration < profiler->filter_threshold) {
|
371
|
+
// 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(&profiler->calls)) {
|
373
|
+
if (profiler->current == call) {
|
374
|
+
profiler->current = call->parent;
|
375
|
+
}
|
376
|
+
|
377
|
+
if (call->parent) {
|
378
|
+
call->parent->children -= 1;
|
379
|
+
call->parent->filtered += 1;
|
380
|
+
call->parent = NULL;
|
381
|
+
}
|
382
|
+
|
383
|
+
Fiber_Profiler_Deque_pop(&profiler->calls);
|
384
|
+
|
385
|
+
return 1;
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
return 0;
|
390
|
+
}
|
391
|
+
|
392
|
+
static const double Fiber_Profiler_Capture_Call_EXPENSIVE_THRESHOLD = 0.2;
|
393
|
+
|
394
|
+
int Fiber_Profiler_Capture_Call_expensive_p(struct Fiber_Profiler_Capture_Call *call, double total_duration) {
|
395
|
+
if (call->duration > total_duration * Fiber_Profiler_Capture_Call_EXPENSIVE_THRESHOLD) {
|
396
|
+
return 1;
|
397
|
+
}
|
398
|
+
|
399
|
+
return 0;
|
400
|
+
}
|
330
401
|
|
331
402
|
static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
|
332
403
|
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(data);
|
333
404
|
|
334
|
-
if (event_flag & RUBY_EVENT_FIBER_SWITCH) {
|
335
|
-
Fiber_Profiler_Capture_fiber_switch(profiler);
|
336
|
-
return;
|
337
|
-
}
|
338
|
-
|
339
405
|
// We don't want to capture data if we're not running:
|
340
406
|
if (!profiler->capture) return;
|
341
407
|
|
342
408
|
if (event_flag_call_p(event_flag)) {
|
343
|
-
struct Fiber_Profiler_Capture_Call *call =
|
409
|
+
struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new(profiler, event_flag, id, klass);
|
410
|
+
|
411
|
+
profiler->nesting += 1;
|
412
|
+
|
344
413
|
Fiber_Profiler_Time_current(&call->enter_time);
|
345
414
|
}
|
346
415
|
|
@@ -350,7 +419,7 @@ static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE da
|
|
350
419
|
// 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:
|
351
420
|
if (call == NULL) {
|
352
421
|
struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(&profiler->calls);
|
353
|
-
call =
|
422
|
+
call = Fiber_Profiler_Capture_Call_new(profiler, event_flag, id, klass);
|
354
423
|
|
355
424
|
if (last_call) {
|
356
425
|
call->enter_time = last_call->enter_time;
|
@@ -359,46 +428,85 @@ static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE da
|
|
359
428
|
}
|
360
429
|
}
|
361
430
|
|
362
|
-
|
431
|
+
call->duration = Fiber_Profiler_Time_delta_current(&call->enter_time);
|
363
432
|
|
364
433
|
profiler->current = call->parent;
|
365
434
|
|
366
435
|
// We may encounter returns without a preceeding call.
|
367
|
-
|
368
|
-
profiler->nesting -= 1;
|
436
|
+
profiler->nesting -= 1;
|
369
437
|
|
370
|
-
//
|
371
|
-
|
372
|
-
|
373
|
-
|
438
|
+
// We need to keep track of how deep the call stack goes:
|
439
|
+
if (profiler->nesting < profiler->nesting_minimum) {
|
440
|
+
profiler->nesting_minimum = profiler->nesting;
|
441
|
+
}
|
442
|
+
|
443
|
+
Fiber_Profiler_Capture_Call_finish(profiler, call);
|
444
|
+
}
|
445
|
+
|
446
|
+
else {
|
447
|
+
struct Fiber_Profiler_Capture_Call *last_call = Fiber_Profiler_Deque_last(&profiler->calls);
|
448
|
+
struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Capture_Call_new(profiler, event_flag, id, klass);
|
449
|
+
|
450
|
+
if (last_call) {
|
451
|
+
call->enter_time = last_call->enter_time;
|
452
|
+
} else {
|
453
|
+
call->enter_time = profiler->start_time;
|
374
454
|
}
|
455
|
+
|
456
|
+
call->duration = Fiber_Profiler_Time_delta_current(&call->enter_time);
|
375
457
|
}
|
376
458
|
}
|
377
459
|
|
378
|
-
|
460
|
+
void Fiber_Profiler_Capture_pause(VALUE self) {
|
379
461
|
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(self);
|
380
462
|
|
381
|
-
if (profiler->
|
463
|
+
if (!profiler->capture) return;
|
382
464
|
|
383
|
-
profiler->
|
465
|
+
profiler->capture = 0;
|
384
466
|
|
385
|
-
|
386
|
-
|
467
|
+
rb_thread_remove_event_hook_with_data(profiler->thread, Fiber_Profiler_Capture_callback, self);
|
468
|
+
}
|
469
|
+
|
470
|
+
void Fiber_Profiler_Capture_resume(VALUE self) {
|
471
|
+
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(self);
|
472
|
+
|
473
|
+
if (profiler->capture) return;
|
474
|
+
|
475
|
+
profiler->capture = 1;
|
387
476
|
|
388
|
-
rb_event_flag_t event_flags =
|
477
|
+
rb_event_flag_t event_flags = 0;
|
389
478
|
|
390
479
|
if (profiler->track_calls) {
|
480
|
+
// event_flags |= RUBY_EVENT_LINE;
|
481
|
+
|
391
482
|
event_flags |= RUBY_EVENT_CALL | RUBY_EVENT_RETURN;
|
392
483
|
event_flags |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
|
393
|
-
|
484
|
+
event_flags |= RUBY_EVENT_B_CALL | RUBY_EVENT_B_RETURN;
|
394
485
|
}
|
395
486
|
|
396
|
-
|
397
|
-
rb_thread_add_event_hook(thread, Fiber_Profiler_Capture_callback, event_flags, self);
|
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
|
+
}
|
491
|
+
|
492
|
+
void Fiber_Profiler_Capture_fiber_switch(VALUE self);
|
493
|
+
|
494
|
+
void Fiber_Profiler_Capture_fiber_switch_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
|
495
|
+
Fiber_Profiler_Capture_fiber_switch(data);
|
496
|
+
}
|
497
|
+
|
498
|
+
VALUE Fiber_Profiler_Capture_start(VALUE self) {
|
499
|
+
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(self);
|
500
|
+
|
501
|
+
if (profiler->running) return Qfalse;
|
502
|
+
|
503
|
+
profiler->running = 1;
|
504
|
+
profiler->thread = rb_thread_current();
|
505
|
+
|
506
|
+
Fiber_Profiler_Capture_reset(profiler);
|
507
|
+
Fiber_Profiler_Time_current(&profiler->start_time);
|
398
508
|
|
399
|
-
|
400
|
-
rb_thread_add_event_hook(thread, Fiber_Profiler_Capture_callback, RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_SWEEP, self);
|
401
|
-
// }
|
509
|
+
rb_thread_add_event_hook(profiler->thread, Fiber_Profiler_Capture_fiber_switch_callback, RUBY_EVENT_FIBER_SWITCH, self);
|
402
510
|
|
403
511
|
return self;
|
404
512
|
}
|
@@ -408,34 +516,34 @@ VALUE Fiber_Profiler_Capture_stop(VALUE self) {
|
|
408
516
|
|
409
517
|
if (!profiler->running) return Qfalse;
|
410
518
|
|
411
|
-
|
519
|
+
Fiber_Profiler_Capture_pause(self);
|
412
520
|
|
413
|
-
|
414
|
-
rb_thread_remove_event_hook_with_data(thread, Fiber_Profiler_Capture_callback, self);
|
521
|
+
rb_thread_remove_event_hook_with_data(profiler->thread, Fiber_Profiler_Capture_fiber_switch_callback, self);
|
415
522
|
|
523
|
+
profiler->running = 0;
|
524
|
+
profiler->thread = Qnil;
|
525
|
+
|
416
526
|
Fiber_Profiler_Time_current(&profiler->stop_time);
|
417
527
|
Fiber_Profiler_Capture_reset(profiler);
|
418
528
|
|
419
529
|
return self;
|
420
530
|
}
|
421
531
|
|
422
|
-
static inline float Fiber_Profiler_Capture_duration(struct Fiber_Profiler_Capture *profiler) {
|
423
|
-
struct timespec duration;
|
424
|
-
|
425
|
-
Fiber_Profiler_Time_current(&profiler->stop_time);
|
426
|
-
Fiber_Profiler_Time_elapsed(&profiler->start_time, &profiler->stop_time, &duration);
|
427
|
-
|
428
|
-
return Fiber_Profiler_Time_duration(&duration);
|
429
|
-
}
|
430
|
-
|
431
532
|
void Fiber_Profiler_Capture_finish(struct Fiber_Profiler_Capture *profiler) {
|
432
533
|
profiler->capture = 0;
|
433
534
|
|
535
|
+
struct timespec stop_time;
|
536
|
+
Fiber_Profiler_Time_current(&stop_time);
|
537
|
+
|
434
538
|
struct Fiber_Profiler_Capture_Call *current = profiler->current;
|
435
539
|
while (current) {
|
436
|
-
|
540
|
+
struct Fiber_Profiler_Capture_Call *parent = current->parent;
|
437
541
|
|
438
|
-
current = current->
|
542
|
+
current->duration = Fiber_Profiler_Time_delta(¤t->enter_time, &stop_time);
|
543
|
+
|
544
|
+
Fiber_Profiler_Capture_Call_finish(profiler, current);
|
545
|
+
|
546
|
+
current = parent;
|
439
547
|
}
|
440
548
|
}
|
441
549
|
|
@@ -454,80 +562,119 @@ int Fiber_Profiler_Capture_sample(struct Fiber_Profiler_Capture *profiler) {
|
|
454
562
|
}
|
455
563
|
}
|
456
564
|
|
457
|
-
void Fiber_Profiler_Capture_fiber_switch(
|
565
|
+
void Fiber_Profiler_Capture_fiber_switch(VALUE self)
|
458
566
|
{
|
459
|
-
|
567
|
+
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(self);
|
568
|
+
Fiber_Profiler_Time_current(&profiler->stop_time);
|
569
|
+
double duration = Fiber_Profiler_Time_delta(&profiler->start_time, &profiler->stop_time);
|
460
570
|
|
461
571
|
if (profiler->capture) {
|
572
|
+
Fiber_Profiler_Capture_pause(self);
|
573
|
+
|
462
574
|
Fiber_Profiler_Capture_finish(profiler);
|
463
575
|
|
464
576
|
if (duration > profiler->stall_threshold) {
|
465
577
|
profiler->stalls += 1;
|
466
578
|
Fiber_Profiler_Capture_print(profiler);
|
467
579
|
}
|
580
|
+
|
581
|
+
Fiber_Profiler_Capture_reset(profiler);
|
468
582
|
}
|
469
583
|
|
470
|
-
Fiber_Profiler_Capture_reset(profiler);
|
471
|
-
|
472
584
|
if (Fiber_Profiler_Capture_sample(profiler)) {
|
473
585
|
// Reset the start time:
|
474
586
|
Fiber_Profiler_Time_current(&profiler->start_time);
|
475
587
|
|
476
|
-
|
588
|
+
Fiber_Profiler_Capture_resume(self);
|
477
589
|
}
|
478
590
|
}
|
479
591
|
|
480
|
-
|
592
|
+
// 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 *profiler, struct Fiber_Profiler_Capture_Call *call) {
|
594
|
+
return call->nesting - profiler->nesting_minimum;
|
595
|
+
}
|
596
|
+
|
597
|
+
// 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
|
+
static const double Fiber_Profiler_Capture_SKIP_THRESHOLD = 0.98;
|
481
599
|
|
482
600
|
void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, FILE *restrict stream) {
|
483
|
-
|
484
|
-
Fiber_Profiler_Time_elapsed(&profiler->start_time, &profiler->stop_time, &total_duration);
|
601
|
+
double total_duration = Fiber_Profiler_Time_delta(&profiler->start_time, &profiler->stop_time);
|
485
602
|
|
486
|
-
fprintf(stderr, "Fiber stalled for %.3f seconds
|
603
|
+
fprintf(stderr, "## Fiber stalled for %.3f seconds ##\n", total_duration);
|
487
604
|
|
488
605
|
size_t skipped = 0;
|
489
606
|
|
490
607
|
Fiber_Profiler_Deque_each(&profiler->calls, struct Fiber_Profiler_Capture_Call, call) {
|
491
|
-
|
492
|
-
|
608
|
+
if (call->children) {
|
609
|
+
if (call->parent && call->parent->children == 1) {
|
610
|
+
if (call->duration > call->parent->duration * Fiber_Profiler_Capture_SKIP_THRESHOLD) {
|
611
|
+
if (!DEBUG_SKIPPED) {
|
612
|
+
// We remove the nesting level as we're skipping this call - and we use this to track the nesting of child calls which MAY be printed:
|
613
|
+
call->nesting = call->parent->nesting;
|
614
|
+
skipped += 1;
|
615
|
+
continue;
|
616
|
+
} else {
|
617
|
+
fprintf(stream, "\e[34m");
|
618
|
+
}
|
619
|
+
}
|
620
|
+
}
|
621
|
+
}
|
622
|
+
|
623
|
+
if (call->parent) {
|
624
|
+
call->nesting = call->parent->nesting + 1;
|
625
|
+
}
|
493
626
|
|
494
|
-
|
495
|
-
|
627
|
+
if (skipped) {
|
628
|
+
fprintf(stream, "\e[2m");
|
496
629
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
} else {
|
501
|
-
fprintf(stream, "\e[2m");
|
630
|
+
size_t nesting = Fiber_Profiler_Capture_absolute_nesting(profiler, call);
|
631
|
+
for (size_t i = 0; i < nesting; i += 1) {
|
632
|
+
fputc('\t', stream);
|
502
633
|
}
|
634
|
+
|
635
|
+
fprintf(stream, "... skipped %zu nested calls ...\e[0m\n", skipped);
|
636
|
+
|
637
|
+
skipped = 0;
|
638
|
+
call->nesting += 1;
|
503
639
|
}
|
504
640
|
|
505
|
-
|
641
|
+
size_t nesting = Fiber_Profiler_Capture_absolute_nesting(profiler, call);
|
642
|
+
for (size_t i = 0; i < nesting; i += 1) {
|
506
643
|
fputc('\t', stream);
|
507
644
|
}
|
508
645
|
|
646
|
+
if (Fiber_Profiler_Capture_Call_expensive_p(call, total_duration)) {
|
647
|
+
fprintf(stream, "\e[31m");
|
648
|
+
}
|
649
|
+
|
509
650
|
VALUE class_inspect = rb_inspect(call->klass);
|
510
651
|
const char *name = rb_id2name(call->id);
|
511
652
|
|
512
|
-
|
653
|
+
struct timespec offset;
|
654
|
+
Fiber_Profiler_Time_elapsed(&profiler->start_time, &call->enter_time, &offset);
|
655
|
+
|
656
|
+
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
|
+
|
658
|
+
fprintf(stream, "\e[0m");
|
513
659
|
|
514
|
-
if (
|
515
|
-
fprintf(stream, "\e[
|
660
|
+
if (call->filtered) {
|
661
|
+
fprintf(stream, "\e[2m");
|
662
|
+
|
663
|
+
for (size_t i = 0; i < nesting + 1; i += 1) {
|
664
|
+
fputc('\t', stream);
|
665
|
+
}
|
666
|
+
|
667
|
+
fprintf(stream, "... filtered %zu direct calls ...\e[0m\n", call->filtered);
|
516
668
|
}
|
517
669
|
}
|
518
|
-
|
519
|
-
if (skipped > 0) {
|
520
|
-
fprintf(stream, "Skipped %zu calls that were too short to be meaningful.\n", skipped);
|
521
|
-
}
|
522
670
|
}
|
523
671
|
|
524
672
|
void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler, FILE *restrict stream) {
|
525
|
-
|
526
|
-
Fiber_Profiler_Time_elapsed(&profiler->start_time, &profiler->stop_time, &total_duration);
|
673
|
+
double total_duration = Fiber_Profiler_Time_delta(&profiler->start_time, &profiler->stop_time);
|
527
674
|
|
528
675
|
fputc('{', stream);
|
529
676
|
|
530
|
-
fprintf(stream, "\"duration\"
|
677
|
+
fprintf(stream, "\"duration\":%0.6f", total_duration);
|
531
678
|
|
532
679
|
size_t skipped = 0;
|
533
680
|
|
@@ -535,20 +682,32 @@ void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler,
|
|
535
682
|
int first = 1;
|
536
683
|
|
537
684
|
Fiber_Profiler_Deque_each(&profiler->calls, struct Fiber_Profiler_Capture_Call, call) {
|
538
|
-
|
539
|
-
|
685
|
+
if (call->children) {
|
686
|
+
if (call->parent && call->parent->children == 1) {
|
687
|
+
if (call->duration > call->parent->duration * Fiber_Profiler_Capture_SKIP_THRESHOLD) {
|
688
|
+
// We remove the nesting level as we're skipping this call - and we use this to track the nesting of child calls which MAY be printed:
|
689
|
+
call->nesting = call->parent->nesting;
|
690
|
+
skipped += 1;
|
691
|
+
continue;
|
692
|
+
}
|
693
|
+
}
|
694
|
+
}
|
540
695
|
|
541
|
-
|
542
|
-
|
543
|
-
skipped += 1;
|
544
|
-
continue;
|
696
|
+
if (call->parent) {
|
697
|
+
call->nesting = call->parent->nesting + 1;
|
545
698
|
}
|
546
699
|
|
547
700
|
VALUE class_inspect = rb_inspect(call->klass);
|
548
701
|
const char *name = rb_id2name(call->id);
|
549
702
|
|
550
|
-
|
703
|
+
size_t nesting = Fiber_Profiler_Capture_absolute_nesting(profiler, call);
|
704
|
+
|
705
|
+
struct timespec offset;
|
706
|
+
Fiber_Profiler_Time_elapsed(&profiler->start_time, &call->enter_time, &offset);
|
707
|
+
|
708
|
+
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);
|
551
709
|
|
710
|
+
skipped = 0;
|
552
711
|
first = 0;
|
553
712
|
}
|
554
713
|
|
data/ext/fiber/profiler/deque.h
CHANGED
@@ -11,7 +11,7 @@ enum {
|
|
11
11
|
#ifdef RUBY_DEBUG
|
12
12
|
Fiber_Profiler_Deque_DEBUG = 1,
|
13
13
|
#else
|
14
|
-
Fiber_Profiler_Deque_DEBUG =
|
14
|
+
Fiber_Profiler_Deque_DEBUG = 1,
|
15
15
|
#endif
|
16
16
|
};
|
17
17
|
|
@@ -172,6 +172,53 @@ inline static size_t Fiber_Profiler_Deque_default_capacity(struct Fiber_Profiler
|
|
172
172
|
return (target_size - sizeof(struct Fiber_Profiler_Deque_Page)) / deque->element_size;
|
173
173
|
}
|
174
174
|
|
175
|
+
static inline void Fiber_Profiler_Deque_reserve(struct Fiber_Profiler_Deque *deque, size_t capacity)
|
176
|
+
{
|
177
|
+
struct Fiber_Profiler_Deque_Page *page = deque->head;
|
178
|
+
|
179
|
+
// Walk over all the pages and see if we have enough capacity:
|
180
|
+
while (page) {
|
181
|
+
size_t available = page->capacity - page->size;
|
182
|
+
|
183
|
+
if (available > capacity) {
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
|
187
|
+
capacity -= available;
|
188
|
+
|
189
|
+
if (page->tail) {
|
190
|
+
page = page->tail;
|
191
|
+
} else {
|
192
|
+
break;
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
// We need to allocate a new page:
|
197
|
+
size_t minimum_capacity = Fiber_Profiler_Deque_default_capacity(deque);
|
198
|
+
if (capacity < minimum_capacity) {
|
199
|
+
capacity = minimum_capacity;
|
200
|
+
}
|
201
|
+
|
202
|
+
struct Fiber_Profiler_Deque_Page *reserved_page = Fiber_Profiler_Deque_Page_allocate(deque->element_size, capacity);
|
203
|
+
if (reserved_page == NULL) {
|
204
|
+
return;
|
205
|
+
}
|
206
|
+
|
207
|
+
if (page) {
|
208
|
+
page->tail = reserved_page;
|
209
|
+
reserved_page->head = page;
|
210
|
+
} else {
|
211
|
+
deque->head = deque->tail = reserved_page;
|
212
|
+
}
|
213
|
+
|
214
|
+
if (Fiber_Profiler_Deque_DEBUG) Fiber_Profiler_Deque_debug(deque, __FUNCTION__);
|
215
|
+
}
|
216
|
+
|
217
|
+
static inline void Fiber_Profiler_Deque_reserve_default(struct Fiber_Profiler_Deque *deque)
|
218
|
+
{
|
219
|
+
Fiber_Profiler_Deque_reserve(deque, Fiber_Profiler_Deque_default_capacity(deque));
|
220
|
+
}
|
221
|
+
|
175
222
|
void *Fiber_Profiler_Deque_push(struct Fiber_Profiler_Deque *deque)
|
176
223
|
{
|
177
224
|
struct Fiber_Profiler_Deque_Page *page = deque->tail;
|
data/ext/fiber/profiler/time.c
CHANGED
@@ -13,23 +13,3 @@ void Fiber_Profiler_Time_elapsed(const struct timespec* start, const struct time
|
|
13
13
|
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
14
14
|
}
|
15
15
|
}
|
16
|
-
|
17
|
-
double Fiber_Profiler_Time_duration(const struct timespec *duration)
|
18
|
-
{
|
19
|
-
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
20
|
-
}
|
21
|
-
|
22
|
-
void Fiber_Profiler_Time_current(struct timespec *time) {
|
23
|
-
clock_gettime(CLOCK_MONOTONIC, time);
|
24
|
-
}
|
25
|
-
|
26
|
-
double Fiber_Profiler_Time_proportion(const struct timespec *duration, const struct timespec *total_duration) {
|
27
|
-
return Fiber_Profiler_Time_duration(duration) / Fiber_Profiler_Time_duration(total_duration);
|
28
|
-
}
|
29
|
-
|
30
|
-
double Fiber_Profiler_Time_delta(const struct timespec *start, const struct timespec *stop) {
|
31
|
-
struct timespec duration;
|
32
|
-
Fiber_Profiler_Time_elapsed(start, stop, &duration);
|
33
|
-
|
34
|
-
return Fiber_Profiler_Time_duration(&duration);
|
35
|
-
}
|
data/ext/fiber/profiler/time.h
CHANGED
@@ -7,11 +7,34 @@
|
|
7
7
|
#include <time.h>
|
8
8
|
|
9
9
|
void Fiber_Profiler_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration);
|
10
|
-
double Fiber_Profiler_Time_duration(const struct timespec *duration);
|
11
|
-
void Fiber_Profiler_Time_current(struct timespec *time);
|
12
10
|
|
13
|
-
double
|
14
|
-
|
11
|
+
static inline double Fiber_Profiler_Time_duration(const struct timespec *duration)
|
12
|
+
{
|
13
|
+
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
14
|
+
}
|
15
|
+
|
16
|
+
static inline double Fiber_Profiler_Time_proportion(const struct timespec *duration, const struct timespec *total_duration)
|
17
|
+
{
|
18
|
+
return Fiber_Profiler_Time_duration(duration) / Fiber_Profiler_Time_duration(total_duration);
|
19
|
+
}
|
20
|
+
|
21
|
+
static inline void Fiber_Profiler_Time_current(struct timespec *time)
|
22
|
+
{
|
23
|
+
clock_gettime(CLOCK_MONOTONIC, time);
|
24
|
+
}
|
25
|
+
|
26
|
+
static inline double Fiber_Profiler_Time_delta(const struct timespec *start, const struct timespec *stop)
|
27
|
+
{
|
28
|
+
return stop->tv_sec - start->tv_sec + (stop->tv_nsec - start->tv_nsec) / 1e9;
|
29
|
+
}
|
30
|
+
|
31
|
+
static inline double Fiber_Profiler_Time_delta_current(const struct timespec *start)
|
32
|
+
{
|
33
|
+
struct timespec stop;
|
34
|
+
Fiber_Profiler_Time_current(&stop);
|
35
|
+
|
36
|
+
return Fiber_Profiler_Time_delta(start, &stop);
|
37
|
+
}
|
15
38
|
|
16
39
|
#define Fiber_Profiler_TIME_PRINTF_TIMESPEC "%.3g"
|
17
40
|
#define Fiber_Profiler_TIME_PRINTF_TIMESPEC_ARGUMENTS(ts) ((double)(ts).tv_sec + (ts).tv_nsec / 1e9)
|
data/ext/time.o
CHANGED
Binary file
|
data/readme.md
CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/fiber-profiler
|
|
18
18
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/fiber-profiler/releases/index) for all releases.
|
20
20
|
|
21
|
+
### v0.1.3
|
22
|
+
|
23
|
+
- Improved performance when not profiling (when sampling is enabled).
|
24
|
+
|
21
25
|
### v0.1.0
|
22
26
|
|
23
27
|
- Initial implementation extracted from `io-event` gem.
|
data/releases.md
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fiber-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
37
37
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
38
38
|
-----END CERTIFICATE-----
|
39
|
-
date: 2025-02-
|
39
|
+
date: 2025-02-14 00:00:00.000000000 Z
|
40
40
|
dependencies: []
|
41
41
|
executables: []
|
42
42
|
extensions:
|
@@ -61,7 +61,6 @@ files:
|
|
61
61
|
- ext/fiber/profiler/profiler.h
|
62
62
|
- ext/fiber/profiler/time.c
|
63
63
|
- ext/fiber/profiler/time.h
|
64
|
-
- ext/mkmf.log
|
65
64
|
- ext/profiler.o
|
66
65
|
- ext/time.o
|
67
66
|
- lib/fiber/profiler.rb
|
metadata.gz.sig
CHANGED
Binary file
|
data/ext/mkmf.log
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
have_func: checking for rb_fiber_current()... -------------------- yes
|
2
|
-
|
3
|
-
DYLD_LIBRARY_PATH=.:/Users/samuel/.rubies/ruby-3.4.1/lib ASAN_OPTIONS=detect_leaks=0 "clang -o conftest -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24 -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fdeclspec -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef -pipe -Wall -Wno-unknown-pragmas -std=c99 conftest.c -L. -L/Users/samuel/.rubies/ruby-3.4.1/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -arch arm64 -lruby.3.4-static -framework CoreFoundation -lgmp -ldl -lobjc -lpthread -lpthread "
|
4
|
-
ld: warning: ignoring duplicate libraries: '-lpthread'
|
5
|
-
checked program was:
|
6
|
-
/* begin */
|
7
|
-
1: #include "ruby.h"
|
8
|
-
2:
|
9
|
-
3: int main(int argc, char **argv)
|
10
|
-
4: {
|
11
|
-
5: return !!argv[argc];
|
12
|
-
6: }
|
13
|
-
/* end */
|
14
|
-
|
15
|
-
DYLD_LIBRARY_PATH=.:/Users/samuel/.rubies/ruby-3.4.1/lib ASAN_OPTIONS=detect_leaks=0 "clang -o conftest -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24 -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fdeclspec -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef -pipe -Wall -Wno-unknown-pragmas -std=c99 conftest.c -L. -L/Users/samuel/.rubies/ruby-3.4.1/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -arch arm64 -lruby.3.4-static -framework CoreFoundation -lgmp -ldl -lobjc -lpthread -lpthread "
|
16
|
-
ld: warning: ignoring duplicate libraries: '-lpthread'
|
17
|
-
checked program was:
|
18
|
-
/* begin */
|
19
|
-
1: #include "ruby.h"
|
20
|
-
2:
|
21
|
-
3: /*top*/
|
22
|
-
4: extern int t(void);
|
23
|
-
5: int main(int argc, char **argv)
|
24
|
-
6: {
|
25
|
-
7: if (argc > 1000000) {
|
26
|
-
8: int (* volatile tp)(void)=(int (*)(void))&t;
|
27
|
-
9: printf("%d", (*tp)());
|
28
|
-
10: }
|
29
|
-
11:
|
30
|
-
12: return !!argv[argc];
|
31
|
-
13: }
|
32
|
-
14: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_fiber_current; return !p; }
|
33
|
-
/* end */
|
34
|
-
|
35
|
-
--------------------
|
36
|
-
|
37
|
-
have_func: checking for rb_ext_ractor_safe()... -------------------- yes
|
38
|
-
|
39
|
-
DYLD_LIBRARY_PATH=.:/Users/samuel/.rubies/ruby-3.4.1/lib ASAN_OPTIONS=detect_leaks=0 "clang -o conftest -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24 -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.4.1/include/ruby-3.4.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fdeclspec -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef -pipe -Wall -Wno-unknown-pragmas -std=c99 conftest.c -L. -L/Users/samuel/.rubies/ruby-3.4.1/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -arch arm64 -lruby.3.4-static -framework CoreFoundation -lgmp -ldl -lobjc -lpthread -lpthread "
|
40
|
-
ld: warning: ignoring duplicate libraries: '-lpthread'
|
41
|
-
checked program was:
|
42
|
-
/* begin */
|
43
|
-
1: #include "ruby.h"
|
44
|
-
2:
|
45
|
-
3: /*top*/
|
46
|
-
4: extern int t(void);
|
47
|
-
5: int main(int argc, char **argv)
|
48
|
-
6: {
|
49
|
-
7: if (argc > 1000000) {
|
50
|
-
8: int (* volatile tp)(void)=(int (*)(void))&t;
|
51
|
-
9: printf("%d", (*tp)());
|
52
|
-
10: }
|
53
|
-
11:
|
54
|
-
12: return !!argv[argc];
|
55
|
-
13: }
|
56
|
-
14: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_ext_ractor_safe; return !p; }
|
57
|
-
/* end */
|
58
|
-
|
59
|
-
--------------------
|
60
|
-
|
61
|
-
extconf.h is:
|
62
|
-
/* begin */
|
63
|
-
1: #ifndef EXTCONF_H
|
64
|
-
2: #define EXTCONF_H
|
65
|
-
3: #define HAVE_RB_FIBER_CURRENT 1
|
66
|
-
4: #define HAVE_RB_EXT_RACTOR_SAFE 1
|
67
|
-
5: #endif
|
68
|
-
/* end */
|
69
|
-
|