Ruby 4.1.0dev (2026-08-28 revision 5eb9a6925b805a17dced976d5b741afe5086aab1)
vm_trace.c (5eb9a6925b805a17dced976d5b741afe5086aab1)
1/**********************************************************************
2
3 vm_trace.c -
4
5 $Author: ko1 $
6 created at: Tue Aug 14 19:37:09 2012
7
8 Copyright (C) 1993-2012 Yukihiro Matsumoto
9
10**********************************************************************/
11
12/*
13 * This file include two parts:
14 *
15 * (1) set_trace_func internal mechanisms
16 * and C level API
17 *
18 * (2) Ruby level API
19 * (2-1) set_trace_func API
20 * (2-2) TracePoint API (not yet)
21 *
22 */
23
24#include "eval_intern.h"
25#include "internal.h"
26#include "internal/bits.h"
27#include "internal/class.h"
28#include "internal/gc.h"
29#include "internal/hash.h"
30#include "internal/symbol.h"
31#include "internal/thread.h"
32#include "iseq.h"
33#include "ruby/atomic.h"
34#include "ruby/debug.h"
35#include "vm_core.h"
36#include "ruby/ractor.h"
37#include "ractor_core.h"
38#include "yjit.h"
39#include "zjit.h"
40
41#include "builtin.h"
42
43static VALUE sym_default;
44
45/* (1) trace mechanisms */
46
47typedef struct rb_event_hook_struct {
48 rb_event_hook_flag_t hook_flags;
49 rb_event_flag_t events;
51 VALUE data;
52 struct rb_event_hook_struct *next;
53
54 struct {
55 rb_thread_t *th;
56 unsigned int target_line;
57 } filter;
59
60typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg);
61
62#define MAX_EVENT_NUM 32
63
64void
65rb_hook_list_mark(rb_hook_list_t *hooks)
66{
67 rb_event_hook_t *hook = hooks->hooks;
68
69 while (hook) {
70 rb_gc_mark(hook->data);
71 hook = hook->next;
72 }
73}
74
75void
76rb_hook_list_mark_and_move(rb_hook_list_t *hooks)
77{
78 if (!rb_gc_checking_shareable()) {
79 // hooks can be unshareable
80
81 rb_event_hook_t *hook = hooks->hooks;
82
83 while (hook) {
84 rb_gc_mark_and_move(&hook->data);
85 hook = hook->next;
86 }
87 }
88}
89
90static void clean_hooks(rb_hook_list_t *list);
91
92void
93rb_hook_list_free(rb_hook_list_t *hooks)
94{
95 hooks->need_clean = true;
96
97 if (hooks->running == 0) {
98 clean_hooks(hooks);
99 }
100}
101
102/* ruby_vm_event_flags management */
103
104void rb_clear_attr_ccs(void);
105void rb_clear_bf_ccs(void);
106
107static bool iseq_trace_set_all_needed(rb_event_flag_t new_events)
108{
109 rb_event_flag_t new_iseq_events = new_events & ISEQ_TRACE_EVENTS;
110 rb_event_flag_t enabled_iseq_events = ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS;
111 return new_iseq_events & ~enabled_iseq_events;
112
113}
114
115static bool clear_attr_ccs_needed(rb_event_flag_t prev_events, rb_event_flag_t new_events)
116{
117 bool enable_c_call = (prev_events & RUBY_EVENT_C_CALL) == 0 && (new_events & RUBY_EVENT_C_CALL);
118 bool enable_c_return = (prev_events & RUBY_EVENT_C_RETURN) == 0 && (new_events & RUBY_EVENT_C_RETURN);
119 return enable_c_call || enable_c_return;
120}
121
122/* If the events are internal events (e.g. gc hooks), it updates them globally for all ractors. Otherwise
123 * they are ractor local. You cannot listen to internal events through set_trace_func or TracePoint.
124 * Some ractor-local tracepoint events cause global level iseq changes, so are still called `global events`.
125 */
126static void
127update_global_event_hooks(rb_hook_list_t *list, rb_event_flag_t prev_events, rb_event_flag_t new_events, int change_iseq_events, int change_c_events)
128{
129 rb_execution_context_t *ec = rb_current_execution_context(false);
130 unsigned int lev;
131
132 // Lock only with a current Ractor. ec is NULL in MMTk's hook-list free; a global
133 // GC's sweep frees dead Ractors' hook lists with GET_RACTOR() == NULL (locking =
134 // NULL deref, and the barrier already excludes); in VM destruct's free-at-exit walk
135 // the thread structs are freed first (GET_RACTOR() = UAF) and it is single-threaded.
136 const bool vm_locked_here = ec && !ruby_vm_during_cleanup && GET_RACTOR() != NULL;
137 if (vm_locked_here) {
138 RB_VM_LOCK_ENTER_LEV(&lev);
139 rb_vm_barrier();
140 }
141
142 rb_event_flag_t new_iseq_events = new_events & ISEQ_TRACE_EVENTS;
143 rb_event_flag_t enabled_iseq_events = ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS;
144 bool new_iseq_events_p = iseq_trace_set_all_needed(new_events);
145 bool enable_call = (prev_events & RUBY_EVENT_CALL) == 0 && (new_events & RUBY_EVENT_CALL);
146 bool enable_return = (prev_events & RUBY_EVENT_RETURN) == 0 && (new_events & RUBY_EVENT_RETURN);
147 bool clear_attr_ccs_p = clear_attr_ccs_needed(prev_events, new_events);
148
149 // FIXME: `ruby_vm_event_flags` should have the global list of event flags for internal events as well
150 // as for all ractors. That's not how it works right now, so we shouldn't rely on it apart from the
151 // internal events. Since it doesn't work like this, we have to track more state with `ruby_vm_iseq_events_enabled`,
152 // `ruby_vm_c_events_enabled`, etc.
153 rb_event_flag_t prev_events_global = ruby_vm_event_flags;
154 rb_event_flag_t new_events_global = (prev_events_global & ~prev_events) | new_events;
155 ruby_vm_event_flags = new_events_global;
156
157 // Modify ISEQs or CCs to enable tracing
158 if (new_iseq_events_p) {
159 // write all ISeqs only when new events are added for the first time
160 rb_iseq_trace_set_all(new_iseq_events | enabled_iseq_events);
161 }
162 else if (clear_attr_ccs_p) { // turn on C_CALL or C_RETURN ractor locally
163 rb_clear_attr_ccs();
164 }
165 else if (enable_call || enable_return) { // turn on CALL or RETURN ractor locally
166 rb_clear_bf_ccs();
167 }
168
169 if (change_iseq_events < 0) {
170 RUBY_ASSERT(ruby_vm_iseq_events_enabled >= (unsigned int)(-change_iseq_events));
171 }
172 ruby_vm_iseq_events_enabled += change_iseq_events;
173 if (change_c_events < 0) {
174 RUBY_ASSERT(ruby_vm_c_events_enabled >= (unsigned int)(-change_c_events));
175 }
176 ruby_vm_c_events_enabled += change_c_events;
177
178 ruby_vm_event_enabled_global_flags |= new_events; // NOTE: this is only ever added to
179 if (new_events_global & RUBY_INTERNAL_EVENT_MASK) {
180 rb_objspace_set_event_hook(new_events_global);
181 }
182
183 // ZJIT's inline allocation fast path bypasses rb_newobj, so it can't fire the
184 // NEWOBJ internal event. Enabling such a hook invalidates the fast path code so
185 // allocation falls back to the interpreter, which fires the event.
186 if ((new_events_global & RUBY_INTERNAL_EVENT_NEWOBJ) && !(prev_events_global & RUBY_INTERNAL_EVENT_NEWOBJ)) {
187 rb_zjit_invalidate_newobj_hook();
188 }
189
190 // Invalidate JIT code as needed
191 if (new_iseq_events_p || clear_attr_ccs_p) {
192 // Invalidate all code when ISEQs are modified to use trace_* insns above.
193 // Also invalidate when enabling c_call or c_return because generated code
194 // never fires these events.
195 // Internal events fire inside C routines so don't need special handling.
196 rb_yjit_tracing_invalidate_all();
197 rb_zjit_tracing_invalidate_all();
198 }
199
200 if (vm_locked_here) {
201 RB_VM_LOCK_LEAVE_LEV(&lev);
202 }
203}
204
205/* add/remove hooks */
206
207static rb_event_hook_t *
208alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
209{
210 rb_event_hook_t *hook;
211
212 if ((events & RUBY_INTERNAL_EVENT_MASK) && (events & ~RUBY_INTERNAL_EVENT_MASK)) {
213 rb_raise(rb_eTypeError, "Can not specify normal event and internal event simultaneously.");
214 }
215
216 hook = ALLOC(rb_event_hook_t);
217 hook->hook_flags = hook_flags;
218 hook->events = events;
219 hook->func = func;
220 hook->data = data;
221
222 /* no filters */
223 hook->filter.th = NULL;
224 hook->filter.target_line = 0;
225
226 return hook;
227}
228
229// Connect a hook onto a ractor, an iseq or a method definition's hook list
230static void
231hook_list_connect(rb_hook_list_t *list, rb_event_hook_t *hook, int global_p)
232{
233 rb_event_flag_t prev_events = list->events;
234 int change_iseq_events = 0;
235 int change_c_events = 0;
236 hook->next = list->hooks;
237 list->hooks = hook;
238 list->events |= hook->events;
239
240 if (global_p) {
241 if (hook->events & ISEQ_TRACE_EVENTS) {
242 change_iseq_events++;
243 }
244 if ((hook->events & RUBY_EVENT_C_CALL) || (hook->events & RUBY_EVENT_C_RETURN)) {
245 change_c_events++;
246 }
247 update_global_event_hooks(list, prev_events, list->events, change_iseq_events, change_c_events);
248 }
249}
250
251static void
252connect_non_targeted_event_hook(const rb_execution_context_t *ec, rb_event_hook_t *hook)
253{
254 rb_hook_list_t *list;
255
256 /* internal events are VM-global, non-internal are ractor-local */
257 VM_ASSERT(!(hook->events & RUBY_INTERNAL_EVENT_OBJSPACE_MASK) || !(hook->events & ~RUBY_INTERNAL_EVENT_OBJSPACE_MASK));
258
259 if (hook->events & RUBY_INTERNAL_EVENT_OBJSPACE_MASK) {
260 list = rb_vm_global_hooks(ec);
261 }
262 else {
263 list = rb_ec_ractor_hooks(ec);
264 }
265 hook_list_connect(list, hook, TRUE);
266}
267
268static void
269rb_threadptr_add_event_hook(const rb_execution_context_t *ec, rb_thread_t *th,
270 rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
271{
272 rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
273 hook->filter.th = th;
274 connect_non_targeted_event_hook(ec, hook);
275}
276
277void
279{
280 rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
281}
282
283void
285{
286 rb_add_event_hook2(func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
287}
288
289void
290rb_thread_add_event_hook2(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
291{
292 rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, hook_flags);
293}
294
295void
296rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
297{
298 rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
299 connect_non_targeted_event_hook(GET_EC(), hook);
300}
301
302static bool
303hook_list_targeted_p(rb_hook_list_t *list)
304{
305 switch (list->type) {
306 case hook_list_type_targeted_iseq:
307 case hook_list_type_targeted_def:
308 return true;
309 default:
310 return false;
311 }
312}
313
314unsigned int
315rb_hook_list_count(rb_hook_list_t *list)
316{
317 rb_event_hook_t *hook = list->hooks;
318 unsigned int count = 0;
319
320 while (hook) {
321 if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED)) {
322 count++;
323 }
324 hook = hook->next;
325 }
326
327 return count;
328}
329
330static void
331clean_hooks(rb_hook_list_t *list)
332{
333 rb_event_hook_t *hook, **nextp = &list->hooks;
334 rb_event_flag_t prev_events = list->events;
335 int change_iseq_events = 0;
336 int change_c_events = 0;
337
338 VM_ASSERT(list->running == 0);
339 VM_ASSERT(list->need_clean == true);
340
341 list->events = 0;
342 list->need_clean = false;
343
344 while ((hook = *nextp) != 0) {
345 if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
346 *nextp = hook->next;
347 if (!hook_list_targeted_p(list)) {
348 if (hook->events & ISEQ_TRACE_EVENTS) {
349 change_iseq_events--;
350 }
351 if ((hook->events & RUBY_EVENT_C_CALL) || (hook->events & RUBY_EVENT_C_RETURN)) {
352 change_c_events--;
353 }
354 }
355 SIZED_FREE(hook);
356 }
357 else {
358 list->events |= hook->events; /* update active events */
359 nextp = &hook->next;
360 }
361 }
362
363 if (hook_list_targeted_p(list)) {
364 if (list->events == 0) {
365 SIZED_FREE(list);
366 }
367 }
368 else {
369 update_global_event_hooks(list, prev_events, list->events, change_iseq_events, change_c_events);
370 }
371}
372
373static void
374clean_hooks_check(rb_hook_list_t *list)
375{
376 if (UNLIKELY(list->need_clean)) {
377 if (list->running == 0) {
378 clean_hooks(list);
379 }
380 }
381}
382
383#define MATCH_ANY_FILTER_TH ((rb_thread_t *)1)
384
385static int
386remove_event_hook_from_list(rb_hook_list_t *list, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
387{
388 int ret = 0;
389 rb_event_hook_t *hook = list->hooks;
390
391 while (hook) {
392 if (func == 0 || hook->func == func) {
393 if (hook->filter.th == filter_th || filter_th == MATCH_ANY_FILTER_TH) {
394 if (UNDEF_P(data) || hook->data == data) {
395 hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
396 list->need_clean = true;
397 ret+=1;
398 }
399 }
400 }
401 hook = hook->next;
402 }
403
404 clean_hooks_check(list);
405 return ret;
406}
407
408/* if func is 0, then clear all funcs */
409static int
410remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
411{
412 int ret = 0;
413
414 ret += remove_event_hook_from_list(rb_ec_ractor_hooks(ec), filter_th, func, data);
415 ret += remove_event_hook_from_list(rb_vm_global_hooks(ec), filter_th, func, data);
416
417 return ret;
418}
419
420static int
421rb_threadptr_remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
422{
423 return remove_event_hook(ec, filter_th, func, data);
424}
425
426int
428{
429 return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, Qundef);
430}
431
432int
434{
435 return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, data);
436}
437
438int
440{
441 return remove_event_hook(GET_EC(), NULL, func, Qundef);
442}
443
444int
446{
447 return remove_event_hook(GET_EC(), NULL, func, data);
448}
449
450void
451rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec)
452{
453 rb_threadptr_remove_event_hook(ec, rb_ec_thread_ptr(ec), 0, Qundef);
454}
455
456void
457rb_ec_clear_all_trace_func(const rb_execution_context_t *ec)
458{
459 rb_threadptr_remove_event_hook(ec, MATCH_ANY_FILTER_TH, 0, Qundef);
460}
461
462/* invoke hooks */
463
464static void
465exec_hooks_body(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
466{
467 rb_event_hook_t *hook;
468
469 for (hook = list->hooks; hook; hook = hook->next) {
470 if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) &&
471 (trace_arg->event & hook->events) &&
472 (LIKELY(hook->filter.th == 0) || hook->filter.th == rb_ec_thread_ptr(ec)) &&
473 (LIKELY(hook->filter.target_line == 0) || (hook->filter.target_line == (unsigned int)rb_vm_get_sourceline(ec->cfp)))) {
474 if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) {
475 (*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass);
476 }
477 else {
478 (*((rb_event_hook_raw_arg_func_t)hook->func))(hook->data, trace_arg);
479 }
480 }
481 }
482}
483
484static int
485exec_hooks_precheck(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
486{
487 if (list->events & trace_arg->event) {
488 list->running++;
489 return TRUE;
490 }
491 else {
492 return FALSE;
493 }
494}
495
496static void
497exec_hooks_postcheck(const rb_execution_context_t *ec, rb_hook_list_t *list)
498{
499 list->running--;
500 clean_hooks_check(list);
501}
502
503static void
504exec_hooks_unprotected(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
505{
506 if (exec_hooks_precheck(ec, list, trace_arg) == 0) return;
507 exec_hooks_body(ec, list, trace_arg);
508 exec_hooks_postcheck(ec, list);
509}
510
511static int
512exec_hooks_protected(rb_execution_context_t *ec_arg, rb_hook_list_t *list_arg, const rb_trace_arg_t *trace_arg)
513{
514 enum ruby_tag_type state;
515 volatile int raised;
516 rb_execution_context_t * volatile ec = ec_arg;
517 rb_hook_list_t * volatile list = list_arg;
518
519 if (exec_hooks_precheck(ec, list, trace_arg) == 0) return 0;
520
521 raised = rb_ec_reset_raised(ec);
522
523 /* TODO: Support !RUBY_EVENT_HOOK_FLAG_SAFE hooks */
524
525 EC_PUSH_TAG(ec);
526 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
527 exec_hooks_body(ec, list, trace_arg);
528 }
529 EC_POP_TAG();
530
531 exec_hooks_postcheck(ec, list);
532
533 if (raised) {
534 rb_ec_set_raised(ec);
535 }
536
537 return state;
538}
539
540// pop_p: Whether to pop the frame for the TracePoint when it throws.
541void
542rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p)
543{
544 rb_execution_context_t *ec = trace_arg->ec;
545
546 if (UNLIKELY(trace_arg->event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK)) {
547 VM_ASSERT(hooks == rb_vm_global_hooks(ec));
548
549 if (ec->trace_arg && (ec->trace_arg->event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK)) {
550 /* skip hooks because this thread doing INTERNAL_EVENT */
551 }
552 else {
553 rb_trace_arg_t *prev_trace_arg = ec->trace_arg;
554
555 ec->trace_arg = trace_arg;
556 /* only global hooks */
557 exec_hooks_unprotected(ec, hooks, trace_arg);
558 ec->trace_arg = prev_trace_arg;
559 }
560 }
561 else {
562 if (ec->trace_arg == NULL && /* check reentrant */
563 trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) {
564 const VALUE errinfo = ec->errinfo;
565 const VALUE old_recursive = ec->local_storage_recursive_hash;
566 enum ruby_tag_type state = 0;
567
568 /* setup */
569 ec->local_storage_recursive_hash = ec->local_storage_recursive_hash_for_trace;
570 ec->errinfo = Qnil;
571 ec->trace_arg = trace_arg;
572
573 /* kick hooks */
574 if ((state = exec_hooks_protected(ec, hooks, trace_arg)) == TAG_NONE) {
575 ec->errinfo = errinfo;
576 }
577
578 /* cleanup */
579 ec->trace_arg = NULL;
580 ec->local_storage_recursive_hash_for_trace = ec->local_storage_recursive_hash;
581 ec->local_storage_recursive_hash = old_recursive;
582
583 if (state) {
584 if (pop_p) {
585 if (VM_FRAME_FINISHED_P(ec->cfp)) {
586 rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
587 ec->tag = ec->tag->prev;
588 }
589 rb_vm_pop_frame(ec);
590 }
591 EC_JUMP_TAG(ec, state);
592 }
593 }
594 }
595}
596
597VALUE
598rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg)
599{
600 volatile int raised;
601 volatile VALUE result = Qnil;
602 rb_execution_context_t *const ec = GET_EC();
603 rb_vm_t *const vm = rb_ec_vm_ptr(ec);
604 enum ruby_tag_type state;
605 rb_trace_arg_t dummy_trace_arg;
606 dummy_trace_arg.event = 0;
607
608 if (!ec->trace_arg) {
609 ec->trace_arg = &dummy_trace_arg;
610 }
611
612 raised = rb_ec_reset_raised(ec);
613
614 EC_PUSH_TAG(ec);
615 if (LIKELY((state = EC_EXEC_TAG()) == TAG_NONE)) {
616 result = (*func)(arg);
617 }
618 else {
619 (void)*&vm; /* suppress "clobbered" warning */
620 }
621 EC_POP_TAG();
622
623 if (raised) {
624 rb_ec_reset_raised(ec);
625 }
626
627 if (ec->trace_arg == &dummy_trace_arg) {
628 ec->trace_arg = NULL;
629 }
630
631 if (state) {
632#if defined RUBY_USE_SETJMPEX && RUBY_USE_SETJMPEX
633 RB_GC_GUARD(result);
634#endif
635 EC_JUMP_TAG(ec, state);
636 }
637
638 return result;
639}
640
641static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
642
643/* (2-1) set_trace_func (old API) */
644
645/*
646 * call-seq:
647 * set_trace_func(proc) -> proc
648 * set_trace_func(nil) -> nil
649 *
650 * Establishes _proc_ as the handler for tracing, or disables
651 * tracing if the parameter is +nil+.
652 *
653 * *Note:* this method is obsolete, please use TracePoint instead.
654 *
655 * _proc_ takes up to six parameters:
656 *
657 * * an event name string
658 * * a filename string
659 * * a line number
660 * * a method name symbol, or nil
661 * * a binding, or nil
662 * * the class, module, or nil
663 *
664 * _proc_ is invoked whenever an event occurs.
665 *
666 * Events are:
667 *
668 * <code>"c-call"</code>:: call a C-language routine
669 * <code>"c-return"</code>:: return from a C-language routine
670 * <code>"call"</code>:: call a Ruby method
671 * <code>"class"</code>:: start a class or module definition
672 * <code>"end"</code>:: finish a class or module definition
673 * <code>"line"</code>:: execute code on a new line
674 * <code>"raise"</code>:: raise an exception
675 * <code>"return"</code>:: return from a Ruby method
676 *
677 * Tracing is disabled within the context of _proc_.
678 *
679 * class Test
680 * def test
681 * a = 1
682 * b = 2
683 * end
684 * end
685 *
686 * set_trace_func proc { |event, file, line, id, binding, class_or_module|
687 * printf "%8s %s:%-2d %16p %14p\n", event, file, line, id, class_or_module
688 * }
689 * t = Test.new
690 * t.test
691 *
692 * Produces:
693 *
694 * c-return prog.rb:8 :set_trace_func Kernel
695 * line prog.rb:11 nil nil
696 * c-call prog.rb:11 :new Class
697 * c-call prog.rb:11 :initialize BasicObject
698 * c-return prog.rb:11 :initialize BasicObject
699 * c-return prog.rb:11 :new Class
700 * line prog.rb:12 nil nil
701 * call prog.rb:2 :test Test
702 * line prog.rb:3 :test Test
703 * line prog.rb:4 :test Test
704 * return prog.rb:5 :test Test
705 */
706
707static VALUE
708set_trace_func(VALUE obj, VALUE trace)
709{
710 rb_remove_event_hook(call_trace_func);
711
712 if (NIL_P(trace)) {
713 return Qnil;
714 }
715
716 if (!rb_obj_is_proc(trace)) {
717 rb_raise(rb_eTypeError, "trace_func needs to be Proc");
718 }
719
720 rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
721 return trace;
722}
723
724static void
725thread_add_trace_func(rb_execution_context_t *ec, rb_thread_t *filter_th, VALUE trace)
726{
727 if (!rb_obj_is_proc(trace)) {
728 rb_raise(rb_eTypeError, "trace_func needs to be Proc");
729 }
730
731 rb_threadptr_add_event_hook(ec, filter_th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE);
732}
733
734/*
735 * call-seq:
736 * thr.add_trace_func(proc) -> proc
737 *
738 * Adds _proc_ as a handler for tracing.
739 *
740 * See Thread#set_trace_func and Kernel#set_trace_func.
741 */
742
743static VALUE
744thread_add_trace_func_m(VALUE obj, VALUE trace)
745{
746 thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
747 return trace;
748}
749
750/*
751 * call-seq:
752 * thr.set_trace_func(proc) -> proc
753 * thr.set_trace_func(nil) -> nil
754 *
755 * Establishes _proc_ on _thr_ as the handler for tracing, or
756 * disables tracing if the parameter is +nil+.
757 *
758 * See Kernel#set_trace_func.
759 */
760
761static VALUE
762thread_set_trace_func_m(VALUE target_thread, VALUE trace)
763{
764 rb_execution_context_t *ec = GET_EC();
765 rb_thread_t *target_th = rb_thread_ptr(target_thread);
766
767 rb_threadptr_remove_event_hook(ec, target_th, call_trace_func, Qundef);
768
769 if (NIL_P(trace)) {
770 return Qnil;
771 }
772 else {
773 thread_add_trace_func(ec, target_th, trace);
774 return trace;
775 }
776}
777
778static const char *
779get_event_name(rb_event_flag_t event)
780{
781 switch (event) {
782 case RUBY_EVENT_LINE: return "line";
783 case RUBY_EVENT_CLASS: return "class";
784 case RUBY_EVENT_END: return "end";
785 case RUBY_EVENT_CALL: return "call";
786 case RUBY_EVENT_RETURN: return "return";
787 case RUBY_EVENT_C_CALL: return "c-call";
788 case RUBY_EVENT_C_RETURN: return "c-return";
789 case RUBY_EVENT_RAISE: return "raise";
790 default:
791 return "unknown";
792 }
793}
794
795static ID
796get_event_id(rb_event_flag_t event)
797{
798 ID id;
799
800 switch (event) {
801#define C(name, NAME) case RUBY_EVENT_##NAME: CONST_ID(id, #name); return id;
802 C(line, LINE);
803 C(class, CLASS);
804 C(end, END);
805 C(call, CALL);
806 C(return, RETURN);
807 C(c_call, C_CALL);
808 C(c_return, C_RETURN);
809 C(raise, RAISE);
810 C(b_call, B_CALL);
811 C(b_return, B_RETURN);
812 C(thread_begin, THREAD_BEGIN);
813 C(thread_end, THREAD_END);
814 C(fiber_switch, FIBER_SWITCH);
815 C(script_compiled, SCRIPT_COMPILED);
816 C(rescue, RESCUE);
817#undef C
818 default:
819 return 0;
820 }
821}
822
823static void
824get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, rb_event_flag_t event, VALUE *pathp, int *linep)
825{
826 cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
827
828 if (cfp) {
829 const rb_iseq_t *iseq = CFP_ISEQ(cfp);
830 *pathp = rb_iseq_path(iseq);
831
832 if (event & (RUBY_EVENT_CLASS |
835 *linep = FIX2INT(rb_iseq_first_lineno(iseq));
836 }
837 else {
838 *linep = rb_vm_get_sourceline(cfp);
839 }
840 }
841 else {
842 *pathp = Qnil;
843 *linep = 0;
844 }
845}
846
847static void
848call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
849{
850 int line;
851 VALUE filename;
852 VALUE eventname = rb_str_new2(get_event_name(event));
853 VALUE argv[6];
854 const rb_execution_context_t *ec = GET_EC();
855
856 get_path_and_lineno(ec, ec->cfp, event, &filename, &line);
857
858 if (!klass) {
859 rb_ec_frame_method_id_and_class(ec, &id, 0, &klass);
860 }
861
862 if (klass) {
863 if (RB_TYPE_P(klass, T_ICLASS)) {
864 klass = RBASIC(klass)->klass;
865 }
866 else if (RCLASS_SINGLETON_P(klass)) {
867 klass = RCLASS_ATTACHED_OBJECT(klass);
868 }
869 }
870
871 argv[0] = eventname;
872 argv[1] = filename;
873 argv[2] = INT2FIX(line);
874 argv[3] = id ? ID2SYM(id) : Qnil;
875 argv[4] = Qnil;
876 if (self && (filename != Qnil) &&
877 event != RUBY_EVENT_C_CALL &&
878 event != RUBY_EVENT_C_RETURN &&
879 (VM_FRAME_RUBYFRAME_P(ec->cfp) && imemo_type_p((VALUE)CFP_ISEQ(ec->cfp), imemo_iseq))) {
880 argv[4] = rb_binding_new();
881 }
882 argv[5] = klass ? klass : Qnil;
883
884 rb_proc_call_with_block(proc, 6, argv, Qnil);
885}
886
887/* (2-2) TracePoint API */
888
889static VALUE rb_cTracePoint;
890
891typedef struct rb_tp_struct {
892 rb_event_flag_t events;
893 int tracing; /* bool */
894 rb_thread_t *target_th;
895 VALUE local_target_set; /* Hash: target ->
896 * Qtrue (if target is iseq) or
897 * Qfalse (if target is bmethod)
898 */
899 void (*func)(VALUE tpval, void *data);
900 void *data;
901 VALUE proc;
902 rb_ractor_t *ractor;
903 VALUE self;
904} rb_tp_t;
905
906static void
907tp_mark_and_move(void *ptr)
908{
909 rb_tp_t *tp = ptr;
910 rb_gc_mark_and_move(&tp->proc);
911 rb_gc_mark_and_move(&tp->local_target_set);
912 if (tp->target_th) rb_gc_mark_and_move(&tp->target_th->self);
913}
914
915static const rb_data_type_t tp_data_type = {
916 "tracepoint",
917 {
918 tp_mark_and_move,
920 NULL, // Nothing allocated externally, so don't need a memsize function
921 tp_mark_and_move,
922 },
923 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
924};
925
926static VALUE
927tp_alloc(VALUE klass)
928{
929 rb_tp_t *tp;
930 return TypedData_Make_Struct(klass, rb_tp_t, &tp_data_type, tp);
931}
932
933static rb_event_flag_t
934symbol2event_flag(VALUE v)
935{
936 ID id;
937 VALUE sym = rb_to_symbol_type(v);
938 const rb_event_flag_t RUBY_EVENT_A_CALL =
940 const rb_event_flag_t RUBY_EVENT_A_RETURN =
942
943#define C(name, NAME) CONST_ID(id, #name); if (sym == ID2SYM(id)) return RUBY_EVENT_##NAME
944 C(line, LINE);
945 C(class, CLASS);
946 C(end, END);
947 C(call, CALL);
948 C(return, RETURN);
949 C(c_call, C_CALL);
950 C(c_return, C_RETURN);
951 C(raise, RAISE);
952 C(b_call, B_CALL);
953 C(b_return, B_RETURN);
954 C(thread_begin, THREAD_BEGIN);
955 C(thread_end, THREAD_END);
956 C(fiber_switch, FIBER_SWITCH);
957 C(script_compiled, SCRIPT_COMPILED);
958 C(rescue, RESCUE);
959
960 /* joke */
961 C(a_call, A_CALL);
962 C(a_return, A_RETURN);
963#undef C
964 rb_raise(rb_eArgError, "unknown event: %"PRIsVALUE, rb_sym2str(sym));
965}
966
967static rb_tp_t *
968tpptr(VALUE tpval)
969{
970 rb_tp_t *tp;
971 TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
972 return tp;
973}
974
975static rb_trace_arg_t *
976get_trace_arg(void)
977{
978 rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
979 if (trace_arg == 0) {
980 rb_raise(rb_eRuntimeError, "access from outside");
981 }
982 return trace_arg;
983}
984
985struct rb_trace_arg_struct *
987{
988 return get_trace_arg();
989}
990
993{
994 return trace_arg->event;
995}
996
997VALUE
999{
1000 return ID2SYM(get_event_id(trace_arg->event));
1001}
1002
1003static void
1004fill_path_and_lineno(rb_trace_arg_t *trace_arg)
1005{
1006 if (UNDEF_P(trace_arg->path)) {
1007 get_path_and_lineno(trace_arg->ec, trace_arg->cfp, trace_arg->event, &trace_arg->path, &trace_arg->lineno);
1008 }
1009}
1010
1011VALUE
1013{
1014 fill_path_and_lineno(trace_arg);
1015 return INT2FIX(trace_arg->lineno);
1016}
1017VALUE
1019{
1020 fill_path_and_lineno(trace_arg);
1021 return trace_arg->path;
1022}
1023
1024static void
1025fill_id_and_klass(rb_trace_arg_t *trace_arg)
1026{
1027 if (!trace_arg->klass_solved) {
1028 if (!trace_arg->klass) {
1029 rb_vm_control_frame_id_and_class(trace_arg->cfp, &trace_arg->id, &trace_arg->called_id, &trace_arg->klass);
1030 }
1031
1032 if (trace_arg->klass) {
1033 if (RB_TYPE_P(trace_arg->klass, T_ICLASS)) {
1034 trace_arg->klass = RBASIC(trace_arg->klass)->klass;
1035 }
1036 }
1037 else {
1038 trace_arg->klass = Qnil;
1039 }
1040
1041 trace_arg->klass_solved = 1;
1042 }
1043}
1044
1045VALUE
1047{
1048 switch (trace_arg->event) {
1049 case RUBY_EVENT_CALL:
1050 case RUBY_EVENT_RETURN:
1051 case RUBY_EVENT_B_CALL:
1052 case RUBY_EVENT_B_RETURN: {
1053 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->ec, trace_arg->cfp);
1054 if (cfp) {
1055 int is_proc = 0;
1056 if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK && !VM_FRAME_LAMBDA_P(cfp)) {
1057 is_proc = 1;
1058 }
1059 return rb_iseq_parameters(CFP_ISEQ(cfp), is_proc);
1060 }
1061 break;
1062 }
1063 case RUBY_EVENT_C_CALL:
1064 case RUBY_EVENT_C_RETURN: {
1065 fill_id_and_klass(trace_arg);
1066 if (trace_arg->klass && trace_arg->id) {
1067 const rb_method_entry_t *me;
1068 VALUE iclass = Qnil;
1069 me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->called_id, &iclass);
1070 if (!me) {
1071 me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->id, &iclass);
1072 }
1073 return rb_unnamed_parameters(rb_method_entry_arity(me));
1074 }
1075 break;
1076 }
1077 case RUBY_EVENT_RAISE:
1078 case RUBY_EVENT_LINE:
1079 case RUBY_EVENT_CLASS:
1080 case RUBY_EVENT_END:
1082 case RUBY_EVENT_RESCUE:
1083 rb_raise(rb_eRuntimeError, "not supported by this event");
1084 break;
1085 }
1086 return Qnil;
1087}
1088
1089VALUE
1091{
1092 fill_id_and_klass(trace_arg);
1093 return trace_arg->id ? ID2SYM(trace_arg->id) : Qnil;
1094}
1095
1096VALUE
1098{
1099 fill_id_and_klass(trace_arg);
1100 return trace_arg->called_id ? ID2SYM(trace_arg->called_id) : Qnil;
1101}
1102
1103VALUE
1105{
1106 fill_id_and_klass(trace_arg);
1107 return trace_arg->klass;
1108}
1109
1110VALUE
1112{
1113 rb_control_frame_t *cfp;
1114 switch (trace_arg->event) {
1115 case RUBY_EVENT_C_CALL:
1117 return Qnil;
1118 }
1119 cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->ec, trace_arg->cfp);
1120
1121 if (cfp && imemo_type_p((VALUE)CFP_ISEQ(cfp), imemo_iseq)) {
1122 return rb_vm_make_binding(trace_arg->ec, cfp);
1123 }
1124 else {
1125 return Qnil;
1126 }
1127}
1128
1129VALUE
1131{
1132 return trace_arg->self;
1133}
1134
1135static void
1136check_event_support(rb_trace_arg_t *trace_arg, rb_event_flag_t supported)
1137{
1138 if (!(trace_arg->event & supported)) {
1139 rb_raise(rb_eRuntimeError, "not supported by this event");
1140 }
1141}
1142
1143VALUE
1145{
1146 check_event_support(trace_arg, RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN);
1147 if (UNDEF_P(trace_arg->data)) {
1148 rb_bug("rb_tracearg_return_value: unreachable");
1149 }
1150 return trace_arg->data;
1151}
1152
1153VALUE
1155{
1156 check_event_support(trace_arg, RUBY_EVENT_RAISE | RUBY_EVENT_RESCUE);
1157 if (UNDEF_P(trace_arg->data)) {
1158 rb_bug("rb_tracearg_raised_exception: unreachable");
1159 }
1160 return trace_arg->data;
1161}
1162
1163VALUE
1165{
1166 VALUE data = trace_arg->data;
1167
1168 check_event_support(trace_arg, RUBY_EVENT_SCRIPT_COMPILED);
1169 if (UNDEF_P(data)) {
1170 rb_bug("rb_tracearg_eval_script: unreachable");
1171 }
1172 if (rb_obj_is_iseq(data)) {
1173 return Qnil;
1174 }
1175 else {
1176 VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
1177 /* [src, iseq] */
1178 return RARRAY_AREF(data, 0);
1179 }
1180}
1181
1182VALUE
1184{
1185 VALUE data = trace_arg->data;
1186
1187 check_event_support(trace_arg, RUBY_EVENT_SCRIPT_COMPILED);
1188 if (UNDEF_P(data)) {
1189 rb_bug("rb_tracearg_instruction_sequence: unreachable");
1190 }
1191
1192 if (rb_obj_is_iseq(data)) {
1193 return rb_iseqw_new((const rb_iseq_t *)data);
1194 }
1195 else {
1196 VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
1197 VM_ASSERT(rb_obj_is_iseq(RARRAY_AREF(data, 1)));
1198
1199 /* [src, iseq] */
1200 return rb_iseqw_new((const rb_iseq_t *)RARRAY_AREF(data, 1));
1201 }
1202}
1203
1204VALUE
1206{
1207 check_event_support(trace_arg, RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ);
1208 if (UNDEF_P(trace_arg->data)) {
1209 rb_bug("rb_tracearg_object: unreachable");
1210 }
1211 return trace_arg->data;
1212}
1213
1214static VALUE
1215tracepoint_attr_event(rb_execution_context_t *ec, VALUE tpval)
1216{
1217 return rb_tracearg_event(get_trace_arg());
1218}
1219
1220static VALUE
1221tracepoint_attr_lineno(rb_execution_context_t *ec, VALUE tpval)
1222{
1223 return rb_tracearg_lineno(get_trace_arg());
1224}
1225static VALUE
1226tracepoint_attr_path(rb_execution_context_t *ec, VALUE tpval)
1227{
1228 return rb_tracearg_path(get_trace_arg());
1229}
1230
1231static VALUE
1232tracepoint_attr_parameters(rb_execution_context_t *ec, VALUE tpval)
1233{
1234 return rb_tracearg_parameters(get_trace_arg());
1235}
1236
1237static VALUE
1238tracepoint_attr_method_id(rb_execution_context_t *ec, VALUE tpval)
1239{
1240 return rb_tracearg_method_id(get_trace_arg());
1241}
1242
1243static VALUE
1244tracepoint_attr_callee_id(rb_execution_context_t *ec, VALUE tpval)
1245{
1246 return rb_tracearg_callee_id(get_trace_arg());
1247}
1248
1249static VALUE
1250tracepoint_attr_defined_class(rb_execution_context_t *ec, VALUE tpval)
1251{
1252 return rb_tracearg_defined_class(get_trace_arg());
1253}
1254
1255static VALUE
1256tracepoint_attr_binding(rb_execution_context_t *ec, VALUE tpval)
1257{
1258 return rb_tracearg_binding(get_trace_arg());
1259}
1260
1261static VALUE
1262tracepoint_attr_self(rb_execution_context_t *ec, VALUE tpval)
1263{
1264 return rb_tracearg_self(get_trace_arg());
1265}
1266
1267static VALUE
1268tracepoint_attr_return_value(rb_execution_context_t *ec, VALUE tpval)
1269{
1270 return rb_tracearg_return_value(get_trace_arg());
1271}
1272
1273static VALUE
1274tracepoint_attr_raised_exception(rb_execution_context_t *ec, VALUE tpval)
1275{
1276 return rb_tracearg_raised_exception(get_trace_arg());
1277}
1278
1279static VALUE
1280tracepoint_attr_eval_script(rb_execution_context_t *ec, VALUE tpval)
1281{
1282 return rb_tracearg_eval_script(get_trace_arg());
1283}
1284
1285static VALUE
1286tracepoint_attr_instruction_sequence(rb_execution_context_t *ec, VALUE tpval)
1287{
1288 return rb_tracearg_instruction_sequence(get_trace_arg());
1289}
1290
1291static void
1292tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
1293{
1294 rb_tp_t *tp = tpptr(tpval);
1295
1296 if (tp->func) {
1297 (*tp->func)(tpval, tp->data);
1298 }
1299 else {
1300 if (tp->ractor == GET_RACTOR()) {
1301 rb_proc_call_with_block((VALUE)tp->proc, 1, &tpval, Qnil);
1302 }
1303 }
1304}
1305
1306VALUE
1308{
1309 rb_tp_t *tp;
1310 tp = tpptr(tpval);
1311
1312 if (tp->local_target_set != Qfalse) {
1313 rb_raise(rb_eArgError, "can't nest-enable a targeting TracePoint");
1314 }
1315
1316 if (tp->tracing) {
1317 return Qundef;
1318 }
1319
1320 if (tp->target_th) {
1321 rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
1322 RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
1323 }
1324 else {
1325 rb_add_event_hook2((rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
1326 RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
1327 }
1328 tp->tracing = 1;
1329 return Qundef;
1330}
1331
1332static const rb_iseq_t *
1333iseq_of(VALUE target)
1334{
1335 VALUE iseqv = rb_funcall(rb_cISeq, rb_intern("of"), 1, target);
1336 if (NIL_P(iseqv)) {
1337 rb_raise(rb_eArgError, "specified target is not supported");
1338 }
1339 else {
1340 return rb_iseqw_to_iseq(iseqv);
1341 }
1342}
1343
1344const rb_method_definition_t *rb_method_def(VALUE method); /* proc.c */
1345
1347rb_method_def_local_hooks(rb_method_definition_t *def, rb_ractor_t *cr, bool create)
1348{
1349 st_data_t val;
1350 rb_hook_list_t *hook_list = NULL;
1351 if (st_lookup(rb_ractor_targeted_hooks(cr), (st_data_t)def, &val)) {
1352 hook_list = (rb_hook_list_t*)val;
1353 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
1354 }
1355 else if (create) {
1356 hook_list = ZALLOC(rb_hook_list_t);
1357 hook_list->type = hook_list_type_targeted_def;
1358 st_insert(&cr->pub.targeted_hooks, (st_data_t)def, (st_data_t)hook_list);
1359 }
1360 return hook_list;
1361}
1362
1363// Enable "local" (targeted) tracepoint
1364static VALUE
1365rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
1366{
1367 rb_tp_t *tp = tpptr(tpval);
1368 const rb_iseq_t *iseq = iseq_of(target); // takes Proc, Iseq, Method
1369 int n = 0;
1370 unsigned int line = 0;
1371 bool target_bmethod = false;
1372 rb_ractor_t *cr = GET_RACTOR();
1373
1374 if (tp->tracing > 0) {
1375 rb_raise(rb_eArgError, "can't nest-enable a targeting TracePoint");
1376 }
1377
1378 if (!NIL_P(target_line)) {
1379 if ((tp->events & RUBY_EVENT_LINE) == 0) {
1380 rb_raise(rb_eArgError, "target_line is specified, but line event is not specified");
1381 }
1382 else {
1383 line = NUM2UINT(target_line);
1384 }
1385 }
1386
1387 VM_ASSERT(tp->local_target_set == Qfalse);
1388 RB_OBJ_WRITE(tpval, &tp->local_target_set, rb_obj_hide(rb_ident_hash_new()));
1389
1390 RB_VM_LOCKING() {
1391 // Rewriting iseq instructions across ractors is not safe unless they are stopped.
1392 rb_vm_barrier();
1393
1394 /* bmethod */
1395 if (rb_obj_is_method(target)) {
1396 rb_method_definition_t *def = (rb_method_definition_t *)rb_method_def(target);
1397 if (def->type == VM_METHOD_TYPE_BMETHOD && (tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN))) {
1398 rb_hook_list_t *hook_list = rb_method_def_local_hooks(def, cr, true);
1399 rb_hook_list_connect_local_tracepoint(hook_list, tpval, 0);
1400 rb_hash_aset(tp->local_target_set, target, Qfalse); // Qfalse means not an iseq
1401 rb_method_definition_addref(def); // in case `tp` gets GC'd and didn't disable the hook, `def` needs to stay alive
1402 def->body.bmethod.local_hooks_cnt++;
1403 target_bmethod = true;
1404 n++;
1405 }
1406 }
1407
1408 /* iseq */
1409 n += rb_iseq_add_local_tracepoint_recursively(iseq, tp->events, tpval, line, target_bmethod);
1410 if (n > 0) {
1411 rb_hash_aset(tp->local_target_set, (VALUE)iseq, Qtrue);
1412
1413 if ((tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN)) &&
1414 ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_SINGLE_NOARG_LEAF) {
1415 rb_clear_bf_ccs();
1416 }
1417
1418 rb_yjit_tracing_invalidate_all();
1419 rb_zjit_tracing_invalidate_all();
1420 rb_ractor_targeted_hooks_incr(tp->ractor);
1421 if (tp->events & ISEQ_TRACE_EVENTS) {
1422 ruby_vm_iseq_events_enabled++;
1423 }
1424 if ((tp->events & RUBY_EVENT_C_CALL) || (tp->events & RUBY_EVENT_C_RETURN)) {
1425 ruby_vm_c_events_enabled++;
1426 }
1427 tp->tracing = 1;
1428 }
1429 }
1430
1431 if (n == 0) {
1432 rb_raise(rb_eArgError, "can not enable any hooks");
1433 }
1434
1435 return Qnil;
1436}
1437
1438static int
1439disable_local_tracepoint_i(VALUE target, VALUE iseq_p, VALUE tpval)
1440{
1441 rb_tp_t *tp = tpptr(tpval);
1442 rb_ractor_t *cr;
1444 rb_hook_list_t *hook_list;
1445 ASSERT_vm_locking_with_barrier();
1446
1447 if (iseq_p) {
1448 rb_iseq_remove_local_tracepoint_recursively((rb_iseq_t *)target, tpval, tp->ractor);
1449 }
1450 else {
1451 cr = GET_RACTOR();
1452 /* bmethod */
1453 def = (rb_method_definition_t *)rb_method_def(target);
1454 hook_list = rb_method_def_local_hooks(def, cr, false);
1455 RUBY_ASSERT(hook_list != NULL);
1456 if (rb_hook_list_remove_local_tracepoint(hook_list, tpval)) {
1457 RUBY_ASSERT(def->body.bmethod.local_hooks_cnt > 0);
1458 def->body.bmethod.local_hooks_cnt--;
1459 if (hook_list->events == 0) {
1460 st_delete(rb_ractor_targeted_hooks(cr), (st_data_t*)&def, NULL);
1461 rb_hook_list_free(hook_list);
1462 }
1463 rb_method_definition_release(def);
1464 }
1465 }
1466 return ST_CONTINUE;
1467}
1468
1469VALUE
1471{
1472 rb_tp_t *tp;
1473
1474 tp = tpptr(tpval);
1475
1476 if (RTEST(tp->local_target_set)) {
1477 RUBY_ASSERT(GET_RACTOR() == tp->ractor);
1478 RB_VM_LOCKING() {
1479 rb_vm_barrier();
1480
1481 rb_hash_foreach(tp->local_target_set, disable_local_tracepoint_i, tpval);
1482 RB_OBJ_WRITE(tpval, &tp->local_target_set, Qfalse);
1483 rb_ractor_targeted_hooks_decr(tp->ractor);
1484 if (tp->events & ISEQ_TRACE_EVENTS) {
1485 RUBY_ASSERT(ruby_vm_iseq_events_enabled > 0);
1486 ruby_vm_iseq_events_enabled--;
1487 }
1488 if ((tp->events & RUBY_EVENT_C_CALL) || (tp->events & RUBY_EVENT_C_RETURN)) {
1489 RUBY_ASSERT(ruby_vm_c_events_enabled > 0);
1490 ruby_vm_c_events_enabled--;
1491 }
1492 }
1493 }
1494 else {
1495 if (tp->target_th) {
1496 rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval);
1497 }
1498 else {
1500 }
1501 }
1502 tp->tracing = 0;
1503 tp->target_th = NULL;
1504 return Qundef;
1505}
1506
1507// connect a targeted (ie: "local") tracepoint to the hook list for the method
1508// ex: tp.enable(target: method(:puts))
1509void
1510rb_hook_list_connect_local_tracepoint(rb_hook_list_t *list, VALUE tpval, unsigned int target_line)
1511{
1512 rb_tp_t *tp = tpptr(tpval);
1513 rb_event_hook_t *hook = alloc_event_hook((rb_event_hook_func_t)tp_call_trace, tp->events & ISEQ_TRACE_EVENTS, tpval,
1514 RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
1515 hook->filter.target_line = target_line;
1516 hook_list_connect(list, hook, FALSE);
1517}
1518
1519bool
1520rb_hook_list_remove_local_tracepoint(rb_hook_list_t *list, VALUE tpval)
1521{
1522 rb_event_hook_t *hook = list->hooks;
1523 rb_event_flag_t events = 0;
1524 bool removed = false;
1525
1526 while (hook) {
1527 if (hook->data == tpval) {
1528 hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
1529 list->need_clean = true;
1530 removed = true;
1531 }
1532 else if ((hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) == 0) {
1533 events |= hook->events;
1534 }
1535 hook = hook->next;
1536 }
1537
1538 list->events = events;
1539 return removed;
1540}
1541
1542static VALUE
1543tracepoint_enable_m(rb_execution_context_t *ec, VALUE tpval, VALUE target, VALUE target_line, VALUE target_thread)
1544{
1545 rb_tp_t *tp = tpptr(tpval);
1546 int previous_tracing = tp->tracing;
1547
1548 if (target_thread == sym_default) {
1549 if (rb_block_given_p() && NIL_P(target) && NIL_P(target_line)) {
1550 target_thread = rb_thread_current();
1551 }
1552 else {
1553 target_thread = Qnil;
1554 }
1555 }
1556
1557 /* check target_thread */
1558 if (RTEST(target_thread)) {
1559 if (tp->target_th) {
1560 rb_raise(rb_eArgError, "can not override target_thread filter");
1561 }
1562 tp->target_th = rb_thread_ptr(target_thread);
1563
1564 RUBY_ASSERT(tp->target_th->self == target_thread);
1565 RB_OBJ_WRITTEN(tpval, Qundef, target_thread);
1566 }
1567 else {
1568 tp->target_th = NULL;
1569 }
1570
1571 if (NIL_P(target)) {
1572 if (!NIL_P(target_line)) {
1573 rb_raise(rb_eArgError, "only target_line is specified");
1574 }
1575 rb_tracepoint_enable(tpval);
1576 }
1577 else {
1578 rb_tracepoint_enable_for_target(tpval, target, target_line);
1579 }
1580
1581 if (rb_block_given_p()) {
1582 return rb_ensure(rb_yield, Qundef,
1583 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
1584 tpval);
1585 }
1586 else {
1587 return RBOOL(previous_tracing);
1588 }
1589}
1590
1591static VALUE
1592tracepoint_disable_m(rb_execution_context_t *ec, VALUE tpval)
1593{
1594 rb_tp_t *tp = tpptr(tpval);
1595 int previous_tracing = tp->tracing;
1596
1597 if (rb_block_given_p()) {
1598 if (tp->local_target_set != Qfalse) {
1599 rb_raise(rb_eArgError, "can't disable a targeting TracePoint in a block");
1600 }
1601
1602 rb_tracepoint_disable(tpval);
1603 return rb_ensure(rb_yield, Qundef,
1604 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
1605 tpval);
1606 }
1607 else {
1608 rb_tracepoint_disable(tpval);
1609 return RBOOL(previous_tracing);
1610 }
1611}
1612
1613VALUE
1615{
1616 rb_tp_t *tp = tpptr(tpval);
1617 return RBOOL(tp->tracing);
1618}
1619
1620static VALUE
1621tracepoint_enabled_p(rb_execution_context_t *ec, VALUE tpval)
1622{
1623 return rb_tracepoint_enabled_p(tpval);
1624}
1625
1626static VALUE
1627tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void (func)(VALUE, void*), void *data, VALUE proc)
1628{
1629 VALUE tpval = tp_alloc(klass);
1630 rb_tp_t *tp;
1631 TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
1632
1633 RB_OBJ_WRITE(tpval, &tp->proc, proc);
1634 tp->ractor = GET_RACTOR();
1635 tp->func = func; // for internal events
1636 tp->data = data;
1637 tp->events = events;
1638 tp->self = tpval;
1639
1640 return tpval;
1641}
1642
1643VALUE
1644rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE, void *), void *data)
1645{
1646 rb_thread_t *target_th = NULL;
1647
1648 if (RTEST(target_thval)) {
1649 target_th = rb_thread_ptr(target_thval);
1650 }
1651 return tracepoint_new(rb_cTracePoint, target_th, events, func, data, Qundef);
1652}
1653
1654static VALUE
1655tracepoint_new_s(rb_execution_context_t *ec, VALUE self, VALUE args)
1656{
1657 rb_event_flag_t events = 0;
1658 long i;
1659 long argc = RARRAY_LEN(args);
1660
1661 if (argc > 0) {
1662 for (i=0; i<argc; i++) {
1663 events |= symbol2event_flag(RARRAY_AREF(args, i));
1664 }
1665 }
1666 else {
1668 }
1669
1670 if (!rb_block_given_p()) {
1671 rb_raise(rb_eArgError, "must be called with a block");
1672 }
1673
1674 return tracepoint_new(self, 0, events, 0, 0, rb_block_proc());
1675}
1676
1677static VALUE
1678tracepoint_trace_s(rb_execution_context_t *ec, VALUE self, VALUE args)
1679{
1680 VALUE trace = tracepoint_new_s(ec, self, args);
1681 rb_tracepoint_enable(trace);
1682 return trace;
1683}
1684
1685static VALUE
1686tracepoint_inspect(rb_execution_context_t *ec, VALUE self)
1687{
1688 rb_tp_t *tp = tpptr(self);
1689 rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
1690
1691 if (trace_arg) {
1692 switch (trace_arg->event) {
1693 case RUBY_EVENT_LINE:
1694 {
1695 VALUE sym = rb_tracearg_method_id(trace_arg);
1696 if (NIL_P(sym))
1697 break;
1698 return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE":%d in '%"PRIsVALUE"'>",
1699 rb_tracearg_event(trace_arg),
1700 rb_tracearg_path(trace_arg),
1701 FIX2INT(rb_tracearg_lineno(trace_arg)),
1702 sym);
1703 }
1704 case RUBY_EVENT_CALL:
1705 case RUBY_EVENT_C_CALL:
1706 case RUBY_EVENT_RETURN:
1708 return rb_sprintf("#<TracePoint:%"PRIsVALUE" '%"PRIsVALUE"' %"PRIsVALUE":%d>",
1709 rb_tracearg_event(trace_arg),
1710 rb_tracearg_method_id(trace_arg),
1711 rb_tracearg_path(trace_arg),
1712 FIX2INT(rb_tracearg_lineno(trace_arg)));
1715 return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE">",
1716 rb_tracearg_event(trace_arg),
1717 rb_tracearg_self(trace_arg));
1718 default:
1719 break;
1720 }
1721 return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE":%d>",
1722 rb_tracearg_event(trace_arg),
1723 rb_tracearg_path(trace_arg),
1724 FIX2INT(rb_tracearg_lineno(trace_arg)));
1725 }
1726 else {
1727 return rb_sprintf("#<TracePoint:%s>", tp->tracing ? "enabled" : "disabled");
1728 }
1729}
1730
1731static void
1732tracepoint_stat_event_hooks(VALUE hash, VALUE key, rb_event_hook_t *hook)
1733{
1734 int active = 0, deleted = 0;
1735
1736 while (hook) {
1737 if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
1738 deleted++;
1739 }
1740 else {
1741 active++;
1742 }
1743 hook = hook->next;
1744 }
1745
1746 rb_hash_aset(hash, key, rb_ary_new3(2, INT2FIX(active), INT2FIX(deleted)));
1747}
1748
1749static VALUE
1750tracepoint_stat_s(rb_execution_context_t *ec, VALUE self)
1751{
1752 rb_vm_t *vm = GET_VM();
1753 VALUE stat = rb_hash_new();
1754
1755 tracepoint_stat_event_hooks(stat, vm->self, rb_ec_ractor_hooks(ec)->hooks);
1756
1757 return stat;
1758}
1759
1760static VALUE
1761disallow_reentry(VALUE val)
1762{
1763 rb_trace_arg_t *arg = (rb_trace_arg_t *)val;
1764 rb_execution_context_t *ec = GET_EC();
1765 if (ec->trace_arg != NULL) rb_bug("should be NULL, but %p", (void *)ec->trace_arg);
1766 ec->trace_arg = arg;
1767 return Qnil;
1768}
1769
1770static VALUE
1771tracepoint_allow_reentry(rb_execution_context_t *ec, VALUE self)
1772{
1773 const rb_trace_arg_t *arg = ec->trace_arg;
1774 if (arg == NULL) rb_raise(rb_eRuntimeError, "No need to allow reentrance.");
1775 ec->trace_arg = NULL;
1776 return rb_ensure(rb_yield, Qnil, disallow_reentry, (VALUE)arg);
1777}
1778
1779#include "trace_point.rbinc"
1780
1781/* This function is called from inits.c */
1782void
1783Init_vm_trace(void)
1784{
1785 sym_default = ID2SYM(rb_intern_const("default"));
1786
1787 /* trace_func */
1788 rb_define_global_function("set_trace_func", set_trace_func, 1);
1789 rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
1790 rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
1791
1792 rb_cTracePoint = rb_define_class("TracePoint", rb_cObject);
1793 rb_undef_alloc_func(rb_cTracePoint);
1794}
1795
1796/*
1797 * Ruby actually has two separate mechanisms for enqueueing work from contexts
1798 * where it is not safe to run Ruby code, to run later on when it is safe. One
1799 * is async-signal-safe but more limited, and accessed through the
1800 * `rb_postponed_job_preregister` and `rb_postponed_job_trigger` functions. The
1801 * other is more flexible but cannot be used in signal handlers, and is accessed
1802 * through the `rb_workqueue_register` function.
1803 *
1804 * The postponed job functions form part of Ruby's extension API, but the
1805 * workqueue functions are for internal use only.
1806 */
1807
1809 struct ccan_list_node jnode; /* <=> vm->workqueue */
1811 void *data;
1812};
1813
1814// Used for VM memsize reporting. Returns the size of a list of rb_workqueue_job
1815// structs. Defined here because the struct definition lives here as well.
1816size_t
1817rb_vm_memsize_workqueue(struct ccan_list_head *workqueue)
1818{
1819 struct rb_workqueue_job *work = 0;
1820 size_t size = 0;
1821
1822 ccan_list_for_each(workqueue, work, jnode) {
1823 size += sizeof(struct rb_workqueue_job);
1824 }
1825
1826 return size;
1827}
1828
1829/*
1830 * thread-safe and called from non-Ruby thread
1831 * returns FALSE on failure (ENOMEM), TRUE otherwise
1832 */
1833int
1834rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
1835{
1836 struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job));
1837 rb_vm_t *vm = GET_VM();
1838
1839 if (!wq_job) return FALSE;
1840 wq_job->func = func;
1841 wq_job->data = data;
1842
1843 rb_nativethread_lock_lock(&vm->workqueue_lock);
1844 ccan_list_add_tail(&vm->workqueue, &wq_job->jnode);
1845 rb_nativethread_lock_unlock(&vm->workqueue_lock);
1846
1847 // TODO: current implementation affects only main ractor
1848 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(rb_vm_main_ractor_ec(vm));
1849
1850 return TRUE;
1851}
1852
1853#define PJOB_TABLE_SIZE (sizeof(rb_atomic_t) * CHAR_BIT)
1854/* pre-registered jobs table, for async-safe jobs */
1856 struct {
1858 void *data;
1859 } table[PJOB_TABLE_SIZE];
1860 /* Bits in this are set when the corresponding entry in prereg_table has non-zero
1861 * triggered_count; i.e. somebody called rb_postponed_job_trigger */
1862 rb_atomic_t triggered_bitset;
1864
1865static rb_postponed_job_queues_t postponed_job_queue;
1866
1868get_valid_ec(rb_vm_t *vm)
1869{
1870 rb_execution_context_t *ec = rb_current_execution_context(false);
1871 if (ec == NULL) ec = rb_vm_main_ractor_ec(vm);
1872 return ec;
1873}
1874
1875void
1876rb_vm_postponed_job_atfork(void)
1877{
1878 rb_postponed_job_queues_t *pjq = &postponed_job_queue;
1879 /* make sure we set the interrupt flag on _this_ thread if we carried any pjobs over
1880 * from the other side of the fork (including jobs that targeted the
1881 * forking Ractor, which is the child's main Ractor now; jobs targeted
1882 * at any other Ractor die with it) */
1883 rb_execution_context_t *ec = get_valid_ec(GET_VM());
1884 if (pjq->triggered_bitset || rb_ec_ractor_ptr(ec)->postponed_job_triggered_bits) {
1885 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
1886 }
1887
1888}
1889
1890// Used for VM memsize reporting. Returns the total size of the postponed job
1891// queue infrastructure.
1892size_t
1893rb_vm_memsize_postponed_job_queue(void)
1894{
1895 return sizeof(rb_postponed_job_queues_t);
1896}
1897
1898
1900rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data)
1901{
1902 /* The doc comments say that this function should be called under the GVL, because
1903 * that is actually required to get the guarantee that "if a given (func, data) pair
1904 * was already pre-registered, this method will return the same handle instance".
1905 *
1906 * However, the actual implementation here is called without the GVL, from inside
1907 * rb_postponed_job_register, to support that legacy interface. In the presence
1908 * of concurrent calls to both _preregister and _register functions on the same
1909 * func, however, the data may get mixed up between them. */
1910
1911 rb_postponed_job_queues_t *pjq = &postponed_job_queue;
1912 for (unsigned int i = 0; i < PJOB_TABLE_SIZE; i++) {
1913 /* Try and set this slot to equal `func` */
1914 rb_postponed_job_func_t existing_func = (rb_postponed_job_func_t)(uintptr_t)RUBY_ATOMIC_PTR_CAS(pjq->table[i].func, NULL, (void *)(uintptr_t)func);
1915 if (existing_func == NULL || existing_func == func) {
1916 /* Either this slot was NULL, and we set it to func, or, this slot was already equal to func.
1917 * In either case, clobber the data with our data. Note that concurrent calls to
1918 * rb_postponed_job_register with the same func & different data will result in either of the
1919 * datas being written */
1920 RUBY_ATOMIC_PTR_EXCHANGE(pjq->table[i].data, data);
1921 return (rb_postponed_job_handle_t)i;
1922 }
1923 else {
1924 /* Try the next slot if this one already has a func in it */
1925 continue;
1926 }
1927 }
1928
1929 /* full */
1930 return POSTPONED_JOB_HANDLE_INVALID;
1931}
1932
1933void
1935{
1936 rb_postponed_job_queues_t *pjq = &postponed_job_queue;
1937
1938 RUBY_ATOMIC_OR(pjq->triggered_bitset, (((rb_atomic_t)1UL) << h));
1939 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(get_valid_ec(GET_VM()));
1940}
1941
1942/* Like rb_postponed_job_trigger(), but run the job on running_ractor
1943 * instead of the caller's or the main Ractor: set the handle's bit in that
1944 * Ractor's mask and post a POSTPONED_JOB interrupt to its running EC (or
1945 * its main thread's EC before it starts). Delivery is lazy: the target is
1946 * not unblocked, and a job for a Ractor that exits first is discarded.
1947 *
1948 * rb_postponed_job_trigger() is safe from a signal handler or any thread
1949 * because it only touches the VM-global queue and the caller's or main EC.
1950 * This one dereferences running_ractor and its EC, so the caller must keep
1951 * running_ractor alive and running for the whole call: holding its VALUE
1952 * is not enough, and a terminated Ractor is unsafe. The main Ractor is
1953 * always safe. */
1954void
1955rb_postponed_job_trigger_for_ractor(unsigned int h, VALUE running_ractor)
1956{
1957 VM_ASSERT(rb_ractor_p(running_ractor));
1958 rb_ractor_t *r = (rb_ractor_t *)DATA_PTR(running_ractor);
1959
1960 RUBY_ATOMIC_OR(r->postponed_job_triggered_bits, (((rb_atomic_t)1UL) << h));
1961
1962 /* The racy running_ec read is benign: whichever of the target's threads
1963 * checks interrupts first drains the whole per-Ractor mask. */
1964 rb_execution_context_t *target_ec = r->threads.running_ec;
1965 if (target_ec == NULL && r->threads.main) {
1966 target_ec = r->threads.main->ec;
1967 }
1968 if (target_ec) {
1969 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(target_ec);
1970 }
1971}
1972
1973void
1974rb_postponed_job_flush(rb_vm_t *vm)
1975{
1976 rb_postponed_job_queues_t *pjq = &postponed_job_queue;
1977 rb_execution_context_t *ec = GET_EC();
1978 const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK | TRAP_INTERRUPT_MASK;
1979 volatile rb_atomic_t saved_mask = ec->interrupt_mask & block_mask;
1980 VALUE volatile saved_errno = ec->errinfo;
1981 struct ccan_list_head tmp;
1982
1983 ccan_list_head_init(&tmp);
1984
1985 rb_nativethread_lock_lock(&vm->workqueue_lock);
1986 ccan_list_append_list(&tmp, &vm->workqueue);
1987 rb_nativethread_lock_unlock(&vm->workqueue_lock);
1988
1989 volatile rb_atomic_t triggered_bits = RUBY_ATOMIC_EXCHANGE(pjq->triggered_bitset, 0);
1990
1991 /* jobs targeted at this Ractor (rb_postponed_job_trigger_for_ractor) */
1992 triggered_bits |= RUBY_ATOMIC_EXCHANGE(rb_ec_ractor_ptr(ec)->postponed_job_triggered_bits, 0);
1993
1994 ec->errinfo = Qnil;
1995 /* mask POSTPONED_JOB dispatch */
1996 ec->interrupt_mask |= block_mask;
1997 {
1998 EC_PUSH_TAG(ec);
1999 if (EC_EXEC_TAG() == TAG_NONE) {
2000 /* execute postponed jobs */
2001 while (triggered_bits) {
2002 unsigned int i = bit_length(triggered_bits) - 1;
2003 triggered_bits ^= ((1UL) << i); /* toggle ith bit off */
2004 /* Read atomically to pair with the atomic CAS/EXCHANGE stores in
2005 * rb_postponed_job_preregister, which can run on another thread. */
2006 rb_postponed_job_func_t func = (rb_postponed_job_func_t)(uintptr_t)RUBY_ATOMIC_PTR_LOAD(pjq->table[i].func);
2007 void *data = RUBY_ATOMIC_PTR_LOAD(pjq->table[i].data);
2008 (func)(data);
2009 }
2010
2011 /* execute workqueue jobs */
2012 struct rb_workqueue_job *wq_job;
2013 while ((wq_job = ccan_list_pop(&tmp, struct rb_workqueue_job, jnode))) {
2014 rb_postponed_job_func_t func = wq_job->func;
2015 void *data = wq_job->data;
2016
2017 free(wq_job);
2018 (func)(data);
2019 }
2020 }
2021 EC_POP_TAG();
2022 }
2023 /* restore POSTPONED_JOB mask */
2024 ec->interrupt_mask &= ~(saved_mask ^ block_mask);
2025 ec->errinfo = saved_errno;
2026
2027 /* If we threw an exception, there might be leftover workqueue items; carry them over
2028 * to a subsequent execution of flush */
2029 if (!ccan_list_empty(&tmp)) {
2030 rb_nativethread_lock_lock(&vm->workqueue_lock);
2031 ccan_list_prepend_list(&vm->workqueue, &tmp);
2032 rb_nativethread_lock_unlock(&vm->workqueue_lock);
2033
2034 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
2035 }
2036 /* likewise with any remaining-to-be-executed bits of the preregistered postponed
2037 * job table. A merged bit can carry a Ractor-directed job that must not run on another
2038 * Ractor (rb_postponed_job_trigger_for_ractor), so re-post it to this Ractor's own mask
2039 * rather than to the global bitset. */
2040 if (triggered_bits) {
2041 RUBY_ATOMIC_OR(rb_ec_ractor_ptr(ec)->postponed_job_triggered_bits, triggered_bits);
2042 RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
2043 }
2044}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
Atomic operations.
#define RUBY_ATOMIC_OR(var, val)
Atomically replaces the value pointed by var with the result of bitwise OR between val and the old va...
Definition atomic.h:141
#define RUBY_ATOMIC_PTR_LOAD(var)
Identical to RUBY_ATOMIC_LOAD, except it expects its arguments are void*.
Definition atomic.h:338
#define RUBY_ATOMIC_PTR_CAS(var, oldval, newval)
Identical to RUBY_ATOMIC_CAS, except it expects its arguments are void*.
Definition atomic.h:365
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define RUBY_ATOMIC_PTR_EXCHANGE(var, val)
Identical to RUBY_ATOMIC_EXCHANGE, except it expects its arguments are void*.
Definition atomic.h:327
#define RUBY_ATOMIC_EXCHANGE(var, val)
Atomically replaces the value pointed by var with val.
Definition atomic.h:152
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
unsigned int rb_postponed_job_handle_t
The type of a handle returned from rb_postponed_job_preregister and passed to rb_postponed_job_trigge...
Definition debug.h:703
VALUE rb_tracearg_binding(rb_trace_arg_t *trace_arg)
Creates a binding object of the point where the trace is at.
Definition vm_trace.c:1111
VALUE rb_tracearg_parameters(rb_trace_arg_t *trace_arg)
Queries the parameters passed on a call or return event.
Definition vm_trace.c:1046
VALUE rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg)
Queries the compiled instruction sequence on a 'script_compiled' event.
Definition vm_trace.c:1183
void rb_postponed_job_trigger(rb_postponed_job_handle_t h)
Triggers a pre-registered job registered with rb_postponed_job_preregister, scheduling it for executi...
Definition vm_trace.c:1934
VALUE rb_tracepoint_enabled_p(VALUE tpval)
Queries if the passed TracePoint is up and running.
Definition vm_trace.c:1614
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg)
Queries the allocated/deallocated object that the trace represents.
Definition vm_trace.c:1205
VALUE rb_tracearg_callee_id(rb_trace_arg_t *trace_arg)
Identical to rb_tracearg_method_id(), except it returns callee id like rb_frame_callee().
Definition vm_trace.c:1097
VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
Queries the class that defines the method that the passed trace is at.
Definition vm_trace.c:1104
VALUE rb_tracepoint_new(VALUE target_thread_not_supported_yet, rb_event_flag_t events, void(*func)(VALUE, void *), void *data)
Creates a tracepoint by registering a callback function for one or more tracepoint events.
Definition vm_trace.c:1644
VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
Queries the raised exception that the trace represents.
Definition vm_trace.c:1154
void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
Identical to rb_add_event_hook(), except its effect is limited to the passed thread.
Definition vm_trace.c:278
rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data)
Pre-registers a func in Ruby's postponed job preregistration table, returning an opaque handle which ...
Definition vm_trace.c:1900
VALUE rb_tracepoint_disable(VALUE tpval)
Stops (disables) an already running instance of TracePoint.
Definition vm_trace.c:1470
VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg)
Queries the receiver of the point trace is at.
Definition vm_trace.c:1130
int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
Identical to rb_remove_event_hook(), except it additionally takes a thread argument.
Definition vm_trace.c:427
VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
Queries the return value that the trace represents.
Definition vm_trace.c:1144
rb_event_flag_t rb_tracearg_event_flag(rb_trace_arg_t *trace_arg)
Queries the event of the passed trace.
Definition vm_trace.c:992
VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg)
Queries the file name of the point where the trace is at.
Definition vm_trace.c:1018
VALUE rb_tracearg_eval_script(rb_trace_arg_t *trace_arg)
Queries the compiled source code of the 'script_compiled' event.
Definition vm_trace.c:1164
int rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data)
Identical to rb_thread_remove_event_hook(), except it additionally takes the data argument.
Definition vm_trace.c:433
VALUE rb_tracepoint_enable(VALUE tpval)
Starts (enables) trace(s) defined by the passed object.
Definition vm_trace.c:1307
VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
Queries the method name of the point where the trace is at.
Definition vm_trace.c:1090
int rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data)
Identical to rb_remove_event_hook(), except it additionally takes the data argument.
Definition vm_trace.c:445
rb_trace_arg_t * rb_tracearg_from_tracepoint(VALUE tpval)
Queries the current event of the passed tracepoint.
Definition vm_trace.c:986
VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
Queries the line of the point where the trace is at.
Definition vm_trace.c:1012
void(* rb_postponed_job_func_t)(void *arg)
Type of postponed jobs.
Definition debug.h:697
VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg)
Identical to rb_tracearg_event_flag(), except it returns the name of the event in Ruby's symbol.
Definition vm_trace.c:998
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_C_CALL
A method, written in C, is called.
Definition event.h:43
#define RUBY_EVENT_TRACEPOINT_ALL
Bitmask of extended events.
Definition event.h:62
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
Registers an event hook function.
Definition vm_trace.c:284
#define RUBY_EVENT_RAISE
Encountered a raise statement.
Definition event.h:45
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#define RUBY_EVENT_SCRIPT_COMPILED
Encountered an eval.
Definition event.h:60
#define RUBY_INTERNAL_EVENT_MASK
Bitmask of internal events.
Definition event.h:101
int rb_remove_event_hook(rb_event_hook_func_t func)
Removes the passed function from the list of event hooks.
Definition vm_trace.c:439
#define RUBY_EVENT_ALL
Bitmask of traditional events.
Definition event.h:46
#define RUBY_EVENT_THREAD_BEGIN
Encountered a new thread.
Definition event.h:57
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
void(* rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
Type of event hooks.
Definition event.h:120
#define RUBY_INTERNAL_EVENT_OBJSPACE_MASK
Bitmask of GC events.
Definition event.h:100
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
#define RUBY_EVENT_C_RETURN
Return from a method, written in C.
Definition event.h:44
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
#define RUBY_INTERNAL_EVENT_FREEOBJ
Object swept.
Definition event.h:94
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_INTERNAL_EVENT_NEWOBJ
Object allocated.
Definition event.h:93
#define RUBY_EVENT_THREAD_END
Encountered an end of a thread.
Definition event.h:58
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1032
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define ZALLOC
Old name of RB_ZALLOC.
Definition memory.h:402
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define rb_ary_new3
Old name of rb_ary_new_from_args.
Definition array.h:658
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1431
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_cThread
Thread class.
Definition vm.c:681
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:468
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:456
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:1575
VALUE rb_proc_call_with_block(VALUE recv, int argc, const VALUE *argv, VALUE proc)
Identical to rb_proc_call(), except you can additionally pass another proc object,...
Definition proc.c:1763
VALUE rb_obj_is_method(VALUE recv)
Queries if the given object is a method.
Definition proc.c:2454
VALUE rb_binding_new(void)
Snapshots the current execution context and turn it into an instance of rb_cBinding.
Definition proc.c:906
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:386
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition thread.c:3422
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1807
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1148
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1378
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY_AREF(a, i)
Definition rarray.h:402
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:81
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
Definition rtypeddata.h:435
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:604
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:238
Definition method.h:55
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
Blocks until the current thread obtains a lock.
Definition thread.c:310
void rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
Releases a lock.
Definition thread.c:316
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376