llrb 0.0.1

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.gitmodules +4 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +56 -0
  8. data/README.md +311 -0
  9. data/Rakefile +30 -0
  10. data/bin/bm_app_fib +41 -0
  11. data/bin/bm_empty_method +33 -0
  12. data/bin/bm_loop_while +27 -0
  13. data/bin/bm_plus +33 -0
  14. data/bin/console +14 -0
  15. data/bin/loop_while.rb +5 -0
  16. data/bin/setup +8 -0
  17. data/ext/llrb/cfg.h +124 -0
  18. data/ext/llrb/compiler.c +987 -0
  19. data/ext/llrb/compiler/funcs.h +164 -0
  20. data/ext/llrb/compiler/stack.h +43 -0
  21. data/ext/llrb/cruby.h +42 -0
  22. data/ext/llrb/cruby/ccan/build_assert/build_assert.h +40 -0
  23. data/ext/llrb/cruby/ccan/check_type/check_type.h +63 -0
  24. data/ext/llrb/cruby/ccan/container_of/container_of.h +142 -0
  25. data/ext/llrb/cruby/ccan/list/list.h +773 -0
  26. data/ext/llrb/cruby/ccan/str/str.h +16 -0
  27. data/ext/llrb/cruby/internal.h +1774 -0
  28. data/ext/llrb/cruby/iseq.h +252 -0
  29. data/ext/llrb/cruby/method.h +213 -0
  30. data/ext/llrb/cruby/node.h +520 -0
  31. data/ext/llrb/cruby/probes_helper.h +43 -0
  32. data/ext/llrb/cruby/ruby_assert.h +54 -0
  33. data/ext/llrb/cruby/ruby_atomic.h +233 -0
  34. data/ext/llrb/cruby/thread_pthread.h +54 -0
  35. data/ext/llrb/cruby/vm_core.h +1646 -0
  36. data/ext/llrb/cruby/vm_debug.h +37 -0
  37. data/ext/llrb/cruby/vm_exec.h +182 -0
  38. data/ext/llrb/cruby/vm_opts.h +57 -0
  39. data/ext/llrb/cruby_extra/id.h +220 -0
  40. data/ext/llrb/cruby_extra/insns.inc +113 -0
  41. data/ext/llrb/cruby_extra/insns_info.inc +796 -0
  42. data/ext/llrb/cruby_extra/probes.h +80 -0
  43. data/ext/llrb/extconf.rb +102 -0
  44. data/ext/llrb/llrb.c +148 -0
  45. data/ext/llrb/optimizer.cc +118 -0
  46. data/ext/llrb/parser.c +191 -0
  47. data/ext/llrb/profiler.c +336 -0
  48. data/ext/llrb_insn_checkkeyword.c +20 -0
  49. data/ext/llrb_insn_checkmatch.c +28 -0
  50. data/ext/llrb_insn_concatarray.c +23 -0
  51. data/ext/llrb_insn_concatstrings.c +21 -0
  52. data/ext/llrb_insn_defined.c +9 -0
  53. data/ext/llrb_insn_getclassvariable.c +10 -0
  54. data/ext/llrb_insn_getinstancevariable.c +44 -0
  55. data/ext/llrb_insn_getlocal.c +14 -0
  56. data/ext/llrb_insn_getlocal_level0.c +8 -0
  57. data/ext/llrb_insn_getlocal_level1.c +8 -0
  58. data/ext/llrb_insn_getspecial.c +14 -0
  59. data/ext/llrb_insn_invokeblock.c +39 -0
  60. data/ext/llrb_insn_invokesuper.c +47 -0
  61. data/ext/llrb_insn_opt_aref.c +25 -0
  62. data/ext/llrb_insn_opt_aset.c +28 -0
  63. data/ext/llrb_insn_opt_div.c +32 -0
  64. data/ext/llrb_insn_opt_eq.c +57 -0
  65. data/ext/llrb_insn_opt_ge.c +28 -0
  66. data/ext/llrb_insn_opt_gt.c +38 -0
  67. data/ext/llrb_insn_opt_le.c +29 -0
  68. data/ext/llrb_insn_opt_lt.c +38 -0
  69. data/ext/llrb_insn_opt_ltlt.c +27 -0
  70. data/ext/llrb_insn_opt_minus.c +36 -0
  71. data/ext/llrb_insn_opt_mod.c +32 -0
  72. data/ext/llrb_insn_opt_mult.c +30 -0
  73. data/ext/llrb_insn_opt_neq.c +103 -0
  74. data/ext/llrb_insn_opt_plus.c +48 -0
  75. data/ext/llrb_insn_opt_send_without_block.c +45 -0
  76. data/ext/llrb_insn_opt_str_freeze.c +12 -0
  77. data/ext/llrb_insn_putspecialobject.c +23 -0
  78. data/ext/llrb_insn_send.c +49 -0
  79. data/ext/llrb_insn_setclassvariable.c +19 -0
  80. data/ext/llrb_insn_setconstant.c +23 -0
  81. data/ext/llrb_insn_setinstancevariable.c +48 -0
  82. data/ext/llrb_insn_setlocal.c +16 -0
  83. data/ext/llrb_insn_setlocal_level0.c +9 -0
  84. data/ext/llrb_insn_setlocal_level1.c +10 -0
  85. data/ext/llrb_insn_setspecial.c +15 -0
  86. data/ext/llrb_insn_splatarray.c +13 -0
  87. data/ext/llrb_insn_throw.c +11 -0
  88. data/ext/llrb_insn_trace.c +37 -0
  89. data/ext/llrb_push_result.c +14 -0
  90. data/ext/llrb_self_from_cfp.c +12 -0
  91. data/ext/llrb_set_pc.c +8 -0
  92. data/lib/llrb.rb +2 -0
  93. data/lib/llrb/jit.rb +76 -0
  94. data/lib/llrb/start.rb +2 -0
  95. data/lib/llrb/version.rb +3 -0
  96. data/llrb.gemspec +48 -0
  97. data/wercker.yml +31 -0
  98. metadata +227 -0
@@ -0,0 +1,191 @@
1
+ /*
2
+ * parser.c: Constructs Control Flow Graph from encoded YARV instructions in ISeq.
3
+ */
4
+
5
+ #include <stdio.h>
6
+ #include "cfg.h"
7
+ #include "cruby.h"
8
+
9
+ static VALUE rb_eParseError;
10
+
11
+ // Return sorted unique Ruby array of Basic Block start positions. Example: [0, 2, 8].
12
+ //
13
+ // It's constructed in the following rule.
14
+ // Rule 1: 0 is always included
15
+ // Rule 2: TS_OFFSET numers for are branch and jump instructions are included
16
+ // Rule 3: Positions immediately after jump instructions (jump, branchnil, branchif, branchunless, opt_case_dispatch, leave) are included
17
+ static VALUE
18
+ llrb_basic_block_starts(const struct rb_iseq_constant_body *body)
19
+ {
20
+ // TODO: No need to check leave? leave is always in the end?
21
+
22
+ // Rule 1
23
+ VALUE starts = rb_ary_new_capa(1); // Maybe GCed. Freeing this causes Bus Error...
24
+ rb_ary_push(starts, INT2FIX(0));
25
+
26
+ for (unsigned int i = 0; i < body->iseq_size;) {
27
+ int insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[i]);
28
+
29
+ // Rule 2
30
+ switch (insn) {
31
+ case YARVINSN_branchif:
32
+ case YARVINSN_branchunless:
33
+ case YARVINSN_branchnil:
34
+ case YARVINSN_jump: {
35
+ VALUE op = body->iseq_encoded[i+1];
36
+ rb_ary_push(starts, INT2FIX(i+insn_len(insn)+op));
37
+ break;
38
+ }
39
+ //case YARVINSN_opt_case_dispatch:
40
+ }
41
+
42
+ // Rule 3
43
+ switch (insn) {
44
+ case YARVINSN_branchif:
45
+ case YARVINSN_branchunless:
46
+ case YARVINSN_branchnil:
47
+ case YARVINSN_jump:
48
+ case YARVINSN_throw:
49
+ if (i+insn_len(insn) < body->iseq_size) {
50
+ rb_ary_push(starts, INT2FIX(i+insn_len(insn)));
51
+ }
52
+ break;
53
+ //case YARVINSN_opt_case_dispatch:
54
+ }
55
+
56
+ i += insn_len(insn);
57
+ }
58
+ starts = rb_ary_sort_bang(starts);
59
+ rb_funcall(starts, rb_intern("uniq!"), 0);
60
+ return starts;
61
+ }
62
+
63
+ static unsigned int
64
+ llrb_find_block_end(const struct rb_iseq_constant_body *body, unsigned int start, VALUE starts)
65
+ {
66
+ unsigned int i = start;
67
+ while (i < body->iseq_size) {
68
+ int insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[i]);
69
+ unsigned int next_i = i + insn_len(insn);
70
+
71
+ // If next insn or end of iseq is found, it's not a block end.
72
+ if (RTEST(rb_ary_includes(starts, INT2FIX(next_i)))
73
+ || next_i == body->iseq_size) {
74
+ break;
75
+ }
76
+ i = next_i;
77
+ }
78
+ return i;
79
+ }
80
+
81
+ static void
82
+ llrb_create_basic_blocks(const struct rb_iseq_constant_body *body, struct llrb_cfg *cfg)
83
+ {
84
+ VALUE starts = llrb_basic_block_starts(body);
85
+ cfg->size = RARRAY_LEN(starts);
86
+ cfg->blocks = (struct llrb_basic_block *)xmalloc(cfg->size * sizeof(struct llrb_basic_block)); // freed in llrb_destruct_cfg
87
+
88
+ for (unsigned int i = 0; i < RARRAY_LEN(starts); i++) {
89
+ unsigned int start = (unsigned int)FIX2INT(RARRAY_AREF(starts, i));
90
+ cfg->blocks[i] = (struct llrb_basic_block){
91
+ .start = start,
92
+ .end = llrb_find_block_end(body, start, starts),
93
+ .incoming_size = 0,
94
+ .incoming_starts = 0,
95
+ .traversed = false,
96
+ };
97
+ }
98
+ RB_GC_GUARD(starts);
99
+ }
100
+
101
+ static void
102
+ llrb_push_incoming_start(struct llrb_basic_block *block, unsigned int start)
103
+ {
104
+ block->incoming_size++;
105
+ if (block->incoming_size == 1) {
106
+ block->incoming_starts = (unsigned int *)xmalloc(sizeof(unsigned int)); // freed in llrb_destruct_cfg
107
+ } else {
108
+ block->incoming_starts = (unsigned int *)xrealloc(
109
+ block->incoming_starts, block->incoming_size * sizeof(unsigned int)); // freed in llrb_destruct_cfg
110
+ }
111
+ block->incoming_starts[block->incoming_size-1] = start;
112
+ }
113
+
114
+ static struct llrb_basic_block *
115
+ llrb_find_block(struct llrb_cfg *cfg, unsigned int start)
116
+ {
117
+ for (unsigned int i = 0; i < cfg->size; i++) {
118
+ struct llrb_basic_block *block = cfg->blocks + i;
119
+ if (block->start == start) return block;
120
+ }
121
+ rb_raise(rb_eParseError, "BasicBlock (start = %d) was not found in llrb_find_block", start);
122
+ }
123
+
124
+ static void
125
+ llrb_set_incoming_blocks_by(const struct rb_iseq_constant_body *body, struct llrb_cfg *cfg, struct llrb_basic_block *block)
126
+ {
127
+ if (block->traversed) return;
128
+ block->traversed = true;
129
+
130
+ struct llrb_basic_block *next_block = 0;
131
+ struct llrb_basic_block *last_block = cfg->blocks + (cfg->size-1);
132
+ if (block < last_block) next_block = block + 1;
133
+
134
+ // TODO: No need to check leave? leave is always in the end?
135
+ int end_insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[block->end]);
136
+ switch (end_insn) {
137
+ case YARVINSN_branchnil:
138
+ case YARVINSN_branchif:
139
+ case YARVINSN_branchunless: {
140
+ VALUE offset = (rb_num_t)body->iseq_encoded[block->end+1];
141
+ struct llrb_basic_block *dest_block = llrb_find_block(cfg, block->end + insn_len(end_insn) + offset);
142
+ llrb_push_incoming_start(dest_block, block->start);
143
+ llrb_set_incoming_blocks_by(body, cfg, dest_block);
144
+
145
+ if (next_block) {
146
+ llrb_push_incoming_start(next_block, block->start);
147
+ llrb_set_incoming_blocks_by(body, cfg, next_block);
148
+ }
149
+ break;
150
+ }
151
+ case YARVINSN_jump: {
152
+ VALUE offset = (rb_num_t)body->iseq_encoded[block->end+1];
153
+ struct llrb_basic_block *dest_block = llrb_find_block(cfg, block->end + insn_len(end_insn) + offset);
154
+ llrb_push_incoming_start(dest_block, block->start);
155
+ llrb_set_incoming_blocks_by(body, cfg, dest_block);
156
+ break;
157
+ }
158
+ case YARVINSN_throw: // TODO: should be modified when catch table is implemented
159
+ break; // no next block
160
+ //case YARVINSN_opt_case_dispatch: // TODO: MUST be modified when opt_case_dispatch is implemented
161
+ default: {
162
+ if (next_block) {
163
+ llrb_push_incoming_start(next_block, block->start);
164
+ llrb_set_incoming_blocks_by(body, cfg, next_block);
165
+ }
166
+ break;
167
+ }
168
+ }
169
+ }
170
+
171
+ // To prevent from creating incoming block from unreachable basic block, this does not loop cfg's basic blocks
172
+ // but traverse insns instead. This may be unnecessary after catch table is implemented...
173
+ static void
174
+ llrb_set_incoming_blocks(const struct rb_iseq_constant_body *body, struct llrb_cfg *cfg)
175
+ {
176
+ llrb_set_incoming_blocks_by(body, cfg, cfg->blocks);
177
+ }
178
+
179
+ void
180
+ llrb_parse_iseq(const struct rb_iseq_constant_body *body, struct llrb_cfg *cfg)
181
+ {
182
+ llrb_create_basic_blocks(body, cfg);
183
+ llrb_set_incoming_blocks(body, cfg);
184
+ if (0) llrb_dump_cfg(body, cfg);
185
+ }
186
+
187
+ void
188
+ Init_parser(VALUE rb_mJIT)
189
+ {
190
+ rb_eParseError = rb_define_class_under(rb_mJIT, "ParseError", rb_eStandardError);
191
+ }
@@ -0,0 +1,336 @@
1
+ #include <stdbool.h>
2
+ #include <signal.h>
3
+ #include <sys/time.h>
4
+ #include <pthread.h>
5
+ #include "ruby.h"
6
+ #include "ruby/debug.h"
7
+ #include "cruby.h"
8
+
9
+ #define LLRB_PROFILE_INTERVAL_USEC 1000
10
+ #define LLRB_COMPILE_INTERVAL_TIMES 200
11
+ #define LLRB_ENABLE_DEBUG 0
12
+
13
+ struct llrb_sample {
14
+ size_t total_calls; // Total count of stack-top occurrence
15
+ bool compiled;
16
+ const rb_callable_method_entry_t *cme;
17
+ };
18
+
19
+ static struct {
20
+ bool running;
21
+ size_t profile_times;
22
+ st_table *sample_by_iseq; // { iseq => llrb_sample }
23
+ } llrb_profiler;
24
+
25
+ static VALUE gc_hook;
26
+
27
+ void
28
+ llrb_dump_iseq(const rb_iseq_t *iseq)
29
+ {
30
+ if (!iseq) return;
31
+
32
+ st_data_t val;
33
+ st_lookup(llrb_profiler.sample_by_iseq, (st_data_t)iseq, &val);
34
+
35
+ struct llrb_sample *sample = (struct llrb_sample *)val;
36
+ if (!sample) return;
37
+ const rb_callable_method_entry_t *cme = sample->cme;
38
+
39
+ fprintf(stderr, "%3ld: ", sample->total_calls);
40
+ fprintf(stderr, "[size=%3d] ", iseq->body->iseq_size);
41
+ switch (iseq->body->type) {
42
+ case ISEQ_TYPE_METHOD:
43
+ fprintf(stderr,"ISEQ_TYPE_METHOD:");
44
+ break;
45
+ case ISEQ_TYPE_CLASS:
46
+ fprintf(stderr,"ISEQ_TYPE_CLASS:");
47
+ break;
48
+ case ISEQ_TYPE_BLOCK:
49
+ fprintf(stderr,"ISEQ_TYPE_BLOCK:");
50
+ break;
51
+ case ISEQ_TYPE_EVAL:
52
+ fprintf(stderr,"ISEQ_TYPE_EVAL:");
53
+ break;
54
+ case ISEQ_TYPE_MAIN:
55
+ fprintf(stderr,"ISEQ_TYPE_MAIN:");
56
+ break;
57
+ case ISEQ_TYPE_TOP:
58
+ fprintf(stderr,"ISEQ_TYPE_TOP:");
59
+ break;
60
+ case ISEQ_TYPE_RESCUE:
61
+ fprintf(stderr,"ISEQ_TYPE_RESCUE:");
62
+ break;
63
+ case ISEQ_TYPE_ENSURE:
64
+ fprintf(stderr,"ISEQ_TYPE_ENSURE:");
65
+ break;
66
+ case ISEQ_TYPE_DEFINED_GUARD:
67
+ fprintf(stderr,"ISEQ_TYPE_DEFINED_GUARD:");
68
+ break;
69
+ default:
70
+ fprintf(stderr,"default:");
71
+ break;
72
+ }
73
+
74
+ if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) {
75
+ VALUE name = rb_profile_frame_full_label((VALUE)cme);
76
+ fprintf(stderr, " %s", RSTRING_PTR(name));
77
+ }
78
+ }
79
+
80
+ static struct llrb_sample *
81
+ llrb_sample_for(const rb_iseq_t *iseq, const rb_control_frame_t *cfp)
82
+ {
83
+ st_data_t key = (st_data_t)iseq, val = 0;
84
+ struct llrb_sample *sample;
85
+
86
+ if (st_lookup(llrb_profiler.sample_by_iseq, key, &val)) {
87
+ sample = (struct llrb_sample *)val;
88
+ } else {
89
+ sample = ALLOC_N(struct llrb_sample, 1); // Not freed
90
+ *sample = (struct llrb_sample){
91
+ .total_calls = 0,
92
+ .compiled = false,
93
+ .cme = rb_vm_frame_method_entry(cfp),
94
+ };
95
+ val = (st_data_t)sample;
96
+ st_insert(llrb_profiler.sample_by_iseq, key, val);
97
+ }
98
+
99
+ return sample;
100
+ }
101
+
102
+ // Profile only stack top.
103
+ static void
104
+ llrb_profile_frame()
105
+ {
106
+ rb_thread_t *th = GET_THREAD();
107
+ rb_control_frame_t *cfp = th->cfp;
108
+
109
+ if (cfp->iseq) {
110
+ struct llrb_sample *sample = llrb_sample_for(cfp->iseq, cfp);
111
+ sample->total_calls++;
112
+ }
113
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
114
+
115
+ llrb_profiler.profile_times++;
116
+ }
117
+
118
+ struct llrb_compile_target {
119
+ const rb_iseq_t *iseq;
120
+ struct llrb_sample* sample;
121
+ };
122
+
123
+ static int
124
+ llrb_search_compile_target_i(st_data_t key, st_data_t val, st_data_t arg)
125
+ {
126
+ struct llrb_compile_target *target = (struct llrb_compile_target *)arg;
127
+ const rb_iseq_t *iseq = (const rb_iseq_t *)key;
128
+ struct llrb_sample *sample = (struct llrb_sample *)val;
129
+
130
+ if (sample->compiled) return ST_CONTINUE;
131
+
132
+ switch (iseq->body->type) {
133
+ case ISEQ_TYPE_METHOD:
134
+ case ISEQ_TYPE_BLOCK:
135
+ case ISEQ_TYPE_MAIN:
136
+ if (!target->iseq && !target->sample) {
137
+ target->iseq = iseq;
138
+ target->sample = sample;
139
+ }
140
+ if (sample->total_calls > target->sample->total_calls) {
141
+ target->iseq = iseq;
142
+ target->sample = sample;
143
+ }
144
+ break;
145
+ default:
146
+ break;
147
+ }
148
+
149
+ return ST_CONTINUE;
150
+ }
151
+
152
+ // Return METHOD or BLOCK iseq which is called the most
153
+ static const rb_iseq_t *
154
+ llrb_search_compile_target()
155
+ {
156
+ struct llrb_compile_target target = (struct llrb_compile_target){
157
+ .sample = 0,
158
+ .iseq = 0,
159
+ };
160
+ st_foreach(llrb_profiler.sample_by_iseq, llrb_search_compile_target_i, (st_data_t)&target);
161
+
162
+ if (target.sample) {
163
+ target.sample->compiled = true;
164
+ }
165
+ return target.iseq;
166
+ }
167
+
168
+ static VALUE
169
+ llrb_compile_error_handler(RB_UNUSED_VAR(VALUE nil), VALUE e)
170
+ {
171
+ fprintf(stderr, "%s\n", RSTRING_PTR(rb_inspect(e)));
172
+ return Qnil;
173
+ }
174
+
175
+ // Compile iseq with compile error suppressed.
176
+ static VALUE
177
+ llrb_safe_compile_iseq(const rb_iseq_t *iseq)
178
+ {
179
+ extern VALUE llrb_compile_iseq_to_method(const rb_iseq_t *iseq);
180
+ return rb_rescue(llrb_compile_iseq_to_method, (VALUE)iseq,
181
+ llrb_compile_error_handler, Qnil);
182
+ }
183
+
184
+ static void
185
+ llrb_job_handler(void *data)
186
+ {
187
+ static int in_job_handler = 0;
188
+ if (in_job_handler) return;
189
+ if (!llrb_profiler.running) return;
190
+
191
+ in_job_handler++;
192
+ llrb_profile_frame();
193
+
194
+ if (llrb_profiler.profile_times % LLRB_COMPILE_INTERVAL_TIMES == 0) {
195
+ const rb_iseq_t *iseq = llrb_search_compile_target();
196
+ if (iseq) {
197
+ if (LLRB_ENABLE_DEBUG) {
198
+ llrb_dump_iseq(iseq);
199
+ fprintf(stderr, " => ");
200
+ }
201
+
202
+ VALUE result = llrb_safe_compile_iseq(iseq);
203
+ if (LLRB_ENABLE_DEBUG) {
204
+ switch (result) {
205
+ case Qtrue:
206
+ fprintf(stderr, "success!");
207
+ break;
208
+ case Qfalse:
209
+ fprintf(stderr, "not compiled");
210
+ break;
211
+ case Qnil:
212
+ fprintf(stderr, "COMPILE ERROR");
213
+ break;
214
+ default:
215
+ fprintf(stderr, "???");
216
+ break;
217
+ }
218
+ fprintf(stderr, "\n");
219
+ }
220
+ }
221
+ }
222
+ in_job_handler--;
223
+ }
224
+
225
+ static void
226
+ llrb_signal_handler(int sig, siginfo_t *sinfo, void *ucontext)
227
+ {
228
+ if (GET_VM()->running && !rb_during_gc()) {
229
+ rb_postponed_job_register_one(0, llrb_job_handler, 0);
230
+ }
231
+ }
232
+
233
+ static VALUE
234
+ rb_jit_start(RB_UNUSED_VAR(VALUE self))
235
+ {
236
+ struct sigaction sa;
237
+ struct itimerval timer;
238
+
239
+ if (llrb_profiler.running) return Qfalse;
240
+ if (!llrb_profiler.sample_by_iseq) {
241
+ llrb_profiler.sample_by_iseq = st_init_numtable();
242
+ }
243
+
244
+ sa.sa_sigaction = llrb_signal_handler;
245
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
246
+ sigemptyset(&sa.sa_mask);
247
+ sigaction(SIGPROF, &sa, NULL);
248
+
249
+ timer.it_interval.tv_sec = 0;
250
+ timer.it_interval.tv_usec = LLRB_PROFILE_INTERVAL_USEC;
251
+ timer.it_value = timer.it_interval;
252
+ setitimer(ITIMER_PROF, &timer, 0);
253
+
254
+ llrb_profiler.running = true;
255
+ return Qtrue;
256
+ }
257
+
258
+ static VALUE
259
+ rb_jit_stop(RB_UNUSED_VAR(VALUE self))
260
+ {
261
+ struct sigaction sa;
262
+ struct itimerval timer;
263
+
264
+ if (!llrb_profiler.running) return Qfalse;
265
+ llrb_profiler.running = false;
266
+
267
+ memset(&timer, 0, sizeof(timer));
268
+ setitimer(ITIMER_PROF, &timer, 0);
269
+
270
+ sa.sa_handler = SIG_IGN;
271
+ sa.sa_flags = SA_RESTART;
272
+ sigemptyset(&sa.sa_mask);
273
+ sigaction(SIGPROF, &sa, NULL);
274
+
275
+ return Qtrue;
276
+ }
277
+
278
+ static int
279
+ llrb_gc_mark_i(st_data_t key, st_data_t val, st_data_t arg)
280
+ {
281
+ VALUE frame = (VALUE)key;
282
+ rb_gc_mark(frame);
283
+ return ST_CONTINUE;
284
+ }
285
+
286
+ static void
287
+ llrb_gc_mark(void *data)
288
+ {
289
+ if (llrb_profiler.sample_by_iseq) {
290
+ st_foreach(llrb_profiler.sample_by_iseq, llrb_gc_mark_i, 0);
291
+ }
292
+ }
293
+
294
+ static void
295
+ llrb_atfork_prepare(void)
296
+ {
297
+ struct itimerval timer;
298
+ if (llrb_profiler.running) {
299
+ memset(&timer, 0, sizeof(timer));
300
+ setitimer(ITIMER_PROF, &timer, 0);
301
+ }
302
+ }
303
+
304
+ static void
305
+ llrb_atfork_parent(void)
306
+ {
307
+ struct itimerval timer;
308
+ if (llrb_profiler.running) {
309
+ timer.it_interval.tv_sec = 0;
310
+ timer.it_interval.tv_usec = LLRB_PROFILE_INTERVAL_USEC;
311
+ timer.it_value = timer.it_interval;
312
+ setitimer(ITIMER_PROF, &timer, 0);
313
+ }
314
+ }
315
+
316
+ static void
317
+ llrb_atfork_child(void)
318
+ {
319
+ rb_jit_stop(Qnil);
320
+ }
321
+
322
+ void
323
+ Init_profiler(VALUE rb_mJIT)
324
+ {
325
+ rb_define_singleton_method(rb_mJIT, "start_internal", RUBY_METHOD_FUNC(rb_jit_start), 0);
326
+ rb_define_singleton_method(rb_mJIT, "stop", RUBY_METHOD_FUNC(rb_jit_stop), 0);
327
+
328
+ llrb_profiler.running = false;
329
+ llrb_profiler.profile_times = 0;
330
+ llrb_profiler.sample_by_iseq = 0;
331
+
332
+ gc_hook = Data_Wrap_Struct(rb_cObject, llrb_gc_mark, NULL, &llrb_profiler);
333
+ rb_global_variable(&gc_hook);
334
+
335
+ pthread_atfork(llrb_atfork_prepare, llrb_atfork_parent, llrb_atfork_child);
336
+ }