1/**********************************************************************
2
3  vm_dump.c -
4
5  $Author: ko1 $
6
7  Copyright (C) 2004-2007 Koichi Sasada
8
9**********************************************************************/
10
11
12#include "ruby/ruby.h"
13#include "addr2line.h"
14#include "vm_core.h"
15#include "internal.h"
16
17/* see vm_insnhelper.h for the values */
18#ifndef VMDEBUG
19#define VMDEBUG 0
20#endif
21
22#define MAX_POSBUF 128
23
24#define VM_CFP_CNT(th, cfp) \
25  ((rb_control_frame_t *)((th)->stack + (th)->stack_size) - (rb_control_frame_t *)(cfp))
26
27static void
28control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
29{
30    ptrdiff_t pc = -1;
31    ptrdiff_t ep = cfp->ep - th->stack;
32    char ep_in_heap = ' ';
33    char posbuf[MAX_POSBUF+1];
34    int line = 0;
35
36    const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-";
37    VALUE tmp;
38
39    if (cfp->block_iseq != 0 && BUILTIN_TYPE(cfp->block_iseq) != T_NODE) {
40	biseq_name = "";	/* RSTRING(cfp->block_iseq->location.label)->ptr; */
41    }
42
43    if (ep < 0 || (size_t)ep > th->stack_size) {
44	ep = (ptrdiff_t)cfp->ep;
45	ep_in_heap = 'p';
46    }
47
48    switch (VM_FRAME_TYPE(cfp)) {
49      case VM_FRAME_MAGIC_TOP:
50	magic = "TOP";
51	break;
52      case VM_FRAME_MAGIC_METHOD:
53	magic = "METHOD";
54	break;
55      case VM_FRAME_MAGIC_CLASS:
56	magic = "CLASS";
57	break;
58      case VM_FRAME_MAGIC_BLOCK:
59	magic = "BLOCK";
60	break;
61      case VM_FRAME_MAGIC_CFUNC:
62	magic = "CFUNC";
63	break;
64      case VM_FRAME_MAGIC_PROC:
65	magic = "PROC";
66	break;
67      case VM_FRAME_MAGIC_LAMBDA:
68	magic = "LAMBDA";
69	break;
70      case VM_FRAME_MAGIC_IFUNC:
71	magic = "IFUNC";
72	break;
73      case VM_FRAME_MAGIC_EVAL:
74	magic = "EVAL";
75	break;
76      case 0:
77	magic = "------";
78	break;
79      default:
80	magic = "(none)";
81	break;
82    }
83
84    if (0) {
85	tmp = rb_inspect(cfp->self);
86	selfstr = StringValueCStr(tmp);
87    }
88    else {
89	selfstr = "";
90    }
91
92    if (cfp->iseq != 0) {
93	if (RUBY_VM_IFUNC_P(cfp->iseq)) {
94	    iseq_name = "<ifunc>";
95	}
96	else {
97	    pc = cfp->pc - cfp->iseq->iseq_encoded;
98	    iseq_name = RSTRING_PTR(cfp->iseq->location.label);
99	    line = rb_vm_get_sourceline(cfp);
100	    if (line) {
101		snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(cfp->iseq->location.path), line);
102	    }
103	}
104    }
105    else if (cfp->me) {
106	iseq_name = rb_id2name(cfp->me->def->original_id);
107	snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name);
108	line = -1;
109    }
110
111    fprintf(stderr, "c:%04"PRIdPTRDIFF" ",
112	    ((rb_control_frame_t *)(th->stack + th->stack_size) - cfp));
113    if (pc == -1) {
114	fprintf(stderr, "p:---- ");
115    }
116    else {
117	fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc);
118    }
119    fprintf(stderr, "s:%04"PRIdPTRDIFF" ", cfp->sp - th->stack);
120    fprintf(stderr, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
121    fprintf(stderr, "%-6s", magic);
122    if (line) {
123	fprintf(stderr, " %s", posbuf);
124    }
125    if (VM_FRAME_TYPE_FINISH_P(cfp)) {
126	fprintf(stderr, " [FINISH]");
127    }
128    if (0) {
129	fprintf(stderr, "              \t");
130	fprintf(stderr, "iseq: %-24s ", iseq_name);
131	fprintf(stderr, "self: %-24s ", selfstr);
132	fprintf(stderr, "%-1s ", biseq_name);
133    }
134    fprintf(stderr, "\n");
135}
136
137void
138rb_vmdebug_stack_dump_raw(rb_thread_t *th, rb_control_frame_t *cfp)
139{
140#if 0
141    VALUE *sp = cfp->sp, *ep = cfp->ep;
142    VALUE *p, *st, *t;
143
144    fprintf(stderr, "-- stack frame ------------\n");
145    for (p = st = th->stack; p < sp; p++) {
146	fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
147
148	t = (VALUE *)*p;
149	if (th->stack <= t && t < sp) {
150	    fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF(t) - th->stack));
151	}
152
153	if (p == ep)
154	    fprintf(stderr, " <- ep");
155
156	fprintf(stderr, "\n");
157    }
158#endif
159
160    fprintf(stderr, "-- Control frame information "
161	    "-----------------------------------------------\n");
162    while ((void *)cfp < (void *)(th->stack + th->stack_size)) {
163	control_frame_dump(th, cfp);
164	cfp++;
165    }
166    fprintf(stderr, "\n");
167}
168
169void
170rb_vmdebug_stack_dump_raw_current(void)
171{
172    rb_thread_t *th = GET_THREAD();
173    rb_vmdebug_stack_dump_raw(th, th->cfp);
174}
175
176void
177rb_vmdebug_env_dump_raw(rb_env_t *env, VALUE *ep)
178{
179    int i;
180    fprintf(stderr, "-- env --------------------\n");
181
182    while (env) {
183	fprintf(stderr, "--\n");
184	for (i = 0; i < env->env_size; i++) {
185	    fprintf(stderr, "%04d: %08"PRIxVALUE" (%p)", -env->local_size + i, env->env[i],
186		   (void *)&env->env[i]);
187	    if (&env->env[i] == ep)
188		fprintf(stderr, " <- ep");
189	    fprintf(stderr, "\n");
190	}
191
192	if (env->prev_envval != 0) {
193	    GetEnvPtr(env->prev_envval, env);
194	}
195	else {
196	    env = 0;
197	}
198    }
199    fprintf(stderr, "---------------------------\n");
200}
201
202void
203rb_vmdebug_proc_dump_raw(rb_proc_t *proc)
204{
205    rb_env_t *env;
206    char *selfstr;
207    VALUE val = rb_inspect(proc->block.self);
208    selfstr = StringValueCStr(val);
209
210    fprintf(stderr, "-- proc -------------------\n");
211    fprintf(stderr, "self: %s\n", selfstr);
212    GetEnvPtr(proc->envval, env);
213    rb_vmdebug_env_dump_raw(env, proc->block.ep);
214}
215
216void
217rb_vmdebug_stack_dump_th(VALUE thval)
218{
219    rb_thread_t *th;
220    GetThreadPtr(thval, th);
221    rb_vmdebug_stack_dump_raw(th, th->cfp);
222}
223
224#if VMDEBUG > 2
225
226/* copy from vm.c */
227static VALUE *
228vm_base_ptr(rb_control_frame_t *cfp)
229{
230    rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
231    VALUE *bp = prev_cfp->sp + cfp->iseq->local_size + 1;
232
233    if (cfp->iseq->type == ISEQ_TYPE_METHOD) {
234	bp += 1;
235    }
236    return bp;
237}
238
239static void
240vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
241{
242    int i;
243
244    VALUE rstr;
245    VALUE *sp = cfp->sp;
246    VALUE *ep = cfp->ep;
247
248    int argc = 0, local_size = 0;
249    const char *name;
250    rb_iseq_t *iseq = cfp->iseq;
251
252    if (iseq == 0) {
253	if (RUBYVM_CFUNC_FRAME_P(cfp)) {
254	    name = rb_id2name(cfp->me->called_id);
255	}
256	else {
257	    name = "?";
258	}
259    }
260    else if (RUBY_VM_IFUNC_P(iseq)) {
261	name = "<ifunc>";
262    }
263    else {
264	argc = iseq->argc;
265	local_size = iseq->local_size;
266	name = RSTRING_PTR(iseq->location.label);
267    }
268
269    /* stack trace header */
270
271    if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_METHOD ||
272	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP ||
273	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK ||
274	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CLASS ||
275	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_PROC ||
276	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA ||
277	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC ||
278	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC ||
279	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_EVAL) {
280
281	VALUE *ptr = ep - local_size;
282
283	control_frame_dump(th, cfp);
284
285	for (i = 0; i < argc; i++) {
286	    rstr = rb_inspect(*ptr);
287	    fprintf(stderr, "  arg   %2d: %8s (%p)\n", i, StringValueCStr(rstr),
288		   (void *)ptr++);
289	}
290	for (; i < local_size - 1; i++) {
291	    rstr = rb_inspect(*ptr);
292	    fprintf(stderr, "  local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
293		   (void *)ptr++);
294	}
295
296	ptr = vm_base_ptr(cfp);
297	for (; ptr < sp; ptr++, i++) {
298	    if (*ptr == Qundef) {
299		rstr = rb_str_new2("undef");
300	    }
301	    else {
302		rstr = rb_inspect(*ptr);
303	    }
304	    fprintf(stderr, "  stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
305		    (ptr - th->stack));
306	}
307    }
308    else if (VM_FRAME_TYPE_FINISH_P(cfp)) {
309	if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 1)) {
310	    vm_stack_dump_each(th, cfp + 1);
311	}
312	else {
313	    /* SDR(); */
314	}
315    }
316    else {
317	rb_bug("unsupport frame type: %08lx", VM_FRAME_TYPE(cfp));
318    }
319}
320#endif
321
322void
323rb_vmdebug_debug_print_register(rb_thread_t *th)
324{
325    rb_control_frame_t *cfp = th->cfp;
326    ptrdiff_t pc = -1;
327    ptrdiff_t ep = cfp->ep - th->stack;
328    ptrdiff_t cfpi;
329
330    if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) {
331	pc = cfp->pc - cfp->iseq->iseq_encoded;
332    }
333
334    if (ep < 0 || (size_t)ep > th->stack_size) {
335	ep = -1;
336    }
337
338    cfpi = ((rb_control_frame_t *)(th->stack + th->stack_size)) - cfp;
339    fprintf(stderr, "  [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [EP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
340	    pc, (cfp->sp - th->stack), ep, cfpi);
341}
342
343void
344rb_vmdebug_thread_dump_regs(VALUE thval)
345{
346    rb_thread_t *th;
347    GetThreadPtr(thval, th);
348    rb_vmdebug_debug_print_register(th);
349}
350
351void
352rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp)
353{
354    rb_iseq_t *iseq = cfp->iseq;
355
356    if (iseq != 0) {
357	VALUE *seq = iseq->iseq;
358	ptrdiff_t pc = cfp->pc - iseq->iseq_encoded;
359	int i;
360
361	for (i=0; i<(int)VM_CFP_CNT(th, cfp); i++) {
362	    printf(" ");
363	}
364	printf("| ");
365	/* printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(th, cfp)); */
366	if (pc >= 0) {
367	    rb_iseq_disasm_insn(0, seq, (size_t)pc, iseq, 0);
368	}
369    }
370
371#if VMDEBUG > 3
372    fprintf(stderr, "        (1)");
373    rb_vmdebug_debug_print_register(th);
374#endif
375}
376
377void
378rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
379#if OPT_STACK_CACHING
380		 , VALUE reg_a, VALUE reg_b
381#endif
382    )
383{
384#if VMDEBUG > 9
385    SDR2(cfp);
386#endif
387
388#if VMDEBUG > 3
389    fprintf(stderr, "        (2)");
390    rb_vmdebug_debug_print_register(th);
391#endif
392    /* stack_dump_raw(th, cfp); */
393
394#if VMDEBUG > 2
395    /* stack_dump_thobj(th); */
396    vm_stack_dump_each(th, th->cfp);
397
398#if OPT_STACK_CACHING
399    {
400	VALUE rstr;
401	rstr = rb_inspect(reg_a);
402	fprintf(stderr, "  sc reg A: %s\n", StringValueCStr(rstr));
403	rstr = rb_inspect(reg_b);
404	fprintf(stderr, "  sc reg B: %s\n", StringValueCStr(rstr));
405    }
406#endif
407    printf
408	("--------------------------------------------------------------\n");
409#endif
410}
411
412VALUE
413rb_vmdebug_thread_dump_state(VALUE self)
414{
415    rb_thread_t *th;
416    rb_control_frame_t *cfp;
417    GetThreadPtr(self, th);
418    cfp = th->cfp;
419
420    fprintf(stderr, "Thread state dump:\n");
421    fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
422    fprintf(stderr, "cfp: %p, ep : %p\n", (void *)cfp, (void *)cfp->ep);
423
424    return Qnil;
425}
426
427#ifndef HAVE_BACKTRACE
428#define HAVE_BACKTRACE 0
429#endif
430#if HAVE_BACKTRACE
431# include <execinfo.h>
432#elif defined(_WIN32)
433# include <imagehlp.h>
434# ifndef SYMOPT_DEBUG
435#  define SYMOPT_DEBUG 0x80000000
436# endif
437# ifndef MAX_SYM_NAME
438# define MAX_SYM_NAME 2000
439typedef struct {
440    DWORD64 Offset;
441    WORD Segment;
442    ADDRESS_MODE Mode;
443} ADDRESS64;
444typedef struct {
445    DWORD64 Thread;
446    DWORD ThCallbackStack;
447    DWORD ThCallbackBStore;
448    DWORD NextCallback;
449    DWORD FramePointer;
450    DWORD64 KiCallUserMode;
451    DWORD64 KeUserCallbackDispatcher;
452    DWORD64 SystemRangeStart;
453    DWORD64 KiUserExceptionDispatcher;
454    DWORD64 StackBase;
455    DWORD64 StackLimit;
456    DWORD64 Reserved[5];
457} KDHELP64;
458typedef struct {
459    ADDRESS64 AddrPC;
460    ADDRESS64 AddrReturn;
461    ADDRESS64 AddrFrame;
462    ADDRESS64 AddrStack;
463    ADDRESS64 AddrBStore;
464    void *FuncTableEntry;
465    DWORD64 Params[4];
466    BOOL Far;
467    BOOL Virtual;
468    DWORD64 Reserved[3];
469    KDHELP64 KdHelp;
470} STACKFRAME64;
471typedef struct {
472    ULONG SizeOfStruct;
473    ULONG TypeIndex;
474    ULONG64 Reserved[2];
475    ULONG Index;
476    ULONG Size;
477    ULONG64 ModBase;
478    ULONG Flags;
479    ULONG64 Value;
480    ULONG64 Address;
481    ULONG Register;
482    ULONG Scope;
483    ULONG Tag;
484    ULONG NameLen;
485    ULONG MaxNameLen;
486    char Name[1];
487} SYMBOL_INFO;
488typedef struct {
489    DWORD SizeOfStruct;
490    void *Key;
491    DWORD LineNumber;
492    char *FileName;
493    DWORD64 Address;
494} IMAGEHLP_LINE64;
495typedef void *PREAD_PROCESS_MEMORY_ROUTINE64;
496typedef void *PFUNCTION_TABLE_ACCESS_ROUTINE64;
497typedef void *PGET_MODULE_BASE_ROUTINE64;
498typedef void *PTRANSLATE_ADDRESS_ROUTINE64;
499# endif
500
501static void
502dump_thread(void *arg)
503{
504    HANDLE dbghelp;
505    BOOL (WINAPI *pSymInitialize)(HANDLE, const char *, BOOL);
506    BOOL (WINAPI *pSymCleanup)(HANDLE);
507    BOOL (WINAPI *pStackWalk64)(DWORD, HANDLE, HANDLE, STACKFRAME64 *, void *, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
508    DWORD64 (WINAPI *pSymGetModuleBase64)(HANDLE, DWORD64);
509    BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *);
510    BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *);
511    HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD);
512    DWORD tid = *(DWORD *)arg;
513    HANDLE ph;
514    HANDLE th;
515
516    dbghelp = LoadLibrary("dbghelp.dll");
517    if (!dbghelp) return;
518    pSymInitialize = (BOOL (WINAPI *)(HANDLE, const char *, BOOL))GetProcAddress(dbghelp, "SymInitialize");
519    pSymCleanup = (BOOL (WINAPI *)(HANDLE))GetProcAddress(dbghelp, "SymCleanup");
520    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");
521    pSymGetModuleBase64 = (DWORD64 (WINAPI *)(HANDLE, DWORD64))GetProcAddress(dbghelp, "SymGetModuleBase64");
522    pSymFromAddr = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *))GetProcAddress(dbghelp, "SymFromAddr");
523    pSymGetLineFromAddr64 = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *))GetProcAddress(dbghelp, "SymGetLineFromAddr64");
524    pOpenThread = (HANDLE (WINAPI *)(DWORD, BOOL, DWORD))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenThread");
525    if (pSymInitialize && pSymCleanup && pStackWalk64 && pSymGetModuleBase64 &&
526	pSymFromAddr && pSymGetLineFromAddr64 && pOpenThread) {
527	SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES);
528	ph = GetCurrentProcess();
529	pSymInitialize(ph, NULL, TRUE);
530	th = pOpenThread(THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, FALSE, tid);
531	if (th) {
532	    if (SuspendThread(th) != (DWORD)-1) {
533		CONTEXT context;
534		memset(&context, 0, sizeof(context));
535		context.ContextFlags = CONTEXT_FULL;
536		if (GetThreadContext(th, &context)) {
537		    char libpath[MAX_PATH];
538		    char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
539		    SYMBOL_INFO *info = (SYMBOL_INFO *)buf;
540		    DWORD mac;
541		    STACKFRAME64 frame;
542		    memset(&frame, 0, sizeof(frame));
543#if defined(_M_AMD64) || defined(__x86_64__)
544		    mac = IMAGE_FILE_MACHINE_AMD64;
545		    frame.AddrPC.Mode = AddrModeFlat;
546		    frame.AddrPC.Offset = context.Rip;
547		    frame.AddrFrame.Mode = AddrModeFlat;
548		    frame.AddrFrame.Offset = context.Rbp;
549		    frame.AddrStack.Mode = AddrModeFlat;
550		    frame.AddrStack.Offset = context.Rsp;
551#elif defined(_M_IA64) || defined(__ia64__)
552		    mac = IMAGE_FILE_MACHINE_IA64;
553		    frame.AddrPC.Mode = AddrModeFlat;
554		    frame.AddrPC.Offset = context.StIIP;
555		    frame.AddrBStore.Mode = AddrModeFlat;
556		    frame.AddrBStore.Offset = context.RsBSP;
557		    frame.AddrStack.Mode = AddrModeFlat;
558		    frame.AddrStack.Offset = context.IntSp;
559#else	/* i386 */
560		    mac = IMAGE_FILE_MACHINE_I386;
561		    frame.AddrPC.Mode = AddrModeFlat;
562		    frame.AddrPC.Offset = context.Eip;
563		    frame.AddrFrame.Mode = AddrModeFlat;
564		    frame.AddrFrame.Offset = context.Ebp;
565		    frame.AddrStack.Mode = AddrModeFlat;
566		    frame.AddrStack.Offset = context.Esp;
567#endif
568
569		    while (pStackWalk64(mac, ph, th, &frame, &context, NULL,
570					NULL, NULL, NULL)) {
571			DWORD64 addr = frame.AddrPC.Offset;
572			IMAGEHLP_LINE64 line;
573			DWORD64 displacement;
574			DWORD tmp;
575
576			if (addr == frame.AddrReturn.Offset || addr == 0 ||
577			    frame.AddrReturn.Offset == 0)
578			    break;
579
580			memset(buf, 0, sizeof(buf));
581			info->SizeOfStruct = sizeof(SYMBOL_INFO);
582			info->MaxNameLen = MAX_SYM_NAME;
583			if (pSymFromAddr(ph, addr, &displacement, info)) {
584			    if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath)))
585				fprintf(stderr, "%s", libpath);
586			    fprintf(stderr, "(%s+0x%I64x)",
587				    info->Name, displacement);
588			}
589			fprintf(stderr, " [0x%p]", (void *)(VALUE)addr);
590			memset(&line, 0, sizeof(line));
591			line.SizeOfStruct = sizeof(line);
592			if (pSymGetLineFromAddr64(ph, addr, &tmp, &line))
593			    fprintf(stderr, " %s:%lu", line.FileName, line.LineNumber);
594			fprintf(stderr, "\n");
595		    }
596		}
597
598		ResumeThread(th);
599	    }
600	    CloseHandle(th);
601	}
602	pSymCleanup(ph);
603    }
604    FreeLibrary(dbghelp);
605}
606#endif
607
608void
609rb_vm_bugreport(void)
610{
611#ifdef __linux__
612# define PROC_MAPS_NAME "/proc/self/maps"
613#endif
614#ifdef PROC_MAPS_NAME
615    enum {other_runtime_info = 1};
616#else
617    enum {other_runtime_info = 0};
618#endif
619    const rb_vm_t *const vm = GET_VM();
620
621#if defined __APPLE__
622    fputs("-- Crash Report log information "
623	  "--------------------------------------------\n"
624	  "   See Crash Report log file under the one of following:\n"
625	  "     * ~/Library/Logs/CrashReporter\n"
626	  "     * /Library/Logs/CrashReporter\n"
627	  "     * ~/Library/Logs/DiagnosticReports\n"
628	  "     * /Library/Logs/DiagnosticReports\n"
629	  "   the more detail of.\n"
630	  "\n",
631	  stderr);
632#endif
633    if (vm) {
634	SDR();
635	rb_backtrace_print_as_bugreport();
636	fputs("\n", stderr);
637    }
638
639#if HAVE_BACKTRACE || defined(_WIN32)
640    fprintf(stderr, "-- C level backtrace information "
641	    "-------------------------------------------\n");
642
643    {
644#if HAVE_BACKTRACE
645#define MAX_NATIVE_TRACE 1024
646	static void *trace[MAX_NATIVE_TRACE];
647	int n = backtrace(trace, MAX_NATIVE_TRACE);
648	char **syms = backtrace_symbols(trace, n);
649
650	if (syms) {
651#ifdef USE_ELF
652	    rb_dump_backtrace_with_lines(n, trace, syms);
653#else
654	    int i;
655	    for (i=0; i<n; i++) {
656		fprintf(stderr, "%s\n", syms[i]);
657	    }
658#endif
659	    free(syms);
660	}
661#elif defined(_WIN32)
662	DWORD tid = GetCurrentThreadId();
663	HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &tid);
664	if (th != (HANDLE)-1)
665	    WaitForSingleObject(th, INFINITE);
666#endif
667    }
668
669    fprintf(stderr, "\n");
670#endif /* HAVE_BACKTRACE */
671
672    if (other_runtime_info || vm) {
673	fprintf(stderr, "-- Other runtime information "
674		"-----------------------------------------------\n\n");
675    }
676    if (vm) {
677	int i;
678	VALUE name;
679	long len;
680	const int max_name_length = 1024;
681# define LIMITED_NAME_LENGTH(s) \
682	(((len = RSTRING_LEN(s)) > max_name_length) ? max_name_length : (int)len)
683
684	name = vm->progname;
685	fprintf(stderr, "* Loaded script: %.*s\n",
686		LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
687	fprintf(stderr, "\n");
688	fprintf(stderr, "* Loaded features:\n\n");
689	for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
690	    name = RARRAY_PTR(vm->loaded_features)[i];
691	    if (RB_TYPE_P(name, T_STRING)) {
692		fprintf(stderr, " %4d %.*s\n", i,
693			LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
694	    }
695	    else {
696		fprintf(stderr, " %4d #<%s:%p>\n", i,
697			rb_class2name(CLASS_OF(name)), (void *)name);
698	    }
699	}
700	fprintf(stderr, "\n");
701    }
702
703    {
704#ifdef PROC_MAPS_NAME
705	{
706	    FILE *fp = fopen(PROC_MAPS_NAME, "r");
707	    if (fp) {
708		fprintf(stderr, "* Process memory map:\n\n");
709
710		while (!feof(fp)) {
711		    char buff[0x100];
712		    size_t rn = fread(buff, 1, 0x100, fp);
713		    if (fwrite(buff, 1, rn, stderr) != rn)
714			break;
715		}
716
717		fclose(fp);
718		fprintf(stderr, "\n\n");
719	    }
720	}
721#endif /* __linux__ */
722    }
723}
724