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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.gitmodules +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +56 -0
- data/README.md +311 -0
- data/Rakefile +30 -0
- data/bin/bm_app_fib +41 -0
- data/bin/bm_empty_method +33 -0
- data/bin/bm_loop_while +27 -0
- data/bin/bm_plus +33 -0
- data/bin/console +14 -0
- data/bin/loop_while.rb +5 -0
- data/bin/setup +8 -0
- data/ext/llrb/cfg.h +124 -0
- data/ext/llrb/compiler.c +987 -0
- data/ext/llrb/compiler/funcs.h +164 -0
- data/ext/llrb/compiler/stack.h +43 -0
- data/ext/llrb/cruby.h +42 -0
- data/ext/llrb/cruby/ccan/build_assert/build_assert.h +40 -0
- data/ext/llrb/cruby/ccan/check_type/check_type.h +63 -0
- data/ext/llrb/cruby/ccan/container_of/container_of.h +142 -0
- data/ext/llrb/cruby/ccan/list/list.h +773 -0
- data/ext/llrb/cruby/ccan/str/str.h +16 -0
- data/ext/llrb/cruby/internal.h +1774 -0
- data/ext/llrb/cruby/iseq.h +252 -0
- data/ext/llrb/cruby/method.h +213 -0
- data/ext/llrb/cruby/node.h +520 -0
- data/ext/llrb/cruby/probes_helper.h +43 -0
- data/ext/llrb/cruby/ruby_assert.h +54 -0
- data/ext/llrb/cruby/ruby_atomic.h +233 -0
- data/ext/llrb/cruby/thread_pthread.h +54 -0
- data/ext/llrb/cruby/vm_core.h +1646 -0
- data/ext/llrb/cruby/vm_debug.h +37 -0
- data/ext/llrb/cruby/vm_exec.h +182 -0
- data/ext/llrb/cruby/vm_opts.h +57 -0
- data/ext/llrb/cruby_extra/id.h +220 -0
- data/ext/llrb/cruby_extra/insns.inc +113 -0
- data/ext/llrb/cruby_extra/insns_info.inc +796 -0
- data/ext/llrb/cruby_extra/probes.h +80 -0
- data/ext/llrb/extconf.rb +102 -0
- data/ext/llrb/llrb.c +148 -0
- data/ext/llrb/optimizer.cc +118 -0
- data/ext/llrb/parser.c +191 -0
- data/ext/llrb/profiler.c +336 -0
- data/ext/llrb_insn_checkkeyword.c +20 -0
- data/ext/llrb_insn_checkmatch.c +28 -0
- data/ext/llrb_insn_concatarray.c +23 -0
- data/ext/llrb_insn_concatstrings.c +21 -0
- data/ext/llrb_insn_defined.c +9 -0
- data/ext/llrb_insn_getclassvariable.c +10 -0
- data/ext/llrb_insn_getinstancevariable.c +44 -0
- data/ext/llrb_insn_getlocal.c +14 -0
- data/ext/llrb_insn_getlocal_level0.c +8 -0
- data/ext/llrb_insn_getlocal_level1.c +8 -0
- data/ext/llrb_insn_getspecial.c +14 -0
- data/ext/llrb_insn_invokeblock.c +39 -0
- data/ext/llrb_insn_invokesuper.c +47 -0
- data/ext/llrb_insn_opt_aref.c +25 -0
- data/ext/llrb_insn_opt_aset.c +28 -0
- data/ext/llrb_insn_opt_div.c +32 -0
- data/ext/llrb_insn_opt_eq.c +57 -0
- data/ext/llrb_insn_opt_ge.c +28 -0
- data/ext/llrb_insn_opt_gt.c +38 -0
- data/ext/llrb_insn_opt_le.c +29 -0
- data/ext/llrb_insn_opt_lt.c +38 -0
- data/ext/llrb_insn_opt_ltlt.c +27 -0
- data/ext/llrb_insn_opt_minus.c +36 -0
- data/ext/llrb_insn_opt_mod.c +32 -0
- data/ext/llrb_insn_opt_mult.c +30 -0
- data/ext/llrb_insn_opt_neq.c +103 -0
- data/ext/llrb_insn_opt_plus.c +48 -0
- data/ext/llrb_insn_opt_send_without_block.c +45 -0
- data/ext/llrb_insn_opt_str_freeze.c +12 -0
- data/ext/llrb_insn_putspecialobject.c +23 -0
- data/ext/llrb_insn_send.c +49 -0
- data/ext/llrb_insn_setclassvariable.c +19 -0
- data/ext/llrb_insn_setconstant.c +23 -0
- data/ext/llrb_insn_setinstancevariable.c +48 -0
- data/ext/llrb_insn_setlocal.c +16 -0
- data/ext/llrb_insn_setlocal_level0.c +9 -0
- data/ext/llrb_insn_setlocal_level1.c +10 -0
- data/ext/llrb_insn_setspecial.c +15 -0
- data/ext/llrb_insn_splatarray.c +13 -0
- data/ext/llrb_insn_throw.c +11 -0
- data/ext/llrb_insn_trace.c +37 -0
- data/ext/llrb_push_result.c +14 -0
- data/ext/llrb_self_from_cfp.c +12 -0
- data/ext/llrb_set_pc.c +8 -0
- data/lib/llrb.rb +2 -0
- data/lib/llrb/jit.rb +76 -0
- data/lib/llrb/start.rb +2 -0
- data/lib/llrb/version.rb +3 -0
- data/llrb.gemspec +48 -0
- data/wercker.yml +31 -0
- metadata +227 -0
data/ext/llrb/parser.c
ADDED
|
@@ -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
|
+
}
|
data/ext/llrb/profiler.c
ADDED
|
@@ -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
|
+
}
|