stackprofx 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ /**********************************************************************
2
+
3
+ vm_debug.h - YARV Debug function interface
4
+
5
+ $Author: nobu $
6
+ created at: 04/08/25 02:33:49 JST
7
+
8
+ Copyright (C) 2004-2007 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+
12
+ #ifndef RUBY_DEBUG_H
13
+ #define RUBY_DEBUG_H
14
+
15
+ #include "ruby/ruby.h"
16
+ #include "node.h"
17
+
18
+ RUBY_SYMBOL_EXPORT_BEGIN
19
+
20
+ #define dpv(h,v) ruby_debug_print_value(-1, 0, (h), (v))
21
+ #define dp(v) ruby_debug_print_value(-1, 0, "", (v))
22
+ #define dpi(i) ruby_debug_print_id(-1, 0, "", (i))
23
+ #define dpn(n) ruby_debug_print_node(-1, 0, "", (n))
24
+
25
+ #define bp() ruby_debug_breakpoint()
26
+
27
+ VALUE ruby_debug_print_value(int level, int debug_level, const char *header, VALUE v);
28
+ ID ruby_debug_print_id(int level, int debug_level, const char *header, ID id);
29
+ NODE *ruby_debug_print_node(int level, int debug_level, const char *header, const NODE *node);
30
+ int ruby_debug_print_indent(int level, int debug_level, int indent_level);
31
+ void ruby_debug_breakpoint(void);
32
+ void ruby_debug_gc_check_func(void);
33
+ void ruby_set_debug_option(const char *str);
34
+
35
+ RUBY_SYMBOL_EXPORT_END
36
+
37
+ #endif /* RUBY_DEBUG_H */
@@ -0,0 +1,56 @@
1
+ /*-*-c-*-*/
2
+ /**********************************************************************
3
+
4
+ vm_opts.h - VM optimize option
5
+
6
+ $Author: tmm1 $
7
+
8
+ Copyright (C) 2004-2007 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+
12
+
13
+ #ifndef RUBY_VM_OPTS_H
14
+ #define RUBY_VM_OPTS_H
15
+
16
+ /* Compile options.
17
+ * You can change these options at runtime by VM::CompileOption.
18
+ * Following definitions are default values.
19
+ */
20
+
21
+ #define OPT_TRACE_INSTRUCTION 1
22
+ #define OPT_TAILCALL_OPTIMIZATION 0
23
+ #define OPT_PEEPHOLE_OPTIMIZATION 1
24
+ #define OPT_SPECIALISED_INSTRUCTION 1
25
+ #define OPT_INLINE_CONST_CACHE 1
26
+
27
+
28
+ /* Build Options.
29
+ * You can't change these options at runtime.
30
+ */
31
+
32
+ /* C compiler dependent */
33
+ #define OPT_DIRECT_THREADED_CODE 1
34
+ #define OPT_TOKEN_THREADED_CODE 0
35
+ #define OPT_CALL_THREADED_CODE 0
36
+
37
+ /* VM running option */
38
+ #define OPT_CHECKED_RUN 1
39
+ #define OPT_INLINE_METHOD_CACHE 1
40
+ #define OPT_GLOBAL_METHOD_CACHE 1
41
+ #define OPT_BLOCKINLINING 0
42
+
43
+ /* architecture independent, affects generated code */
44
+ #define OPT_OPERANDS_UNIFICATION 1
45
+ #define OPT_INSTRUCTIONS_UNIFICATION 0
46
+ #define OPT_UNIFY_ALL_COMBINATION 0
47
+ #define OPT_STACK_CACHING 0
48
+
49
+ /* misc */
50
+ #define SUPPORT_JOKE 0
51
+
52
+ #ifndef VM_COLLECT_USAGE_DETAILS
53
+ #define VM_COLLECT_USAGE_DETAILS 0
54
+ #endif
55
+
56
+ #endif /* RUBY_VM_OPTS_H */
data/ext/stackprofx.c ADDED
@@ -0,0 +1,622 @@
1
+ /**********************************************************************
2
+
3
+ stackprofx.c - Sampling call-stack frame profiler for MRI.
4
+
5
+ vim: setl noexpandtab shiftwidth=4 tabstop=8 softtabstop=4
6
+
7
+ **********************************************************************/
8
+
9
+ #include <ruby/ruby.h>
10
+ #include <ruby/debug.h>
11
+ #include <ruby/st.h>
12
+ #include <ruby/io.h>
13
+ #include <ruby/intern.h>
14
+ #include <signal.h>
15
+ #include <sys/time.h>
16
+ #include <pthread.h>
17
+
18
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
19
+
20
+ #include "vm_core.h"
21
+ #include "iseq.h"
22
+
23
+ static inline const rb_data_type_t *
24
+ threadptr_data_type(void)
25
+ {
26
+ static const rb_data_type_t *thread_data_type;
27
+ if (!thread_data_type) {
28
+ VALUE current_thread = rb_thread_current();
29
+ thread_data_type = RTYPEDDATA_TYPE(current_thread);
30
+ }
31
+ return thread_data_type;
32
+ }
33
+
34
+ #define ruby_thread_data_type *threadptr_data_type()
35
+ #define ruby_threadptr_data_type *threadptr_data_type()
36
+
37
+ #define BUF_SIZE 2048
38
+
39
+ typedef struct {
40
+ size_t total_samples;
41
+ size_t caller_samples;
42
+ st_table *edges;
43
+ st_table *lines;
44
+ } frame_data_t;
45
+
46
+ static struct {
47
+ int running;
48
+ int raw;
49
+ int aggregate;
50
+
51
+ VALUE mode;
52
+ VALUE interval;
53
+ VALUE out;
54
+
55
+ VALUE *raw_samples;
56
+ size_t raw_samples_len;
57
+ size_t raw_samples_capa;
58
+ size_t raw_sample_index;
59
+
60
+ size_t overall_signals;
61
+ size_t overall_samples;
62
+ size_t during_gc;
63
+ st_table *frames;
64
+
65
+ st_table *threads;
66
+
67
+ VALUE frames_buffer[BUF_SIZE];
68
+ int lines_buffer[BUF_SIZE];
69
+ } _stackprofx;
70
+
71
+ static VALUE sym_object, sym_wall, sym_cpu, sym_custom, sym_name, sym_file, sym_line, sym_threads;
72
+ static VALUE sym_samples, sym_total_samples, sym_missed_samples, sym_edges, sym_lines;
73
+ static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out, sym_aggregate;
74
+ static VALUE sym_gc_samples, objtracer;
75
+ static VALUE gc_hook;
76
+ static VALUE rb_mStackProfx;
77
+
78
+ static void stackprofx_newobj_handler(VALUE, void*);
79
+ static void stackprofx_signal_handler(int sig, siginfo_t* sinfo, void* ucontext);
80
+
81
+ static VALUE
82
+ stackprofx_start(int argc, VALUE *argv, VALUE self)
83
+ {
84
+ struct sigaction sa;
85
+ struct itimerval timer;
86
+ VALUE opts = Qnil, mode = Qnil, interval = Qnil, out = Qfalse, threads = Qnil;
87
+ int raw = 0, aggregate = 1;
88
+
89
+ if (_stackprofx.running)
90
+ return Qfalse;
91
+
92
+ rb_scan_args(argc, argv, "0:", &opts);
93
+
94
+ if (RTEST(opts)) {
95
+ mode = rb_hash_aref(opts, sym_mode);
96
+ interval = rb_hash_aref(opts, sym_interval);
97
+ out = rb_hash_aref(opts, sym_out);
98
+ threads = rb_hash_aref(opts, sym_threads);
99
+
100
+ if (RTEST(rb_hash_aref(opts, sym_raw)))
101
+ raw = 1;
102
+ if (rb_hash_lookup2(opts, sym_aggregate, Qundef) == Qfalse)
103
+ aggregate = 0;
104
+ }
105
+ if (!RTEST(mode)) mode = sym_wall;
106
+
107
+ if (RTEST(threads))
108
+ {
109
+ _stackprofx.threads = st_init_numtable();
110
+ for (int i = 0; i < RARRAY_LEN(threads); i++)
111
+ {
112
+ VALUE thr = rb_ary_entry(threads, i);
113
+ st_add_direct(_stackprofx.threads, thr, 0);
114
+ rb_gc_mark(thr);
115
+ }
116
+ }
117
+ else
118
+ {
119
+ _stackprofx.threads = 0;
120
+ }
121
+
122
+ if (!_stackprofx.frames) {
123
+ _stackprofx.frames = st_init_numtable();
124
+ _stackprofx.overall_signals = 0;
125
+ _stackprofx.overall_samples = 0;
126
+ _stackprofx.during_gc = 0;
127
+ }
128
+
129
+ if (mode == sym_object) {
130
+ if (!RTEST(interval)) interval = INT2FIX(1);
131
+
132
+ objtracer = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, stackprofx_newobj_handler, 0);
133
+ rb_tracepoint_enable(objtracer);
134
+ } else if (mode == sym_wall || mode == sym_cpu) {
135
+ if (!RTEST(interval)) interval = INT2FIX(1000);
136
+
137
+ sa.sa_sigaction = stackprofx_signal_handler;
138
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
139
+ sigemptyset(&sa.sa_mask);
140
+ sigaction(mode == sym_wall ? SIGALRM : SIGPROF, &sa, NULL);
141
+
142
+ timer.it_interval.tv_sec = 0;
143
+ timer.it_interval.tv_usec = NUM2LONG(interval);
144
+ timer.it_value = timer.it_interval;
145
+ setitimer(mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
146
+ } else if (mode == sym_custom) {
147
+ /* sampled manually */
148
+ interval = Qnil;
149
+ } else {
150
+ rb_raise(rb_eArgError, "unknown profiler mode");
151
+ }
152
+
153
+ _stackprofx.running = 1;
154
+ _stackprofx.raw = raw;
155
+ _stackprofx.aggregate = aggregate;
156
+ _stackprofx.mode = mode;
157
+ _stackprofx.interval = interval;
158
+ _stackprofx.out = out;
159
+
160
+ return Qtrue;
161
+ }
162
+
163
+ static VALUE
164
+ stackprofx_stop(VALUE self)
165
+ {
166
+ struct sigaction sa;
167
+ struct itimerval timer;
168
+
169
+ if (!_stackprofx.running)
170
+ return Qfalse;
171
+ _stackprofx.running = 0;
172
+
173
+ st_free_table(_stackprofx.threads);
174
+
175
+ if (_stackprofx.mode == sym_object) {
176
+ rb_tracepoint_disable(objtracer);
177
+ } else if (_stackprofx.mode == sym_wall || _stackprofx.mode == sym_cpu) {
178
+ memset(&timer, 0, sizeof(timer));
179
+ setitimer(_stackprofx.mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
180
+
181
+ sa.sa_handler = SIG_IGN;
182
+ sa.sa_flags = SA_RESTART;
183
+ sigemptyset(&sa.sa_mask);
184
+ sigaction(_stackprofx.mode == sym_wall ? SIGALRM : SIGPROF, &sa, NULL);
185
+ } else if (_stackprofx.mode == sym_custom) {
186
+ /* sampled manually */
187
+ } else {
188
+ rb_raise(rb_eArgError, "unknown profiler mode");
189
+ }
190
+
191
+ return Qtrue;
192
+ }
193
+
194
+ static int
195
+ frame_edges_i(st_data_t key, st_data_t val, st_data_t arg)
196
+ {
197
+ VALUE edges = (VALUE)arg;
198
+
199
+ intptr_t weight = (intptr_t)val;
200
+ rb_hash_aset(edges, rb_obj_id((VALUE)key), INT2FIX(weight));
201
+ return ST_CONTINUE;
202
+ }
203
+
204
+ static int
205
+ frame_lines_i(st_data_t key, st_data_t val, st_data_t arg)
206
+ {
207
+ VALUE lines = (VALUE)arg;
208
+
209
+ size_t weight = (size_t)val;
210
+ size_t total = weight & (~(size_t)0 << (8*SIZEOF_SIZE_T/2));
211
+ weight -= total;
212
+ total = total >> (8*SIZEOF_SIZE_T/2);
213
+ rb_hash_aset(lines, INT2FIX(key), rb_ary_new3(2, ULONG2NUM(total), ULONG2NUM(weight)));
214
+ return ST_CONTINUE;
215
+ }
216
+
217
+ static int
218
+ frame_i(st_data_t key, st_data_t val, st_data_t arg)
219
+ {
220
+ VALUE frame = (VALUE)key;
221
+ frame_data_t *frame_data = (frame_data_t *)val;
222
+ VALUE results = (VALUE)arg;
223
+ VALUE details = rb_hash_new();
224
+ VALUE name, file, edges, lines;
225
+ VALUE line;
226
+
227
+ rb_hash_aset(results, rb_obj_id(frame), details);
228
+
229
+ name = rb_profile_frame_full_label(frame);
230
+ rb_hash_aset(details, sym_name, name);
231
+
232
+ file = rb_profile_frame_absolute_path(frame);
233
+ if (NIL_P(file))
234
+ file = rb_profile_frame_path(frame);
235
+ rb_hash_aset(details, sym_file, file);
236
+
237
+ if ((line = rb_profile_frame_first_lineno(frame)) != INT2FIX(0))
238
+ rb_hash_aset(details, sym_line, line);
239
+
240
+ rb_hash_aset(details, sym_total_samples, SIZET2NUM(frame_data->total_samples));
241
+ rb_hash_aset(details, sym_samples, SIZET2NUM(frame_data->caller_samples));
242
+
243
+ if (frame_data->edges) {
244
+ edges = rb_hash_new();
245
+ rb_hash_aset(details, sym_edges, edges);
246
+ st_foreach(frame_data->edges, frame_edges_i, (st_data_t)edges);
247
+ st_free_table(frame_data->edges);
248
+ frame_data->edges = NULL;
249
+ }
250
+
251
+ if (frame_data->lines) {
252
+ lines = rb_hash_new();
253
+ rb_hash_aset(details, sym_lines, lines);
254
+ st_foreach(frame_data->lines, frame_lines_i, (st_data_t)lines);
255
+ st_free_table(frame_data->lines);
256
+ frame_data->lines = NULL;
257
+ }
258
+
259
+ xfree(frame_data);
260
+ return ST_DELETE;
261
+ }
262
+
263
+ static VALUE
264
+ stackprofx_results(int argc, VALUE *argv, VALUE self)
265
+ {
266
+ VALUE results, frames;
267
+
268
+ if (!_stackprofx.frames || _stackprofx.running)
269
+ return Qnil;
270
+
271
+ results = rb_hash_new();
272
+ rb_hash_aset(results, sym_version, DBL2NUM(1.1));
273
+ rb_hash_aset(results, sym_mode, _stackprofx.mode);
274
+ rb_hash_aset(results, sym_interval, _stackprofx.interval);
275
+ rb_hash_aset(results, sym_samples, SIZET2NUM(_stackprofx.overall_samples));
276
+ rb_hash_aset(results, sym_gc_samples, SIZET2NUM(_stackprofx.during_gc));
277
+ rb_hash_aset(results, sym_missed_samples, SIZET2NUM(_stackprofx.overall_signals - _stackprofx.overall_samples));
278
+
279
+ frames = rb_hash_new();
280
+ rb_hash_aset(results, sym_frames, frames);
281
+ st_foreach(_stackprofx.frames, frame_i, (st_data_t)frames);
282
+
283
+ st_free_table(_stackprofx.frames);
284
+ _stackprofx.frames = NULL;
285
+
286
+ if (_stackprofx.raw && _stackprofx.raw_samples_len) {
287
+ size_t len, n, o;
288
+ VALUE raw_samples = rb_ary_new_capa(_stackprofx.raw_samples_len);
289
+
290
+ for (n = 0; n < _stackprofx.raw_samples_len; n++) {
291
+ len = (size_t)_stackprofx.raw_samples[n];
292
+ rb_ary_push(raw_samples, SIZET2NUM(len));
293
+
294
+ for (o = 0, n++; o < len; n++, o++)
295
+ rb_ary_push(raw_samples, rb_obj_id(_stackprofx.raw_samples[n]));
296
+ rb_ary_push(raw_samples, SIZET2NUM((size_t)_stackprofx.raw_samples[n]));
297
+ }
298
+
299
+ free(_stackprofx.raw_samples);
300
+ _stackprofx.raw_samples = NULL;
301
+ _stackprofx.raw_samples_len = 0;
302
+ _stackprofx.raw_samples_capa = 0;
303
+ _stackprofx.raw_sample_index = 0;
304
+ _stackprofx.raw = 0;
305
+
306
+ rb_hash_aset(results, sym_raw, raw_samples);
307
+ }
308
+
309
+ if (argc == 1)
310
+ _stackprofx.out = argv[0];
311
+
312
+ if (RTEST(_stackprofx.out)) {
313
+ VALUE file;
314
+ if (RB_TYPE_P(_stackprofx.out, T_STRING)) {
315
+ file = rb_file_open_str(_stackprofx.out, "w");
316
+ } else {
317
+ file = rb_io_check_io(_stackprofx.out);
318
+ }
319
+ rb_marshal_dump(results, file);
320
+ rb_io_flush(file);
321
+ _stackprofx.out = Qnil;
322
+ return file;
323
+ } else {
324
+ return results;
325
+ }
326
+ }
327
+
328
+ static VALUE
329
+ stackprofx_run(int argc, VALUE *argv, VALUE self)
330
+ {
331
+ rb_need_block();
332
+ stackprofx_start(argc, argv, self);
333
+ rb_ensure(rb_yield, Qundef, stackprofx_stop, self);
334
+ return stackprofx_results(0, 0, self);
335
+ }
336
+
337
+ static VALUE
338
+ stackprofx_running_p(VALUE self)
339
+ {
340
+ return _stackprofx.running ? Qtrue : Qfalse;
341
+ }
342
+
343
+ static inline frame_data_t *
344
+ sample_for(VALUE frame)
345
+ {
346
+ st_data_t key = (st_data_t)frame, val = 0;
347
+ frame_data_t *frame_data;
348
+
349
+ if (st_lookup(_stackprofx.frames, key, &val)) {
350
+ frame_data = (frame_data_t *)val;
351
+ } else {
352
+ frame_data = ALLOC_N(frame_data_t, 1);
353
+ MEMZERO(frame_data, frame_data_t, 1);
354
+ val = (st_data_t)frame_data;
355
+ st_insert(_stackprofx.frames, key, val);
356
+ }
357
+
358
+ return frame_data;
359
+ }
360
+
361
+ static int
362
+ numtable_increment_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
363
+ {
364
+ size_t *weight = (size_t *)value;
365
+ size_t increment = (size_t)arg;
366
+
367
+ if (existing)
368
+ (*weight) += increment;
369
+ else
370
+ *weight = increment;
371
+
372
+ return ST_CONTINUE;
373
+ }
374
+
375
+ void
376
+ st_numtable_increment(st_table *table, st_data_t key, size_t increment)
377
+ {
378
+ st_update(table, key, numtable_increment_callback, (st_data_t)increment);
379
+ }
380
+
381
+ // thanks to https://bugs.ruby-lang.org/issues/10602
382
+ int
383
+ rb_profile_frames_thread(int start, int limit, VALUE *buff, int *lines, rb_thread_t *th)
384
+ {
385
+ int i;
386
+ rb_control_frame_t *cfp = th->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
387
+
388
+ for (i=0; i<limit && cfp != end_cfp;) {
389
+ if (cfp->iseq && cfp->pc) { /* should be NORMAL_ISEQ */
390
+ if (start > 0) {
391
+ start--;
392
+ continue;
393
+ }
394
+
395
+ /* record frame info */
396
+ buff[i] = cfp->iseq->self;
397
+ if (lines) lines[i] = rb_iseq_line_no(cfp->iseq, cfp->pc - cfp->iseq->iseq_encoded);
398
+ i++;
399
+ }
400
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
401
+ }
402
+
403
+ return i;
404
+ }
405
+
406
+ int
407
+ stackprofx_record_sample_i(st_data_t key, st_data_t val, st_data_t arg)
408
+ {
409
+ int num, i, n;
410
+ VALUE prev_frame = Qnil;
411
+
412
+ rb_thread_t *th;
413
+ GetThreadPtr((VALUE)key, th);
414
+ if (th->status == THREAD_KILLED) return ST_CONTINUE;
415
+
416
+ num = rb_profile_frames_thread(0, sizeof(_stackprofx.frames_buffer) / sizeof(VALUE), _stackprofx.frames_buffer, _stackprofx.lines_buffer, th);
417
+
418
+ if (_stackprofx.raw) {
419
+ int found = 0;
420
+
421
+ if (!_stackprofx.raw_samples) {
422
+ _stackprofx.raw_samples_capa = num * 100;
423
+ _stackprofx.raw_samples = malloc(sizeof(VALUE) * _stackprofx.raw_samples_capa);
424
+ }
425
+
426
+ if (_stackprofx.raw_samples_capa <= _stackprofx.raw_samples_len + num) {
427
+ _stackprofx.raw_samples_capa *= 2;
428
+ _stackprofx.raw_samples = realloc(_stackprofx.raw_samples, sizeof(VALUE) * _stackprofx.raw_samples_capa);
429
+ }
430
+
431
+ if (_stackprofx.raw_samples_len > 0 && _stackprofx.raw_samples[_stackprofx.raw_sample_index] == (VALUE)num) {
432
+ for (i = num-1, n = 0; i >= 0; i--, n++) {
433
+ VALUE frame = _stackprofx.frames_buffer[i];
434
+ if (_stackprofx.raw_samples[_stackprofx.raw_sample_index + 1 + n] != frame)
435
+ break;
436
+ }
437
+ if (i == -1) {
438
+ _stackprofx.raw_samples[_stackprofx.raw_samples_len-1] += 1;
439
+ found = 1;
440
+ }
441
+ }
442
+
443
+ if (!found) {
444
+ _stackprofx.raw_sample_index = _stackprofx.raw_samples_len;
445
+ _stackprofx.raw_samples[_stackprofx.raw_samples_len++] = (VALUE)num;
446
+ for (i = num-1; i >= 0; i--) {
447
+ VALUE frame = _stackprofx.frames_buffer[i];
448
+ _stackprofx.raw_samples[_stackprofx.raw_samples_len++] = frame;
449
+ }
450
+ _stackprofx.raw_samples[_stackprofx.raw_samples_len++] = (VALUE)1;
451
+ }
452
+ }
453
+
454
+ for (i = 0; i < num; i++) {
455
+ int line = _stackprofx.lines_buffer[i];
456
+ VALUE frame = _stackprofx.frames_buffer[i];
457
+ frame_data_t *frame_data = sample_for(frame);
458
+
459
+ frame_data->total_samples++;
460
+
461
+ if (i == 0) {
462
+ frame_data->caller_samples++;
463
+ } else if (_stackprofx.aggregate) {
464
+ if (!frame_data->edges)
465
+ frame_data->edges = st_init_numtable();
466
+ st_numtable_increment(frame_data->edges, (st_data_t)prev_frame, 1);
467
+ }
468
+
469
+ if (_stackprofx.aggregate && line > 0) {
470
+ if (!frame_data->lines)
471
+ frame_data->lines = st_init_numtable();
472
+ size_t half = (size_t)1<<(8*SIZEOF_SIZE_T/2);
473
+ size_t increment = i == 0 ? half + 1 : half;
474
+ st_numtable_increment(frame_data->lines, (st_data_t)line, increment);
475
+ }
476
+
477
+ prev_frame = frame;
478
+ }
479
+
480
+ return ST_CONTINUE;
481
+ }
482
+
483
+ void
484
+ stackprofx_record_sample()
485
+ {
486
+ _stackprofx.overall_samples++;
487
+ st_table *tbl = _stackprofx.threads ?: GET_THREAD()->vm->living_threads;
488
+ st_foreach(tbl, stackprofx_record_sample_i, 0);
489
+ }
490
+
491
+ static void
492
+ stackprofx_job_handler(void *data)
493
+ {
494
+ static int in_signal_handler = 0;
495
+ if (in_signal_handler) return;
496
+ if (!_stackprofx.running) return;
497
+
498
+ in_signal_handler++;
499
+ stackprofx_record_sample();
500
+ in_signal_handler--;
501
+ }
502
+
503
+ static void
504
+ stackprofx_signal_handler(int sig, siginfo_t *sinfo, void *ucontext)
505
+ {
506
+ _stackprofx.overall_signals++;
507
+ if (rb_during_gc())
508
+ _stackprofx.during_gc++, _stackprofx.overall_samples++;
509
+ else
510
+ rb_postponed_job_register_one(0, stackprofx_job_handler, 0);
511
+ }
512
+
513
+ static void
514
+ stackprofx_newobj_handler(VALUE tpval, void *data)
515
+ {
516
+ _stackprofx.overall_signals++;
517
+ if (RTEST(_stackprofx.interval) && _stackprofx.overall_signals % NUM2LONG(_stackprofx.interval))
518
+ return;
519
+ stackprofx_job_handler(0);
520
+ }
521
+
522
+ static VALUE
523
+ stackprofx_sample(VALUE self)
524
+ {
525
+ if (!_stackprofx.running)
526
+ return Qfalse;
527
+
528
+ _stackprofx.overall_signals++;
529
+ stackprofx_job_handler(0);
530
+ return Qtrue;
531
+ }
532
+
533
+ static int
534
+ frame_mark_i(st_data_t key, st_data_t val, st_data_t arg)
535
+ {
536
+ VALUE frame = (VALUE)key;
537
+ rb_gc_mark(frame);
538
+ return ST_CONTINUE;
539
+ }
540
+
541
+ static void
542
+ stackprofx_gc_mark(void *data)
543
+ {
544
+ if (RTEST(_stackprofx.out))
545
+ rb_gc_mark(_stackprofx.out);
546
+
547
+ if (_stackprofx.frames)
548
+ st_foreach(_stackprofx.frames, frame_mark_i, 0);
549
+ }
550
+
551
+ static void
552
+ stackprofx_atfork_prepare(void)
553
+ {
554
+ struct itimerval timer;
555
+ if (_stackprofx.running) {
556
+ if (_stackprofx.mode == sym_wall || _stackprofx.mode == sym_cpu) {
557
+ memset(&timer, 0, sizeof(timer));
558
+ setitimer(_stackprofx.mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
559
+ }
560
+ }
561
+ }
562
+
563
+ static void
564
+ stackprofx_atfork_parent(void)
565
+ {
566
+ struct itimerval timer;
567
+ if (_stackprofx.running) {
568
+ if (_stackprofx.mode == sym_wall || _stackprofx.mode == sym_cpu) {
569
+ timer.it_interval.tv_sec = 0;
570
+ timer.it_interval.tv_usec = NUM2LONG(_stackprofx.interval);
571
+ timer.it_value = timer.it_interval;
572
+ setitimer(_stackprofx.mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
573
+ }
574
+ }
575
+ }
576
+
577
+ static void
578
+ stackprofx_atfork_child(void)
579
+ {
580
+ stackprofx_stop(rb_mStackProfx);
581
+ }
582
+
583
+ void
584
+ Init_stackprofx(void)
585
+ {
586
+ #define S(name) sym_##name = ID2SYM(rb_intern(#name));
587
+ S(object);
588
+ S(custom);
589
+ S(wall);
590
+ S(threads);
591
+ S(cpu);
592
+ S(name);
593
+ S(file);
594
+ S(line);
595
+ S(total_samples);
596
+ S(gc_samples);
597
+ S(missed_samples);
598
+ S(samples);
599
+ S(edges);
600
+ S(lines);
601
+ S(version);
602
+ S(mode);
603
+ S(interval);
604
+ S(raw);
605
+ S(out);
606
+ S(frames);
607
+ S(aggregate);
608
+ #undef S
609
+
610
+ gc_hook = Data_Wrap_Struct(rb_cObject, stackprofx_gc_mark, NULL, NULL);
611
+ rb_global_variable(&gc_hook);
612
+
613
+ rb_mStackProfx = rb_define_module("StackProfx");
614
+ rb_define_singleton_method(rb_mStackProfx, "running?", stackprofx_running_p, 0);
615
+ rb_define_singleton_method(rb_mStackProfx, "run", stackprofx_run, -1);
616
+ rb_define_singleton_method(rb_mStackProfx, "start", stackprofx_start, -1);
617
+ rb_define_singleton_method(rb_mStackProfx, "stop", stackprofx_stop, 0);
618
+ rb_define_singleton_method(rb_mStackProfx, "results", stackprofx_results, -1);
619
+ rb_define_singleton_method(rb_mStackProfx, "sample", stackprofx_sample, 0);
620
+
621
+ pthread_atfork(stackprofx_atfork_prepare, stackprofx_atfork_parent, stackprofx_atfork_child);
622
+ }