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"
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
);
50 control_frame_dump(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
)
53 ptrdiff_t ep
= cfp
->ep
- ec
->vm_stack
;
54 char ep_in_heap
= ' ';
55 char posbuf
[MAX_POSBUF
+1];
57 const char *magic
, *iseq_name
= "-", *selfstr
= "-", *biseq_name
= "-";
59 const rb_iseq_t
*iseq
= NULL
;
60 const rb_callable_method_entry_t
*me
= rb_vm_frame_method_entry(cfp
);
62 if (ep
< 0 || (size_t)ep
> ec
->vm_stack_size
) {
63 ep
= (ptrdiff_t)cfp
->ep
;
67 switch (VM_FRAME_TYPE(cfp
)) {
68 case VM_FRAME_MAGIC_TOP
:
71 case VM_FRAME_MAGIC_METHOD
:
74 case VM_FRAME_MAGIC_CLASS
:
77 case VM_FRAME_MAGIC_BLOCK
:
80 case VM_FRAME_MAGIC_CFUNC
:
83 case VM_FRAME_MAGIC_IFUNC
:
86 case VM_FRAME_MAGIC_EVAL
:
89 case VM_FRAME_MAGIC_RESCUE
:
101 tmp
= rb_inspect(cfp
->self
);
102 selfstr
= StringValueCStr(tmp
);
108 if (cfp
->iseq
!= 0) {
109 #define RUBY_VM_IFUNC_P(ptr) IMEMO_TYPE_P(ptr, imemo_ifunc)
110 if (RUBY_VM_IFUNC_P(cfp
->iseq
)) {
111 iseq_name
= "<ifunc>";
113 else if (SYMBOL_P((VALUE
)cfp
->iseq
)) {
114 tmp
= rb_sym2str((VALUE
)cfp
->iseq
);
115 iseq_name
= RSTRING_PTR(tmp
);
116 snprintf(posbuf
, MAX_POSBUF
, ":%s", iseq_name
);
121 pc
= cfp
->pc
- iseq
->body
->iseq_encoded
;
122 iseq_name
= RSTRING_PTR(iseq
->body
->location
.label
);
123 line
= rb_vm_get_sourceline(cfp
);
125 snprintf(posbuf
, MAX_POSBUF
, "%s:%d", RSTRING_PTR(rb_iseq_path(iseq
)), line
);
129 else if (me
!= NULL
) {
130 iseq_name
= rb_id2name(me
->def
->original_id
);
131 snprintf(posbuf
, MAX_POSBUF
, ":%s", iseq_name
);
135 fprintf(stderr
, "c:%04"PRIdPTRDIFF
" ",
136 ((rb_control_frame_t
*)(ec
->vm_stack
+ ec
->vm_stack_size
) - cfp
));
138 fprintf(stderr
, "p:---- ");
141 fprintf(stderr
, "p:%04"PRIdPTRDIFF
" ", pc
);
143 fprintf(stderr
, "s:%04"PRIdPTRDIFF
" ", cfp
->sp
- ec
->vm_stack
);
144 fprintf(stderr
, ep_in_heap
== ' ' ? "e:%06"PRIdPTRDIFF
" " : "E:%06"PRIxPTRDIFF
" ", ep
% 10000);
145 fprintf(stderr
, "%-6s", magic
);
147 fprintf(stderr
, " %s", posbuf
);
149 if (VM_FRAME_FINISHED_P(cfp
)) {
150 fprintf(stderr
, " [FINISH]");
153 fprintf(stderr
, " \t");
154 fprintf(stderr
, "iseq: %-24s ", iseq_name
);
155 fprintf(stderr
, "self: %-24s ", selfstr
);
156 fprintf(stderr
, "%-1s ", biseq_name
);
158 fprintf(stderr
, "\n");
160 // additional information for CI machines
165 if (IMEMO_TYPE_P(me
, imemo_ment
)) {
166 fprintf(stderr
, " me:\n");
167 fprintf(stderr
, " called_id: %s, type: %s\n", rb_id2name(me
->called_id
), rb_method_type_name(me
->def
->type
));
168 fprintf(stderr
, " owner class: %s\n", rb_raw_obj_info(buff
, 0x100, me
->owner
));
169 if (me
->owner
!= me
->defined_class
) {
170 fprintf(stderr
, " defined_class: %s\n", rb_raw_obj_info(buff
, 0x100, me
->defined_class
));
174 fprintf(stderr
, " me is corrupted (%s)\n", rb_raw_obj_info(buff
, 0x100, (VALUE
)me
));
178 fprintf(stderr
, " self: %s\n", rb_raw_obj_info(buff
, 0x100, cfp
->self
));
181 if (iseq
->body
->local_table_size
> 0) {
182 fprintf(stderr
, " lvars:\n");
183 for (unsigned int i
=0; i
<iseq
->body
->local_table_size
; i
++) {
184 const VALUE
*argv
= cfp
->ep
- cfp
->iseq
->body
->local_table_size
- VM_ENV_DATA_SIZE
+ 1;
185 fprintf(stderr
, " %s: %s\n",
186 rb_id2name(iseq
->body
->local_table
[i
]),
187 rb_raw_obj_info(buff
, 0x100, argv
[i
]));
195 rb_vmdebug_stack_dump_raw(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
)
199 const VALUE
*ep
= cfp
->ep
;
202 fprintf(stderr
, "-- stack frame ------------\n");
203 for (p
= st
= ec
->vm_stack
; p
< sp
; p
++) {
204 fprintf(stderr
, "%04ld (%p): %08"PRIxVALUE
, (long)(p
- st
), p
, *p
);
207 if (ec
->vm_stack
<= t
&& t
< sp
) {
208 fprintf(stderr
, " (= %ld)", (long)((VALUE
*)GC_GUARDED_PTR_REF((VALUE
)t
) - ec
->vm_stack
));
212 fprintf(stderr
, " <- ep");
214 fprintf(stderr
, "\n");
218 fprintf(stderr
, "-- Control frame information "
219 "-----------------------------------------------\n");
220 while ((void *)cfp
< (void *)(ec
->vm_stack
+ ec
->vm_stack_size
)) {
221 control_frame_dump(ec
, cfp
);
224 fprintf(stderr
, "\n");
228 rb_vmdebug_stack_dump_raw_current(void)
230 const rb_execution_context_t
*ec
= GET_EC();
231 rb_vmdebug_stack_dump_raw(ec
, ec
->cfp
);
235 rb_vmdebug_env_dump_raw(const rb_env_t
*env
, const VALUE
*ep
)
238 fprintf(stderr
, "-- env --------------------\n");
241 fprintf(stderr
, "--\n");
242 for (i
= 0; i
< env
->env_size
; i
++) {
243 fprintf(stderr
, "%04d: %08"PRIxVALUE
" (%p)", i
, env
->env
[i
], (void *)&env
->env
[i
]);
244 if (&env
->env
[i
] == ep
) fprintf(stderr
, " <- ep");
245 fprintf(stderr
, "\n");
248 env
= rb_vm_env_prev_env(env
);
250 fprintf(stderr
, "---------------------------\n");
254 rb_vmdebug_proc_dump_raw(rb_proc_t
*proc
)
258 VALUE val
= rb_inspect(vm_block_self(&proc
->block
));
259 selfstr
= StringValueCStr(val
);
261 fprintf(stderr
, "-- proc -------------------\n");
262 fprintf(stderr
, "self: %s\n", selfstr
);
263 env
= VM_ENV_ENVVAL_PTR(vm_block_ep(&proc
->block
));
264 rb_vmdebug_env_dump_raw(env
, vm_block_ep(&proc
->block
));
268 rb_vmdebug_stack_dump_th(VALUE thval
)
270 rb_thread_t
*target_th
= rb_thread_ptr(thval
);
271 rb_vmdebug_stack_dump_raw(target_th
->ec
, target_th
->ec
->cfp
);
278 vm_base_ptr(const rb_control_frame_t
*cfp
)
280 const rb_control_frame_t
*prev_cfp
= RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp
);
281 const VALUE
*bp
= prev_cfp
->sp
+ cfp
->iseq
->body
->local_table_size
+ VM_ENV_DATA_SIZE
;
283 if (cfp
->iseq
->body
->type
== ISEQ_TYPE_METHOD
) {
290 vm_stack_dump_each(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
)
292 int i
, argc
= 0, local_table_size
= 0;
295 const VALUE
*ep
= cfp
->ep
;
297 if (VM_FRAME_RUBYFRAME_P(cfp
)) {
298 const rb_iseq_t
*iseq
= cfp
->iseq
;
299 argc
= iseq
->body
->param
.lead_num
;
300 local_table_size
= iseq
->body
->local_table_size
;
303 /* stack trace header */
305 if (VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_METHOD
||
306 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_TOP
||
307 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_BLOCK
||
308 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_CLASS
||
309 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_CFUNC
||
310 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_IFUNC
||
311 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_EVAL
||
312 VM_FRAME_TYPE(cfp
) == VM_FRAME_MAGIC_RESCUE
)
314 const VALUE
*ptr
= ep
- local_table_size
;
316 control_frame_dump(ec
, cfp
);
318 for (i
= 0; i
< argc
; i
++) {
319 rstr
= rb_inspect(*ptr
);
320 fprintf(stderr
, " arg %2d: %8s (%p)\n", i
, StringValueCStr(rstr
),
323 for (; i
< local_table_size
- 1; i
++) {
324 rstr
= rb_inspect(*ptr
);
325 fprintf(stderr
, " local %2d: %8s (%p)\n", i
, StringValueCStr(rstr
),
329 ptr
= vm_base_ptr(cfp
);
330 for (; ptr
< sp
; ptr
++, i
++) {
331 switch (TYPE(*ptr
)) {
333 rstr
= rb_str_new2("undef");
336 rstr
= rb_str_new2("imemo"); /* TODO: can put mode detail information */
339 rstr
= rb_inspect(*ptr
);
342 fprintf(stderr
, " stack %2d: %8s (%"PRIdPTRDIFF
")\n", i
, StringValueCStr(rstr
),
343 (ptr
- ec
->vm_stack
));
346 else if (VM_FRAME_FINISHED_P(cfp
)) {
347 if (ec
->vm_stack
+ ec
->vm_stack_size
> (VALUE
*)(cfp
+ 1)) {
348 vm_stack_dump_each(ec
, cfp
+ 1);
355 rb_bug("unsupported frame type: %08lx", VM_FRAME_TYPE(cfp
));
361 rb_vmdebug_debug_print_register(const rb_execution_context_t
*ec
)
363 rb_control_frame_t
*cfp
= ec
->cfp
;
365 ptrdiff_t ep
= cfp
->ep
- ec
->vm_stack
;
368 if (VM_FRAME_RUBYFRAME_P(cfp
)) {
369 pc
= cfp
->pc
- cfp
->iseq
->body
->iseq_encoded
;
372 if (ep
< 0 || (size_t)ep
> ec
->vm_stack_size
) {
376 cfpi
= ((rb_control_frame_t
*)(ec
->vm_stack
+ ec
->vm_stack_size
)) - cfp
;
377 fprintf(stderr
, " [PC] %04"PRIdPTRDIFF
", [SP] %04"PRIdPTRDIFF
", [EP] %04"PRIdPTRDIFF
", [CFP] %04"PRIdPTRDIFF
"\n",
378 pc
, (cfp
->sp
- ec
->vm_stack
), ep
, cfpi
);
382 rb_vmdebug_thread_dump_regs(VALUE thval
)
384 rb_vmdebug_debug_print_register(rb_thread_ptr(thval
)->ec
);
388 rb_vmdebug_debug_print_pre(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
, const VALUE
*_pc
)
390 const rb_iseq_t
*iseq
= cfp
->iseq
;
393 ptrdiff_t pc
= _pc
- iseq
->body
->iseq_encoded
;
396 for (i
=0; i
<(int)VM_CFP_CNT(ec
, cfp
); i
++) {
400 if(0)printf("[%03ld] ", (long)(cfp
->sp
- ec
->vm_stack
));
402 /* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(ec, cfp)); */
404 const VALUE
*iseq_original
= rb_iseq_original_iseq((rb_iseq_t
*)iseq
);
406 rb_iseq_disasm_insn(0, iseq_original
, (size_t)pc
, iseq
, 0);
411 fprintf(stderr
, " (1)");
412 rb_vmdebug_debug_print_register(ec
);
417 rb_vmdebug_debug_print_post(const rb_execution_context_t
*ec
, const rb_control_frame_t
*cfp
418 #if OPT_STACK_CACHING
419 , VALUE reg_a
, VALUE reg_b
428 fprintf(stderr
, " (2)");
429 rb_vmdebug_debug_print_register(ec
);
431 /* stack_dump_raw(ec, cfp); */
434 /* stack_dump_thobj(ec); */
435 vm_stack_dump_each(ec
, ec
->cfp
);
437 #if OPT_STACK_CACHING
440 rstr
= rb_inspect(reg_a
);
441 fprintf(stderr
, " sc reg A: %s\n", StringValueCStr(rstr
));
442 rstr
= rb_inspect(reg_b
);
443 fprintf(stderr
, " sc reg B: %s\n", StringValueCStr(rstr
));
447 ("--------------------------------------------------------------\n");
452 rb_vmdebug_thread_dump_state(VALUE self
)
454 rb_thread_t
*th
= rb_thread_ptr(self
);
455 rb_control_frame_t
*cfp
= th
->ec
->cfp
;
457 fprintf(stderr
, "Thread state dump:\n");
458 fprintf(stderr
, "pc : %p, sp : %p\n", (void *)cfp
->pc
, (void *)cfp
->sp
);
459 fprintf(stderr
, "cfp: %p, ep : %p\n", (void *)cfp
, (void *)cfp
->ep
);
464 #if defined __APPLE__
466 # define MCTX_SS_REG(reg) __ss.__##reg
468 # define MCTX_SS_REG(reg) ss.reg
472 #if defined(HAVE_BACKTRACE)
473 # define USE_BACKTRACE 1
474 # ifdef HAVE_LIBUNWIND
476 # define backtrace unw_backtrace
477 # elif defined(__APPLE__) && defined(__x86_64__) && defined(HAVE_LIBUNWIND_H)
478 # define UNW_LOCAL_ONLY
479 # include <libunwind.h>
480 # include <sys/mman.h>
483 backtrace(void **trace
, int size
)
485 unw_cursor_t cursor
; unw_context_t uc
;
490 unw_init_local(&cursor
, &uc
);
491 while (unw_step(&cursor
) > 0) {
492 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
493 trace
[n
++] = (void *)ip
;
496 unw_get_proc_name(&cursor
, buf
, 256, &ip
);
497 if (strncmp("_sigtramp", buf
, sizeof("_sigtramp")) == 0) {
498 goto darwin_sigtramp
;
504 /* darwin's bundled libunwind doesn't support signal trampoline */
509 /* get previous frame information from %rbx at _sigtramp and set values to cursor
510 * http://www.opensource.apple.com/source/Libc/Libc-825.25/i386/sys/_sigtramp.s
511 * http://www.opensource.apple.com/source/libunwind/libunwind-35.1/src/unw_getcontext.s
513 unw_get_reg(&cursor
, UNW_X86_64_RBX
, &ip
);
514 uctx
= (ucontext_t
*)ip
;
515 unw_set_reg(&cursor
, UNW_X86_64_RAX
, uctx
->uc_mcontext
->MCTX_SS_REG(rax
));
516 unw_set_reg(&cursor
, UNW_X86_64_RBX
, uctx
->uc_mcontext
->MCTX_SS_REG(rbx
));
517 unw_set_reg(&cursor
, UNW_X86_64_RCX
, uctx
->uc_mcontext
->MCTX_SS_REG(rcx
));
518 unw_set_reg(&cursor
, UNW_X86_64_RDX
, uctx
->uc_mcontext
->MCTX_SS_REG(rdx
));
519 unw_set_reg(&cursor
, UNW_X86_64_RDI
, uctx
->uc_mcontext
->MCTX_SS_REG(rdi
));
520 unw_set_reg(&cursor
, UNW_X86_64_RSI
, uctx
->uc_mcontext
->MCTX_SS_REG(rsi
));
521 unw_set_reg(&cursor
, UNW_X86_64_RBP
, uctx
->uc_mcontext
->MCTX_SS_REG(rbp
));
522 unw_set_reg(&cursor
, UNW_X86_64_RSP
, 8+(uctx
->uc_mcontext
->MCTX_SS_REG(rsp
)));
523 unw_set_reg(&cursor
, UNW_X86_64_R8
, uctx
->uc_mcontext
->MCTX_SS_REG(r8
));
524 unw_set_reg(&cursor
, UNW_X86_64_R9
, uctx
->uc_mcontext
->MCTX_SS_REG(r9
));
525 unw_set_reg(&cursor
, UNW_X86_64_R10
, uctx
->uc_mcontext
->MCTX_SS_REG(r10
));
526 unw_set_reg(&cursor
, UNW_X86_64_R11
, uctx
->uc_mcontext
->MCTX_SS_REG(r11
));
527 unw_set_reg(&cursor
, UNW_X86_64_R12
, uctx
->uc_mcontext
->MCTX_SS_REG(r12
));
528 unw_set_reg(&cursor
, UNW_X86_64_R13
, uctx
->uc_mcontext
->MCTX_SS_REG(r13
));
529 unw_set_reg(&cursor
, UNW_X86_64_R14
, uctx
->uc_mcontext
->MCTX_SS_REG(r14
));
530 unw_set_reg(&cursor
, UNW_X86_64_R15
, uctx
->uc_mcontext
->MCTX_SS_REG(r15
));
531 ip
= uctx
->uc_mcontext
->MCTX_SS_REG(rip
);
533 /* There are 4 cases for SEGV:
534 * (1) called invalid address
535 * (2) read or write invalid address
536 * (3) received signal
539 * (1) called invalid address
540 * In this case, saved ip is invalid address.
541 * It needs to just save the address for the information,
542 * skip the frame, and restore the frame calling the
543 * invalid address from %rsp.
544 * The problem is how to check whether the ip is valid or not.
545 * This code uses mincore(2) and assume the address's page is
546 * incore/referenced or not reflects the problem.
547 * Note that High Sierra's mincore(2) may return -128.
548 * (2) read or write invalid address
549 * saved ip is valid. just restart backtracing.
550 * (3) received signal in user space
552 * (4) received signal in kernel
553 * In this case saved ip points just after syscall, but registers are
554 * already overwritten by kernel. To fix register consistency,
555 * skip libc's kernel wrapper.
556 * To detect this case, just previous two bytes of ip is "\x0f\x05",
557 * syscall instruction of x86_64.
559 r
= mincore((const void *)ip
, 1, vec
);
560 if (r
|| vec
[0] <= 0 || memcmp((const char *)ip
-2, "\x0f\x05", 2) == 0) {
561 /* if segv is caused by invalid call or signal received in syscall */
562 /* the frame is invalid; skip */
563 trace
[n
++] = (void *)ip
;
564 ip
= *(unw_word_t
*)uctx
->uc_mcontext
->MCTX_SS_REG(rsp
);
566 trace
[n
++] = (void *)ip
;
567 unw_set_reg(&cursor
, UNW_REG_IP
, ip
);
569 while (unw_step(&cursor
) > 0) {
570 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
571 trace
[n
++] = (void *)ip
;
575 # elif defined(BROKEN_BACKTRACE)
576 # undef USE_BACKTRACE
577 # define USE_BACKTRACE 0
580 # define USE_BACKTRACE 0
584 # include <execinfo.h>
585 #elif defined(_WIN32)
586 # include <imagehlp.h>
587 # ifndef SYMOPT_DEBUG
588 # define SYMOPT_DEBUG 0x80000000
590 # ifndef MAX_SYM_NAME
591 # define MAX_SYM_NAME 2000
599 DWORD ThCallbackStack
;
600 DWORD ThCallbackBStore
;
603 DWORD64 KiCallUserMode
;
604 DWORD64 KeUserCallbackDispatcher
;
605 DWORD64 SystemRangeStart
;
606 DWORD64 KiUserExceptionDispatcher
;
613 ADDRESS64 AddrReturn
;
616 ADDRESS64 AddrBStore
;
617 void *FuncTableEntry
;
648 typedef void *PREAD_PROCESS_MEMORY_ROUTINE64
;
649 typedef void *PFUNCTION_TABLE_ACCESS_ROUTINE64
;
650 typedef void *PGET_MODULE_BASE_ROUTINE64
;
651 typedef void *PTRANSLATE_ADDRESS_ROUTINE64
;
655 dump_thread(void *arg
)
658 BOOL (WINAPI
*pSymInitialize
)(HANDLE
, const char *, BOOL
);
659 BOOL (WINAPI
*pSymCleanup
)(HANDLE
);
660 BOOL (WINAPI
*pStackWalk64
)(DWORD
, HANDLE
, HANDLE
, STACKFRAME64
*, void *, PREAD_PROCESS_MEMORY_ROUTINE64
, PFUNCTION_TABLE_ACCESS_ROUTINE64
, PGET_MODULE_BASE_ROUTINE64
, PTRANSLATE_ADDRESS_ROUTINE64
);
661 DWORD64 (WINAPI
*pSymGetModuleBase64
)(HANDLE
, DWORD64
);
662 BOOL (WINAPI
*pSymFromAddr
)(HANDLE
, DWORD64
, DWORD64
*, SYMBOL_INFO
*);
663 BOOL (WINAPI
*pSymGetLineFromAddr64
)(HANDLE
, DWORD64
, DWORD
*, IMAGEHLP_LINE64
*);
664 HANDLE (WINAPI
*pOpenThread
)(DWORD
, BOOL
, DWORD
);
665 DWORD tid
= *(DWORD
*)arg
;
669 dbghelp
= LoadLibrary("dbghelp.dll");
670 if (!dbghelp
) return;
671 pSymInitialize
= (BOOL (WINAPI
*)(HANDLE
, const char *, BOOL
))GetProcAddress(dbghelp
, "SymInitialize");
672 pSymCleanup
= (BOOL (WINAPI
*)(HANDLE
))GetProcAddress(dbghelp
, "SymCleanup");
673 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");
674 pSymGetModuleBase64
= (DWORD64 (WINAPI
*)(HANDLE
, DWORD64
))GetProcAddress(dbghelp
, "SymGetModuleBase64");
675 pSymFromAddr
= (BOOL (WINAPI
*)(HANDLE
, DWORD64
, DWORD64
*, SYMBOL_INFO
*))GetProcAddress(dbghelp
, "SymFromAddr");
676 pSymGetLineFromAddr64
= (BOOL (WINAPI
*)(HANDLE
, DWORD64
, DWORD
*, IMAGEHLP_LINE64
*))GetProcAddress(dbghelp
, "SymGetLineFromAddr64");
677 pOpenThread
= (HANDLE (WINAPI
*)(DWORD
, BOOL
, DWORD
))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenThread");
678 if (pSymInitialize
&& pSymCleanup
&& pStackWalk64
&& pSymGetModuleBase64
&&
679 pSymFromAddr
&& pSymGetLineFromAddr64
&& pOpenThread
) {
680 SymSetOptions(SYMOPT_UNDNAME
| SYMOPT_DEFERRED_LOADS
| SYMOPT_DEBUG
| SYMOPT_LOAD_LINES
);
681 ph
= GetCurrentProcess();
682 pSymInitialize(ph
, NULL
, TRUE
);
683 th
= pOpenThread(THREAD_SUSPEND_RESUME
|THREAD_GET_CONTEXT
, FALSE
, tid
);
685 if (SuspendThread(th
) != (DWORD
)-1) {
687 memset(&context
, 0, sizeof(context
));
688 context
.ContextFlags
= CONTEXT_FULL
;
689 if (GetThreadContext(th
, &context
)) {
690 char libpath
[MAX_PATH
];
691 char buf
[sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
];
692 SYMBOL_INFO
*info
= (SYMBOL_INFO
*)buf
;
695 memset(&frame
, 0, sizeof(frame
));
696 #if defined(_M_AMD64) || defined(__x86_64__)
697 mac
= IMAGE_FILE_MACHINE_AMD64
;
698 frame
.AddrPC
.Mode
= AddrModeFlat
;
699 frame
.AddrPC
.Offset
= context
.Rip
;
700 frame
.AddrFrame
.Mode
= AddrModeFlat
;
701 frame
.AddrFrame
.Offset
= context
.Rbp
;
702 frame
.AddrStack
.Mode
= AddrModeFlat
;
703 frame
.AddrStack
.Offset
= context
.Rsp
;
705 mac
= IMAGE_FILE_MACHINE_I386
;
706 frame
.AddrPC
.Mode
= AddrModeFlat
;
707 frame
.AddrPC
.Offset
= context
.Eip
;
708 frame
.AddrFrame
.Mode
= AddrModeFlat
;
709 frame
.AddrFrame
.Offset
= context
.Ebp
;
710 frame
.AddrStack
.Mode
= AddrModeFlat
;
711 frame
.AddrStack
.Offset
= context
.Esp
;
714 while (pStackWalk64(mac
, ph
, th
, &frame
, &context
, NULL
,
716 DWORD64 addr
= frame
.AddrPC
.Offset
;
717 IMAGEHLP_LINE64 line
;
718 DWORD64 displacement
;
721 if (addr
== frame
.AddrReturn
.Offset
|| addr
== 0 ||
722 frame
.AddrReturn
.Offset
== 0)
725 memset(buf
, 0, sizeof(buf
));
726 info
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
727 info
->MaxNameLen
= MAX_SYM_NAME
;
728 if (pSymFromAddr(ph
, addr
, &displacement
, info
)) {
729 if (GetModuleFileName((HANDLE
)(uintptr_t)pSymGetModuleBase64(ph
, addr
), libpath
, sizeof(libpath
)))
730 fprintf(stderr
, "%s", libpath
);
731 fprintf(stderr
, "(%s+0x%"PRI_64_PREFIX
"x)",
732 info
->Name
, displacement
);
734 fprintf(stderr
, " [0x%p]", (void *)(VALUE
)addr
);
735 memset(&line
, 0, sizeof(line
));
736 line
.SizeOfStruct
= sizeof(line
);
737 if (pSymGetLineFromAddr64(ph
, addr
, &tmp
, &line
))
738 fprintf(stderr
, " %s:%lu", line
.FileName
, line
.LineNumber
);
739 fprintf(stderr
, "\n");
749 FreeLibrary(dbghelp
);
754 rb_print_backtrace(void)
757 #define MAX_NATIVE_TRACE 1024
758 static void *trace
[MAX_NATIVE_TRACE
];
759 int n
= (int)backtrace(trace
, MAX_NATIVE_TRACE
);
760 #if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
761 rb_dump_backtrace_with_lines(n
, trace
);
763 char **syms
= backtrace_symbols(trace
, n
);
766 for (i
=0; i
<n
; i
++) {
767 fprintf(stderr
, "%s\n", syms
[i
]);
772 #elif defined(_WIN32)
773 DWORD tid
= GetCurrentThreadId();
774 HANDLE th
= (HANDLE
)_beginthread(dump_thread
, 0, &tid
);
775 if (th
!= (HANDLE
)-1)
776 WaitForSingleObject(th
, INFINITE
);
780 #ifdef HAVE_LIBPROCSTAT
781 #include "missing/procstat_vm.c"
784 #if defined __linux__
785 # if defined __x86_64__ || defined __i386__ || defined __aarch64__ || defined __arm__ || defined __riscv
786 # define HAVE_PRINT_MACHINE_REGISTERS 1
788 #elif defined __APPLE__
789 # if defined __x86_64__ || defined __i386__ || defined __aarch64__
790 # define HAVE_PRINT_MACHINE_REGISTERS 1
794 #ifdef HAVE_PRINT_MACHINE_REGISTERS
796 print_machine_register(size_t reg
, const char *reg_name
, int col_count
, int max_col
)
800 static const int size_width
= sizeof(size_t) * CHAR_BIT
/ 4;
802 ret
= snprintf(buf
, sizeof(buf
), " %3.3s: 0x%.*" PRIxSIZE
, reg_name
, size_width
, reg
);
803 if (col_count
+ ret
> max_col
) {
812 # if defined(__x86_64__) || defined(__i386__)
813 # define dump_machine_register(reg) (col_count = print_machine_register(mctx->gregs[REG_##reg], #reg, col_count, 80))
814 # elif defined(__aarch64__) || defined(__arm__) || defined(__riscv)
815 # define dump_machine_register(reg, regstr) (col_count = print_machine_register(reg, regstr, col_count, 80))
817 # elif defined __APPLE__
818 # if defined(__aarch64__)
819 # define dump_machine_register(reg, regstr) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), regstr, col_count, 80))
821 # define dump_machine_register(reg) (col_count = print_machine_register(mctx->MCTX_SS_REG(reg), #reg, col_count, 80))
826 rb_dump_machine_register(const ucontext_t
*ctx
)
831 fprintf(stderr
, "-- Machine register context "
832 "------------------------------------------------\n");
834 # if defined __linux__
836 const mcontext_t
*const mctx
= &ctx
->uc_mcontext
;
837 # if defined __x86_64__
838 dump_machine_register(RIP
);
839 dump_machine_register(RBP
);
840 dump_machine_register(RSP
);
841 dump_machine_register(RAX
);
842 dump_machine_register(RBX
);
843 dump_machine_register(RCX
);
844 dump_machine_register(RDX
);
845 dump_machine_register(RDI
);
846 dump_machine_register(RSI
);
847 dump_machine_register(R8
);
848 dump_machine_register(R9
);
849 dump_machine_register(R10
);
850 dump_machine_register(R11
);
851 dump_machine_register(R12
);
852 dump_machine_register(R13
);
853 dump_machine_register(R14
);
854 dump_machine_register(R15
);
855 dump_machine_register(EFL
);
856 # elif defined __i386__
857 dump_machine_register(GS
);
858 dump_machine_register(FS
);
859 dump_machine_register(ES
);
860 dump_machine_register(DS
);
861 dump_machine_register(EDI
);
862 dump_machine_register(ESI
);
863 dump_machine_register(EBP
);
864 dump_machine_register(ESP
);
865 dump_machine_register(EBX
);
866 dump_machine_register(EDX
);
867 dump_machine_register(ECX
);
868 dump_machine_register(EAX
);
869 dump_machine_register(TRAPNO
);
870 dump_machine_register(ERR
);
871 dump_machine_register(EIP
);
872 dump_machine_register(CS
);
873 dump_machine_register(EFL
);
874 dump_machine_register(UESP
);
875 dump_machine_register(SS
);
876 # elif defined __aarch64__
877 dump_machine_register(mctx
->regs
[0], "x0");
878 dump_machine_register(mctx
->regs
[1], "x1");
879 dump_machine_register(mctx
->regs
[2], "x2");
880 dump_machine_register(mctx
->regs
[3], "x3");
881 dump_machine_register(mctx
->regs
[4], "x4");
882 dump_machine_register(mctx
->regs
[5], "x5");
883 dump_machine_register(mctx
->regs
[6], "x6");
884 dump_machine_register(mctx
->regs
[7], "x7");
885 dump_machine_register(mctx
->regs
[18], "x18");
886 dump_machine_register(mctx
->regs
[19], "x19");
887 dump_machine_register(mctx
->regs
[20], "x20");
888 dump_machine_register(mctx
->regs
[21], "x21");
889 dump_machine_register(mctx
->regs
[22], "x22");
890 dump_machine_register(mctx
->regs
[23], "x23");
891 dump_machine_register(mctx
->regs
[24], "x24");
892 dump_machine_register(mctx
->regs
[25], "x25");
893 dump_machine_register(mctx
->regs
[26], "x26");
894 dump_machine_register(mctx
->regs
[27], "x27");
895 dump_machine_register(mctx
->regs
[28], "x28");
896 dump_machine_register(mctx
->regs
[29], "x29");
897 dump_machine_register(mctx
->sp
, "sp");
898 dump_machine_register(mctx
->fault_address
, "fault_address");
899 # elif defined __arm__
900 dump_machine_register(mctx
->arm_r0
, "r0");
901 dump_machine_register(mctx
->arm_r1
, "r1");
902 dump_machine_register(mctx
->arm_r2
, "r2");
903 dump_machine_register(mctx
->arm_r3
, "r3");
904 dump_machine_register(mctx
->arm_r4
, "r4");
905 dump_machine_register(mctx
->arm_r5
, "r5");
906 dump_machine_register(mctx
->arm_r6
, "r6");
907 dump_machine_register(mctx
->arm_r7
, "r7");
908 dump_machine_register(mctx
->arm_r8
, "r8");
909 dump_machine_register(mctx
->arm_r9
, "r9");
910 dump_machine_register(mctx
->arm_r10
, "r10");
911 dump_machine_register(mctx
->arm_sp
, "sp");
912 dump_machine_register(mctx
->fault_address
, "fault_address");
913 # elif defined __riscv
914 dump_machine_register(mctx
->__gregs
[REG_SP
], "sp");
915 dump_machine_register(mctx
->__gregs
[REG_S0
], "s0");
916 dump_machine_register(mctx
->__gregs
[REG_S1
], "s1");
917 dump_machine_register(mctx
->__gregs
[REG_A0
], "a0");
918 dump_machine_register(mctx
->__gregs
[REG_A0
+1], "a1");
919 dump_machine_register(mctx
->__gregs
[REG_A0
+2], "a2");
920 dump_machine_register(mctx
->__gregs
[REG_A0
+3], "a3");
921 dump_machine_register(mctx
->__gregs
[REG_A0
+4], "a4");
922 dump_machine_register(mctx
->__gregs
[REG_A0
+5], "a5");
923 dump_machine_register(mctx
->__gregs
[REG_A0
+6], "a6");
924 dump_machine_register(mctx
->__gregs
[REG_A0
+7], "a7");
925 dump_machine_register(mctx
->__gregs
[REG_S2
], "s2");
926 dump_machine_register(mctx
->__gregs
[REG_S2
+1], "s3");
927 dump_machine_register(mctx
->__gregs
[REG_S2
+2], "s4");
928 dump_machine_register(mctx
->__gregs
[REG_S2
+3], "s5");
929 dump_machine_register(mctx
->__gregs
[REG_S2
+4], "s6");
930 dump_machine_register(mctx
->__gregs
[REG_S2
+5], "s7");
931 dump_machine_register(mctx
->__gregs
[REG_S2
+6], "s8");
932 dump_machine_register(mctx
->__gregs
[REG_S2
+7], "s9");
933 dump_machine_register(mctx
->__gregs
[REG_S2
+8], "s10");
934 dump_machine_register(mctx
->__gregs
[REG_S2
+9], "s11");
937 # elif defined __APPLE__
939 const mcontext_t mctx
= ctx
->uc_mcontext
;
940 # if defined __x86_64__
941 dump_machine_register(rax
);
942 dump_machine_register(rbx
);
943 dump_machine_register(rcx
);
944 dump_machine_register(rdx
);
945 dump_machine_register(rdi
);
946 dump_machine_register(rsi
);
947 dump_machine_register(rbp
);
948 dump_machine_register(rsp
);
949 dump_machine_register(r8
);
950 dump_machine_register(r9
);
951 dump_machine_register(r10
);
952 dump_machine_register(r11
);
953 dump_machine_register(r12
);
954 dump_machine_register(r13
);
955 dump_machine_register(r14
);
956 dump_machine_register(r15
);
957 dump_machine_register(rip
);
958 dump_machine_register(rflags
);
959 # elif defined __i386__
960 dump_machine_register(eax
);
961 dump_machine_register(ebx
);
962 dump_machine_register(ecx
);
963 dump_machine_register(edx
);
964 dump_machine_register(edi
);
965 dump_machine_register(esi
);
966 dump_machine_register(ebp
);
967 dump_machine_register(esp
);
968 dump_machine_register(ss
);
969 dump_machine_register(eflags
);
970 dump_machine_register(eip
);
971 dump_machine_register(cs
);
972 dump_machine_register(ds
);
973 dump_machine_register(es
);
974 dump_machine_register(fs
);
975 dump_machine_register(gs
);
976 # elif defined __aarch64__
977 dump_machine_register(x
[0], "x0");
978 dump_machine_register(x
[1], "x1");
979 dump_machine_register(x
[2], "x2");
980 dump_machine_register(x
[3], "x3");
981 dump_machine_register(x
[4], "x4");
982 dump_machine_register(x
[5], "x5");
983 dump_machine_register(x
[6], "x6");
984 dump_machine_register(x
[7], "x7");
985 dump_machine_register(x
[18], "x18");
986 dump_machine_register(x
[19], "x19");
987 dump_machine_register(x
[20], "x20");
988 dump_machine_register(x
[21], "x21");
989 dump_machine_register(x
[22], "x22");
990 dump_machine_register(x
[23], "x23");
991 dump_machine_register(x
[24], "x24");
992 dump_machine_register(x
[25], "x25");
993 dump_machine_register(x
[26], "x26");
994 dump_machine_register(x
[27], "x27");
995 dump_machine_register(x
[28], "x28");
996 dump_machine_register(lr
, "lr");
997 dump_machine_register(fp
, "fp");
998 dump_machine_register(sp
, "sp");
1002 fprintf(stderr
, "\n\n");
1005 # define rb_dump_machine_register(ctx) ((void)0)
1006 #endif /* HAVE_PRINT_MACHINE_REGISTERS */
1009 rb_vm_bugreport(const void *ctx
)
1012 const char *cmd
= getenv("RUBY_ON_BUG");
1015 snprintf(buf
, sizeof(buf
), "%s %"PRI_PIDT_PREFIX
"d", cmd
, getpid());
1016 int r
= system(buf
);
1018 snprintf(buf
, sizeof(buf
), "Launching RUBY_ON_BUG command failed.");
1024 # define PROC_MAPS_NAME "/proc/self/maps"
1026 #ifdef PROC_MAPS_NAME
1027 enum {other_runtime_info
= 1};
1029 enum {other_runtime_info
= 0};
1031 const rb_vm_t
*const vm
= GET_VM();
1032 const rb_execution_context_t
*ec
= rb_current_execution_context(false);
1036 rb_backtrace_print_as_bugreport();
1037 fputs("\n", stderr
);
1040 rb_dump_machine_register(ctx
);
1042 #if USE_BACKTRACE || defined(_WIN32)
1043 fprintf(stderr
, "-- C level backtrace information "
1044 "-------------------------------------------\n");
1045 rb_print_backtrace();
1048 fprintf(stderr
, "\n");
1049 #endif /* USE_BACKTRACE */
1051 if (other_runtime_info
|| vm
) {
1052 fprintf(stderr
, "-- Other runtime information "
1053 "-----------------------------------------------\n\n");
1055 if (vm
&& !rb_during_gc()) {
1059 const int max_name_length
= 1024;
1060 # define LIMITED_NAME_LENGTH(s) \
1061 (((len = RSTRING_LEN(s)) > max_name_length) ? max_name_length : (int)len)
1063 name
= vm
->progname
;
1065 fprintf(stderr
, "* Loaded script: %.*s\n",
1066 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1067 fprintf(stderr
, "\n");
1069 if (vm
->loaded_features
) {
1070 fprintf(stderr
, "* Loaded features:\n\n");
1071 for (i
=0; i
<RARRAY_LEN(vm
->loaded_features
); i
++) {
1072 name
= RARRAY_AREF(vm
->loaded_features
, i
);
1073 if (RB_TYPE_P(name
, T_STRING
)) {
1074 fprintf(stderr
, " %4d %.*s\n", i
,
1075 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1077 else if (RB_TYPE_P(name
, T_CLASS
) || RB_TYPE_P(name
, T_MODULE
)) {
1078 const char *const type
= RB_TYPE_P(name
, T_CLASS
) ?
1080 name
= rb_search_class_path(rb_class_real(name
));
1081 if (!RB_TYPE_P(name
, T_STRING
)) {
1082 fprintf(stderr
, " %4d %s:<unnamed>\n", i
, type
);
1085 fprintf(stderr
, " %4d %s:%.*s\n", i
, type
,
1086 LIMITED_NAME_LENGTH(name
), RSTRING_PTR(name
));
1089 VALUE klass
= rb_search_class_path(rb_obj_class(name
));
1090 if (!RB_TYPE_P(klass
, T_STRING
)) {
1091 fprintf(stderr
, " %4d #<%p:%p>\n", i
,
1092 (void *)CLASS_OF(name
), (void *)name
);
1095 fprintf(stderr
, " %4d #<%.*s:%p>\n", i
,
1096 LIMITED_NAME_LENGTH(klass
), RSTRING_PTR(klass
),
1101 fprintf(stderr
, "\n");
1105 #ifdef PROC_MAPS_NAME
1107 FILE *fp
= fopen(PROC_MAPS_NAME
, "r");
1109 fprintf(stderr
, "* Process memory map:\n\n");
1113 size_t rn
= fread(buff
, 1, 0x100, fp
);
1114 if (fwrite(buff
, 1, rn
, stderr
) != rn
)
1119 fprintf(stderr
, "\n\n");
1122 #endif /* __linux__ */
1123 #ifdef HAVE_LIBPROCSTAT
1124 # define MIB_KERN_PROC_PID_LEN 4
1125 int mib
[MIB_KERN_PROC_PID_LEN
];
1126 struct kinfo_proc kp
;
1127 size_t len
= sizeof(struct kinfo_proc
);
1130 mib
[2] = KERN_PROC_PID
;
1132 if (sysctl(mib
, MIB_KERN_PROC_PID_LEN
, &kp
, &len
, NULL
, 0) == -1) {
1136 struct procstat
*prstat
= procstat_open_sysctl();
1137 fprintf(stderr
, "* Process memory map:\n\n");
1138 procstat_vm(prstat
, &kp
);
1139 procstat_close(prstat
);
1140 fprintf(stderr
, "\n");
1142 #endif /* __FreeBSD__ */
1144 vm_address_t addr
= 0;
1146 struct vm_region_submap_info map
;
1147 mach_msg_type_number_t count
= VM_REGION_SUBMAP_INFO_COUNT
;
1148 natural_t depth
= 0;
1150 fprintf(stderr
, "* Process memory map:\n\n");
1152 if (vm_region_recurse(mach_task_self(), &addr
, &size
, &depth
,
1153 (vm_region_recurse_info_t
)&map
, &count
) != KERN_SUCCESS
) {
1157 if (map
.is_submap
) {
1158 // We only look at main addresses
1162 fprintf(stderr
, "%lx-%lx %s%s%s", addr
, (addr
+size
),
1163 ((map
.protection
& VM_PROT_READ
) != 0 ? "r" : "-"),
1164 ((map
.protection
& VM_PROT_WRITE
) != 0 ? "w" : "-"),
1165 ((map
.protection
& VM_PROT_EXECUTE
) != 0 ? "x" : "-"));
1166 #ifdef HAVE_LIBPROC_H
1167 char buff
[PATH_MAX
];
1168 if (proc_regionfilename(getpid(), addr
, buff
, sizeof(buff
)) > 0) {
1169 fprintf(stderr
, " %s", buff
);
1172 fprintf(stderr
, "\n");
1182 #ifdef NON_SCALAR_THREAD_ID
1183 const char *ruby_fill_thread_id_string(rb_nativethread_id_t thid
, rb_thread_id_string_t buf
);
1187 rb_vmdebug_stack_dump_all_threads(void)
1189 rb_thread_t
*th
= NULL
;
1190 rb_ractor_t
*r
= GET_RACTOR();
1192 // TODO: now it only shows current ractor
1193 list_for_each(&r
->threads
.set
, th
, lt_node
) {
1194 #ifdef NON_SCALAR_THREAD_ID
1195 rb_thread_id_string_t buf
;
1196 ruby_fill_thread_id_string(th
->thread_id
, buf
);
1197 fprintf(stderr
, "th: %p, native_id: %s\n", th
, buf
);
1199 fprintf(stderr
, "th: %p, native_id: %p\n", (void *)th
, (void *)(uintptr_t)th
->thread_id
);
1201 rb_vmdebug_stack_dump_raw(th
->ec
, th
->ec
->cfp
);