Ruby  1.9.3p484(2013-11-22revision43786)
vm_dump.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  vm_dump.c -
4 
5  $Author: naruse $
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 
16 #define MAX_POSBUF 128
17 
18 #define VM_CFP_CNT(th, cfp) \
19  ((rb_control_frame_t *)((th)->stack + (th)->stack_size) - (rb_control_frame_t *)(cfp))
20 
21 static void
23 {
24  ptrdiff_t pc = -1, bp = -1;
25  ptrdiff_t lfp = cfp->lfp - th->stack;
26  ptrdiff_t dfp = cfp->dfp - th->stack;
27  char lfp_in_heap = ' ', dfp_in_heap = ' ';
28  char posbuf[MAX_POSBUF+1];
29  int line = 0;
30  int nopos = 0;
31 
32  const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-";
33  VALUE tmp;
34 
35  if (cfp->block_iseq != 0 && BUILTIN_TYPE(cfp->block_iseq) != T_NODE) {
36  biseq_name = ""; /* RSTRING(cfp->block_iseq->name)->ptr; */
37  }
38 
39  if (lfp < 0 || (size_t)lfp > th->stack_size) {
40  lfp = (ptrdiff_t)cfp->lfp;
41  lfp_in_heap = 'p';
42  }
43  if (dfp < 0 || (size_t)dfp > th->stack_size) {
44  dfp = (ptrdiff_t)cfp->dfp;
45  dfp_in_heap = 'p';
46  }
47  if (cfp->bp) {
48  bp = cfp->bp - th->stack;
49  }
50 
51  switch (VM_FRAME_TYPE(cfp)) {
52  case VM_FRAME_MAGIC_TOP:
53  magic = "TOP";
54  break;
56  magic = "METHOD";
57  break;
59  magic = "CLASS";
60  break;
62  magic = "BLOCK";
63  break;
65  magic = "FINISH";
66  nopos = 1;
67  break;
69  magic = "CFUNC";
70  break;
72  magic = "PROC";
73  break;
75  magic = "LAMBDA";
76  break;
78  magic = "IFUNC";
79  break;
81  magic = "EVAL";
82  break;
83  case 0:
84  magic = "------";
85  break;
86  default:
87  magic = "(none)";
88  break;
89  }
90 
91  if (0) {
92  tmp = rb_inspect(cfp->self);
93  selfstr = StringValueCStr(tmp);
94  }
95  else {
96  selfstr = "";
97  }
98 
99  if (nopos) {
100  /* no name */
101  }
102  else if (cfp->iseq != 0) {
103  if (RUBY_VM_IFUNC_P(cfp->iseq)) {
104  iseq_name = "<ifunc>";
105  }
106  else {
107  pc = cfp->pc - cfp->iseq->iseq_encoded;
108  iseq_name = RSTRING_PTR(cfp->iseq->name);
109  line = rb_vm_get_sourceline(cfp);
110  if (line) {
111  snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(cfp->iseq->filename), line);
112  }
113  }
114  }
115  else if (cfp->me) {
116  iseq_name = rb_id2name(cfp->me->def->original_id);
117  snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name);
118  line = -1;
119  }
120 
121  fprintf(stderr, "c:%04"PRIdPTRDIFF" ",
122  ((rb_control_frame_t *)(th->stack + th->stack_size) - cfp));
123  if (pc == -1) {
124  fprintf(stderr, "p:---- ");
125  }
126  else {
127  fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc);
128  }
129  fprintf(stderr, "s:%04"PRIdPTRDIFF" b:%04"PRIdPTRDIFF" ", (cfp->sp - th->stack), bp);
130  fprintf(stderr, lfp_in_heap == ' ' ? "l:%06"PRIdPTRDIFF" " : "l:%06"PRIxPTRDIFF" ", lfp % 10000);
131  fprintf(stderr, dfp_in_heap == ' ' ? "d:%06"PRIdPTRDIFF" " : "d:%06"PRIxPTRDIFF" ", dfp % 10000);
132  fprintf(stderr, "%-6s", magic);
133  if (line && !nopos) {
134  fprintf(stderr, " %s", posbuf);
135  }
136  if (0) {
137  fprintf(stderr, " \t");
138  fprintf(stderr, "iseq: %-24s ", iseq_name);
139  fprintf(stderr, "self: %-24s ", selfstr);
140  fprintf(stderr, "%-1s ", biseq_name);
141  }
142  fprintf(stderr, "\n");
143 }
144 
145 void
147 {
148 #if 0
149  VALUE *sp = cfp->sp, *bp = cfp->bp;
150  VALUE *lfp = cfp->lfp;
151  VALUE *dfp = cfp->dfp;
152  VALUE *p, *st, *t;
153 
154  fprintf(stderr, "-- stack frame ------------\n");
155  for (p = st = th->stack; p < sp; p++) {
156  fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
157 
158  t = (VALUE *)*p;
159  if (th->stack <= t && t < sp) {
160  fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF(t) - th->stack));
161  }
162 
163  if (p == lfp)
164  fprintf(stderr, " <- lfp");
165  if (p == dfp)
166  fprintf(stderr, " <- dfp");
167  if (p == bp)
168  fprintf(stderr, " <- bp"); /* should not be */
169 
170  fprintf(stderr, "\n");
171  }
172 #endif
173 
174  fprintf(stderr, "-- Control frame information "
175  "-----------------------------------------------\n");
176  while ((void *)cfp < (void *)(th->stack + th->stack_size)) {
177  control_frame_dump(th, cfp);
178  cfp++;
179  }
180  fprintf(stderr, "\n");
181 }
182 
183 void
185 {
186  rb_thread_t *th = GET_THREAD();
188 }
189 
190 void
192 {
193  int i;
194  fprintf(stderr, "-- env --------------------\n");
195 
196  while (env) {
197  fprintf(stderr, "--\n");
198  for (i = 0; i < env->env_size; i++) {
199  fprintf(stderr, "%04d: %08"PRIxVALUE" (%p)", -env->local_size + i, env->env[i],
200  (void *)&env->env[i]);
201  if (&env->env[i] == lfp)
202  fprintf(stderr, " <- lfp");
203  if (&env->env[i] == dfp)
204  fprintf(stderr, " <- dfp");
205  fprintf(stderr, "\n");
206  }
207 
208  if (env->prev_envval != 0) {
209  GetEnvPtr(env->prev_envval, env);
210  }
211  else {
212  env = 0;
213  }
214  }
215  fprintf(stderr, "---------------------------\n");
216 }
217 
218 void
220 {
221  rb_env_t *env;
222  char *selfstr;
223  VALUE val = rb_inspect(proc->block.self);
224  selfstr = StringValueCStr(val);
225 
226  fprintf(stderr, "-- proc -------------------\n");
227  fprintf(stderr, "self: %s\n", selfstr);
228  GetEnvPtr(proc->envval, env);
229  rb_vmdebug_env_dump_raw(env, proc->block.lfp, proc->block.dfp);
230 }
231 
232 void
234 {
235  rb_thread_t *th;
236  GetThreadPtr(thval, th);
238 }
239 
240 #if VMDEBUG > 2
241 static void
242 vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
243 {
244  int i;
245 
246  VALUE rstr;
247  VALUE *sp = cfp->sp;
248  VALUE *lfp = cfp->lfp;
249  VALUE *dfp = cfp->dfp;
250 
251  int argc = 0, local_size = 0;
252  const char *name;
253  rb_iseq_t *iseq = cfp->iseq;
254 
255  if (iseq == 0) {
256  if (RUBYVM_CFUNC_FRAME_P(cfp)) {
257  name = rb_id2name(cfp->me->original_id);
258  }
259  else {
260  name = "?";
261  }
262  }
263  else if (RUBY_VM_IFUNC_P(iseq)) {
264  name = "<ifunc>";
265  }
266  else {
267  argc = iseq->argc;
268  local_size = iseq->local_size;
269  name = RSTRING_PTR(iseq->name);
270  }
271 
272  /* stack trace header */
273 
274  if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_METHOD ||
283 
284  VALUE *ptr = dfp - local_size;
285 
286  vm_stack_dump_each(th, cfp + 1);
287  control_frame_dump(th, cfp);
288 
289  if (lfp != dfp) {
290  local_size++;
291  }
292  for (i = 0; i < argc; i++) {
293  rstr = rb_inspect(*ptr);
294  fprintf(stderr, " arg %2d: %8s (%p)\n", i, StringValueCStr(rstr),
295  (void *)ptr++);
296  }
297  for (; i < local_size - 1; i++) {
298  rstr = rb_inspect(*ptr);
299  fprintf(stderr, " local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
300  (void *)ptr++);
301  }
302 
303  ptr = cfp->bp;
304  for (; ptr < sp; ptr++, i++) {
305  if (*ptr == Qundef) {
306  rstr = rb_str_new2("undef");
307  }
308  else {
309  rstr = rb_inspect(*ptr);
310  }
311  fprintf(stderr, " stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
312  (ptr - th->stack));
313  }
314  }
315  else if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
316  if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 2)) {
317  vm_stack_dump_each(th, cfp + 1);
318  }
319  else {
320  /* SDR(); */
321  }
322  }
323  else {
324  rb_bug("unsupport frame type: %08lx", VM_FRAME_TYPE(cfp));
325  }
326 }
327 #endif
328 
329 void
331 {
332  rb_control_frame_t *cfp = th->cfp;
333  ptrdiff_t pc = -1;
334  ptrdiff_t lfp = cfp->lfp - th->stack;
335  ptrdiff_t dfp = cfp->dfp - th->stack;
336  ptrdiff_t cfpi;
337 
338  if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) {
339  pc = cfp->pc - cfp->iseq->iseq_encoded;
340  }
341 
342  if (lfp < 0 || (size_t)lfp > th->stack_size)
343  lfp = -1;
344  if (dfp < 0 || (size_t)dfp > th->stack_size)
345  dfp = -1;
346 
347  cfpi = ((rb_control_frame_t *)(th->stack + th->stack_size)) - cfp;
348  fprintf(stderr, " [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [LFP] %04"PRIdPTRDIFF", [DFP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
349  pc, (cfp->sp - th->stack), lfp, dfp, cfpi);
350 }
351 
352 void
354 {
355  rb_thread_t *th;
356  GetThreadPtr(thval, th);
358 }
359 
360 void
362 {
363  rb_iseq_t *iseq = cfp->iseq;
364 
365  if (iseq != 0 && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_FINISH) {
366  VALUE *seq = iseq->iseq;
367  ptrdiff_t pc = cfp->pc - iseq->iseq_encoded;
368 
369  printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(th, cfp));
370  if (pc >= 0) {
371  rb_iseq_disasm_insn(0, seq, (size_t)pc, iseq, 0);
372  }
373  }
374 
375 #if VMDEBUG > 3
376  fprintf(stderr, " (1)");
378 #endif
379 }
380 
381 void
383 #if OPT_STACK_CACHING
384  , VALUE reg_a, VALUE reg_b
385 #endif
386  )
387 {
388 #if VMDEBUG > 9
389  SDR2(cfp);
390 #endif
391 
392 #if VMDEBUG > 3
393  fprintf(stderr, " (2)");
395 #endif
396  /* stack_dump_raw(th, cfp); */
397 
398 #if VMDEBUG > 2
399  /* stack_dump_thobj(th); */
400  vm_stack_dump_each(th, th->cfp);
401 #if OPT_STACK_CACHING
402  {
403  VALUE rstr;
404  rstr = rb_inspect(reg_a);
405  fprintf(stderr, " sc reg A: %s\n", StringValueCStr(rstr));
406  rstr = rb_inspect(reg_b);
407  fprintf(stderr, " sc reg B: %s\n", StringValueCStr(rstr));
408  }
409 #endif
410  printf
411  ("--------------------------------------------------------------\n");
412 #endif
413 }
414 
415 #ifdef COLLECT_USAGE_ANALYSIS
416 /* uh = {
417  * insn(Fixnum) => ihash(Hash)
418  * }
419  * ihash = {
420  * -1(Fixnum) => count, # insn usage
421  * 0(Fixnum) => ophash, # operand usage
422  * }
423  * ophash = {
424  * val(interned string) => count(Fixnum)
425  * }
426  */
427 void
428 vm_analysis_insn(int insn)
429 {
430  ID usage_hash;
431  ID bigram_hash;
432  static int prev_insn = -1;
433 
434  VALUE uh;
435  VALUE ihash;
436  VALUE cv;
437 
438  CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
439  CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM");
440  uh = rb_const_get(rb_cRubyVM, usage_hash);
441  if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
442  ihash = rb_hash_new();
443  rb_hash_aset(uh, INT2FIX(insn), ihash);
444  }
445  if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) {
446  cv = INT2FIX(0);
447  }
448  rb_hash_aset(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1));
449 
450  /* calc bigram */
451  if (prev_insn != -1) {
452  VALUE bi;
453  VALUE ary[2];
454  VALUE cv;
455 
456  ary[0] = INT2FIX(prev_insn);
457  ary[1] = INT2FIX(insn);
458  bi = rb_ary_new4(2, &ary[0]);
459 
460  uh = rb_const_get(rb_cRubyVM, bigram_hash);
461  if ((cv = rb_hash_aref(uh, bi)) == Qnil) {
462  cv = INT2FIX(0);
463  }
464  rb_hash_aset(uh, bi, INT2FIX(FIX2INT(cv) + 1));
465  }
466  prev_insn = insn;
467 }
468 
469 /* from disasm.c */
470 extern VALUE insn_operand_intern(int insn, int op_no, VALUE op,
471  int len, int pos, VALUE child);
472 
473 void
474 vm_analysis_operand(int insn, int n, VALUE op)
475 {
476  ID usage_hash;
477 
478  VALUE uh;
479  VALUE ihash;
480  VALUE ophash;
481  VALUE valstr;
482  VALUE cv;
483 
484  CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
485 
486  uh = rb_const_get(rb_cRubyVM, usage_hash);
487  if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
488  ihash = rb_hash_new();
489  rb_hash_aset(uh, INT2FIX(insn), ihash);
490  }
491  if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) {
492  ophash = rb_hash_new();
493  rb_hash_aset(ihash, INT2FIX(n), ophash);
494  }
495  /* intern */
496  valstr = insn_operand_intern(insn, n, op, 0, 0, 0);
497 
498  /* set count */
499  if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) {
500  cv = INT2FIX(0);
501  }
502  rb_hash_aset(ophash, valstr, INT2FIX(FIX2INT(cv) + 1));
503 }
504 
505 void
506 vm_analysis_register(int reg, int isset)
507 {
508  ID usage_hash;
509  VALUE uh;
510  VALUE rhash;
511  VALUE valstr;
512  static const char regstrs[][5] = {
513  "pc", /* 0 */
514  "sp", /* 1 */
515  "cfp", /* 2 */
516  "lfp", /* 3 */
517  "dfp", /* 4 */
518  "self", /* 5 */
519  "iseq", /* 6 */
520  };
521  static const char getsetstr[][4] = {
522  "get",
523  "set",
524  };
525  static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2];
526 
527  VALUE cv;
528 
529  CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS");
530  if (syms[0] == 0) {
531  char buff[0x10];
532  int i;
533 
534  for (i = 0; i < sizeof(regstrs) / sizeof(regstrs[0]); i++) {
535  int j;
536  for (j = 0; j < 2; j++) {
537  snfprintf(stderr, buff, 0x10, "%d %s %-4s", i, getsetstr[j],
538  regstrs[i]);
539  syms[i][j] = ID2SYM(rb_intern(buff));
540  }
541  }
542  }
543  valstr = syms[reg][isset];
544 
545  uh = rb_const_get(rb_cRubyVM, usage_hash);
546  if ((cv = rb_hash_aref(uh, valstr)) == Qnil) {
547  cv = INT2FIX(0);
548  }
549  rb_hash_aset(uh, valstr, INT2FIX(FIX2INT(cv) + 1));
550 }
551 
552 
553 #endif
554 
555 VALUE
557 {
558  rb_thread_t *th;
559  rb_control_frame_t *cfp;
560  GetThreadPtr(self, th);
561  cfp = th->cfp;
562 
563  fprintf(stderr, "Thread state dump:\n");
564  fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
565  fprintf(stderr, "cfp: %p, lfp: %p, dfp: %p\n", (void *)cfp, (void *)cfp->lfp, (void *)cfp->dfp);
566 
567  return Qnil;
568 }
569 
570 static int
571 bugreport_backtrace(void *arg, VALUE file, int line, VALUE method)
572 {
573  const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
574  if (!*(int *)arg) {
575  fprintf(stderr, "-- Ruby level backtrace information "
576  "----------------------------------------\n");
577  *(int *)arg = 1;
578  }
579  if (NIL_P(method)) {
580  fprintf(stderr, "%s:%d:in unknown method\n", filename, line);
581  }
582  else {
583  fprintf(stderr, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method));
584  }
585  return 0;
586 }
587 
588 #if defined(__FreeBSD__) && defined(__OPTIMIZE__)
589 #undef HAVE_BACKTRACE
590 #endif
591 #if HAVE_BACKTRACE
592 # include <execinfo.h>
593 #elif defined(_WIN32)
594 # include <imagehlp.h>
595 # ifndef SYMOPT_DEBUG
596 # define SYMOPT_DEBUG 0x80000000
597 # endif
598 # ifndef MAX_SYM_NAME
599 # define MAX_SYM_NAME 2000
600 typedef struct {
601  DWORD64 Offset;
602  WORD Segment;
603  ADDRESS_MODE Mode;
604 } ADDRESS64;
605 typedef struct {
606  DWORD64 Thread;
607  DWORD ThCallbackStack;
608  DWORD ThCallbackBStore;
609  DWORD NextCallback;
610  DWORD FramePointer;
611  DWORD64 KiCallUserMode;
612  DWORD64 KeUserCallbackDispatcher;
613  DWORD64 SystemRangeStart;
614  DWORD64 KiUserExceptionDispatcher;
615  DWORD64 StackBase;
616  DWORD64 StackLimit;
617  DWORD64 Reserved[5];
618 } KDHELP64;
619 typedef struct {
620  ADDRESS64 AddrPC;
621  ADDRESS64 AddrReturn;
622  ADDRESS64 AddrFrame;
623  ADDRESS64 AddrStack;
624  ADDRESS64 AddrBStore;
625  void *FuncTableEntry;
626  DWORD64 Params[4];
627  BOOL Far;
628  BOOL Virtual;
629  DWORD64 Reserved[3];
630  KDHELP64 KdHelp;
631 } STACKFRAME64;
632 typedef struct {
633  ULONG SizeOfStruct;
634  ULONG TypeIndex;
635  ULONG64 Reserved[2];
636  ULONG Index;
637  ULONG Size;
638  ULONG64 ModBase;
639  ULONG Flags;
640  ULONG64 Value;
641  ULONG64 Address;
642  ULONG Register;
643  ULONG Scope;
644  ULONG Tag;
645  ULONG NameLen;
646  ULONG MaxNameLen;
647  char Name[1];
648 } SYMBOL_INFO;
649 typedef struct {
650  DWORD SizeOfStruct;
651  void *Key;
652  DWORD LineNumber;
653  char *FileName;
654  DWORD64 Address;
655 } IMAGEHLP_LINE64;
656 typedef void *PREAD_PROCESS_MEMORY_ROUTINE64;
657 typedef void *PFUNCTION_TABLE_ACCESS_ROUTINE64;
658 typedef void *PGET_MODULE_BASE_ROUTINE64;
659 typedef void *PTRANSLATE_ADDRESS_ROUTINE64;
660 # endif
661 
662 static void
663 dump_thread(void *arg)
664 {
665  HANDLE dbghelp;
666  BOOL (WINAPI *pSymInitialize)(HANDLE, const char *, BOOL);
667  BOOL (WINAPI *pSymCleanup)(HANDLE);
668  BOOL (WINAPI *pStackWalk64)(DWORD, HANDLE, HANDLE, STACKFRAME64 *, void *, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
669  DWORD64 (WINAPI *pSymGetModuleBase64)(HANDLE, DWORD64);
670  BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *);
671  BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *);
672  HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD);
673  DWORD tid = *(DWORD *)arg;
674  HANDLE ph;
675  HANDLE th;
676 
677  dbghelp = LoadLibrary("dbghelp.dll");
678  if (!dbghelp) return;
679  pSymInitialize = (BOOL (WINAPI *)(HANDLE, const char *, BOOL))GetProcAddress(dbghelp, "SymInitialize");
680  pSymCleanup = (BOOL (WINAPI *)(HANDLE))GetProcAddress(dbghelp, "SymCleanup");
681  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");
682  pSymGetModuleBase64 = (DWORD64 (WINAPI *)(HANDLE, DWORD64))GetProcAddress(dbghelp, "SymGetModuleBase64");
683  pSymFromAddr = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *))GetProcAddress(dbghelp, "SymFromAddr");
684  pSymGetLineFromAddr64 = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *))GetProcAddress(dbghelp, "SymGetLineFromAddr64");
685  pOpenThread = (HANDLE (WINAPI *)(DWORD, BOOL, DWORD))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenThread");
686  if (pSymInitialize && pSymCleanup && pStackWalk64 && pSymGetModuleBase64 &&
687  pSymFromAddr && pSymGetLineFromAddr64 && pOpenThread) {
688  SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES);
689  ph = GetCurrentProcess();
690  pSymInitialize(ph, NULL, TRUE);
691  th = pOpenThread(THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, FALSE, tid);
692  if (th) {
693  if (SuspendThread(th) != (DWORD)-1) {
694  CONTEXT context;
695  memset(&context, 0, sizeof(context));
696  context.ContextFlags = CONTEXT_FULL;
697  if (GetThreadContext(th, &context)) {
698  char libpath[MAX_PATH];
699  char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
700  SYMBOL_INFO *info = (SYMBOL_INFO *)buf;
701  DWORD mac;
702  STACKFRAME64 frame;
703  memset(&frame, 0, sizeof(frame));
704 #if defined(_M_AMD64) || defined(__x86_64__)
705  mac = IMAGE_FILE_MACHINE_AMD64;
706  frame.AddrPC.Mode = AddrModeFlat;
707  frame.AddrPC.Offset = context.Rip;
708  frame.AddrFrame.Mode = AddrModeFlat;
709  frame.AddrFrame.Offset = context.Rbp;
710  frame.AddrStack.Mode = AddrModeFlat;
711  frame.AddrStack.Offset = context.Rsp;
712 #elif defined(_M_IA64) || defined(__ia64__)
713  mac = IMAGE_FILE_MACHINE_IA64;
714  frame.AddrPC.Mode = AddrModeFlat;
715  frame.AddrPC.Offset = context.StIIP;
716  frame.AddrBStore.Mode = AddrModeFlat;
717  frame.AddrBStore.Offset = context.RsBSP;
718  frame.AddrStack.Mode = AddrModeFlat;
719  frame.AddrStack.Offset = context.IntSp;
720 #else /* i386 */
721  mac = IMAGE_FILE_MACHINE_I386;
722  frame.AddrPC.Mode = AddrModeFlat;
723  frame.AddrPC.Offset = context.Eip;
724  frame.AddrFrame.Mode = AddrModeFlat;
725  frame.AddrFrame.Offset = context.Ebp;
726  frame.AddrStack.Mode = AddrModeFlat;
727  frame.AddrStack.Offset = context.Esp;
728 #endif
729 
730  while (pStackWalk64(mac, ph, th, &frame, &context, NULL,
731  NULL, NULL, NULL)) {
732  DWORD64 addr = frame.AddrPC.Offset;
733  IMAGEHLP_LINE64 line;
734  DWORD64 displacement;
735  DWORD tmp;
736 
737  if (addr == frame.AddrReturn.Offset || addr == 0 ||
738  frame.AddrReturn.Offset == 0)
739  break;
740 
741  memset(buf, 0, sizeof(buf));
742  info->SizeOfStruct = sizeof(SYMBOL_INFO);
743  info->MaxNameLen = MAX_SYM_NAME;
744  if (pSymFromAddr(ph, addr, &displacement, info)) {
745  if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath)))
746  fprintf(stderr, "%s", libpath);
747  fprintf(stderr, "(%s+0x%I64x)",
748  info->Name, displacement);
749  }
750  fprintf(stderr, " [0x%p]", (void *)(VALUE)addr);
751  memset(&line, 0, sizeof(line));
752  line.SizeOfStruct = sizeof(line);
753  if (pSymGetLineFromAddr64(ph, addr, &tmp, &line))
754  fprintf(stderr, " %s:%lu", line.FileName, line.LineNumber);
755  fprintf(stderr, "\n");
756  }
757  }
758 
759  ResumeThread(th);
760  }
761  CloseHandle(th);
762  }
763  pSymCleanup(ph);
764  }
765  FreeLibrary(dbghelp);
766 }
767 #endif
768 
769 void
771 {
772  rb_vm_t *vm = GET_VM();
773  if (vm) {
774  int i = 0;
775  SDR();
776 
778  fputs("\n", stderr);
779  }
780  }
781 
782 #if HAVE_BACKTRACE || defined(_WIN32)
783  fprintf(stderr, "-- C level backtrace information "
784  "-------------------------------------------\n");
785 
786  {
787 #if defined __MACH__ && defined __APPLE__
788  fprintf(stderr, "\n");
789  fprintf(stderr, " See Crash Report log file under "
790  "~/Library/Logs/CrashReporter or\n");
791  fprintf(stderr, " /Library/Logs/CrashReporter, for "
792  "the more detail of.\n");
793 #elif HAVE_BACKTRACE
794 #define MAX_NATIVE_TRACE 1024
795  static void *trace[MAX_NATIVE_TRACE];
796  int n = backtrace(trace, MAX_NATIVE_TRACE);
797  char **syms = backtrace_symbols(trace, n);
798 
799  if (syms) {
800 #ifdef USE_ELF
801  rb_dump_backtrace_with_lines(n, trace, syms);
802 #else
803  int i;
804  for (i=0; i<n; i++) {
805  fprintf(stderr, "%s\n", syms[i]);
806  }
807 #endif
808  free(syms);
809  }
810 #elif defined(_WIN32)
811  DWORD tid = GetCurrentThreadId();
812  HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &tid);
813  if (th != (HANDLE)-1)
814  WaitForSingleObject(th, INFINITE);
815 #endif
816  }
817 
818  fprintf(stderr, "\n");
819 #endif /* HAVE_BACKTRACE */
820 
821  fprintf(stderr, "-- Other runtime information "
822  "-----------------------------------------------\n\n");
823  {
824  int i;
825 
826  fprintf(stderr, "* Loaded script: %s\n", StringValueCStr(vm->progname));
827  fprintf(stderr, "\n");
828  fprintf(stderr, "* Loaded features:\n\n");
829  for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
830  fprintf(stderr, " %4d %s\n", i, StringValueCStr(RARRAY_PTR(vm->loaded_features)[i]));
831  }
832  fprintf(stderr, "\n");
833 
834 #if __linux__
835  {
836  FILE *fp = fopen("/proc/self/maps", "r");
837  if (fp) {
838  fprintf(stderr, "* Process memory map:\n\n");
839 
840  while (!feof(fp)) {
841  char buff[0x100];
842  size_t rn = fread(buff, 1, 0x100, fp);
843  fwrite(buff, 1, rn, stderr);
844  }
845 
846  fclose(fp);
847  fprintf(stderr, "\n\n");
848  }
849  }
850 #endif /* __linux__ */
851  }
852 }
853