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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +130 -0
  4. data/Rakefile +20 -0
  5. data/exe/corkscrews +9 -0
  6. data/ext/corkscrews/corkscrews.c +24 -0
  7. data/ext/corkscrews/corkscrews_native.h +85 -0
  8. data/ext/corkscrews/delay.c +259 -0
  9. data/ext/corkscrews/extconf.rb +25 -0
  10. data/ext/corkscrews/hooks.c +156 -0
  11. data/ext/corkscrews/monitor.c +117 -0
  12. data/ext/corkscrews/record.c +109 -0
  13. data/ext/corkscrews/sampler.c +99 -0
  14. data/lib/corkscrews/analysis.rb +452 -0
  15. data/lib/corkscrews/boot.rb +25 -0
  16. data/lib/corkscrews/cli.rb +159 -0
  17. data/lib/corkscrews/controller.rb +137 -0
  18. data/lib/corkscrews/firefox_profile.rb +290 -0
  19. data/lib/corkscrews/native.rb +108 -0
  20. data/lib/corkscrews/primitives.rb +127 -0
  21. data/lib/corkscrews/rack.rb +39 -0
  22. data/lib/corkscrews/recorder.rb +903 -0
  23. data/lib/corkscrews/report.rb +241 -0
  24. data/lib/corkscrews/statistics.rb +112 -0
  25. data/lib/corkscrews/time_source.rb +11 -0
  26. data/lib/corkscrews/validate.rb +348 -0
  27. data/lib/corkscrews/version.rb +5 -0
  28. data/lib/corkscrews.rb +43 -0
  29. data/validate/benchmarks/b01_serial_hotspot/base.rb +28 -0
  30. data/validate/benchmarks/b01_serial_hotspot/manifest.yml +23 -0
  31. data/validate/benchmarks/b01_serial_hotspot/optimized.rb +28 -0
  32. data/validate/benchmarks/b02_false_hotspot/base.rb +43 -0
  33. data/validate/benchmarks/b02_false_hotspot/manifest.yml +19 -0
  34. data/validate/benchmarks/b02_false_hotspot/optimized.rb +43 -0
  35. data/validate/benchmarks/b03_io_wait/base.rb +29 -0
  36. data/validate/benchmarks/b03_io_wait/manifest.yml +16 -0
  37. data/validate/benchmarks/b03_io_wait/optimized.rb +29 -0
  38. data/validate/benchmarks/b04_lock_contention/base.rb +30 -0
  39. data/validate/benchmarks/b04_lock_contention/manifest.yml +19 -0
  40. data/validate/benchmarks/b04_lock_contention/optimized.rb +30 -0
  41. data/validate/benchmarks/b05_gvl_contention/base.rb +24 -0
  42. data/validate/benchmarks/b05_gvl_contention/manifest.yml +17 -0
  43. data/validate/benchmarks/b05_gvl_contention/optimized.rb +24 -0
  44. data/validate/benchmarks/b06_gc_pressure/base.rb +16 -0
  45. data/validate/benchmarks/b06_gc_pressure/manifest.yml +16 -0
  46. data/validate/benchmarks/b06_gc_pressure/optimized.rb +17 -0
  47. data/validate/benchmarks/b07_pipeline/base.rb +34 -0
  48. data/validate/benchmarks/b07_pipeline/manifest.yml +17 -0
  49. data/validate/benchmarks/b07_pipeline/optimized.rb +34 -0
  50. data/validate/benchmarks/b08_native_attribution/base.rb +18 -0
  51. data/validate/benchmarks/b08_native_attribution/manifest.yml +18 -0
  52. data/validate/benchmarks/b08_native_attribution/optimized.rb +18 -0
  53. data/validate/benchmarks/support.rb +29 -0
  54. data/validate/harness/run_all.rb +16 -0
  55. data/validate/harness/stats_check.py +27 -0
  56. data/validate/harness/t1.rb +15 -0
  57. data/validate/harness/t4.rb +15 -0
  58. data/validate/harness/t5.rb +15 -0
  59. 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, &current, 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
+ }