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