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