fiber-profiler 0.1.3 → 0.1.5
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/capture.c +201 -71
- 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/lib/fiber/profiler/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +1 -1
- 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: 7eff364d5f9fc0850ed71072242195873f790518f3f10b784329fe31dafcbf6f
|
4
|
+
data.tar.gz: ad7c9544f0d14fa2b2085d91d9211c7bcb42dc26e0840ca19bf7c064c5067cba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '069d76a3551bd65c3cf877e371f00c5dd57c3b0bb8c1d937f7187a291adca561b85628ed46b28b3ad2325e6e37e1da1ec4480cb7f4ef4e180b91c55ffd6fcea6'
|
7
|
+
data.tar.gz: d44f0981b66d6fc382005e13e2900b1c56cc879993d6dd0c141293b1b5ed6223e81e3201ce2e345cfa4e663b8e1b42ce3bacea53b7abff9db901470fc897d0df
|
checksums.yaml.gz.sig
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,43 +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
|
|
76
|
-
//
|
83
|
+
// The output object to write to.
|
77
84
|
VALUE output;
|
85
|
+
|
86
|
+
// The stream print function to use.
|
78
87
|
Fiber_Profiler_Stream_Print print;
|
88
|
+
|
89
|
+
// The stream buffer used for printing.
|
79
90
|
struct Fiber_Profiler_Stream stream;
|
80
91
|
|
81
|
-
// Whether or not the profiler is currently running
|
92
|
+
// Whether or not the profiler is currently running.
|
82
93
|
int running;
|
94
|
+
|
95
|
+
// The thread being profiled.
|
83
96
|
VALUE thread;
|
84
97
|
|
85
|
-
// Whether or not to capture call data
|
98
|
+
// Whether or not to capture call data.
|
86
99
|
int capture;
|
87
100
|
|
101
|
+
// The number of stalls encountered.
|
88
102
|
size_t stalls;
|
89
103
|
|
90
|
-
//
|
104
|
+
// The start time of the profile.
|
91
105
|
struct timespec start_time;
|
106
|
+
|
107
|
+
// The stop time of the profile.
|
92
108
|
struct timespec stop_time;
|
93
109
|
|
94
|
-
// The depth of the call stack
|
95
|
-
|
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;
|
96
115
|
|
97
|
-
// The current call frame
|
116
|
+
// The current call frame.
|
98
117
|
struct Fiber_Profiler_Capture_Call *current;
|
99
118
|
|
119
|
+
// The call recorded during the profiling session.
|
100
120
|
struct Fiber_Profiler_Deque calls;
|
101
121
|
};
|
102
122
|
|
103
123
|
void Fiber_Profiler_Capture_reset(struct Fiber_Profiler_Capture *profiler) {
|
104
124
|
profiler->nesting = 0;
|
125
|
+
profiler->nesting_minimum = 0;
|
105
126
|
profiler->current = NULL;
|
106
127
|
Fiber_Profiler_Deque_truncate(&profiler->calls);
|
107
128
|
}
|
@@ -111,10 +132,11 @@ void Fiber_Profiler_Capture_Call_initialize(void *element) {
|
|
111
132
|
|
112
133
|
call->enter_time.tv_sec = 0;
|
113
134
|
call->enter_time.tv_nsec = 0;
|
114
|
-
call->
|
115
|
-
call->exit_time.tv_nsec = 0;
|
135
|
+
call->duration = 0;
|
116
136
|
|
117
137
|
call->nesting = 0;
|
138
|
+
call->children = 0;
|
139
|
+
call->filtered = 0;
|
118
140
|
|
119
141
|
call->event_flag = 0;
|
120
142
|
call->id = 0;
|
@@ -224,18 +246,21 @@ VALUE Fiber_Profiler_Capture_allocate(VALUE klass) {
|
|
224
246
|
profiler->capture = 0;
|
225
247
|
profiler->stalls = 0;
|
226
248
|
profiler->nesting = 0;
|
249
|
+
profiler->nesting_minimum = 0;
|
227
250
|
profiler->current = NULL;
|
228
251
|
|
229
252
|
profiler->stall_threshold = Fiber_Profiler_Capture_stall_threshold;
|
230
253
|
profiler->track_calls = Fiber_Profiler_Capture_track_calls;
|
231
254
|
profiler->sample_rate = Fiber_Profiler_Capture_sample_rate;
|
232
255
|
|
233
|
-
// Filter calls that are less than
|
234
|
-
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;
|
235
258
|
|
236
259
|
profiler->calls.element_initialize = (void (*)(void*))Fiber_Profiler_Capture_Call_initialize;
|
237
260
|
profiler->calls.element_free = (void (*)(void*))Fiber_Profiler_Capture_Call_free;
|
261
|
+
|
238
262
|
Fiber_Profiler_Deque_initialize(&profiler->calls, sizeof(struct Fiber_Profiler_Capture_Call));
|
263
|
+
Fiber_Profiler_Deque_reserve_default(&profiler->calls);
|
239
264
|
|
240
265
|
return TypedData_Wrap_Struct(klass, &Fiber_Profiler_Capture_Type, profiler);
|
241
266
|
}
|
@@ -302,20 +327,24 @@ const char *event_flag_name(rb_event_flag_t event_flag) {
|
|
302
327
|
case RUBY_INTERNAL_EVENT_GC_START: return "gc-start";
|
303
328
|
case RUBY_INTERNAL_EVENT_GC_END_MARK: return "gc-end-mark";
|
304
329
|
case RUBY_INTERNAL_EVENT_GC_END_SWEEP: return "gc-end-sweep";
|
330
|
+
case RUBY_EVENT_LINE: return "line";
|
305
331
|
default: return "unknown";
|
306
332
|
}
|
307
333
|
}
|
308
334
|
|
309
|
-
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) {
|
310
336
|
struct Fiber_Profiler_Capture_Call *call = Fiber_Profiler_Deque_push(&profiler->calls);
|
311
337
|
|
312
338
|
call->event_flag = event_flag;
|
313
339
|
|
314
340
|
call->parent = profiler->current;
|
341
|
+
if (call->parent) {
|
342
|
+
call->parent->children += 1;
|
343
|
+
}
|
344
|
+
|
315
345
|
profiler->current = call;
|
316
|
-
|
346
|
+
|
317
347
|
call->nesting = profiler->nesting;
|
318
|
-
profiler->nesting += 1;
|
319
348
|
|
320
349
|
if (id) {
|
321
350
|
call->id = id;
|
@@ -333,6 +362,43 @@ static struct Fiber_Profiler_Capture_Call* profiler_event_record_call(struct Fib
|
|
333
362
|
return call;
|
334
363
|
}
|
335
364
|
|
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
|
+
}
|
401
|
+
|
336
402
|
static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE data, VALUE self, ID id, VALUE klass) {
|
337
403
|
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(data);
|
338
404
|
|
@@ -340,7 +406,10 @@ static void Fiber_Profiler_Capture_callback(rb_event_flag_t event_flag, VALUE da
|
|
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,19 +428,32 @@ 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
|
-
Fiber_Profiler_Deque_pop(&profiler->calls);
|
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;
|
374
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;
|
454
|
+
}
|
455
|
+
|
456
|
+
call->duration = Fiber_Profiler_Time_delta_current(&call->enter_time);
|
375
457
|
}
|
376
458
|
}
|
377
459
|
|
@@ -395,9 +477,11 @@ void Fiber_Profiler_Capture_resume(VALUE self) {
|
|
395
477
|
rb_event_flag_t event_flags = 0;
|
396
478
|
|
397
479
|
if (profiler->track_calls) {
|
480
|
+
// event_flags |= RUBY_EVENT_LINE;
|
481
|
+
|
398
482
|
event_flags |= RUBY_EVENT_CALL | RUBY_EVENT_RETURN;
|
399
483
|
event_flags |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
|
400
|
-
|
484
|
+
event_flags |= RUBY_EVENT_B_CALL | RUBY_EVENT_B_RETURN;
|
401
485
|
}
|
402
486
|
|
403
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:
|
@@ -445,23 +529,21 @@ VALUE Fiber_Profiler_Capture_stop(VALUE self) {
|
|
445
529
|
return self;
|
446
530
|
}
|
447
531
|
|
448
|
-
static inline float Fiber_Profiler_Capture_duration(struct Fiber_Profiler_Capture *profiler) {
|
449
|
-
struct timespec duration;
|
450
|
-
|
451
|
-
Fiber_Profiler_Time_current(&profiler->stop_time);
|
452
|
-
Fiber_Profiler_Time_elapsed(&profiler->start_time, &profiler->stop_time, &duration);
|
453
|
-
|
454
|
-
return Fiber_Profiler_Time_duration(&duration);
|
455
|
-
}
|
456
|
-
|
457
532
|
void Fiber_Profiler_Capture_finish(struct Fiber_Profiler_Capture *profiler) {
|
458
533
|
profiler->capture = 0;
|
459
534
|
|
535
|
+
struct timespec stop_time;
|
536
|
+
Fiber_Profiler_Time_current(&stop_time);
|
537
|
+
|
460
538
|
struct Fiber_Profiler_Capture_Call *current = profiler->current;
|
461
539
|
while (current) {
|
462
|
-
|
540
|
+
struct Fiber_Profiler_Capture_Call *parent = current->parent;
|
541
|
+
|
542
|
+
current->duration = Fiber_Profiler_Time_delta(¤t->enter_time, &stop_time);
|
543
|
+
|
544
|
+
Fiber_Profiler_Capture_Call_finish(profiler, current);
|
463
545
|
|
464
|
-
current =
|
546
|
+
current = parent;
|
465
547
|
}
|
466
548
|
}
|
467
549
|
|
@@ -483,7 +565,8 @@ int Fiber_Profiler_Capture_sample(struct Fiber_Profiler_Capture *profiler) {
|
|
483
565
|
void Fiber_Profiler_Capture_fiber_switch(VALUE self)
|
484
566
|
{
|
485
567
|
struct Fiber_Profiler_Capture *profiler = Fiber_Profiler_Capture_get(self);
|
486
|
-
|
568
|
+
Fiber_Profiler_Time_current(&profiler->stop_time);
|
569
|
+
double duration = Fiber_Profiler_Time_delta(&profiler->start_time, &profiler->stop_time);
|
487
570
|
|
488
571
|
if (profiler->capture) {
|
489
572
|
Fiber_Profiler_Capture_pause(self);
|
@@ -506,57 +589,92 @@ void Fiber_Profiler_Capture_fiber_switch(VALUE self)
|
|
506
589
|
}
|
507
590
|
}
|
508
591
|
|
509
|
-
|
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;
|
510
599
|
|
511
600
|
void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *profiler, FILE *restrict stream) {
|
512
|
-
|
513
|
-
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);
|
514
602
|
|
515
|
-
fprintf(stderr, "Fiber stalled for %.3f seconds
|
603
|
+
fprintf(stderr, "## Fiber stalled for %.3f seconds ##\n", total_duration);
|
516
604
|
|
517
605
|
size_t skipped = 0;
|
518
606
|
|
519
607
|
Fiber_Profiler_Deque_each(&profiler->calls, struct Fiber_Profiler_Capture_Call, call) {
|
520
|
-
|
521
|
-
|
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
|
+
}
|
522
622
|
|
523
|
-
|
524
|
-
|
623
|
+
if (call->parent) {
|
624
|
+
call->nesting = call->parent->nesting + 1;
|
625
|
+
}
|
626
|
+
|
627
|
+
if (skipped) {
|
628
|
+
fprintf(stream, "\e[2m");
|
525
629
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
} else {
|
530
|
-
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);
|
531
633
|
}
|
634
|
+
|
635
|
+
fprintf(stream, "... skipped %zu nested calls ...\e[0m\n", skipped);
|
636
|
+
|
637
|
+
skipped = 0;
|
638
|
+
call->nesting += 1;
|
532
639
|
}
|
533
640
|
|
534
|
-
|
641
|
+
size_t nesting = Fiber_Profiler_Capture_absolute_nesting(profiler, call);
|
642
|
+
for (size_t i = 0; i < nesting; i += 1) {
|
535
643
|
fputc('\t', stream);
|
536
644
|
}
|
537
645
|
|
646
|
+
if (Fiber_Profiler_Capture_Call_expensive_p(call, total_duration)) {
|
647
|
+
fprintf(stream, "\e[31m");
|
648
|
+
}
|
649
|
+
|
538
650
|
VALUE class_inspect = rb_inspect(call->klass);
|
539
651
|
const char *name = rb_id2name(call->id);
|
540
652
|
|
541
|
-
|
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));
|
542
657
|
|
543
|
-
|
544
|
-
|
658
|
+
fprintf(stream, "\e[0m");
|
659
|
+
|
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);
|
545
668
|
}
|
546
669
|
}
|
547
|
-
|
548
|
-
if (skipped > 0) {
|
549
|
-
fprintf(stream, "Skipped %zu calls that were too short to be meaningful.\n", skipped);
|
550
|
-
}
|
551
670
|
}
|
552
671
|
|
553
672
|
void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler, FILE *restrict stream) {
|
554
|
-
|
555
|
-
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);
|
556
674
|
|
557
675
|
fputc('{', stream);
|
558
676
|
|
559
|
-
fprintf(stream, "\"duration\"
|
677
|
+
fprintf(stream, "\"duration\":%0.6f", total_duration);
|
560
678
|
|
561
679
|
size_t skipped = 0;
|
562
680
|
|
@@ -564,20 +682,32 @@ void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *profiler,
|
|
564
682
|
int first = 1;
|
565
683
|
|
566
684
|
Fiber_Profiler_Deque_each(&profiler->calls, struct Fiber_Profiler_Capture_Call, call) {
|
567
|
-
|
568
|
-
|
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
|
+
}
|
569
695
|
|
570
|
-
|
571
|
-
|
572
|
-
skipped += 1;
|
573
|
-
continue;
|
696
|
+
if (call->parent) {
|
697
|
+
call->nesting = call->parent->nesting + 1;
|
574
698
|
}
|
575
699
|
|
576
700
|
VALUE class_inspect = rb_inspect(call->klass);
|
577
701
|
const char *name = rb_id2name(call->id);
|
578
702
|
|
579
|
-
|
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);
|
580
709
|
|
710
|
+
skipped = 0;
|
581
711
|
first = 0;
|
582
712
|
}
|
583
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.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
Binary file
|