Ruby 3.4.3p32 (2025-04-14 revision d0b7e5b6a04bde21ca483d20a1546b28b401c2d4)
vm_backtrace.c
1/**********************************************************************
2
3 vm_backtrace.c -
4
5 $Author: ko1 $
6 created at: Sun Jun 03 00:14:20 2012
7
8 Copyright (C) 1993-2012 Yukihiro Matsumoto
9
10**********************************************************************/
11
12#include "eval_intern.h"
13#include "internal.h"
14#include "internal/class.h"
15#include "internal/error.h"
16#include "internal/vm.h"
17#include "iseq.h"
18#include "ruby/debug.h"
19#include "ruby/encoding.h"
20#include "vm_core.h"
21
22static VALUE rb_cBacktrace;
23static VALUE rb_cBacktraceLocation;
24
25static VALUE
26id2str(ID id)
27{
28 VALUE str = rb_id2str(id);
29 if (!str) return Qnil;
30 return str;
31}
32#define rb_id2str(id) id2str(id)
33
34#define BACKTRACE_START 0
35#define ALL_BACKTRACE_LINES -1
36
37inline static int
38calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
39{
40 VM_ASSERT(iseq);
41
42 if (pc == NULL) {
43 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_TOP) {
44 VM_ASSERT(! ISEQ_BODY(iseq)->local_table);
45 VM_ASSERT(! ISEQ_BODY(iseq)->local_table_size);
46 return 0;
47 }
48 if (lineno) *lineno = ISEQ_BODY(iseq)->location.first_lineno;
49#ifdef USE_ISEQ_NODE_ID
50 if (node_id) *node_id = -1;
51#endif
52 return 1;
53 }
54 else {
55 VM_ASSERT(ISEQ_BODY(iseq));
56 VM_ASSERT(ISEQ_BODY(iseq)->iseq_encoded);
57 VM_ASSERT(ISEQ_BODY(iseq)->iseq_size);
58
59 ptrdiff_t n = pc - ISEQ_BODY(iseq)->iseq_encoded;
60 VM_ASSERT(n >= 0);
61#if SIZEOF_PTRDIFF_T > SIZEOF_INT
62 VM_ASSERT(n <= (ptrdiff_t)UINT_MAX);
63#endif
64 VM_ASSERT((unsigned int)n <= ISEQ_BODY(iseq)->iseq_size);
65 ASSUME(n >= 0);
66 size_t pos = n; /* no overflow */
67 if (LIKELY(pos)) {
68 /* use pos-1 because PC points next instruction at the beginning of instruction */
69 pos--;
70 }
71#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
72 else {
73 /* SDR() is not possible; that causes infinite loop. */
74 rb_print_backtrace(stderr);
75 __builtin_trap();
76 }
77#endif
78 if (lineno) *lineno = rb_iseq_line_no(iseq, pos);
79#ifdef USE_ISEQ_NODE_ID
80 if (node_id) *node_id = rb_iseq_node_id(iseq, pos);
81#endif
82 return 1;
83 }
84}
85
86inline static int
87calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
88{
89 int lineno;
90 if (calc_pos(iseq, pc, &lineno, NULL)) return lineno;
91 return 0;
92}
93
94#ifdef USE_ISEQ_NODE_ID
95inline static int
96calc_node_id(const rb_iseq_t *iseq, const VALUE *pc)
97{
98 int node_id;
99 if (calc_pos(iseq, pc, NULL, &node_id)) return node_id;
100 return -1;
101}
102#endif
103
104int
105rb_vm_get_sourceline(const rb_control_frame_t *cfp)
106{
107 if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) {
108 const rb_iseq_t *iseq = cfp->iseq;
109 int line = calc_lineno(iseq, cfp->pc);
110 if (line != 0) {
111 return line;
112 }
113 else {
114 return ISEQ_BODY(iseq)->location.first_lineno;
115 }
116 }
117 else {
118 return 0;
119 }
120}
121
123 const rb_callable_method_entry_t *cme;
124 const rb_iseq_t *iseq;
125 const VALUE *pc;
126} rb_backtrace_location_t;
127
129 rb_backtrace_location_t *loc;
130 VALUE btobj;
131};
132
133static void
134location_mark(void *ptr)
135{
136 struct valued_frame_info *vfi = (struct valued_frame_info *)ptr;
137 rb_gc_mark_movable(vfi->btobj);
138}
139
140static void
141location_ref_update(void *ptr)
142{
143 struct valued_frame_info *vfi = ptr;
144 vfi->btobj = rb_gc_location(vfi->btobj);
145}
146
147static void
148location_mark_entry(rb_backtrace_location_t *fi)
149{
150 rb_gc_mark((VALUE)fi->cme);
151 if (fi->iseq) rb_gc_mark_movable((VALUE)fi->iseq);
152}
153
154static const rb_data_type_t location_data_type = {
155 "frame_info",
156 {
157 location_mark,
159 NULL, // No external memory to report,
160 location_ref_update,
161 },
162 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
163};
164
165int
166rb_frame_info_p(VALUE obj)
167{
168 return rb_typeddata_is_kind_of(obj, &location_data_type);
169}
170
171static inline rb_backtrace_location_t *
172location_ptr(VALUE locobj)
173{
174 struct valued_frame_info *vloc;
175 TypedData_Get_Struct(locobj, struct valued_frame_info, &location_data_type, vloc);
176 return vloc->loc;
177}
178
179static int
180location_lineno(rb_backtrace_location_t *loc)
181{
182 if (loc->iseq) {
183 return calc_lineno(loc->iseq, loc->pc);
184 }
185 return 0;
186}
187
188/*
189 * Returns the line number of this frame.
190 *
191 * For example, using +caller_locations.rb+ from Thread::Backtrace::Location
192 *
193 * loc = c(0..1).first
194 * loc.lineno #=> 2
195 */
196static VALUE
197location_lineno_m(VALUE self)
198{
199 return INT2FIX(location_lineno(location_ptr(self)));
200}
201
202VALUE rb_mod_name0(VALUE klass, bool *permanent);
203
204VALUE
205rb_gen_method_name(VALUE owner, VALUE name)
206{
207 bool permanent;
208 if (RB_TYPE_P(owner, T_CLASS) || RB_TYPE_P(owner, T_MODULE)) {
209 if (RCLASS_SINGLETON_P(owner)) {
210 VALUE v = RCLASS_ATTACHED_OBJECT(owner);
211 if (RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE)) {
212 v = rb_mod_name0(v, &permanent);
213 if (permanent && !NIL_P(v)) {
214 return rb_sprintf("%"PRIsVALUE".%"PRIsVALUE, v, name);
215 }
216 }
217 }
218 else {
219 owner = rb_mod_name0(owner, &permanent);
220 if (permanent && !NIL_P(owner)) {
221 return rb_sprintf("%"PRIsVALUE"#%"PRIsVALUE, owner, name);
222 }
223 }
224 }
225 return name;
226}
227
228static VALUE
229calculate_iseq_label(VALUE owner, const rb_iseq_t *iseq)
230{
231retry:
232 switch (ISEQ_BODY(iseq)->type) {
233 case ISEQ_TYPE_TOP:
234 case ISEQ_TYPE_CLASS:
235 case ISEQ_TYPE_MAIN:
236 return ISEQ_BODY(iseq)->location.label;
237 case ISEQ_TYPE_METHOD:
238 return rb_gen_method_name(owner, ISEQ_BODY(iseq)->location.label);
239 case ISEQ_TYPE_BLOCK:
240 case ISEQ_TYPE_PLAIN: {
241 int level = 0;
242 const rb_iseq_t *orig_iseq = iseq;
243 if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
244 while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
245 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
246 level++;
247 }
248 iseq = ISEQ_BODY(iseq)->parent_iseq;
249 }
250 }
251 if (level <= 1) {
252 return rb_sprintf("block in %"PRIsVALUE, calculate_iseq_label(owner, iseq));
253 }
254 else {
255 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, calculate_iseq_label(owner, iseq));
256 }
257 }
258 case ISEQ_TYPE_RESCUE:
259 case ISEQ_TYPE_ENSURE:
260 case ISEQ_TYPE_EVAL:
261 iseq = ISEQ_BODY(iseq)->parent_iseq;
262 goto retry;
263 default:
264 rb_bug("calculate_iseq_label: unreachable");
265 }
266}
267
268// Return true if a given location is a C method or supposed to behave like one.
269static inline bool
270location_cfunc_p(rb_backtrace_location_t *loc)
271{
272 if (!loc->cme) return false;
273
274 switch (loc->cme->def->type) {
275 case VM_METHOD_TYPE_CFUNC:
276 return true;
277 case VM_METHOD_TYPE_ISEQ:
278 return rb_iseq_attr_p(loc->cme->def->body.iseq.iseqptr, BUILTIN_ATTR_C_TRACE);
279 default:
280 return false;
281 }
282}
283
284static VALUE
285location_label(rb_backtrace_location_t *loc)
286{
287 if (location_cfunc_p(loc)) {
288 return rb_gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
289 }
290 else {
291 VALUE owner = Qnil;
292 if (loc->cme) {
293 owner = loc->cme->owner;
294 }
295 return calculate_iseq_label(owner, loc->iseq);
296 }
297}
298/*
299 * Returns the label of this frame.
300 *
301 * Usually consists of method, class, module, etc names with decoration.
302 *
303 * Consider the following example:
304 *
305 * def foo
306 * puts caller_locations(0).first.label
307 *
308 * 1.times do
309 * puts caller_locations(0).first.label
310 *
311 * 1.times do
312 * puts caller_locations(0).first.label
313 * end
314 * end
315 * end
316 *
317 * The result of calling +foo+ is this:
318 *
319 * foo
320 * block in foo
321 * block (2 levels) in foo
322 *
323 */
324static VALUE
325location_label_m(VALUE self)
326{
327 return location_label(location_ptr(self));
328}
329
330static VALUE
331location_base_label(rb_backtrace_location_t *loc)
332{
333 if (location_cfunc_p(loc)) {
334 return rb_id2str(loc->cme->def->original_id);
335 }
336
337 return ISEQ_BODY(loc->iseq)->location.base_label;
338}
339
340/*
341 * Returns the base label of this frame, which is usually equal to the label,
342 * without decoration.
343 *
344 * Consider the following example:
345 *
346 * def foo
347 * puts caller_locations(0).first.base_label
348 *
349 * 1.times do
350 * puts caller_locations(0).first.base_label
351 *
352 * 1.times do
353 * puts caller_locations(0).first.base_label
354 * end
355 * end
356 * end
357 *
358 * The result of calling +foo+ is this:
359 *
360 * foo
361 * foo
362 * foo
363 */
364static VALUE
365location_base_label_m(VALUE self)
366{
367 return location_base_label(location_ptr(self));
368}
369
370static const rb_iseq_t *
371location_iseq(rb_backtrace_location_t *loc)
372{
373 return loc->iseq;
374}
375
376/*
377 * Returns the file name of this frame. This will generally be an absolute
378 * path, unless the frame is in the main script, in which case it will be the
379 * script location passed on the command line.
380 *
381 * For example, using +caller_locations.rb+ from Thread::Backtrace::Location
382 *
383 * loc = c(0..1).first
384 * loc.path #=> caller_locations.rb
385 */
386static VALUE
387location_path_m(VALUE self)
388{
389 const rb_iseq_t *iseq = location_iseq(location_ptr(self));
390 return iseq ? rb_iseq_path(iseq) : Qnil;
391}
392
393#ifdef USE_ISEQ_NODE_ID
394static int
395location_node_id(rb_backtrace_location_t *loc)
396{
397 if (loc->iseq && loc->pc) {
398 return calc_node_id(loc->iseq, loc->pc);
399 }
400 return -1;
401}
402#endif
403
404int
405rb_get_node_id_from_frame_info(VALUE obj)
406{
407#ifdef USE_ISEQ_NODE_ID
408 rb_backtrace_location_t *loc = location_ptr(obj);
409 return location_node_id(loc);
410#else
411 return -1;
412#endif
413}
414
415const rb_iseq_t *
416rb_get_iseq_from_frame_info(VALUE obj)
417{
418 rb_backtrace_location_t *loc = location_ptr(obj);
419 const rb_iseq_t *iseq = location_iseq(loc);
420 return iseq;
421}
422
423static VALUE
424location_realpath(rb_backtrace_location_t *loc)
425{
426 if (loc->iseq) {
427 return rb_iseq_realpath(loc->iseq);
428 }
429 return Qnil;
430}
431
432/*
433 * Returns the full file path of this frame.
434 *
435 * Same as #path, except that it will return absolute path
436 * even if the frame is in the main script.
437 */
438static VALUE
439location_absolute_path_m(VALUE self)
440{
441 return location_realpath(location_ptr(self));
442}
443
444static VALUE
445location_format(VALUE file, int lineno, VALUE name)
446{
447 VALUE s = rb_enc_sprintf(rb_enc_compatible(file, name), "%s", RSTRING_PTR(file));
448 if (lineno != 0) {
449 rb_str_catf(s, ":%d", lineno);
450 }
451 rb_str_cat_cstr(s, ":in ");
452 if (NIL_P(name)) {
453 rb_str_cat_cstr(s, "unknown method");
454 }
455 else {
456 rb_str_catf(s, "'%s'", RSTRING_PTR(name));
457 }
458 return s;
459}
460
461static VALUE
462location_to_str(rb_backtrace_location_t *loc)
463{
464 VALUE file, owner = Qnil, name;
465 int lineno;
466
467 if (location_cfunc_p(loc)) {
468 if (loc->iseq && loc->pc) {
469 file = rb_iseq_path(loc->iseq);
470 lineno = calc_lineno(loc->iseq, loc->pc);
471 }
472 else {
473 file = GET_VM()->progname;
474 lineno = 0;
475 }
476 name = rb_gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
477 }
478 else {
479 file = rb_iseq_path(loc->iseq);
480 lineno = calc_lineno(loc->iseq, loc->pc);
481 if (loc->cme) {
482 owner = loc->cme->owner;
483 }
484 name = calculate_iseq_label(owner, loc->iseq);
485 }
486
487 return location_format(file, lineno, name);
488}
489
490/*
491 * Returns a Kernel#caller style string representing this frame.
492 */
493static VALUE
494location_to_str_m(VALUE self)
495{
496 return location_to_str(location_ptr(self));
497}
498
499/*
500 * Returns the same as calling +inspect+ on the string representation of
501 * #to_str
502 */
503static VALUE
504location_inspect_m(VALUE self)
505{
506 return rb_str_inspect(location_to_str(location_ptr(self)));
507}
508
509typedef struct rb_backtrace_struct {
510 int backtrace_size;
511 VALUE strary;
512 VALUE locary;
513 rb_backtrace_location_t backtrace[1];
514} rb_backtrace_t;
515
516static void
517backtrace_mark(void *ptr)
518{
519 rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
520 size_t i, s = bt->backtrace_size;
521
522 for (i=0; i<s; i++) {
523 location_mark_entry(&bt->backtrace[i]);
524 }
525 rb_gc_mark_movable(bt->strary);
526 rb_gc_mark_movable(bt->locary);
527}
528
529static void
530location_update_entry(rb_backtrace_location_t *fi)
531{
532 fi->cme = (rb_callable_method_entry_t *)rb_gc_location((VALUE)fi->cme);
533 if (fi->iseq) {
534 fi->iseq = (rb_iseq_t *)rb_gc_location((VALUE)fi->iseq);
535 }
536}
537
538static void
539backtrace_update(void *ptr)
540{
541 rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
542 size_t i, s = bt->backtrace_size;
543
544 for (i=0; i<s; i++) {
545 location_update_entry(&bt->backtrace[i]);
546 }
547 bt->strary = rb_gc_location(bt->strary);
548 bt->locary = rb_gc_location(bt->locary);
549}
550
551static const rb_data_type_t backtrace_data_type = {
552 "backtrace",
553 {
554 backtrace_mark,
556 NULL, // No external memory to report,
557 backtrace_update,
558 },
559 /* Cannot set the RUBY_TYPED_EMBEDDABLE flag because the loc of frame_info
560 * points elements in the backtrace array. This can cause the loc to become
561 * incorrect if this backtrace object is moved by compaction. */
562 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
563};
564
565int
566rb_backtrace_p(VALUE obj)
567{
568 return rb_typeddata_is_kind_of(obj, &backtrace_data_type);
569}
570
571static VALUE
572backtrace_alloc(VALUE klass)
573{
574 rb_backtrace_t *bt;
575 VALUE obj = TypedData_Make_Struct(klass, rb_backtrace_t, &backtrace_data_type, bt);
576 return obj;
577}
578
579static VALUE
580backtrace_alloc_capa(long num_frames, rb_backtrace_t **backtrace)
581{
582 size_t memsize = offsetof(rb_backtrace_t, backtrace) + num_frames * sizeof(rb_backtrace_location_t);
583 VALUE btobj = rb_data_typed_object_zalloc(rb_cBacktrace, memsize, &backtrace_data_type);
584 TypedData_Get_Struct(btobj, rb_backtrace_t, &backtrace_data_type, *backtrace);
585 return btobj;
586}
587
588
589static long
590backtrace_size(const rb_execution_context_t *ec)
591{
592 const rb_control_frame_t *last_cfp = ec->cfp;
593 const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
594
595 if (start_cfp == NULL) {
596 return -1;
597 }
598
599 start_cfp =
600 RUBY_VM_NEXT_CONTROL_FRAME(
601 RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
602
603 if (start_cfp < last_cfp) {
604 return 0;
605 }
606
607 return start_cfp - last_cfp + 1;
608}
609
610static bool
611is_internal_location(const rb_control_frame_t *cfp)
612{
613 static const char prefix[] = "<internal:";
614 const size_t prefix_len = sizeof(prefix) - 1;
615 VALUE file = rb_iseq_path(cfp->iseq);
616 return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0;
617}
618
619static bool
620is_rescue_or_ensure_frame(const rb_control_frame_t *cfp)
621{
622 enum rb_iseq_type type = ISEQ_BODY(cfp->iseq)->type;
623 return type == ISEQ_TYPE_RESCUE || type == ISEQ_TYPE_ENSURE;
624}
625
626static void
627bt_update_cfunc_loc(unsigned long cfunc_counter, rb_backtrace_location_t *cfunc_loc, const rb_iseq_t *iseq, const VALUE *pc)
628{
629 for (; cfunc_counter > 0; cfunc_counter--, cfunc_loc--) {
630 cfunc_loc->iseq = iseq;
631 cfunc_loc->pc = pc;
632 }
633}
634
635static VALUE location_create(rb_backtrace_location_t *srcloc, void *btobj);
636
637static void
638bt_yield_loc(rb_backtrace_location_t *loc, long num_frames, VALUE btobj)
639{
640 for (; num_frames > 0; num_frames--, loc++) {
641 rb_yield(location_create(loc, (void *)btobj));
642 }
643}
644
645static VALUE
646rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_frame, long num_frames, int* start_too_large, bool skip_internal, bool do_yield)
647{
648 const rb_control_frame_t *cfp = ec->cfp;
649 const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
650 ptrdiff_t size;
651 rb_backtrace_t *bt = NULL;
652 VALUE btobj = Qnil;
653 rb_backtrace_location_t *loc = NULL;
654 unsigned long cfunc_counter = 0;
655 bool skip_next_frame = FALSE;
656
657 // In the case the thread vm_stack or cfp is not initialized, there is no backtrace.
658 if (end_cfp == NULL) {
659 num_frames = 0;
660 }
661 else {
662 end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
663
664 /*
665 * top frame (dummy) <- RUBY_VM_END_CONTROL_FRAME
666 * top frame (dummy) <- end_cfp
667 * top frame <- main script
668 * top frame
669 * ...
670 * 2nd frame <- lev:0
671 * current frame <- ec->cfp
672 */
673
674 size = end_cfp - cfp + 1;
675 if (size < 0) {
676 num_frames = 0;
677 }
678 else if (num_frames < 0 || num_frames > size) {
679 num_frames = size;
680 }
681 }
682
683 btobj = backtrace_alloc_capa(num_frames, &bt);
684
685 bt->backtrace_size = 0;
686 if (num_frames == 0) {
687 if (start_too_large) *start_too_large = 0;
688 return btobj;
689 }
690
691 for (; cfp != end_cfp && (bt->backtrace_size < num_frames); cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
692 if (cfp->iseq) {
693 if (cfp->pc) {
694 if (start_frame > 0) {
695 start_frame--;
696 }
697 else if (!(skip_internal && is_internal_location(cfp))) {
698 if (!skip_next_frame) {
699 const rb_iseq_t *iseq = cfp->iseq;
700 const VALUE *pc = cfp->pc;
701 loc = &bt->backtrace[bt->backtrace_size++];
702 RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp));
703 // Ruby methods with `Primitive.attr! :c_trace` should behave like C methods
704 if (rb_iseq_attr_p(cfp->iseq, BUILTIN_ATTR_C_TRACE)) {
705 loc->iseq = NULL;
706 loc->pc = NULL;
707 cfunc_counter++;
708 }
709 else {
710 RB_OBJ_WRITE(btobj, &loc->iseq, iseq);
711 loc->pc = pc;
712 bt_update_cfunc_loc(cfunc_counter, loc-1, iseq, pc);
713 if (do_yield) {
714 bt_yield_loc(loc - cfunc_counter, cfunc_counter+1, btobj);
715 }
716 cfunc_counter = 0;
717 }
718 }
719 skip_next_frame = is_rescue_or_ensure_frame(cfp);
720 }
721 }
722 }
723 else {
724 VM_ASSERT(RUBYVM_CFUNC_FRAME_P(cfp));
725 if (start_frame > 0) {
726 start_frame--;
727 }
728 else {
729 loc = &bt->backtrace[bt->backtrace_size++];
730 RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp));
731 loc->iseq = NULL;
732 loc->pc = NULL;
733 cfunc_counter++;
734 }
735 }
736 }
737
738 // When a backtrace entry corresponds to a method defined in C (e.g. rb_define_method), the reported file:line
739 // is the one of the caller Ruby frame, so if the last entry is a C frame we find the caller Ruby frame here.
740 if (cfunc_counter > 0) {
741 for (; cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
742 if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp))) {
743 VM_ASSERT(!skip_next_frame); // ISEQ_TYPE_RESCUE/ISEQ_TYPE_ENSURE should have a caller Ruby ISEQ, not a cfunc
744 bt_update_cfunc_loc(cfunc_counter, loc, cfp->iseq, cfp->pc);
745 RB_OBJ_WRITTEN(btobj, Qundef, cfp->iseq);
746 if (do_yield) {
747 bt_yield_loc(loc - cfunc_counter, cfunc_counter, btobj);
748 }
749 break;
750 }
751 }
752 }
753
754 if (start_too_large) *start_too_large = (start_frame > 0 ? -1 : 0);
755 return btobj;
756}
757
758VALUE
759rb_ec_backtrace_object(const rb_execution_context_t *ec)
760{
761 return rb_ec_partial_backtrace_object(ec, BACKTRACE_START, ALL_BACKTRACE_LINES, NULL, FALSE, FALSE);
762}
763
764static VALUE
765backtrace_collect(rb_backtrace_t *bt, VALUE (*func)(rb_backtrace_location_t *, void *arg), void *arg)
766{
767 VALUE btary;
768 int i;
769
770 btary = rb_ary_new2(bt->backtrace_size);
771
772 for (i=0; i<bt->backtrace_size; i++) {
773 rb_backtrace_location_t *loc = &bt->backtrace[i];
774 rb_ary_push(btary, func(loc, arg));
775 }
776
777 return btary;
778}
779
780static VALUE
781location_to_str_dmyarg(rb_backtrace_location_t *loc, void *dmy)
782{
783 return location_to_str(loc);
784}
785
786static VALUE
787backtrace_to_str_ary(VALUE self)
788{
789 VALUE r;
790 rb_backtrace_t *bt;
791 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
792 r = backtrace_collect(bt, location_to_str_dmyarg, 0);
793 RB_GC_GUARD(self);
794 return r;
795}
796
797VALUE
798rb_backtrace_to_str_ary(VALUE self)
799{
800 rb_backtrace_t *bt;
801 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
802
803 if (!bt->strary) {
804 RB_OBJ_WRITE(self, &bt->strary, backtrace_to_str_ary(self));
805 }
806 return bt->strary;
807}
808
809void
810rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self)
811{
812 rb_backtrace_t *bt;
813 rb_backtrace_location_t *loc;
814
815 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
816 VM_ASSERT(bt->backtrace_size > 0);
817
818 loc = &bt->backtrace[0];
819
820 VM_ASSERT(!loc->cme || loc->cme->def->type == VM_METHOD_TYPE_ISEQ);
821
822 loc->pc = NULL; // means location.first_lineno
823}
824
825static VALUE
826location_create(rb_backtrace_location_t *srcloc, void *btobj)
827{
828 VALUE obj;
829 struct valued_frame_info *vloc;
830 obj = TypedData_Make_Struct(rb_cBacktraceLocation, struct valued_frame_info, &location_data_type, vloc);
831
832 vloc->loc = srcloc;
833 RB_OBJ_WRITE(obj, &vloc->btobj, (VALUE)btobj);
834
835 return obj;
836}
837
838static VALUE
839backtrace_to_location_ary(VALUE self)
840{
841 VALUE r;
842 rb_backtrace_t *bt;
843 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
844 r = backtrace_collect(bt, location_create, (void *)self);
845 RB_GC_GUARD(self);
846 return r;
847}
848
849VALUE
850rb_backtrace_to_location_ary(VALUE self)
851{
852 rb_backtrace_t *bt;
853 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
854
855 if (!bt->locary) {
856 RB_OBJ_WRITE(self, &bt->locary, backtrace_to_location_ary(self));
857 }
858 return bt->locary;
859}
860
861VALUE
862rb_location_ary_to_backtrace(VALUE ary)
863{
864 if (!RB_TYPE_P(ary, T_ARRAY) || !rb_frame_info_p(RARRAY_AREF(ary, 0))) {
865 return Qfalse;
866 }
867
868 rb_backtrace_t *new_backtrace;
869 long num_frames = RARRAY_LEN(ary);
870 VALUE btobj = backtrace_alloc_capa(num_frames, &new_backtrace);
871
872 for (long index = 0; index < RARRAY_LEN(ary); index++) {
873 VALUE locobj = RARRAY_AREF(ary, index);
874
875 if (!rb_frame_info_p(locobj)) {
876 return Qfalse;
877 }
878
879 struct valued_frame_info *src_vloc;
880 TypedData_Get_Struct(locobj, struct valued_frame_info, &location_data_type, src_vloc);
881
882 rb_backtrace_location_t *dst_location = &new_backtrace->backtrace[index];
883 RB_OBJ_WRITE(btobj, &dst_location->cme, src_vloc->loc->cme);
884 RB_OBJ_WRITE(btobj, &dst_location->iseq, src_vloc->loc->iseq);
885 dst_location->pc = src_vloc->loc->pc;
886
887 new_backtrace->backtrace_size++;
888
889 RB_GC_GUARD(locobj);
890 }
891
892 return btobj;
893}
894
895static VALUE
896backtrace_dump_data(VALUE self)
897{
898 VALUE str = rb_backtrace_to_str_ary(self);
899 return str;
900}
901
902static VALUE
903backtrace_load_data(VALUE self, VALUE str)
904{
905 rb_backtrace_t *bt;
906 TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt);
907 RB_OBJ_WRITE(self, &bt->strary, str);
908 return self;
909}
910
911/*
912 * call-seq: Thread::Backtrace::limit -> integer
913 *
914 * Returns maximum backtrace length set by <tt>--backtrace-limit</tt>
915 * command-line option. The default is <tt>-1</tt> which means unlimited
916 * backtraces. If the value is zero or positive, the error backtraces,
917 * produced by Exception#full_message, are abbreviated and the extra lines
918 * are replaced by <tt>... 3 levels... </tt>
919 *
920 * $ ruby -r net/http -e "p Thread::Backtrace.limit; Net::HTTP.get(URI('http://wrong.address'))"
921 * - 1
922 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': Failed to open TCP connection to wrong.address:80 (getaddrinfo: Name or service not known) (SocketError)
923 * from .../lib/ruby/3.1.0/socket.rb:227:in `foreach'
924 * from .../lib/ruby/3.1.0/socket.rb:632:in `tcp'
925 * from .../lib/ruby/3.1.0/net/http.rb:998:in `connect'
926 * from .../lib/ruby/3.1.0/net/http.rb:976:in `do_start'
927 * from .../lib/ruby/3.1.0/net/http.rb:965:in `start'
928 * from .../lib/ruby/3.1.0/net/http.rb:627:in `start'
929 * from .../lib/ruby/3.1.0/net/http.rb:503:in `get_response'
930 * from .../lib/ruby/3.1.0/net/http.rb:474:in `get'
931 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': getaddrinfo: Name or service not known (SocketError)
932 * from .../lib/ruby/3.1.0/socket.rb:227:in `foreach'
933 * from .../lib/ruby/3.1.0/socket.rb:632:in `tcp'
934 * from .../lib/ruby/3.1.0/net/http.rb:998:in `connect'
935 * from .../lib/ruby/3.1.0/net/http.rb:976:in `do_start'
936 * from .../lib/ruby/3.1.0/net/http.rb:965:in `start'
937 * from .../lib/ruby/3.1.0/net/http.rb:627:in `start'
938 * from .../lib/ruby/3.1.0/net/http.rb:503:in `get_response'
939 * from .../lib/ruby/3.1.0/net/http.rb:474:in `get'
940 * from -e:1:in `<main>'
941 *
942 * $ ruby --backtrace-limit 2 -r net/http -e "p Thread::Backtrace.limit; Net::HTTP.get(URI('http://wrong.address'))"
943 * 2
944 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': Failed to open TCP connection to wrong.address:80 (getaddrinfo: Name or service not known) (SocketError)
945 * from .../lib/ruby/3.1.0/socket.rb:227:in `foreach'
946 * from .../lib/ruby/3.1.0/socket.rb:632:in `tcp'
947 * ... 7 levels...
948 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': getaddrinfo: Name or service not known (SocketError)
949 * from .../lib/ruby/3.1.0/socket.rb:227:in `foreach'
950 * from .../lib/ruby/3.1.0/socket.rb:632:in `tcp'
951 * ... 7 levels...
952 *
953 * $ ruby --backtrace-limit 0 -r net/http -e "p Thread::Backtrace.limit; Net::HTTP.get(URI('http://wrong.address'))"
954 * 0
955 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': Failed to open TCP connection to wrong.address:80 (getaddrinfo: Name or service not known) (SocketError)
956 * ... 9 levels...
957 * .../lib/ruby/3.1.0/socket.rb:227:in `getaddrinfo': getaddrinfo: Name or service not known (SocketError)
958 * ... 9 levels...
959 *
960 */
961static VALUE
962backtrace_limit(VALUE self)
963{
964 return LONG2NUM(rb_backtrace_length_limit);
965}
966
967VALUE
968rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n)
969{
970 return rb_backtrace_to_str_ary(rb_ec_partial_backtrace_object(ec, lev, n, NULL, FALSE, FALSE));
971}
972
973VALUE
974rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n, bool skip_internal)
975{
976 return rb_backtrace_to_location_ary(rb_ec_partial_backtrace_object(ec, lev, n, NULL, skip_internal, FALSE));
977}
978
979/* make old style backtrace directly */
980
981static void
982backtrace_each(const rb_execution_context_t *ec,
983 void (*init)(void *arg, size_t size),
984 void (*iter_iseq)(void *arg, const rb_control_frame_t *cfp),
985 void (*iter_cfunc)(void *arg, const rb_control_frame_t *cfp, ID mid),
986 void *arg)
987{
988 const rb_control_frame_t *last_cfp = ec->cfp;
989 const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
990 const rb_control_frame_t *cfp;
991 ptrdiff_t size, i;
992
993 // In the case the thread vm_stack or cfp is not initialized, there is no backtrace.
994 if (start_cfp == NULL) {
995 init(arg, 0);
996 return;
997 }
998
999 /* <- start_cfp (end control frame)
1000 * top frame (dummy)
1001 * top frame (dummy)
1002 * top frame <- start_cfp
1003 * top frame
1004 * ...
1005 * 2nd frame <- lev:0
1006 * current frame <- ec->cfp
1007 */
1008
1009 start_cfp =
1010 RUBY_VM_NEXT_CONTROL_FRAME(
1011 RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
1012
1013 if (start_cfp < last_cfp) {
1014 size = 0;
1015 }
1016 else {
1017 size = start_cfp - last_cfp + 1;
1018 }
1019
1020 init(arg, size);
1021
1022 /* SDR(); */
1023 for (i=0, cfp = start_cfp; i<size; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
1024 /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */
1025 if (cfp->iseq) {
1026 if (cfp->pc) {
1027 iter_iseq(arg, cfp);
1028 }
1029 }
1030 else {
1031 VM_ASSERT(RUBYVM_CFUNC_FRAME_P(cfp));
1032 const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
1033 ID mid = me->def->original_id;
1034
1035 iter_cfunc(arg, cfp, mid);
1036 }
1037 }
1038}
1039
1041 VALUE filename;
1042 int lineno;
1043 void (*func)(void *data, VALUE file, int lineno, VALUE name);
1044 void *data; /* result */
1045};
1046
1047static void
1048oldbt_init(void *ptr, size_t dmy)
1049{
1050 struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
1051 arg->filename = GET_VM()->progname;
1052 arg->lineno = 0;
1053}
1054
1055static void
1056oldbt_iter_iseq(void *ptr, const rb_control_frame_t *cfp)
1057{
1058 const rb_iseq_t *iseq = cfp->iseq;
1059 const VALUE *pc = cfp->pc;
1060 struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
1061 VALUE file = arg->filename = rb_iseq_path(iseq);
1062 VALUE name = ISEQ_BODY(iseq)->location.label;
1063 int lineno = arg->lineno = calc_lineno(iseq, pc);
1064
1065 (arg->func)(arg->data, file, lineno, name);
1066}
1067
1068static void
1069oldbt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid)
1070{
1071 struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
1072 VALUE file = arg->filename;
1073 VALUE name = rb_id2str(mid);
1074 int lineno = arg->lineno;
1075
1076 (arg->func)(arg->data, file, lineno, name);
1077}
1078
1079static void
1080oldbt_print(void *data, VALUE file, int lineno, VALUE name)
1081{
1082 FILE *fp = (FILE *)data;
1083
1084 if (NIL_P(name)) {
1085 fprintf(fp, "\tfrom %s:%d:in unknown method\n",
1086 RSTRING_PTR(file), lineno);
1087 }
1088 else {
1089 fprintf(fp, "\tfrom %s:%d:in '%s'\n",
1090 RSTRING_PTR(file), lineno, RSTRING_PTR(name));
1091 }
1092}
1093
1094static void
1095vm_backtrace_print(FILE *fp)
1096{
1097 struct oldbt_arg arg;
1098
1099 arg.func = oldbt_print;
1100 arg.data = (void *)fp;
1101 backtrace_each(GET_EC(),
1102 oldbt_init,
1103 oldbt_iter_iseq,
1104 oldbt_iter_cfunc,
1105 &arg);
1106}
1107
1109 FILE *fp;
1110 int count;
1111};
1112
1113static void
1114oldbt_bugreport(void *arg, VALUE file, int line, VALUE method)
1115{
1116 struct oldbt_bugreport_arg *p = arg;
1117 FILE *fp = p->fp;
1118 const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
1119 if (!p->count) {
1120 fprintf(fp, "-- Ruby level backtrace information "
1121 "----------------------------------------\n");
1122 p->count = 1;
1123 }
1124 if (NIL_P(method)) {
1125 fprintf(fp, "%s:%d:in unknown method\n", filename, line);
1126 }
1127 else {
1128 fprintf(fp, "%s:%d:in '%s'\n", filename, line, RSTRING_PTR(method));
1129 }
1130}
1131
1132void
1133rb_backtrace_print_as_bugreport(FILE *fp)
1134{
1135 struct oldbt_arg arg;
1136 struct oldbt_bugreport_arg barg = {fp, 0};
1137
1138 arg.func = oldbt_bugreport;
1139 arg.data = &barg;
1140
1141 backtrace_each(GET_EC(),
1142 oldbt_init,
1143 oldbt_iter_iseq,
1144 oldbt_iter_cfunc,
1145 &arg);
1146}
1147
1148void
1150{
1151 vm_backtrace_print(stderr);
1152}
1153
1155 VALUE (*iter)(VALUE recv, VALUE str);
1156 VALUE output;
1157};
1158
1159static void
1160oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
1161{
1162 const struct print_to_arg *arg = data;
1163 VALUE str = rb_sprintf("\tfrom %"PRIsVALUE":%d:in ", file, lineno);
1164
1165 if (NIL_P(name)) {
1166 rb_str_cat2(str, "unknown method\n");
1167 }
1168 else {
1169 rb_str_catf(str, " '%"PRIsVALUE"'\n", name);
1170 }
1171 (*arg->iter)(arg->output, str);
1172}
1173
1174void
1175rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output)
1176{
1177 struct oldbt_arg arg;
1178 struct print_to_arg parg;
1179
1180 parg.iter = iter;
1181 parg.output = output;
1182 arg.func = oldbt_print_to;
1183 arg.data = &parg;
1184 backtrace_each(GET_EC(),
1185 oldbt_init,
1186 oldbt_iter_iseq,
1187 oldbt_iter_cfunc,
1188 &arg);
1189}
1190
1191VALUE
1192rb_make_backtrace(void)
1193{
1194 return rb_ec_backtrace_str_ary(GET_EC(), BACKTRACE_START, ALL_BACKTRACE_LINES);
1195}
1196
1197static long
1198ec_backtrace_range(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, long *len_ptr)
1199{
1200 VALUE level, vn, opts;
1201 long lev, n;
1202
1203 rb_scan_args(argc, argv, "02:", &level, &vn, &opts);
1204
1205 if (!NIL_P(opts)) {
1206 rb_get_kwargs(opts, (ID []){0}, 0, 0, NULL);
1207 }
1208 if (argc == 2 && NIL_P(vn)) argc--;
1209
1210 switch (argc) {
1211 case 0:
1212 lev = lev_default + lev_plus;
1213 n = ALL_BACKTRACE_LINES;
1214 break;
1215 case 1:
1216 {
1217 long beg, len, bt_size = backtrace_size(ec);
1218 switch (rb_range_beg_len(level, &beg, &len, bt_size - lev_plus, 0)) {
1219 case Qfalse:
1220 lev = NUM2LONG(level);
1221 if (lev < 0) {
1222 rb_raise(rb_eArgError, "negative level (%ld)", lev);
1223 }
1224 lev += lev_plus;
1225 n = ALL_BACKTRACE_LINES;
1226 break;
1227 case Qnil:
1228 return -1;
1229 default:
1230 lev = beg + lev_plus;
1231 n = len;
1232 break;
1233 }
1234 break;
1235 }
1236 case 2:
1237 lev = NUM2LONG(level);
1238 n = NUM2LONG(vn);
1239 if (lev < 0) {
1240 rb_raise(rb_eArgError, "negative level (%ld)", lev);
1241 }
1242 if (n < 0) {
1243 rb_raise(rb_eArgError, "negative size (%ld)", n);
1244 }
1245 lev += lev_plus;
1246 break;
1247 default:
1248 lev = n = 0; /* to avoid warning */
1249 break;
1250 }
1251
1252 *len_ptr = n;
1253 return lev;
1254}
1255
1256static VALUE
1257ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
1258{
1259 long lev, n;
1260 VALUE btval, r;
1261 int too_large;
1262
1263 lev = ec_backtrace_range(ec, argc, argv, lev_default, lev_plus, &n);
1264 if (lev < 0) return Qnil;
1265
1266 if (n == 0) {
1267 return rb_ary_new();
1268 }
1269
1270 btval = rb_ec_partial_backtrace_object(ec, lev, n, &too_large, FALSE, FALSE);
1271
1272 if (too_large) {
1273 return Qnil;
1274 }
1275
1276 if (to_str) {
1277 r = backtrace_to_str_ary(btval);
1278 }
1279 else {
1280 r = backtrace_to_location_ary(btval);
1281 }
1282 RB_GC_GUARD(btval);
1283 return r;
1284}
1285
1286static VALUE
1287thread_backtrace_to_ary(int argc, const VALUE *argv, VALUE thval, int to_str)
1288{
1289 rb_thread_t *target_th = rb_thread_ptr(thval);
1290
1291 if (target_th->to_kill || target_th->status == THREAD_KILLED)
1292 return Qnil;
1293
1294 return ec_backtrace_to_ary(target_th->ec, argc, argv, 0, 0, to_str);
1295}
1296
1297VALUE
1298rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval)
1299{
1300 return thread_backtrace_to_ary(argc, argv, thval, 1);
1301}
1302
1303VALUE
1304rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
1305{
1306 return thread_backtrace_to_ary(argc, argv, thval, 0);
1307}
1308
1309VALUE
1310rb_vm_backtrace(int argc, const VALUE * argv, struct rb_execution_context_struct * ec)
1311{
1312 return ec_backtrace_to_ary(ec, argc, argv, 0, 0, 1);
1313}
1314
1315VALUE
1316rb_vm_backtrace_locations(int argc, const VALUE * argv, struct rb_execution_context_struct * ec)
1317{
1318 return ec_backtrace_to_ary(ec, argc, argv, 0, 0, 0);
1319}
1320
1321/*
1322 * call-seq:
1323 * caller(start=1, length=nil) -> array or nil
1324 * caller(range) -> array or nil
1325 *
1326 * Returns the current execution stack---an array containing strings in
1327 * the form <code>file:line</code> or <code>file:line: in
1328 * `method'</code>.
1329 *
1330 * The optional _start_ parameter determines the number of initial stack
1331 * entries to omit from the top of the stack.
1332 *
1333 * A second optional +length+ parameter can be used to limit how many entries
1334 * are returned from the stack.
1335 *
1336 * Returns +nil+ if _start_ is greater than the size of
1337 * current execution stack.
1338 *
1339 * Optionally you can pass a range, which will return an array containing the
1340 * entries within the specified range.
1341 *
1342 * def a(skip)
1343 * caller(skip)
1344 * end
1345 * def b(skip)
1346 * a(skip)
1347 * end
1348 * def c(skip)
1349 * b(skip)
1350 * end
1351 * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10:in `<main>'"]
1352 * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11:in `<main>'"]
1353 * c(2) #=> ["prog:8:in `c'", "prog:12:in `<main>'"]
1354 * c(3) #=> ["prog:13:in `<main>'"]
1355 * c(4) #=> []
1356 * c(5) #=> nil
1357 */
1358
1359static VALUE
1360rb_f_caller(int argc, VALUE *argv, VALUE _)
1361{
1362 return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 1);
1363}
1364
1365/*
1366 * call-seq:
1367 * caller_locations(start=1, length=nil) -> array or nil
1368 * caller_locations(range) -> array or nil
1369 *
1370 * Returns the current execution stack---an array containing
1371 * backtrace location objects.
1372 *
1373 * See Thread::Backtrace::Location for more information.
1374 *
1375 * The optional _start_ parameter determines the number of initial stack
1376 * entries to omit from the top of the stack.
1377 *
1378 * A second optional +length+ parameter can be used to limit how many entries
1379 * are returned from the stack.
1380 *
1381 * Returns +nil+ if _start_ is greater than the size of
1382 * current execution stack.
1383 *
1384 * Optionally you can pass a range, which will return an array containing the
1385 * entries within the specified range.
1386 */
1387static VALUE
1388rb_f_caller_locations(int argc, VALUE *argv, VALUE _)
1389{
1390 return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 0);
1391}
1392
1393/*
1394 * call-seq:
1395 * Thread.each_caller_location(...) { |loc| ... } -> nil
1396 *
1397 * Yields each frame of the current execution stack as a
1398 * backtrace location object.
1399 */
1400static VALUE
1401each_caller_location(int argc, VALUE *argv, VALUE _)
1402{
1403 rb_execution_context_t *ec = GET_EC();
1404 long n, lev = ec_backtrace_range(ec, argc, argv, 1, 1, &n);
1405 if (lev >= 0 && n != 0) {
1406 rb_ec_partial_backtrace_object(ec, lev, n, NULL, FALSE, TRUE);
1407 }
1408 return Qnil;
1409}
1410
1411/* called from Init_vm() in vm.c */
1412void
1413Init_vm_backtrace(void)
1414{
1415 /*
1416 * An internal representation of the backtrace. The user will never interact with
1417 * objects of this class directly, but class methods can be used to get backtrace
1418 * settings of the current session.
1419 */
1420 rb_cBacktrace = rb_define_class_under(rb_cThread, "Backtrace", rb_cObject);
1421 rb_define_alloc_func(rb_cBacktrace, backtrace_alloc);
1422 rb_undef_method(CLASS_OF(rb_cBacktrace), "new");
1423 rb_marshal_define_compat(rb_cBacktrace, rb_cArray, backtrace_dump_data, backtrace_load_data);
1424 rb_define_singleton_method(rb_cBacktrace, "limit", backtrace_limit, 0);
1425
1426 /*
1427 * An object representation of a stack frame, initialized by
1428 * Kernel#caller_locations.
1429 *
1430 * For example:
1431 *
1432 * # caller_locations.rb
1433 * def a(skip)
1434 * caller_locations(skip)
1435 * end
1436 * def b(skip)
1437 * a(skip)
1438 * end
1439 * def c(skip)
1440 * b(skip)
1441 * end
1442 *
1443 * c(0..2).map do |call|
1444 * puts call.to_s
1445 * end
1446 *
1447 * Running <code>ruby caller_locations.rb</code> will produce:
1448 *
1449 * caller_locations.rb:2:in `a'
1450 * caller_locations.rb:5:in `b'
1451 * caller_locations.rb:8:in `c'
1452 *
1453 * Here's another example with a slightly different result:
1454 *
1455 * # foo.rb
1456 * class Foo
1457 * attr_accessor :locations
1458 * def initialize(skip)
1459 * @locations = caller_locations(skip)
1460 * end
1461 * end
1462 *
1463 * Foo.new(0..2).locations.map do |call|
1464 * puts call.to_s
1465 * end
1466 *
1467 * Now run <code>ruby foo.rb</code> and you should see:
1468 *
1469 * init.rb:4:in `initialize'
1470 * init.rb:8:in `new'
1471 * init.rb:8:in `<main>'
1472 */
1473 rb_cBacktraceLocation = rb_define_class_under(rb_cBacktrace, "Location", rb_cObject);
1474 rb_undef_alloc_func(rb_cBacktraceLocation);
1475 rb_undef_method(CLASS_OF(rb_cBacktraceLocation), "new");
1476 rb_define_method(rb_cBacktraceLocation, "lineno", location_lineno_m, 0);
1477 rb_define_method(rb_cBacktraceLocation, "label", location_label_m, 0);
1478 rb_define_method(rb_cBacktraceLocation, "base_label", location_base_label_m, 0);
1479 rb_define_method(rb_cBacktraceLocation, "path", location_path_m, 0);
1480 rb_define_method(rb_cBacktraceLocation, "absolute_path", location_absolute_path_m, 0);
1481 rb_define_method(rb_cBacktraceLocation, "to_s", location_to_str_m, 0);
1482 rb_define_method(rb_cBacktraceLocation, "inspect", location_inspect_m, 0);
1483
1484 rb_define_global_function("caller", rb_f_caller, -1);
1485 rb_define_global_function("caller_locations", rb_f_caller_locations, -1);
1486
1487 rb_define_singleton_method(rb_cThread, "each_caller_location", each_caller_location, -1);
1488}
1489
1490/* debugger API */
1491
1492RUBY_SYMBOL_EXPORT_BEGIN
1493
1494RUBY_SYMBOL_EXPORT_END
1495
1497 rb_execution_context_t *ec;
1498 rb_control_frame_t *cfp;
1499 VALUE backtrace;
1500 VALUE contexts; /* [[klass, binding, iseq, cfp], ...] */
1501 long backtrace_size;
1502};
1503
1504enum {
1505 CALLER_BINDING_SELF,
1506 CALLER_BINDING_CLASS,
1507 CALLER_BINDING_BINDING,
1508 CALLER_BINDING_ISEQ,
1509 CALLER_BINDING_CFP,
1510 CALLER_BINDING_DEPTH,
1511};
1512
1514 VALUE ary;
1515 const rb_execution_context_t *ec;
1516};
1517
1518static void
1519collect_caller_bindings_init(void *arg, size_t size)
1520{
1521 /* */
1522}
1523
1524static VALUE
1525get_klass(const rb_control_frame_t *cfp)
1526{
1527 VALUE klass;
1528 if (rb_vm_control_frame_id_and_class(cfp, 0, 0, &klass)) {
1529 if (RB_TYPE_P(klass, T_ICLASS)) {
1530 return RBASIC(klass)->klass;
1531 }
1532 else {
1533 return klass;
1534 }
1535 }
1536 else {
1537 return Qnil;
1538 }
1539}
1540
1541static int
1542frame_depth(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
1543{
1544 VM_ASSERT(RUBY_VM_END_CONTROL_FRAME(ec) >= cfp);
1545 return (int)(RUBY_VM_END_CONTROL_FRAME(ec) - cfp);
1546}
1547
1548static void
1549collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp)
1550{
1552 VALUE frame = rb_ary_new2(6);
1553
1554 rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
1555 rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
1556 rb_ary_store(frame, CALLER_BINDING_BINDING, GC_GUARDED_PTR(cfp)); /* create later */
1557 rb_ary_store(frame, CALLER_BINDING_ISEQ, cfp->iseq ? (VALUE)cfp->iseq : Qnil);
1558 rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
1559 rb_ary_store(frame, CALLER_BINDING_DEPTH, INT2FIX(frame_depth(data->ec, cfp)));
1560
1561 rb_ary_push(data->ary, frame);
1562}
1563
1564static void
1565collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
1566{
1568 VALUE frame = rb_ary_new2(6);
1569
1570 rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
1571 rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
1572 rb_ary_store(frame, CALLER_BINDING_BINDING, Qnil); /* not available */
1573 rb_ary_store(frame, CALLER_BINDING_ISEQ, Qnil); /* not available */
1574 rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
1575 rb_ary_store(frame, CALLER_BINDING_DEPTH, INT2FIX(frame_depth(data->ec, cfp)));
1576
1577 rb_ary_push(data->ary, frame);
1578}
1579
1580static VALUE
1581collect_caller_bindings(const rb_execution_context_t *ec)
1582{
1583 int i;
1584 VALUE result;
1585 struct collect_caller_bindings_data data = {
1586 rb_ary_new(), ec
1587 };
1588
1589 backtrace_each(ec,
1590 collect_caller_bindings_init,
1591 collect_caller_bindings_iseq,
1592 collect_caller_bindings_cfunc,
1593 &data);
1594
1595 result = rb_ary_reverse(data.ary);
1596
1597 /* bindings should be created from top of frame */
1598 for (i=0; i<RARRAY_LEN(result); i++) {
1599 VALUE entry = rb_ary_entry(result, i);
1600 VALUE cfp_val = rb_ary_entry(entry, CALLER_BINDING_BINDING);
1601
1602 if (!NIL_P(cfp_val)) {
1603 rb_control_frame_t *cfp = GC_GUARDED_PTR_REF(cfp_val);
1604 rb_ary_store(entry, CALLER_BINDING_BINDING, rb_vm_make_binding(ec, cfp));
1605 }
1606 }
1607
1608 return result;
1609}
1610
1611/*
1612 * Note that the passed `rb_debug_inspector_t' will be disabled
1613 * after `rb_debug_inspector_open'.
1614 */
1615
1616VALUE
1618{
1619 rb_debug_inspector_t dbg_context;
1620 rb_execution_context_t *ec = GET_EC();
1621 enum ruby_tag_type state;
1622 volatile VALUE MAYBE_UNUSED(result);
1623
1624 /* escape all env to heap */
1625 rb_vm_stack_to_heap(ec);
1626
1627 dbg_context.ec = ec;
1628 dbg_context.cfp = dbg_context.ec->cfp;
1629 dbg_context.backtrace = rb_ec_backtrace_location_ary(ec, BACKTRACE_START, ALL_BACKTRACE_LINES, FALSE);
1630 dbg_context.backtrace_size = RARRAY_LEN(dbg_context.backtrace);
1631 dbg_context.contexts = collect_caller_bindings(ec);
1632
1633 EC_PUSH_TAG(ec);
1634 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1635 result = (*func)(&dbg_context, data);
1636 }
1637 EC_POP_TAG();
1638
1639 /* invalidate bindings? */
1640
1641 if (state) {
1642 EC_JUMP_TAG(ec, state);
1643 }
1644
1645 return result;
1646}
1647
1648static VALUE
1649frame_get(const rb_debug_inspector_t *dc, long index)
1650{
1651 if (index < 0 || index >= dc->backtrace_size) {
1652 rb_raise(rb_eArgError, "no such frame");
1653 }
1654 return rb_ary_entry(dc->contexts, index);
1655}
1656
1657VALUE
1659{
1660 VALUE frame = frame_get(dc, index);
1661 return rb_ary_entry(frame, CALLER_BINDING_SELF);
1662}
1663
1664VALUE
1666{
1667 VALUE frame = frame_get(dc, index);
1668 return rb_ary_entry(frame, CALLER_BINDING_CLASS);
1669}
1670
1671VALUE
1673{
1674 VALUE frame = frame_get(dc, index);
1675 return rb_ary_entry(frame, CALLER_BINDING_BINDING);
1676}
1677
1678VALUE
1680{
1681 VALUE frame = frame_get(dc, index);
1682 VALUE iseq = rb_ary_entry(frame, CALLER_BINDING_ISEQ);
1683
1684 return RTEST(iseq) ? rb_iseqw_new((rb_iseq_t *)iseq) : Qnil;
1685}
1686
1687VALUE
1689{
1690 VALUE frame = frame_get(dc, index);
1691 return rb_ary_entry(frame, CALLER_BINDING_DEPTH);
1692}
1693
1694VALUE
1696{
1697 rb_execution_context_t *ec = GET_EC();
1698 return INT2FIX(frame_depth(ec, ec->cfp));
1699}
1700
1701VALUE
1703{
1704 return dc->backtrace;
1705}
1706
1707static int
1708thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *buff, int *lines)
1709{
1710 int i;
1711 const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
1712 const rb_control_frame_t *top = cfp;
1713 const rb_callable_method_entry_t *cme;
1714
1715 // If this function is called inside a thread after thread creation, but
1716 // before the CFP has been created, just return 0. This can happen when
1717 // sampling via signals. Threads can be interrupted randomly by the
1718 // signal, including during the time after the thread has been created, but
1719 // before the CFP has been allocated
1720 if (!cfp) {
1721 return 0;
1722 }
1723
1724 // Skip dummy frame; see `rb_ec_partial_backtrace_object` for details
1725 end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp);
1726
1727 for (i=0; i<limit && cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
1728 if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc != 0) {
1729 if (start > 0) {
1730 start--;
1731 continue;
1732 }
1733
1734 /* record frame info */
1735 cme = rb_vm_frame_method_entry(cfp);
1736 if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) {
1737 buff[i] = (VALUE)cme;
1738 }
1739 else {
1740 buff[i] = (VALUE)cfp->iseq;
1741 }
1742
1743 if (lines) {
1744 // The topmost frame may not have an updated PC because the JIT
1745 // may not have set one. The JIT compiler will update the PC
1746 // before entering a new function (so that `caller` will work),
1747 // so only the topmost frame could possibly have an out of date PC
1748 if (cfp == top && cfp->jit_return) {
1749 lines[i] = 0;
1750 }
1751 else {
1752 lines[i] = calc_lineno(cfp->iseq, cfp->pc);
1753 }
1754 }
1755
1756 i++;
1757 }
1758 else {
1759 cme = rb_vm_frame_method_entry(cfp);
1760 if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
1761 if (start > 0) {
1762 start--;
1763 continue;
1764 }
1765 buff[i] = (VALUE)cme;
1766 if (lines) lines[i] = 0;
1767 i++;
1768 }
1769 }
1770 }
1771
1772 return i;
1773}
1774
1775int
1776rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
1777{
1778 rb_execution_context_t *ec = rb_current_execution_context(false);
1779
1780 // If there is no EC, we may be attempting to profile a non-Ruby thread or a
1781 // M:N shared native thread which has no active Ruby thread.
1782 if (!ec) {
1783 return 0;
1784 }
1785
1786 return thread_profile_frames(ec, start, limit, buff, lines);
1787}
1788
1789int
1790rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines)
1791{
1792 rb_thread_t *th = rb_thread_ptr(thread);
1793 return thread_profile_frames(th->ec, start, limit, buff, lines);
1794}
1795
1796static const rb_iseq_t *
1797frame2iseq(VALUE frame)
1798{
1799 if (NIL_P(frame)) return NULL;
1800
1801 if (RB_TYPE_P(frame, T_IMEMO)) {
1802 switch (imemo_type(frame)) {
1803 case imemo_iseq:
1804 return (const rb_iseq_t *)frame;
1805 case imemo_ment:
1806 {
1807 const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
1808 switch (cme->def->type) {
1809 case VM_METHOD_TYPE_ISEQ:
1810 return cme->def->body.iseq.iseqptr;
1811 default:
1812 return NULL;
1813 }
1814 }
1815 default:
1816 break;
1817 }
1818 }
1819 rb_bug("frame2iseq: unreachable");
1820}
1821
1822VALUE
1824{
1825 const rb_iseq_t *iseq = frame2iseq(frame);
1826 return iseq ? rb_iseq_path(iseq) : Qnil;
1827}
1828
1829static const rb_callable_method_entry_t *
1830cframe(VALUE frame)
1831{
1832 if (NIL_P(frame)) return NULL;
1833
1834 if (RB_TYPE_P(frame, T_IMEMO)) {
1835 switch (imemo_type(frame)) {
1836 case imemo_ment:
1837 {
1838 const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
1839 switch (cme->def->type) {
1840 case VM_METHOD_TYPE_CFUNC:
1841 return cme;
1842 default:
1843 return NULL;
1844 }
1845 }
1846 default:
1847 return NULL;
1848 }
1849 }
1850
1851 return NULL;
1852}
1853
1854VALUE
1856{
1857 if (cframe(frame)) {
1858 static VALUE cfunc_str = Qfalse;
1859 if (!cfunc_str) {
1860 cfunc_str = rb_str_new_literal("<cfunc>");
1861 rb_vm_register_global_object(cfunc_str);
1862 }
1863 return cfunc_str;
1864 }
1865 const rb_iseq_t *iseq = frame2iseq(frame);
1866 return iseq ? rb_iseq_realpath(iseq) : Qnil;
1867}
1868
1869VALUE
1871{
1872 const rb_iseq_t *iseq = frame2iseq(frame);
1873 return iseq ? rb_iseq_label(iseq) : Qnil;
1874}
1875
1876VALUE
1878{
1879 const rb_iseq_t *iseq = frame2iseq(frame);
1880 return iseq ? rb_iseq_base_label(iseq) : Qnil;
1881}
1882
1883VALUE
1885{
1886 const rb_iseq_t *iseq = frame2iseq(frame);
1887 return iseq ? rb_iseq_first_lineno(iseq) : Qnil;
1888}
1889
1890static VALUE
1891frame2klass(VALUE frame)
1892{
1893 if (NIL_P(frame)) return Qnil;
1894
1895 if (RB_TYPE_P(frame, T_IMEMO)) {
1896 const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
1897
1898 if (imemo_type(frame) == imemo_ment) {
1899 return cme->defined_class;
1900 }
1901 }
1902 return Qnil;
1903}
1904
1905VALUE
1907{
1908 VALUE klass = frame2klass(frame);
1909
1910 if (klass && !NIL_P(klass)) {
1911 if (RB_TYPE_P(klass, T_ICLASS)) {
1912 klass = RBASIC(klass)->klass;
1913 }
1914 else if (RCLASS_SINGLETON_P(klass)) {
1915 klass = RCLASS_ATTACHED_OBJECT(klass);
1916 if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
1917 return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
1918 }
1919 return rb_class_path(klass);
1920 }
1921 else {
1922 return Qnil;
1923 }
1924}
1925
1926VALUE
1928{
1929 VALUE klass = frame2klass(frame);
1930
1931 return RBOOL(klass && !NIL_P(klass) && RCLASS_SINGLETON_P(klass));
1932}
1933
1934VALUE
1936{
1937 const rb_callable_method_entry_t *cme = cframe(frame);
1938 if (cme) {
1939 ID mid = cme->def->original_id;
1940 return id2str(mid);
1941 }
1942 const rb_iseq_t *iseq = frame2iseq(frame);
1943 return iseq ? rb_iseq_method_name(iseq) : Qnil;
1944}
1945
1946static VALUE
1947qualified_method_name(VALUE frame, VALUE method_name)
1948{
1949 if (method_name != Qnil) {
1950 VALUE classpath = rb_profile_frame_classpath(frame);
1951 VALUE singleton_p = rb_profile_frame_singleton_method_p(frame);
1952
1953 if (classpath != Qnil) {
1954 return rb_sprintf("%"PRIsVALUE"%s%"PRIsVALUE,
1955 classpath, singleton_p == Qtrue ? "." : "#", method_name);
1956 }
1957 else {
1958 return method_name;
1959 }
1960 }
1961 else {
1962 return Qnil;
1963 }
1964}
1965
1966VALUE
1968{
1969 VALUE method_name = rb_profile_frame_method_name(frame);
1970
1971 return qualified_method_name(frame, method_name);
1972}
1973
1974VALUE
1976{
1977 const rb_callable_method_entry_t *cme = cframe(frame);
1978 if (cme) {
1979 ID mid = cme->def->original_id;
1980 VALUE method_name = id2str(mid);
1981 return qualified_method_name(frame, method_name);
1982 }
1983
1984 VALUE label = rb_profile_frame_label(frame);
1985 VALUE base_label = rb_profile_frame_base_label(frame);
1986 VALUE qualified_method_name = rb_profile_frame_qualified_method_name(frame);
1987
1988 if (NIL_P(qualified_method_name) || base_label == qualified_method_name) {
1989 return label;
1990 }
1991 else {
1992 long label_length = RSTRING_LEN(label);
1993 long base_label_length = RSTRING_LEN(base_label);
1994 int prefix_len = rb_long2int(label_length - base_label_length);
1995
1996 return rb_sprintf("%.*s%"PRIsVALUE, prefix_len, RSTRING_PTR(label), qualified_method_name);
1997 }
1998}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
int rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
Queries mysterious "frame"s of the given range.
VALUE rb_profile_frame_full_label(VALUE frame)
Identical to rb_profile_frame_label(), except it returns a qualified result.
struct rb_debug_inspector_struct rb_debug_inspector_t
Opaque struct representing a debug inspector.
Definition debug.h:207
VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
Queries the instruction sequence of the passed context's upper frame.
VALUE rb_debug_inspector_current_depth(void)
Return current frmae depth.
VALUE rb_profile_frame_method_name(VALUE frame)
Queries the name of the method of the passed frame.
VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index)
Queries the depth of the passed context's upper frame.
VALUE rb_profile_frame_qualified_method_name(VALUE frame)
Identical to rb_profile_frame_method_name(), except it "qualifies" the return value with its defining...
VALUE rb_profile_frame_label(VALUE frame)
Queries human-readable "label" string.
VALUE rb_profile_frame_singleton_method_p(VALUE frame)
Queries if the method of the passed frame is a singleton class.
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc)
Queries the backtrace object of the context.
VALUE rb_profile_frame_absolute_path(VALUE frame)
Identical to rb_profile_frame_path(), except it tries to expand the returning path.
VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data)
Prepares, executes, then cleans up a debug session.
VALUE rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index)
Queries the current receiver of the passed context's upper frame.
VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index)
Queries the binding of the passed context's upper frame.
VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index)
Queries the current class of the passed context's upper frame.
VALUE rb_profile_frame_classpath(VALUE frame)
Queries the class path of the method that the passed frame represents.
VALUE rb_profile_frame_path(VALUE frame)
Queries the path of the passed backtrace.
VALUE rb_profile_frame_first_lineno(VALUE frame)
Queries the first line of the method of the passed frame pointer.
int rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines)
Queries mysterious "frame"s of the given range.
VALUE(* rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void *data)
Type of the callback function passed to rb_debug_inspector_open().
Definition debug.h:218
VALUE rb_profile_frame_base_label(VALUE frame)
Identical to rb_profile_frame_label(), except it does not "qualify" the result.
VALUE rb_enc_sprintf(rb_encoding *enc, const char *fmt,...)
Identical to rb_sprintf(), except it additionally takes an encoding.
Definition sprintf.c:1198
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1012
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2166
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:2635
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
Definition class.c:2424
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1683
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:203
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#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.
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define rb_ary_new2
Old name of rb_ary_new_capa.
Definition array.h:657
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1380
VALUE rb_cArray
Array class.
Definition array.c:40
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:247
VALUE rb_cThread
Thread class.
Definition vm.c:544
#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:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
Encoding relates APIs.
VALUE rb_range_beg_len(VALUE range, long *begp, long *lenp, long len, int err)
Deconstructs a numerical range.
Definition range.c:1892
#define rb_str_new_literal(str)
Just another name of rb_str_new_lit.
Definition string.h:1750
VALUE rb_str_inspect(VALUE str)
Generates a "readable" version of the receiver.
Definition string.c:7201
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1656
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
Definition variable.c:373
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1291
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
void rb_backtrace(void)
Prints the backtrace out to the standard error.
int len
Length of the buffer.
Definition io.h:8
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1354
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE(*dumper)(VALUE), VALUE(*loader)(VALUE, VALUE))
Marshal format compatibility layer.
Definition marshal.c:134
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:79
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:515
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:197
#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:497
const char * rb_class2name(VALUE klass)
Queries the name of the passed class.
Definition variable.c:498
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
const rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
Definition method.h:135
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