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