corkscrews 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +130 -0
- data/Rakefile +20 -0
- data/exe/corkscrews +9 -0
- data/ext/corkscrews/corkscrews.c +24 -0
- data/ext/corkscrews/corkscrews_native.h +85 -0
- data/ext/corkscrews/delay.c +259 -0
- data/ext/corkscrews/extconf.rb +25 -0
- data/ext/corkscrews/hooks.c +156 -0
- data/ext/corkscrews/monitor.c +117 -0
- data/ext/corkscrews/record.c +109 -0
- data/ext/corkscrews/sampler.c +99 -0
- data/lib/corkscrews/analysis.rb +452 -0
- data/lib/corkscrews/boot.rb +25 -0
- data/lib/corkscrews/cli.rb +159 -0
- data/lib/corkscrews/controller.rb +137 -0
- data/lib/corkscrews/firefox_profile.rb +290 -0
- data/lib/corkscrews/native.rb +108 -0
- data/lib/corkscrews/primitives.rb +127 -0
- data/lib/corkscrews/rack.rb +39 -0
- data/lib/corkscrews/recorder.rb +903 -0
- data/lib/corkscrews/report.rb +241 -0
- data/lib/corkscrews/statistics.rb +112 -0
- data/lib/corkscrews/time_source.rb +11 -0
- data/lib/corkscrews/validate.rb +348 -0
- data/lib/corkscrews/version.rb +5 -0
- data/lib/corkscrews.rb +43 -0
- data/validate/benchmarks/b01_serial_hotspot/base.rb +28 -0
- data/validate/benchmarks/b01_serial_hotspot/manifest.yml +23 -0
- data/validate/benchmarks/b01_serial_hotspot/optimized.rb +28 -0
- data/validate/benchmarks/b02_false_hotspot/base.rb +43 -0
- data/validate/benchmarks/b02_false_hotspot/manifest.yml +19 -0
- data/validate/benchmarks/b02_false_hotspot/optimized.rb +43 -0
- data/validate/benchmarks/b03_io_wait/base.rb +29 -0
- data/validate/benchmarks/b03_io_wait/manifest.yml +16 -0
- data/validate/benchmarks/b03_io_wait/optimized.rb +29 -0
- data/validate/benchmarks/b04_lock_contention/base.rb +30 -0
- data/validate/benchmarks/b04_lock_contention/manifest.yml +19 -0
- data/validate/benchmarks/b04_lock_contention/optimized.rb +30 -0
- data/validate/benchmarks/b05_gvl_contention/base.rb +24 -0
- data/validate/benchmarks/b05_gvl_contention/manifest.yml +17 -0
- data/validate/benchmarks/b05_gvl_contention/optimized.rb +24 -0
- data/validate/benchmarks/b06_gc_pressure/base.rb +16 -0
- data/validate/benchmarks/b06_gc_pressure/manifest.yml +16 -0
- data/validate/benchmarks/b06_gc_pressure/optimized.rb +17 -0
- data/validate/benchmarks/b07_pipeline/base.rb +34 -0
- data/validate/benchmarks/b07_pipeline/manifest.yml +17 -0
- data/validate/benchmarks/b07_pipeline/optimized.rb +34 -0
- data/validate/benchmarks/b08_native_attribution/base.rb +18 -0
- data/validate/benchmarks/b08_native_attribution/manifest.yml +18 -0
- data/validate/benchmarks/b08_native_attribution/optimized.rb +18 -0
- data/validate/benchmarks/support.rb +29 -0
- data/validate/harness/run_all.rb +16 -0
- data/validate/harness/stats_check.py +27 -0
- data/validate/harness/t1.rb +15 -0
- data/validate/harness/t4.rb +15 -0
- data/validate/harness/t5.rb +15 -0
- metadata +118 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#include "corkscrews_native.h"
|
|
2
|
+
|
|
3
|
+
#include <time.h>
|
|
4
|
+
|
|
5
|
+
static rb_internal_thread_event_hook_t *thread_hook = NULL;
|
|
6
|
+
static uint64_t gc_enter_started_ns = 0;
|
|
7
|
+
static int gc_hooks_installed = 0;
|
|
8
|
+
|
|
9
|
+
static uint64_t
|
|
10
|
+
monotonic_ns(void)
|
|
11
|
+
{
|
|
12
|
+
struct timespec ts;
|
|
13
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
14
|
+
return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static void
|
|
18
|
+
increment_state_bucket(uint8_t state)
|
|
19
|
+
{
|
|
20
|
+
if (state == 0) atomic_fetch_add(&cs_global.thread_state_running, 1);
|
|
21
|
+
else if (state == 1) atomic_fetch_add(&cs_global.thread_state_ready, 1);
|
|
22
|
+
else if (state == 2) atomic_fetch_add(&cs_global.thread_state_suspended, 1);
|
|
23
|
+
else atomic_fetch_add(&cs_global.thread_state_dead, 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static void
|
|
27
|
+
decrement_state_bucket(uint8_t state)
|
|
28
|
+
{
|
|
29
|
+
if (state == 0) atomic_fetch_sub(&cs_global.thread_state_running, 1);
|
|
30
|
+
else if (state == 1) atomic_fetch_sub(&cs_global.thread_state_ready, 1);
|
|
31
|
+
else if (state == 2) atomic_fetch_sub(&cs_global.thread_state_suspended, 1);
|
|
32
|
+
else atomic_fetch_sub(&cs_global.thread_state_dead, 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static void
|
|
36
|
+
update_max_live_threads(uint64_t live)
|
|
37
|
+
{
|
|
38
|
+
uint64_t current = atomic_load(&cs_global.thread_max_live_count);
|
|
39
|
+
while (live > current &&
|
|
40
|
+
!atomic_compare_exchange_weak(&cs_global.thread_max_live_count, ¤t, live)) {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void
|
|
45
|
+
transition_thread_state(cs_thread_t *data, uint8_t next_state)
|
|
46
|
+
{
|
|
47
|
+
uint8_t initialized = atomic_exchange(&data->state_initialized, 1);
|
|
48
|
+
uint8_t previous = atomic_exchange(&data->state, next_state);
|
|
49
|
+
|
|
50
|
+
if (initialized) {
|
|
51
|
+
decrement_state_bucket(previous);
|
|
52
|
+
if (previous != 3 && next_state == 3) {
|
|
53
|
+
atomic_fetch_sub(&cs_global.thread_live_count, 1);
|
|
54
|
+
}
|
|
55
|
+
else if (previous == 3 && next_state != 3) {
|
|
56
|
+
uint64_t live = atomic_fetch_add(&cs_global.thread_live_count, 1) + 1;
|
|
57
|
+
update_max_live_threads(live);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (next_state != 3) {
|
|
61
|
+
uint64_t live = atomic_fetch_add(&cs_global.thread_live_count, 1) + 1;
|
|
62
|
+
update_max_live_threads(live);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
increment_state_bucket(next_state);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static void
|
|
69
|
+
thread_event_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data)
|
|
70
|
+
{
|
|
71
|
+
(void)user_data;
|
|
72
|
+
cs_thread_t *data = cs_thread_data_for(event_data->thread, event != RUBY_INTERNAL_THREAD_EVENT_EXITED);
|
|
73
|
+
if (!data) return;
|
|
74
|
+
|
|
75
|
+
if (event == RUBY_INTERNAL_THREAD_EVENT_STARTED) {
|
|
76
|
+
transition_thread_state(data, 0);
|
|
77
|
+
atomic_fetch_add(&cs_global.thread_events_started, 1);
|
|
78
|
+
}
|
|
79
|
+
else if (event == RUBY_INTERNAL_THREAD_EVENT_READY) {
|
|
80
|
+
transition_thread_state(data, 1);
|
|
81
|
+
atomic_fetch_add(&cs_global.thread_events_ready, 1);
|
|
82
|
+
}
|
|
83
|
+
else if (event == RUBY_INTERNAL_THREAD_EVENT_RESUMED) {
|
|
84
|
+
transition_thread_state(data, 0);
|
|
85
|
+
atomic_fetch_add(&cs_global.thread_events_resumed, 1);
|
|
86
|
+
cs_delay_request_settle();
|
|
87
|
+
}
|
|
88
|
+
else if (event == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED) {
|
|
89
|
+
transition_thread_state(data, 2);
|
|
90
|
+
atomic_store(&data->suspended_since_id, atomic_load(&cs_global.experiment_id));
|
|
91
|
+
atomic_fetch_add(&cs_global.thread_events_suspended, 1);
|
|
92
|
+
}
|
|
93
|
+
else if (event == RUBY_INTERNAL_THREAD_EVENT_EXITED) {
|
|
94
|
+
transition_thread_state(data, 3);
|
|
95
|
+
atomic_fetch_add(&cs_global.thread_events_exited, 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static void
|
|
100
|
+
gc_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
|
|
101
|
+
{
|
|
102
|
+
(void)data;
|
|
103
|
+
(void)self;
|
|
104
|
+
(void)mid;
|
|
105
|
+
(void)klass;
|
|
106
|
+
|
|
107
|
+
if (event == RUBY_INTERNAL_EVENT_GC_ENTER) {
|
|
108
|
+
gc_enter_started_ns = monotonic_ns();
|
|
109
|
+
atomic_fetch_add(&cs_global.gc_enter_count, 1);
|
|
110
|
+
}
|
|
111
|
+
else if (event == RUBY_INTERNAL_EVENT_GC_EXIT) {
|
|
112
|
+
uint64_t now = monotonic_ns();
|
|
113
|
+
if (gc_enter_started_ns > 0 && now > gc_enter_started_ns) {
|
|
114
|
+
uint64_t pause_ns = now - gc_enter_started_ns;
|
|
115
|
+
atomic_fetch_add(&cs_global.gc_pause_ns, pause_ns);
|
|
116
|
+
uint32_t speedup = atomic_load(&cs_global.speedup_pct);
|
|
117
|
+
if (atomic_load(&cs_global.target_kind) == 2 && speedup > 0) {
|
|
118
|
+
atomic_fetch_add(&cs_global.vclock_credit_ns, pause_ns * speedup / 100);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
atomic_fetch_add(&cs_global.gc_exit_count, 1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static VALUE
|
|
126
|
+
hooks_install(VALUE self)
|
|
127
|
+
{
|
|
128
|
+
(void)self;
|
|
129
|
+
if (!thread_hook) {
|
|
130
|
+
thread_hook = rb_internal_thread_add_event_hook(
|
|
131
|
+
thread_event_callback,
|
|
132
|
+
RUBY_INTERNAL_THREAD_EVENT_MASK,
|
|
133
|
+
NULL
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (!gc_hooks_installed) {
|
|
137
|
+
rb_add_event_hook(gc_event_hook, RUBY_INTERNAL_EVENT_GC_ENTER | RUBY_INTERNAL_EVENT_GC_EXIT, Qnil);
|
|
138
|
+
gc_hooks_installed = 1;
|
|
139
|
+
}
|
|
140
|
+
return Qtrue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static VALUE
|
|
144
|
+
hooks_available_p(VALUE self)
|
|
145
|
+
{
|
|
146
|
+
(void)self;
|
|
147
|
+
return Qtrue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
void
|
|
151
|
+
Init_corkscrews_hooks(VALUE rb_mCorkscrews)
|
|
152
|
+
{
|
|
153
|
+
VALUE rb_mHooks = rb_define_module_under(rb_mCorkscrews, "Hooks");
|
|
154
|
+
rb_define_singleton_method(rb_mHooks, "available?", hooks_available_p, 0);
|
|
155
|
+
rb_define_singleton_method(rb_mHooks, "install!", hooks_install, 0);
|
|
156
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#include "corkscrews_native.h"
|
|
2
|
+
|
|
3
|
+
#include <pthread.h>
|
|
4
|
+
#include <signal.h>
|
|
5
|
+
#include <time.h>
|
|
6
|
+
|
|
7
|
+
static pthread_t monitor_thread;
|
|
8
|
+
static pthread_t target_thread;
|
|
9
|
+
static _Atomic int monitor_thread_started;
|
|
10
|
+
|
|
11
|
+
static void
|
|
12
|
+
sleep_for_sample_period(void)
|
|
13
|
+
{
|
|
14
|
+
uint64_t ns = atomic_load(&cs_global.sample_period_ns);
|
|
15
|
+
if (ns == 0) ns = 1000000ULL;
|
|
16
|
+
|
|
17
|
+
struct timespec ts;
|
|
18
|
+
ts.tv_sec = (time_t)(ns / 1000000000ULL);
|
|
19
|
+
ts.tv_nsec = (long)(ns % 1000000000ULL);
|
|
20
|
+
nanosleep(&ts, NULL);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static void *
|
|
24
|
+
monitor_loop(void *arg)
|
|
25
|
+
{
|
|
26
|
+
(void)arg;
|
|
27
|
+
while (atomic_load(&cs_global.monitor_running)) {
|
|
28
|
+
sleep_for_sample_period();
|
|
29
|
+
atomic_fetch_add(&cs_global.monitor_ticks, 1);
|
|
30
|
+
if (atomic_load(&cs_global.monitor_target_registered)) {
|
|
31
|
+
if (pthread_kill(target_thread, SIGPROF) == 0) {
|
|
32
|
+
atomic_fetch_add(&cs_global.monitor_signals, 1);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
atomic_fetch_add(&cs_global.monitor_signal_failures, 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return NULL;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static VALUE
|
|
43
|
+
monitor_register_current_thread(VALUE self)
|
|
44
|
+
{
|
|
45
|
+
(void)self;
|
|
46
|
+
target_thread = pthread_self();
|
|
47
|
+
atomic_store(&cs_global.monitor_target_registered, 1);
|
|
48
|
+
return Qtrue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static VALUE
|
|
52
|
+
monitor_clear_current_thread(VALUE self)
|
|
53
|
+
{
|
|
54
|
+
(void)self;
|
|
55
|
+
atomic_store(&cs_global.monitor_target_registered, 0);
|
|
56
|
+
return Qtrue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static VALUE
|
|
60
|
+
monitor_start(VALUE self)
|
|
61
|
+
{
|
|
62
|
+
(void)self;
|
|
63
|
+
if (atomic_exchange(&cs_global.monitor_running, 1)) {
|
|
64
|
+
return Qfalse;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (pthread_create(&monitor_thread, NULL, monitor_loop, NULL) != 0) {
|
|
68
|
+
atomic_store(&cs_global.monitor_running, 0);
|
|
69
|
+
rb_raise(rb_eRuntimeError, "failed to start corkscrews monitor thread");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
atomic_store(&monitor_thread_started, 1);
|
|
73
|
+
atomic_fetch_add(&cs_global.monitor_start_count, 1);
|
|
74
|
+
return Qtrue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static VALUE
|
|
78
|
+
monitor_stop(VALUE self)
|
|
79
|
+
{
|
|
80
|
+
(void)self;
|
|
81
|
+
if (!atomic_exchange(&cs_global.monitor_running, 0)) {
|
|
82
|
+
return Qfalse;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (atomic_exchange(&monitor_thread_started, 0)) {
|
|
86
|
+
pthread_join(monitor_thread, NULL);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
atomic_fetch_add(&cs_global.monitor_stop_count, 1);
|
|
90
|
+
return Qtrue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static VALUE
|
|
94
|
+
monitor_running_p(VALUE self)
|
|
95
|
+
{
|
|
96
|
+
(void)self;
|
|
97
|
+
return atomic_load(&cs_global.monitor_running) ? Qtrue : Qfalse;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static VALUE
|
|
101
|
+
monitor_available_p(VALUE self)
|
|
102
|
+
{
|
|
103
|
+
(void)self;
|
|
104
|
+
return Qtrue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void
|
|
108
|
+
Init_corkscrews_monitor(VALUE rb_mCorkscrews)
|
|
109
|
+
{
|
|
110
|
+
VALUE rb_mMonitor = rb_define_module_under(rb_mCorkscrews, "Monitor");
|
|
111
|
+
rb_define_singleton_method(rb_mMonitor, "available?", monitor_available_p, 0);
|
|
112
|
+
rb_define_singleton_method(rb_mMonitor, "register_current_thread!", monitor_register_current_thread, 0);
|
|
113
|
+
rb_define_singleton_method(rb_mMonitor, "clear_current_thread!", monitor_clear_current_thread, 0);
|
|
114
|
+
rb_define_singleton_method(rb_mMonitor, "start!", monitor_start, 0);
|
|
115
|
+
rb_define_singleton_method(rb_mMonitor, "stop!", monitor_stop, 0);
|
|
116
|
+
rb_define_singleton_method(rb_mMonitor, "running?", monitor_running_p, 0);
|
|
117
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#include "corkscrews_native.h"
|
|
2
|
+
|
|
3
|
+
static cs_record_entry_t record_ring[CS_RECORD_RING_SIZE];
|
|
4
|
+
static _Atomic uint64_t record_head;
|
|
5
|
+
static _Atomic uint64_t record_dropped;
|
|
6
|
+
|
|
7
|
+
void
|
|
8
|
+
cs_record_line_sample(uintptr_t site_key, uint32_t line, uint8_t target_hit, uint64_t delay_ns)
|
|
9
|
+
{
|
|
10
|
+
uint64_t sequence = atomic_fetch_add(&record_head, 1);
|
|
11
|
+
if (sequence >= CS_RECORD_RING_SIZE) {
|
|
12
|
+
atomic_fetch_add(&record_dropped, 1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
cs_record_entry_t *entry = &record_ring[sequence % CS_RECORD_RING_SIZE];
|
|
16
|
+
entry->site_key = site_key;
|
|
17
|
+
entry->line = line;
|
|
18
|
+
entry->target_kind = atomic_load(&cs_global.target_kind);
|
|
19
|
+
entry->speedup_pct = atomic_load(&cs_global.speedup_pct);
|
|
20
|
+
entry->experiment_id = atomic_load(&cs_global.experiment_id);
|
|
21
|
+
entry->delay_ns = delay_ns;
|
|
22
|
+
entry->target_hit = target_hit;
|
|
23
|
+
entry->sequence = sequence;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
VALUE
|
|
27
|
+
cs_record_snapshot(VALUE self)
|
|
28
|
+
{
|
|
29
|
+
(void)self;
|
|
30
|
+
VALUE hash = rb_hash_new();
|
|
31
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("global_delay_ns")), ULL2NUM(atomic_load(&cs_global.global_delay_ns)));
|
|
32
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("experiment_id")), ULL2NUM(atomic_load(&cs_global.experiment_id)));
|
|
33
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_kind")), UINT2NUM(atomic_load(&cs_global.target_kind)));
|
|
34
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_iseq")), ULL2NUM(atomic_load(&cs_global.target_iseq)));
|
|
35
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_line")), UINT2NUM(atomic_load(&cs_global.target_line)));
|
|
36
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("speedup_pct")), UINT2NUM(atomic_load(&cs_global.speedup_pct)));
|
|
37
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("vclock_credit_ns")), ULL2NUM(atomic_load(&cs_global.vclock_credit_ns)));
|
|
38
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("samples")), ULL2NUM(atomic_load(&cs_global.samples)));
|
|
39
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_hits")), ULL2NUM(atomic_load(&cs_global.target_hits)));
|
|
40
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_events_started")), ULL2NUM(atomic_load(&cs_global.thread_events_started)));
|
|
41
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_events_ready")), ULL2NUM(atomic_load(&cs_global.thread_events_ready)));
|
|
42
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_events_resumed")), ULL2NUM(atomic_load(&cs_global.thread_events_resumed)));
|
|
43
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_events_suspended")), ULL2NUM(atomic_load(&cs_global.thread_events_suspended)));
|
|
44
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_events_exited")), ULL2NUM(atomic_load(&cs_global.thread_events_exited)));
|
|
45
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_state_running")), ULL2NUM(atomic_load(&cs_global.thread_state_running)));
|
|
46
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_state_ready")), ULL2NUM(atomic_load(&cs_global.thread_state_ready)));
|
|
47
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_state_suspended")), ULL2NUM(atomic_load(&cs_global.thread_state_suspended)));
|
|
48
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_state_dead")), ULL2NUM(atomic_load(&cs_global.thread_state_dead)));
|
|
49
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_live_count")), ULL2NUM(atomic_load(&cs_global.thread_live_count)));
|
|
50
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("thread_max_live_count")), ULL2NUM(atomic_load(&cs_global.thread_max_live_count)));
|
|
51
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("gc_enter_count")), ULL2NUM(atomic_load(&cs_global.gc_enter_count)));
|
|
52
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("gc_exit_count")), ULL2NUM(atomic_load(&cs_global.gc_exit_count)));
|
|
53
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("gc_pause_ns")), ULL2NUM(atomic_load(&cs_global.gc_pause_ns)));
|
|
54
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("debt_settlements")), ULL2NUM(atomic_load(&cs_global.debt_settlements)));
|
|
55
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("debt_settled_ns")), ULL2NUM(atomic_load(&cs_global.debt_settled_ns)));
|
|
56
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_ticks")), ULL2NUM(atomic_load(&cs_global.monitor_ticks)));
|
|
57
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_signals")), ULL2NUM(atomic_load(&cs_global.monitor_signals)));
|
|
58
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_signal_failures")), ULL2NUM(atomic_load(&cs_global.monitor_signal_failures)));
|
|
59
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_start_count")), ULL2NUM(atomic_load(&cs_global.monitor_start_count)));
|
|
60
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_stop_count")), ULL2NUM(atomic_load(&cs_global.monitor_stop_count)));
|
|
61
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_running")), atomic_load(&cs_global.monitor_running) ? Qtrue : Qfalse);
|
|
62
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("monitor_target_registered")), atomic_load(&cs_global.monitor_target_registered) ? Qtrue : Qfalse);
|
|
63
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("record_ring_size")), UINT2NUM(CS_RECORD_RING_SIZE));
|
|
64
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("record_head")), ULL2NUM(atomic_load(&record_head)));
|
|
65
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("record_dropped")), ULL2NUM(atomic_load(&record_dropped)));
|
|
66
|
+
return hash;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static VALUE
|
|
70
|
+
record_events(VALUE self)
|
|
71
|
+
{
|
|
72
|
+
(void)self;
|
|
73
|
+
uint64_t head = atomic_load(&record_head);
|
|
74
|
+
uint64_t start = head > CS_RECORD_RING_SIZE ? head - CS_RECORD_RING_SIZE : 0;
|
|
75
|
+
VALUE ary = rb_ary_new_capa((long)(head - start));
|
|
76
|
+
|
|
77
|
+
for (uint64_t sequence = start; sequence < head; sequence++) {
|
|
78
|
+
cs_record_entry_t *entry = &record_ring[sequence % CS_RECORD_RING_SIZE];
|
|
79
|
+
if (entry->sequence != sequence) continue;
|
|
80
|
+
|
|
81
|
+
VALUE hash = rb_hash_new();
|
|
82
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("sequence")), ULL2NUM(entry->sequence));
|
|
83
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("site_key")), ULL2NUM(entry->site_key));
|
|
84
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("line")), UINT2NUM(entry->line));
|
|
85
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_kind")), UINT2NUM(entry->target_kind));
|
|
86
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("speedup_pct")), UINT2NUM(entry->speedup_pct));
|
|
87
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("experiment_id")), ULL2NUM(entry->experiment_id));
|
|
88
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("delay_ns")), ULL2NUM(entry->delay_ns));
|
|
89
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("target_hit")), entry->target_hit ? Qtrue : Qfalse);
|
|
90
|
+
rb_ary_push(ary, hash);
|
|
91
|
+
}
|
|
92
|
+
return ary;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static VALUE
|
|
96
|
+
record_available_p(VALUE self)
|
|
97
|
+
{
|
|
98
|
+
(void)self;
|
|
99
|
+
return Qtrue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void
|
|
103
|
+
Init_corkscrews_record(VALUE rb_mCorkscrews)
|
|
104
|
+
{
|
|
105
|
+
VALUE rb_mRecord = rb_define_module_under(rb_mCorkscrews, "Record");
|
|
106
|
+
rb_define_singleton_method(rb_mRecord, "available?", record_available_p, 0);
|
|
107
|
+
rb_define_singleton_method(rb_mRecord, "snapshot", cs_record_snapshot, 0);
|
|
108
|
+
rb_define_singleton_method(rb_mRecord, "events", record_events, 0);
|
|
109
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#include "corkscrews_native.h"
|
|
2
|
+
|
|
3
|
+
#define CS_MAX_DEPTH 64
|
|
4
|
+
|
|
5
|
+
static void
|
|
6
|
+
record_line_hit(uintptr_t site_key, uint32_t line)
|
|
7
|
+
{
|
|
8
|
+
atomic_fetch_add(&cs_global.samples, 1);
|
|
9
|
+
|
|
10
|
+
/* Paper basis: Coz SOSP'15 Section 3.4 implements virtual
|
|
11
|
+
* speedups by sampling and inserting delay when the sampled line
|
|
12
|
+
* matches the current experiment target.
|
|
13
|
+
* Paper URL: https://arxiv.org/abs/1608.03676 */
|
|
14
|
+
uint8_t hit = 0;
|
|
15
|
+
uint64_t delay = 0;
|
|
16
|
+
uint32_t speedup = atomic_load(&cs_global.speedup_pct);
|
|
17
|
+
uint32_t kind = atomic_load(&cs_global.target_kind);
|
|
18
|
+
uint32_t target_line = atomic_load(&cs_global.target_line);
|
|
19
|
+
uintptr_t target_key = atomic_load(&cs_global.target_iseq);
|
|
20
|
+
if (kind == 1 && speedup > 0 && line == target_line && site_key == target_key) {
|
|
21
|
+
uint64_t period = atomic_load(&cs_global.sample_period_ns);
|
|
22
|
+
delay = period * speedup / 100;
|
|
23
|
+
hit = 1;
|
|
24
|
+
atomic_fetch_add(&cs_global.target_hits, 1);
|
|
25
|
+
cs_delay_issue_for_current(delay);
|
|
26
|
+
cs_delay_request_settle();
|
|
27
|
+
}
|
|
28
|
+
cs_record_line_sample(site_key, line, hit, delay);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static VALUE
|
|
32
|
+
sampler_available_p(VALUE self)
|
|
33
|
+
{
|
|
34
|
+
(void)self;
|
|
35
|
+
return Qtrue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static VALUE
|
|
39
|
+
sampler_sample_current(VALUE self)
|
|
40
|
+
{
|
|
41
|
+
(void)self;
|
|
42
|
+
VALUE frames[CS_MAX_DEPTH];
|
|
43
|
+
int lines[CS_MAX_DEPTH];
|
|
44
|
+
int n = rb_profile_frames(0, CS_MAX_DEPTH, frames, lines);
|
|
45
|
+
VALUE ary = rb_ary_new_capa(n);
|
|
46
|
+
|
|
47
|
+
atomic_fetch_add(&cs_global.samples, 1);
|
|
48
|
+
if (n > 0) {
|
|
49
|
+
uint8_t hit = 0;
|
|
50
|
+
uint64_t delay = 0;
|
|
51
|
+
uint32_t speedup = atomic_load(&cs_global.speedup_pct);
|
|
52
|
+
uint32_t kind = atomic_load(&cs_global.target_kind);
|
|
53
|
+
uint32_t target_line = atomic_load(&cs_global.target_line);
|
|
54
|
+
uintptr_t target_key = atomic_load(&cs_global.target_iseq);
|
|
55
|
+
if (kind == 1 && speedup > 0 && target_key == 0 && (uint32_t)lines[0] == target_line) {
|
|
56
|
+
uint64_t period = atomic_load(&cs_global.sample_period_ns);
|
|
57
|
+
delay = period * speedup / 100;
|
|
58
|
+
hit = 1;
|
|
59
|
+
atomic_fetch_add(&cs_global.target_hits, 1);
|
|
60
|
+
cs_delay_issue_for_current(delay);
|
|
61
|
+
cs_delay_request_settle();
|
|
62
|
+
}
|
|
63
|
+
cs_record_line_sample(0, (uint32_t)lines[0], hit, delay);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (int i = 0; i < n; i++) {
|
|
67
|
+
VALUE frame = rb_hash_new();
|
|
68
|
+
rb_hash_aset(frame, ID2SYM(rb_intern("frame")), frames[i]);
|
|
69
|
+
rb_hash_aset(frame, ID2SYM(rb_intern("line")), INT2NUM(lines[i]));
|
|
70
|
+
rb_ary_push(ary, frame);
|
|
71
|
+
}
|
|
72
|
+
return ary;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static VALUE
|
|
76
|
+
sampler_record_line(VALUE self, VALUE line_value)
|
|
77
|
+
{
|
|
78
|
+
(void)self;
|
|
79
|
+
record_line_hit(0, NUM2UINT(line_value));
|
|
80
|
+
return Qnil;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static VALUE
|
|
84
|
+
sampler_record_site(VALUE self, VALUE site_key_value, VALUE line_value)
|
|
85
|
+
{
|
|
86
|
+
(void)self;
|
|
87
|
+
record_line_hit((uintptr_t)NUM2ULL(site_key_value), NUM2UINT(line_value));
|
|
88
|
+
return Qnil;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
void
|
|
92
|
+
Init_corkscrews_sampler(VALUE rb_mCorkscrews)
|
|
93
|
+
{
|
|
94
|
+
VALUE rb_mSampler = rb_define_module_under(rb_mCorkscrews, "Sampler");
|
|
95
|
+
rb_define_singleton_method(rb_mSampler, "available?", sampler_available_p, 0);
|
|
96
|
+
rb_define_singleton_method(rb_mSampler, "sample_current", sampler_sample_current, 0);
|
|
97
|
+
rb_define_singleton_method(rb_mSampler, "record_line", sampler_record_line, 1);
|
|
98
|
+
rb_define_singleton_method(rb_mSampler, "record_site", sampler_record_site, 2);
|
|
99
|
+
}
|