1 /**********************************************************************
7 Copyright (C) 2004-2007 Koichi Sasada
9 **********************************************************************/
11 #include "ruby/internal/config.h"
13 #ifdef HAVE_UCONTEXT_H
14 # include <ucontext.h>
18 # ifdef HAVE_LIBPROC_H
21 # include <mach/vm_map.h>
22 # include <mach/mach_init.h>
24 # define vm_region_recurse vm_region_recurse_64
26 /* that is defined in sys/queue.h, and conflicts with
31 #include "addr2line.h"
33 #include "internal/gc.h"
34 #include "internal/variable.h"
35 #include "internal/vm.h"
38 #include "ractor_core.h"
40 #define MAX_POSBUF 128
42 #define VM_CFP_CNT(ec, cfp) \
43 ((rb_control_frame_t *)((ec)->vm_stack + (ec)->vm_stack_size) - \
44 (rb_control_frame_t *)(cfp))
46 const char *rb_method_type_name(rb_method_type_t type
);
49 #define kprintf(...) if (fprintf(errout, __VA_ARGS__) < 0) goto error
50 #define kputs(s) if (fputs(s, errout) < 0) goto error
53 control_frame_dump(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, FILE *errout
)
56 ptrdiff_t ep
= cfp
->ep
- ec
->vm_stack
;
57 char ep_in_heap
= ' ';
58 char posbuf
[MAX_POSBUF
+1];
60 const char *magic
, *iseq_name
= "-", *selfstr
= "-", *biseq_name
= "-";
62 const rb_iseq_t
*iseq
= NULL
;
63 const rb_callable_method_entry_t
*me
= rb_vm_frame_method_entry(cfp
);
65 if (ep
< 0 || (size_t)ep
> ec
->vm_stack_size
) {
66 ep
= (ptrdiff_t)cfp
->ep
;
70 switch (VM_FRAME_TYPE(cfp
)) {
71 case VM_FRAME_MAGIC_TOP
:
74 case VM_FRAME_MAGIC_METHOD
:
77 case VM_FRAME_MAGIC_CLASS
:
80 case VM_FRAME_MAGIC_BLOCK
:
83 case VM_FRAME_MAGIC_CFUNC
:
86 case VM_FRAME_MAGIC_IFUNC
:
89 case VM_FRAME_MAGIC_EVAL
:
92 case VM_FRAME_MAGIC_RESCUE
:
95 case VM_FRAME_MAGIC_DUMMY
:
107 tmp
= rb_inspect(cfp
->self
);
108 selfstr
= StringValueCStr(tmp
);
114 if (cfp
->iseq
!= 0) {
115 #define RUBY_VM_IFUNC_P(ptr) IMEMO_TYPE_P(ptr, imemo_ifunc)
116 if (RUBY_VM_IFUNC_P(cfp
->iseq
)) {
117 iseq_name
= "<ifunc>";
119 else if (SYMBOL_P((VALUE
)cfp
->iseq
)) {
120 tmp
= rb_sym2str((VALUE
)cfp
->iseq
);
121 iseq_name
= RSTRING_PTR(tmp
);
122 snprintf(posbuf
, MAX_POSBUF
, ":%s", iseq_name
);
128 pc
= cfp
->pc
- ISEQ_BODY(iseq
)->iseq_encoded
;
129 iseq_name
= RSTRING_PTR(ISEQ_BODY(iseq
)->location
.label
);
130 line
= rb_vm_get_sourceline(cfp
);
132 snprintf(posbuf
, MAX_POSBUF
, "%s:%d", RSTRING_PTR(rb_iseq_path(iseq
)), line
);
136 iseq_name
= "<dummy_frame>";
140 else if (me
!= NULL
) {
141 iseq_name
= rb_id2name(me
->def
->original_id
);
142 snprintf(posbuf
, MAX_POSBUF
, ":%s", iseq_name
);
146 kprintf("c:%04"PRIdPTRDIFF
" ",
147 ((rb_control_frame_t
*)(ec
->vm_stack
+ ec
->vm_stack_size
) - cfp
));
152 kprintf("p:%04"PRIdPTRDIFF
" ", pc
);
154 kprintf("s:%04"PRIdPTRDIFF
" ", cfp
->sp
- ec
->vm_stack
);
155 kprintf(ep_in_heap
== ' ' ? "e:%06"PRIdPTRDIFF
" " : "E:%06"PRIxPTRDIFF
" ", ep
% 10000);
156 kprintf("%-6s", magic
);
158 kprintf(" %s", posbuf
);
160 if (VM_FRAME_FINISHED_P(cfp
)) {
161 kprintf(" [FINISH]");
165 kprintf("iseq: %-24s ", iseq_name
);
166 kprintf("self: %-24s ", selfstr
);
167 kprintf("%-1s ", biseq_name
);
171 // additional information for CI machines
176 if (IMEMO_TYPE_P(me
, imemo_ment
)) {
178 kprintf(" called_id: %s, type: %s\n", rb_id2name(me
->called_id
), rb_method_type_name(me
->def
->type
));
179 kprintf(" owner class: %s\n", rb_raw_obj_info(buff
, 0x100, me
->owner
));
180 if (me
->owner
!= me
->defined_class
) {
181 kprintf(" defined_class: %s\n", rb_raw_obj_info(buff
, 0x100, me
->defined_class
));
185 kprintf(" me is corrupted (%s)\n", rb_raw_obj_info(buff
, 0x100, (VALUE
)me
));
189 kprintf(" self: %s\n", rb_raw_obj_info(buff
, 0x100, cfp
->self
));
192 if (ISEQ_BODY(iseq
)->local_table_size
> 0) {
193 kprintf(" lvars:\n");
194 for (unsigned int i
=0; i
<ISEQ_BODY(iseq
)->local_table_size
; i
++) {
195 const VALUE
*argv
= cfp
->ep
- ISEQ_BODY(cfp
->iseq
)->local_table_size
- VM_ENV_DATA_SIZE
+ 1;
197 rb_id2name(ISEQ_BODY(iseq
)->local_table
[i
]),
198 rb_raw_obj_info(buff
, 0x100, argv
[i
]));
209 rb_vmdebug_stack_dump_raw(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, FILE *errout
)
213 const VALUE
*ep
= cfp
->ep
;
216 kprintf("-- stack frame ------------\n");
217 for (p
= st
= ec
->vm_stack
; p
< sp
; p
++) {
218 kprintf("%04ld (%p): %08"PRIxVALUE
, (long)(p
- st
), p
, *p
);
221 if (ec
->vm_stack
<= t
&& t
< sp
) {
222 kprintf(" (= %ld)", (long)((VALUE
*)GC_GUARDED_PTR_REF((VALUE
)t
) - ec
->vm_stack
));
232 kprintf("-- Control frame information "
233 "-----------------------------------------------\n");
234 while ((void *)cfp
< (void *)(ec
->vm_stack
+ ec
->vm_stack_size
)) {
235 control_frame_dump(ec
, cfp
, errout
);
246 rb_vmdebug_stack_dump_raw_current(void)
248 const rb_execution_context_t
*ec
= GET_EC();
249 return rb_vmdebug_stack_dump_raw(ec
, ec
->cfp
, stderr
);
253 rb_vmdebug_env_dump_raw(const rb_env_t
*env
, const VALUE
*ep
, FILE *errout
)
256 kprintf("-- env --------------------\n");
260 for (i
= 0; i
< env
->env_size
; i
++) {
261 kprintf("%04d: %08"PRIxVALUE
" (%p)", i
, env
->env
[i
], (void *)&env
->env
[i
]);
262 if (&env
->env
[i
] == ep
) kprintf(" <- ep");
266 env
= rb_vm_env_prev_env(env
);
268 kprintf("---------------------------\n");
276 rb_vmdebug_proc_dump_raw(rb_proc_t
*proc
, FILE *errout
)
280 VALUE val
= rb_inspect(vm_block_self(&proc
->block
));
281 selfstr
= StringValueCStr(val
);
283 kprintf("-- proc -------------------\n");
284 kprintf("self: %s\n", selfstr
);
285 env
= VM_ENV_ENVVAL_PTR(vm_block_ep(&proc
->block
));
286 rb_vmdebug_env_dump_raw(env
, vm_block_ep(&proc
->block
), errout
);
294 rb_vmdebug_stack_dump_th(VALUE thval
, FILE *errout
)
296 rb_thread_t
*target_th
= rb_thread_ptr(thval
);
297 return rb_vmdebug_stack_dump_raw(target_th
->ec
, target_th
->ec
->cfp
, errout
);
302 /* copy from vm_insnhelper.c */
304 vm_base_ptr(const rb_control_frame_t
*cfp
)
306 const rb_control_frame_t
*prev_cfp
= RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp
);
307 const VALUE
*bp
= prev_cfp
->sp
+ ISEQ_BODY(cfp
->iseq
)->local_table_size
+ VM_ENV_DATA_SIZE
;
309 if (ISEQ_BODY(cfp
->iseq
)->type
== ISEQ_TYPE_METHOD
|| VM_FRAME_BMETHOD_P(cfp
)) {
316 vm_stack_dump_each(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, FILE *errout
)
318 int i
, argc
= 0, local_table_size
= 0;
321 const VALUE
*ep
= cfp
->ep
;
323 if (VM_FRAME_RUBYFRAME_P(cfp
)) {
324 const rb_iseq_t
*iseq
= cfp
->iseq
;
325 argc
= ISEQ_BODY(iseq
)->param
.lead_num
;
326 local_table_size
= ISEQ_BODY(iseq
)->local_table_size
;
329 /* stack trace header */
331 if (VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_METHOD
||
332 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_TOP
||
333 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_BLOCK
||
334 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_CLASS
||
335 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_CFUNC
||
336 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_IFUNC
||
337 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_EVAL
||
338 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_RESCUE
)
340 const VALUE
*ptr
= ep
- local_table_size
;
342 control_frame_dump(ec
, cfp
, errout
);
344 for (i
= 0; i
< argc
; i
++) {
345 rstr
= rb_inspect(*ptr
);
346 kprintf(" arg %2d: %8s (%p)\n", i
, StringValueCStr(rstr
),
349 for (; i
< local_table_size
- 1; i
++) {
350 rstr
= rb_inspect(*ptr
);
351 kprintf(" local %2d: %8s (%p)\n", i
, StringValueCStr(rstr
),
355 ptr
= vm_base_ptr(cfp
);
356 for (; ptr
< sp
; ptr
++, i
++) {
357 switch (TYPE(*ptr
)) {
359 rstr
= rb_str_new2("undef");
362 rstr
= rb_str_new2("imemo"); /* TODO: can put mode detail information */
365 rstr
= rb_inspect(*ptr
);
368 kprintf(" stack %2d: %8s (%"PRIdPTRDIFF
")\n", i
, StringValueCStr(rstr
),
369 (ptr
- ec
->vm_stack
));
372 else if (VM_FRAME_FINISHED_P(cfp
)) {
373 if (ec
->vm_stack
+ ec
->vm_stack_size
> (VALUE
*)(cfp
+ 1)) {
374 vm_stack_dump_each(ec
, cfp
+ 1, errout
);
381 rb_bug("unsupported frame type: %08lx", VM_FRAME_TYPE(cfp
));
387 rb_vmdebug_debug_print_register(const rb_execution_context_t
*ec
, FILE *errout
)
389 rb_control_frame_t
*cfp
= ec
->cfp
;
391 ptrdiff_t ep
= cfp
->ep
- ec
->vm_stack
;
394 if (VM_FRAME_RUBYFRAME_P(cfp
)) {
395 pc
= cfp
->pc
- ISEQ_BODY(cfp
->iseq
)->iseq_encoded
;
398 if (ep
< 0 || (size_t)ep
> ec
->vm_stack_size
) {
402 cfpi
= ((rb_control_frame_t
*)(ec
->vm_stack
+ ec
->vm_stack_size
)) - cfp
;
403 kprintf(" [PC] %04"PRIdPTRDIFF
", [SP] %04"PRIdPTRDIFF
", [EP] %04"PRIdPTRDIFF
", [CFP] %04"PRIdPTRDIFF
"\n",
404 pc
, (cfp
->sp
- ec
->vm_stack
), ep
, cfpi
);
412 rb_vmdebug_thread_dump_regs(VALUE thval
, FILE *errout
)
414 return rb_vmdebug_debug_print_register(rb_thread_ptr(thval
)->ec
, errout
);
418 rb_vmdebug_debug_print_pre(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, const VALUE
*_pc
, FILE *errout
)
420 const rb_iseq_t
*iseq
= cfp
->iseq
;
423 ptrdiff_t pc
= _pc
- ISEQ_BODY(iseq
)->iseq_encoded
;
426 for (i
=0; i
<(int)VM_CFP_CNT(ec
, cfp
); i
++) {
430 if(0) kprintf("[%03ld] ", (long)(cfp
->sp
- ec
->vm_stack
));
432 /* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(ec, cfp)); */
434 const VALUE
*iseq_original
= rb_iseq_original_iseq((rb_iseq_t
*)iseq
);
436 rb_iseq_disasm_insn(0, iseq_original
, (size_t)pc
, iseq
, 0);
442 rb_vmdebug_debug_print_register(errout
, ec
);
451 rb_vmdebug_debug_print_post(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, FILE *errout
)
454 if (!rb_vmdebug_stack_dump_raw(ec
, cfp
, errout
)) goto errout
;
459 rb_vmdebug_debug_print_register(errout
, ec
);
461 /* stack_dump_raw(ec, cfp); */
464 /* stack_dump_thobj(ec); */
465 vm_stack_dump_each(ec
, ec
->cfp
, errout
);
468 ("--------------------------------------------------------------\n");
479 rb_vmdebug_thread_dump_state(FILE *errout
, VALUE self
)
481 rb_thread_t
*th
= rb_thread_ptr(self
);
482 rb_control_frame_t
*cfp
= th
->ec
->cfp
;
484 kprintf("Thread state dump:\n");
485 kprintf("pc : %p, sp : %p\n", (void *)cfp
->pc
, (void *)cfp
->sp
);
486 kprintf("cfp: %p, ep : %p\n", (void *)cfp
, (void *)cfp
->ep
);
492 #if defined __APPLE__
494 # define MCTX_SS_REG(reg) __ss.__##reg
496 # define MCTX_SS_REG(reg) ss.reg
500 #if defined(HAVE_BACKTRACE)
501 # define USE_BACKTRACE 1
502 # ifdef HAVE_LIBUNWIND
504 # define backtrace unw_backtrace
505 # elif defined(__APPLE__) && defined(HAVE_LIBUNWIND_H)
506 # define UNW_LOCAL_ONLY
507 # include <libunwind.h>
508 # include <sys/mman.h>
511 backtrace(void **trace
, int size
)
513 unw_cursor_t cursor
; unw_context_t uc
;
518 unw_init_local(&cursor
, &uc
);
519 # if defined(__x86_64__)
520 while (unw_step(&cursor
) > 0) {
521 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
522 trace
[n
++] = (void *)ip
;
525 unw_get_proc_name(&cursor
, buf
, 256, &ip
);
526 if (strncmp("_sigtramp", buf
, sizeof("_sigtramp")) == 0) {
527 goto darwin_sigtramp
;
533 /* darwin's bundled libunwind doesn't support signal trampoline */
538 /* get previous frame information from %rbx at _sigtramp and set values to cursor
539 * https://www.opensource.apple.com/source/Libc/Libc-825.25/i386/sys/_sigtramp.s
540 * https://www.opensource.apple.com/source/libunwind/libunwind-35.1/src/unw_getcontext.s
542 unw_get_reg(&cursor
, UNW_X86_64_RBX
, &ip
);
543 uctx
= (ucontext_t
*)ip
;
544 unw_set_reg(&cursor
, UNW_X86_64_RAX
, uctx
->uc_mcontext
->MCTX_SS_REG(rax
));
545 unw_set_reg(&cursor
, UNW_X86_64_RBX
, uctx
->uc_mcontext
->MCTX_SS_REG(rbx
));
546 unw_set_reg(&cursor
, UNW_X86_64_RCX
, uctx
->uc_mcontext
->MCTX_SS_REG(rcx
));
547 unw_set_reg(&cursor
, UNW_X86_64_RDX
, uctx
->uc_mcontext
->MCTX_SS_REG(rdx
));
548 unw_set_reg(&cursor
, UNW_X86_64_RDI
, uctx
->uc_mcontext
->MCTX_SS_REG(rdi
));
549 unw_set_reg(&cursor
, UNW_X86_64_RSI
, uctx
->uc_mcontext
->MCTX_SS_REG(rsi
));
550 unw_set_reg(&cursor
, UNW_X86_64_RBP
, uctx
->uc_mcontext
->MCTX_SS_REG(rbp
));
551 unw_set_reg(&cursor
, UNW_X86_64_RSP
, 8+(uctx
->uc_mcontext
->MCTX_SS_REG(rsp
)));
552 unw_set_reg(&cursor
, UNW_X86_64_R8
, uctx
->uc_mcontext
->MCTX_SS_REG(r8
));
553 unw_set_reg(&cursor
, UNW_X86_64_R9
, uctx
->uc_mcontext
->MCTX_SS_REG(r9
));
554 unw_set_reg(&cursor
, UNW_X86_64_R10
, uctx
->uc_mcontext
->MCTX_SS_REG(r10
));
555 unw_set_reg(&cursor
, UNW_X86_64_R11
, uctx
->uc_mcontext
->MCTX_SS_REG(r11
));
556 unw_set_reg(&cursor
, UNW_X86_64_R12
, uctx
->uc_mcontext
->MCTX_SS_REG(r12
));
557 unw_set_reg(&cursor
, UNW_X86_64_R13
, uctx
->uc_mcontext
->MCTX_SS_REG(r13
));
558 unw_set_reg(&cursor
, UNW_X86_64_R14
, uctx
->uc_mcontext
->MCTX_SS_REG(r14
));
559 unw_set_reg(&cursor
, UNW_X86_64_R15
, uctx
->uc_mcontext
->MCTX_SS_REG(r15
));
560 ip
= uctx
->uc_mcontext
->MCTX_SS_REG(rip
);
562 /* There are 4 cases for SEGV:
563 * (1) called invalid address
564 * (2) read or write invalid address
565 * (3) received signal
568 * (1) called invalid address
569 * In this case, saved ip is invalid address.
570 * It needs to just save the address for the information,
571 * skip the frame, and restore the frame calling the
572 * invalid address from %rsp.
573 * The problem is how to check whether the ip is valid or not.
574 * This code uses mincore(2) and assume the address's page is
575 * incore/referenced or not reflects the problem.
576 * Note that High Sierra's mincore(2) may return -128.
577 * (2) read or write invalid address
578 * saved ip is valid. just restart backtracing.
579 * (3) received signal in user space
581 * (4) received signal in kernel
582 * In this case saved ip points just after syscall, but registers are
583 * already overwritten by kernel. To fix register consistency,
584 * skip libc's kernel wrapper.
585 * To detect this case, just previous two bytes of ip is "\x0f\x05",
586 * syscall instruction of x86_64.
588 r
= mincore((const void *)ip
, 1, vec
);
589 if (r
|| vec
[0] <= 0 || memcmp((const char *)ip
-2, "\x0f\x05", 2) == 0) {
590 /* if segv is caused by invalid call or signal received in syscall */
591 /* the frame is invalid; skip */
592 trace
[n
++] = (void *)ip
;
593 ip
= *(unw_word_t
*)uctx
->uc_mcontext
->MCTX_SS_REG(rsp
);
596 trace
[n
++] = (void *)ip
;
597 unw_set_reg(&cursor
, UNW_REG_IP
, ip
);
599 while (unw_step(&cursor
) > 0) {
600 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
601 trace
[n
++] = (void *)ip
;
605 # else /* defined(__arm64__) */
606 /* Since Darwin arm64's _sigtramp is implemented as normal function,
607 * unwind can unwind frames without special code.
608 * https://github.com/apple/darwin-libplatform/blob/215b09856ab5765b7462a91be7076183076600df/src/setjmp/generic/sigtramp.c
610 while (unw_step(&cursor
) > 0) {
611 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
612 // Strip Arm64's pointer authentication.
613 // https://developer.apple.com/documentation/security/preparing_your_app_to_work_with_pointer_authentication
614 // I wish I could use "ptrauth_strip()" but I get an error:
615 // "this target does not support pointer authentication"
616 trace
[n
++] = (void *)(ip
& 0x7fffffffffffull
);
621 # elif defined(BROKEN_BACKTRACE)
622 # undef USE_BACKTRACE
623 # define USE_BACKTRACE 0
626 # define USE_BACKTRACE 0
630 # include <execinfo.h>
631 #elif defined(_WIN32)
632 # include <imagehlp.h>
633 # ifndef SYMOPT_DEBUG
634 # define SYMOPT_DEBUG 0x80000000
636 # ifndef MAX_SYM_NAME
637 # define MAX_SYM_NAME 2000
645 DWORD ThCallbackStack
;
646 DWORD ThCallbackBStore
;
649 DWORD64 KiCallUserMode
;
650 DWORD64 KeUserCallbackDispatcher
;
651 DWORD64 SystemRangeStart
;
652 DWORD64 KiUserExceptionDispatcher
;
659 ADDRESS64 AddrReturn
;
662 ADDRESS64 AddrBStore
;
663 void *FuncTableEntry
;
694 typedef void *PREAD_PROCESS_MEMORY_ROUTINE64
;
695 typedef void *PFUNCTION_TABLE_ACCESS_ROUTINE64
;
696 typedef void *PGET_MODULE_BASE_ROUTINE64
;
697 typedef void *PTRANSLATE_ADDRESS_ROUTINE64
;
700 struct dump_thead_arg
{
706 dump_thread(void *arg
)
709 BOOL (WINAPI
*pSymInitialize
)(HANDLE
, const char *, BOOL
);
710 BOOL (WINAPI
*pSymCleanup
)(HANDLE
);
711 BOOL (WINAPI
*pStackWalk64
)(DWORD
, HANDLE
, HANDLE
, STACKFRAME64
*, void *, PREAD_PROCESS_MEMORY_ROUTINE64
, PFUNCTION_TABLE_ACCESS_ROUTINE64
, PGET_MODULE_BASE_ROUTINE64
, PTRANSLATE_ADDRESS_ROUTINE64
);
712 DWORD64 (WINAPI
*pSymGetModuleBase64
)(HANDLE
, DWORD64
);
713 BOOL (WINAPI
*pSymFromAddr
)(HANDLE
, DWORD64
, DWORD64
*, SYMBOL_INFO
*);
714 BOOL (WINAPI
*pSymGetLineFromAddr64
)(HANDLE
, DWORD64
, DWORD
*, IMAGEHLP_LINE64
*);
715 HANDLE (WINAPI
*pOpenThread
)(DWORD
, BOOL
, DWORD
);
716 DWORD tid
= ((struct dump_thead_arg
*)arg
)->tid
;
717 FILE *errout
= ((struct dump_thead_arg
*)arg
)->errout
;
721 dbghelp
= LoadLibrary("dbghelp.dll");
722 if (!dbghelp
) return;
723 pSymInitialize
= (BOOL (WINAPI
*)(HANDLE
, const char *, BOOL
))GetProcAddress(dbghelp
, "SymInitialize");
724 pSymCleanup
= (BOOL (WINAPI
*)(HANDLE
))GetProcAddress(dbghelp
, "SymCleanup");
725 pStackWalk64
= (BOOL (WINAPI
*)(DWORD
, HANDLE
, HANDLE
, STACKFRAME64
*, void *, PREAD_PROCESS_MEMORY_ROUTINE64
, PFUNCTION_TABLE_ACCESS_ROUTINE64
, PGET_MODULE_BASE_ROUTINE64
, PTRANSLATE_ADDRESS_ROUTINE64
))GetProcAddress(dbghelp
, "StackWalk64");
726 pSymGetModuleBase64
= (DWORD64 (WINAPI
*)(HANDLE
, DWORD64
))GetProcAddress(dbghelp
, "SymGetModuleBase64");
727 pSymFromAddr
= (BOOL (WINAPI
*)(HANDLE
, DWORD64
, DWORD64
*, SYMBOL_INFO
*))GetProcAddress(dbghelp
, "SymFromAddr");
728 pSymGetLineFromAddr64
= (BOOL (WINAPI
*)(HANDLE
, DWORD64
, DWORD
*, IMAGEHLP_LINE64
*))GetProcAddress(dbghelp
, "SymGetLineFromAddr64");
729 pOpenThread
= (HANDLE (WINAPI
*)(DWORD
, BOOL
, DWORD
))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenThread");
730 if (pSymInitialize
&& pSymCleanup
&& pStackWalk64
&& pSymGetModuleBase64
&&
731 pSymFromAddr
&& pSymGetLineFromAddr64
&& pOpenThread
) {
732 SymSetOptions(SYMOPT_UNDNAME
| SYMOPT_DEFERRED_LOADS
| SYMOPT_DEBUG
| SYMOPT_LOAD_LINES
);
733 ph
= GetCurrentProcess();
734 pSymInitialize(ph
, NULL
, TRUE
);
735 th
= pOpenThread(THREAD_SUSPEND_RESUME
|THREAD_GET_CONTEXT
, FALSE
, tid
);
737 if (SuspendThread(th
) != (DWORD
)-1) {
739 memset(&context
, 0, sizeof(context
));
740 context
.ContextFlags
= CONTEXT_FULL
;
741 if (GetThreadContext(th
, &context
)) {
742 char libpath
[MAX_PATH
];
743 char buf
[sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
];
744 SYMBOL_INFO
*info
= (SYMBOL_INFO
*)buf
;
747 memset(&frame
, 0, sizeof(frame
));
748 #if defined(_M_AMD64) || defined(__x86_64__)
749 mac
= IMAGE_FILE_MACHINE_AMD64
;
750 frame
.AddrPC
.Mode
= AddrModeFlat
;
751 frame
.AddrPC
.Offset
= context
.Rip
;
752 frame
.AddrFrame
.Mode
= AddrModeFlat
;
753 frame
.AddrFrame
.Offset
= context
.Rbp
;
754 frame
.AddrStack
.Mode
= AddrModeFlat
;
755 frame
.AddrStack
.Offset
= context
.Rsp
;
756 #elif defined(__aarch64__)
757 mac
= IMAGE_FILE_MACHINE_ARM64
;
758 frame
.AddrPC
.Mode
= AddrModeFlat
;
759 frame
.AddrPC
.Offset
= context
.Pc
;
760 frame
.AddrFrame
.Mode
= AddrModeFlat
;
761 frame
.AddrFrame
.Offset
= context
.Fp
;
762 frame
.AddrStack
.Mode
= AddrModeFlat
;
763 frame
.AddrStack
.Offset
= context
.Sp
;
765 mac
= IMAGE_FILE_MACHINE_I386
;
766 frame
.AddrPC
.Mode
= AddrModeFlat
;
767 frame
.AddrPC
.Offset
= context
.Eip
;
768 frame
.AddrFrame
.Mode
= AddrModeFlat
;
769 frame
.AddrFrame
.Offset
= context
.Ebp
;
770 frame
.AddrStack
.Mode
= AddrModeFlat
;
771 frame
.AddrStack
.Offset
= context
.Esp
;
774 while (pStackWalk64(mac
, ph
, th
, &frame
, &context
, NULL
,
776 DWORD64 addr
= frame
.AddrPC
.Offset
;
777 IMAGEHLP_LINE64 line
;
778 DWORD64 displacement
;
781 if (addr
== frame
.AddrReturn
.Offset
|| addr
== 0 ||
782 frame
.AddrReturn
.Offset
== 0)
785 memset(buf
, 0, sizeof(buf
));
786 info
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
787 info
->MaxNameLen
= MAX_SYM_NAME
;
788 if (pSymFromAddr(ph
, addr
, &displacement
, info
)) {
789 if (GetModuleFileName((HANDLE
)(uintptr_t)pSymGetModuleBase64(ph
, addr
), libpath
, sizeof(libpath
)))
790 kprintf("%s", libpath
);
791 kprintf("(%s+0x%"PRI_64_PREFIX
"x)",
792 info
->Name
, displacement
);
794 kprintf(" [0x%p]", (void *)(VALUE
)addr
);
795 memset(&line
, 0, sizeof(line
));
796 line
.SizeOfStruct
= sizeof(line
);
797 if (pSymGetLineFromAddr64(ph
, addr
, &tmp
, &line
))
798 kprintf(" %s:%lu", line
.FileName
, line
.LineNumber
);
810 FreeLibrary(dbghelp
);
815 rb_print_backtrace(FILE *errout
)
818 #define MAX_NATIVE_TRACE 1024
819 static void *trace
[MAX_NATIVE_TRACE
];
820 int n
= (int)backtrace(trace
, MAX_NATIVE_TRACE
);
821 #if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
822 rb_dump_backtrace_with_lines(n
, trace
, errout
);
824 char **syms
= backtrace_symbols(trace
, n
);
827 for (i
=0; i
<n
; i
++) {
828 kprintf("%s\n", syms
[i
]);
833 /* ignore errors at writing */;
835 #elif defined(_WIN32)
836 struct dump_thead_arg arg
= {
837 .tid
= GetCurrentThreadId(),
840 HANDLE th
= (HANDLE
)_beginthread(dump_thread
, 0, &arg
);
841 if (th
!= (HANDLE
)-1)
842 WaitForSingleObject(th
, INFINITE
);
846 #ifdef HAVE_LIBPROCSTAT
849 static void procstat_vm(struct procstat
*, struct kinfo_proc
*, FILE *);
850 #include "missing/procstat_vm.c"
853 #if defined __linux__
854 # if defined(__x86_64__) || defined(__i386__)
855 # define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->gregs[REG_##reg], #reg, col_count, 80))
856 # elif defined(__aarch64__) || defined(__arm__) || defined(__riscv) || defined(__loongarch64)
857 # define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, reg, regstr, col_count, 80))
859 #elif defined __APPLE__
860 # if defined(__aarch64__)
861 # define dump_machine_register(reg, regstr) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), regstr, col_count, 80))
863 # define dump_machine_register(reg) (col_count = print_machine_register(errout, mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
867 #ifdef dump_machine_register
869 print_machine_register(FILE *errout
, size_t reg
, const char *reg_name
, int col_count
, int max_col
)
873 static const int size_width
= sizeof(size_t) * CHAR_BIT
/ 4;
875 ret
= snprintf(buf
, sizeof(buf
), " %3.3s: 0x%.*" PRIxSIZE
, reg_name
, size_width
, reg
);
876 if (col_count
+ ret
> max_col
) {
889 rb_dump_machine_register(FILE *errout
, const ucontext_t
*ctx
)
892 if (!ctx
) return true;
894 kprintf("-- Machine register context "
895 "------------------------------------------------\n");
897 # if defined __linux__
899 const mcontext_t
*const mctx
= &ctx
->uc_mcontext
;
900 # if defined __x86_64__
901 dump_machine_register(RIP
);
902 dump_machine_register(RBP
);
903 dump_machine_register(RSP
);
904 dump_machine_register(RAX
);
905 dump_machine_register(RBX
);
906 dump_machine_register(RCX
);
907 dump_machine_register(RDX
);
908 dump_machine_register(RDI
);
909 dump_machine_register(RSI
);
910 dump_machine_register(R8
);
911 dump_machine_register(R9
);
912 dump_machine_register(R10
);
913 dump_machine_register(R11
);
914 dump_machine_register(R12
);
915 dump_machine_register(R13
);
916 dump_machine_register(R14
);
917 dump_machine_register(R15
);
918 dump_machine_register(EFL
);
919 # elif defined __i386__
920 dump_machine_register(GS
);
921 dump_machine_register(FS
);
922 dump_machine_register(ES
);
923 dump_machine_register(DS
);
924 dump_machine_register(EDI
);
925 dump_machine_register(ESI
);
926 dump_machine_register(EBP
);
927 dump_machine_register(ESP
);
928 dump_machine_register(EBX
);
929 dump_machine_register(EDX
);
930 dump_machine_register(ECX
);
931 dump_machine_register(EAX
);
932 dump_machine_register(TRAPNO
);
933 dump_machine_register(ERR
);
934 dump_machine_register(EIP
);
935 dump_machine_register(CS
);
936 dump_machine_register(EFL
);
937 dump_machine_register(UESP
);
938 dump_machine_register(SS
);
939 # elif defined __aarch64__
940 dump_machine_register(mctx
->regs
[0], "x0");
941 dump_machine_register(mctx
->regs
[1], "x1");
942 dump_machine_register(mctx
->regs
[2], "x2");
943 dump_machine_register(mctx
->regs
[3], "x3");
944 dump_machine_register(mctx
->regs
[4], "x4");
945 dump_machine_register(mctx
->regs
[5], "x5");
946 dump_machine_register(mctx
->regs
[6], "x6");
947 dump_machine_register(mctx
->regs
[7], "x7");
948 dump_machine_register(mctx
->regs
[18], "x18");
949 dump_machine_register(mctx
->regs
[19], "x19");
950 dump_machine_register(mctx
->regs
[20], "x20");
951 dump_machine_register(mctx
->regs
[21], "x21");
952 dump_machine_register(mctx
->regs
[22], "x22");
953 dump_machine_register(mctx
->regs
[23], "x23");
954 dump_machine_register(mctx
->regs
[24], "x24");
955 dump_machine_register(mctx
->regs
[25], "x25");
956 dump_machine_register(mctx
->regs
[26], "x26");
957 dump_machine_register(mctx
->regs
[27], "x27");
958 dump_machine_register(mctx
->regs
[28], "x28");
959 dump_machine_register(mctx
->regs
[29], "x29");
960 dump_machine_register(mctx
->sp
, "sp");
961 dump_machine_register(mctx
->fault_address
, "fault_address");
962 # elif defined __arm__
963 dump_machine_register(mctx
->arm_r0
, "r0");
964 dump_machine_register(mctx
->arm_r1
, "r1");
965 dump_machine_register(mctx
->arm_r2
, "r2");
966 dump_machine_register(mctx
->arm_r3
, "r3");
967 dump_machine_register(mctx
->arm_r4
, "r4");
968 dump_machine_register(mctx
->arm_r5
, "r5");
969 dump_machine_register(mctx
->arm_r6
, "r6");
970 dump_machine_register(mctx
->arm_r7
, "r7");
971 dump_machine_register(mctx
->arm_r8
, "r8");
972 dump_machine_register(mctx
->arm_r9
, "r9");
973 dump_machine_register(mctx
->arm_r10
, "r10");
974 dump_machine_register(mctx
->arm_sp
, "sp");
975 dump_machine_register(mctx
->fault_address
, "fault_address");
976 # elif defined __riscv
977 dump_machine_register(mctx
->__gregs
[REG_SP
], "sp");
978 dump_machine_register(mctx
->__gregs
[REG_S0
], "s0");
979 dump_machine_register(mctx
->__gregs
[REG_S1
], "s1");
980 dump_machine_register(mctx
->__gregs
[REG_A0
], "a0");
981 dump_machine_register(mctx
->__gregs
[REG_A0
+1], "a1");
982 dump_machine_register(mctx
->__gregs
[REG_A0
+2], "a2");
983 dump_machine_register(mctx
->__gregs
[REG_A0
+3], "a3");
984 dump_machine_register(mctx
->__gregs
[REG_A0
+4], "a4");
985 dump_machine_register(mctx
->__gregs
[REG_A0
+5], "a5");
986 dump_machine_register(mctx
->__gregs
[REG_A0
+6], "a6");
987 dump_machine_register(mctx
->__gregs
[REG_A0
+7], "a7");
988 dump_machine_register(mctx
->__gregs
[REG_S2
], "s2");
989 dump_machine_register(mctx
->__gregs
[REG_S2
+1], "s3");
990 dump_machine_register(mctx
->__gregs
[REG_S2
+2], "s4");
991 dump_machine_register(mctx
->__gregs
[REG_S2
+3], "s5");
992 dump_machine_register(mctx
->__gregs
[REG_S2
+4], "s6");
993 dump_machine_register(mctx
->__gregs
[REG_S2
+5], "s7");
994 dump_machine_register(mctx
->__gregs
[REG_S2
+6], "s8");
995 dump_machine_register(mctx
->__gregs
[REG_S2
+7], "s9");
996 dump_machine_register(mctx
->__gregs
[REG_S2
+8], "s10");
997 dump_machine_register(mctx
->__gregs
[REG_S2
+9], "s11");
998 # elif defined __loongarch64
999 dump_machine_register(mctx
->__gregs
[LARCH_REG_SP
], "sp");
1000 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
], "s0");
1001 dump_machine_register(mctx
->__gregs
[LARCH_REG_S1
], "s1");
1002 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
], "a0");
1003 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+1], "a1");
1004 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+2], "a2");
1005 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+3], "a3");
1006 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+4], "a4");
1007 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+5], "a5");
1008 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+6], "a6");
1009 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+7], "a7");
1010 dump_machine_register(mctx
->__gregs
[LARCH_REG_A0
+7], "a7");
1011 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
], "s0");
1012 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+1], "s1");
1013 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+2], "s2");
1014 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+3], "s3");
1015 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+4], "s4");
1016 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+5], "s5");
1017 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+6], "s6");
1018 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+7], "s7");
1019 dump_machine_register(mctx
->__gregs
[LARCH_REG_S0
+8], "s8");
1022 # elif defined __APPLE__
1024 const mcontext_t mctx
= ctx
->uc_mcontext
;
1025 # if defined __x86_64__
1026 dump_machine_register(rax
);
1027 dump_machine_register(rbx
);
1028 dump_machine_register(rcx
);
1029 dump_machine_register(rdx
);
1030 dump_machine_register(rdi
);
1031 dump_machine_register(rsi
);
1032 dump_machine_register(rbp
);
1033 dump_machine_register(rsp
);
1034 dump_machine_register(r8
);
1035 dump_machine_register(r9
);
1036 dump_machine_register(r10
);
1037 dump_machine_register(r11
);
1038 dump_machine_register(r12
);
1039 dump_machine_register(r13
);
1040 dump_machine_register(r14
);
1041 dump_machine_register(r15
);
1042 dump_machine_register(rip
);
1043 dump_machine_register(rflags
);
1044 # elif defined __i386__
1045 dump_machine_register(eax
);
1046 dump_machine_register(ebx
);
1047 dump_machine_register(ecx
);
1048 dump_machine_register(edx
);
1049 dump_machine_register(edi
);
1050 dump_machine_register(esi
);
1051 dump_machine_register(ebp
);
1052 dump_machine_register(esp
);
1053 dump_machine_register(ss
);
1054 dump_machine_register(eflags
);
1055 dump_machine_register(eip
);
1056 dump_machine_register(cs
);
1057 dump_machine_register(ds
);
1058 dump_machine_register(es
);
1059 dump_machine_register(fs
);
1060 dump_machine_register(gs
);
1061 # elif defined __aarch64__
1062 dump_machine_register(x
[0], "x0");
1063 dump_machine_register(x
[1], "x1");
1064 dump_machine_register(x
[2], "x2");
1065 dump_machine_register(x
[3], "x3");
1066 dump_machine_register(x
[4], "x4");
1067 dump_machine_register(x
[5], "x5");
1068 dump_machine_register(x
[6], "x6");
1069 dump_machine_register(x
[7], "x7");
1070 dump_machine_register(x
[18], "x18");
1071 dump_machine_register(x
[19], "x19");
1072 dump_machine_register(x
[20], "x20");
1073 dump_machine_register(x
[21], "x21");
1074 dump_machine_register(x
[22], "x22");
1075 dump_machine_register(x
[23], "x23");
1076 dump_machine_register(x
[24], "x24");
1077 dump_machine_register(x
[25], "x25");
1078 dump_machine_register(x
[26], "x26");
1079 dump_machine_register(x
[27], "x27");
1080 dump_machine_register(x
[28], "x28");
1081 dump_machine_register(lr
, "lr");
1082 dump_machine_register(fp
, "fp");
1083 dump_machine_register(sp
, "sp");
1094 # define rb_dump_machine_register(errout, ctx) ((void)0)
1095 #endif /* dump_machine_register */
1098 rb_vm_bugreport(const void *ctx
, FILE *errout
)
1100 const char *cmd
= getenv("RUBY_ON_BUG");
1103 snprintf(buf
, sizeof(buf
), "%s %"PRI_PIDT_PREFIX
"d", cmd
, getpid());
1104 int r
= system(buf
);
1106 snprintf(buf
, sizeof(buf
), "Launching RUBY_ON_BUG command failed.");
1110 // Thread unsafe best effort attempt to stop printing the bug report in an
1111 // infinite loop. Can happen with corrupt Ruby stack.
1113 static bool crashing
= false;
1115 kprintf("Crashed while printing bug report\n");
1122 # define PROC_MAPS_NAME "/proc/self/maps"
1124 #ifdef PROC_MAPS_NAME
1125 enum {other_runtime_info
= 1};
1127 enum {other_runtime_info
= 0};
1129 const rb_vm_t
*const vm
= GET_VM();
1130 const rb_execution_context_t
*ec
= rb_current_execution_context(false);
1133 rb_vmdebug_stack_dump_raw(ec
, ec
->cfp
, errout
);
1134 rb_backtrace_print_as_bugreport(errout
);
1136 // If we get here, hopefully things are intact enough that
1137 // we can read these two numbers. It is an estimate because
1138 // we are reading without synchronization.
1139 kprintf("-- Threading information "
1140 "---------------------------------------------------\n");
1141 kprintf("Total ractor count: %u\n", vm
->ractor
.cnt
);
1142 kprintf("Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec
)->threads
.cnt
);
1146 rb_dump_machine_register(errout
, ctx
);
1148 #if USE_BACKTRACE || defined(_WIN32)
1149 kprintf("-- C level backtrace information "
1150 "-------------------------------------------\n");
1151 rb_print_backtrace(errout
);
1155 #endif /* USE_BACKTRACE */
1157 if (other_runtime_info
|| vm
) {
1158 kprintf("-- Other runtime information "
1159 "-----------------------------------------------\n\n");
1161 if (vm
&& !rb_during_gc()) {
1165 const int max_name_length
= 1024;
1166 # define LIMITED_NAME_LENGTH(s) \
1167 (((len = RSTRING_LEN(s)) > max_name_length) ? max_name_length : (int)len)
1169 name
= vm
->progname
;
1171 kprintf("* Loaded script: %.*s\n",
1172 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1175 if (vm
->loaded_features
) {
1176 kprintf("* Loaded features:\n\n");
1177 for (i
=0; i
<RARRAY_LEN(vm
->loaded_features
); i
++) {
1178 name
= RARRAY_AREF(vm
->loaded_features
, i
);
1179 if (RB_TYPE_P(name
, T_STRING
)) {
1180 kprintf(" %4d %.*s\n", i
,
1181 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1183 else if (RB_TYPE_P(name
, T_CLASS
) || RB_TYPE_P(name
, T_MODULE
)) {
1184 const char *const type
= RB_TYPE_P(name
, T_CLASS
) ?
1186 name
= rb_search_class_path(rb_class_real(name
));
1187 if (!RB_TYPE_P(name
, T_STRING
)) {
1188 kprintf(" %4d %s:<unnamed>\n", i
, type
);
1191 kprintf(" %4d %s:%.*s\n", i
, type
,
1192 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1195 VALUE klass
= rb_search_class_path(rb_obj_class(name
));
1196 if (!RB_TYPE_P(klass
, T_STRING
)) {
1197 kprintf(" %4d #<%p:%p>\n", i
,
1198 (void *)CLASS_OF(name
), (void *)name
);
1201 kprintf(" %4d #<%.*s:%p>\n", i
,
1202 LIMITED_NAME_LENGTH(klass
), RSTRING_PTR(klass
),
1211 #ifdef PROC_MAPS_NAME
1213 FILE *fp
= fopen(PROC_MAPS_NAME
, "r");
1215 kprintf("* Process memory map:\n\n");
1219 size_t rn
= fread(buff
, 1, 0x100, fp
);
1220 if (fwrite(buff
, 1, rn
, errout
) != rn
)
1228 #endif /* __linux__ */
1229 #ifdef HAVE_LIBPROCSTAT
1230 # define MIB_KERN_PROC_PID_LEN 4
1231 int mib
[MIB_KERN_PROC_PID_LEN
];
1232 struct kinfo_proc kp
;
1233 size_t len
= sizeof(struct kinfo_proc
);
1236 mib
[2] = KERN_PROC_PID
;
1238 if (sysctl(mib
, MIB_KERN_PROC_PID_LEN
, &kp
, &len
, NULL
, 0) == -1) {
1239 kprintf("sysctl: %s\n", strerror(errno
));
1242 struct procstat
*prstat
= procstat_open_sysctl();
1243 kprintf("* Process memory map:\n\n");
1244 procstat_vm(prstat
, &kp
, errout
);
1245 procstat_close(prstat
);
1248 #endif /* __FreeBSD__ */
1250 vm_address_t addr
= 0;
1252 struct vm_region_submap_info map
;
1253 mach_msg_type_number_t count
= VM_REGION_SUBMAP_INFO_COUNT
;
1254 natural_t depth
= 0;
1256 kprintf("* Process memory map:\n\n");
1258 if (vm_region_recurse(mach_task_self(), &addr
, &size
, &depth
,
1259 (vm_region_recurse_info_t
)&map
, &count
) != KERN_SUCCESS
) {
1263 if (map
.is_submap
) {
1264 // We only look at main addresses
1268 kprintf("%lx-%lx %s%s%s", addr
, (addr
+size
),
1269 ((map
.protection
& VM_PROT_READ
) != 0 ? "r" : "-"),
1270 ((map
.protection
& VM_PROT_WRITE
) != 0 ? "w" : "-"),
1271 ((map
.protection
& VM_PROT_EXECUTE
) != 0 ? "x" : "-"));
1272 #ifdef HAVE_LIBPROC_H
1273 char buff
[PATH_MAX
];
1274 if (proc_regionfilename(getpid(), addr
, buff
, sizeof(buff
)) > 0) {
1275 kprintf(" %s", buff
);
1293 rb_vmdebug_stack_dump_all_threads(void)
1295 rb_thread_t
*th
= NULL
;
1296 rb_ractor_t
*r
= GET_RACTOR();
1297 FILE *errout
= stderr
;
1299 // TODO: now it only shows current ractor
1300 ccan_list_for_each(&r
->threads
.set
, th
, lt_node
) {
1301 #ifdef NON_SCALAR_THREAD_ID
1302 kprintf("th: %p, native_id: N/A\n", th
);
1304 kprintf("th: %p, native_id: %p\n", (void *)th
, (void *)(uintptr_t)th
->nt
->thread_id
);
1306 if (!rb_vmdebug_stack_dump_raw(th
->ec
, th
->ec
->cfp
, errout
)) goto error
;