Ruby 1.9.3p327(2012-11-10revision37606)
vm_dump.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   vm_dump.c -
00004 
00005   $Author: naruse $
00006 
00007   Copyright (C) 2004-2007 Koichi Sasada
00008 
00009 **********************************************************************/
00010 
00011 
00012 #include "ruby/ruby.h"
00013 #include "addr2line.h"
00014 #include "vm_core.h"
00015 
00016 #define MAX_POSBUF 128
00017 
00018 #define VM_CFP_CNT(th, cfp) \
00019   ((rb_control_frame_t *)((th)->stack + (th)->stack_size) - (rb_control_frame_t *)(cfp))
00020 
00021 static void
00022 control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
00023 {
00024     ptrdiff_t pc = -1, bp = -1;
00025     ptrdiff_t lfp = cfp->lfp - th->stack;
00026     ptrdiff_t dfp = cfp->dfp - th->stack;
00027     char lfp_in_heap = ' ', dfp_in_heap = ' ';
00028     char posbuf[MAX_POSBUF+1];
00029     int line = 0;
00030     int nopos = 0;
00031 
00032     const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-";
00033     VALUE tmp;
00034 
00035     if (cfp->block_iseq != 0 && BUILTIN_TYPE(cfp->block_iseq) != T_NODE) {
00036         biseq_name = "";        /* RSTRING(cfp->block_iseq->name)->ptr; */
00037     }
00038 
00039     if (lfp < 0 || (size_t)lfp > th->stack_size) {
00040         lfp = (ptrdiff_t)cfp->lfp;
00041         lfp_in_heap = 'p';
00042     }
00043     if (dfp < 0 || (size_t)dfp > th->stack_size) {
00044         dfp = (ptrdiff_t)cfp->dfp;
00045         dfp_in_heap = 'p';
00046     }
00047     if (cfp->bp) {
00048         bp = cfp->bp - th->stack;
00049     }
00050 
00051     switch (VM_FRAME_TYPE(cfp)) {
00052       case VM_FRAME_MAGIC_TOP:
00053         magic = "TOP";
00054         break;
00055       case VM_FRAME_MAGIC_METHOD:
00056         magic = "METHOD";
00057         break;
00058       case VM_FRAME_MAGIC_CLASS:
00059         magic = "CLASS";
00060         break;
00061       case VM_FRAME_MAGIC_BLOCK:
00062         magic = "BLOCK";
00063         break;
00064       case VM_FRAME_MAGIC_FINISH:
00065         magic = "FINISH";
00066         nopos = 1;
00067         break;
00068       case VM_FRAME_MAGIC_CFUNC:
00069         magic = "CFUNC";
00070         break;
00071       case VM_FRAME_MAGIC_PROC:
00072         magic = "PROC";
00073         break;
00074       case VM_FRAME_MAGIC_LAMBDA:
00075         magic = "LAMBDA";
00076         break;
00077       case VM_FRAME_MAGIC_IFUNC:
00078         magic = "IFUNC";
00079         break;
00080       case VM_FRAME_MAGIC_EVAL:
00081         magic = "EVAL";
00082         break;
00083       case 0:
00084         magic = "------";
00085         break;
00086       default:
00087         magic = "(none)";
00088         break;
00089     }
00090 
00091     if (0) {
00092         tmp = rb_inspect(cfp->self);
00093         selfstr = StringValueCStr(tmp);
00094     }
00095     else {
00096         selfstr = "";
00097     }
00098 
00099     if (nopos) {
00100         /* no name */
00101     }
00102     else if (cfp->iseq != 0) {
00103         if (RUBY_VM_IFUNC_P(cfp->iseq)) {
00104             iseq_name = "<ifunc>";
00105         }
00106         else {
00107             pc = cfp->pc - cfp->iseq->iseq_encoded;
00108             iseq_name = RSTRING_PTR(cfp->iseq->name);
00109             line = rb_vm_get_sourceline(cfp);
00110             if (line) {
00111                 snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(cfp->iseq->filename), line);
00112             }
00113         }
00114     }
00115     else if (cfp->me) {
00116         iseq_name = rb_id2name(cfp->me->def->original_id);
00117         snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name);
00118         line = -1;
00119     }
00120 
00121     fprintf(stderr, "c:%04"PRIdPTRDIFF" ",
00122             ((rb_control_frame_t *)(th->stack + th->stack_size) - cfp));
00123     if (pc == -1) {
00124         fprintf(stderr, "p:---- ");
00125     }
00126     else {
00127         fprintf(stderr, "p:%04"PRIdPTRDIFF" ", pc);
00128     }
00129     fprintf(stderr, "s:%04"PRIdPTRDIFF" b:%04"PRIdPTRDIFF" ", (cfp->sp - th->stack), bp);
00130     fprintf(stderr, lfp_in_heap == ' ' ? "l:%06"PRIdPTRDIFF" " : "l:%06"PRIxPTRDIFF" ", lfp % 10000);
00131     fprintf(stderr, dfp_in_heap == ' ' ? "d:%06"PRIdPTRDIFF" " : "d:%06"PRIxPTRDIFF" ", dfp % 10000);
00132     fprintf(stderr, "%-6s", magic);
00133     if (line && !nopos) {
00134         fprintf(stderr, " %s", posbuf);
00135     }
00136     if (0) {
00137         fprintf(stderr, "              \t");
00138         fprintf(stderr, "iseq: %-24s ", iseq_name);
00139         fprintf(stderr, "self: %-24s ", selfstr);
00140         fprintf(stderr, "%-1s ", biseq_name);
00141     }
00142     fprintf(stderr, "\n");
00143 }
00144 
00145 void
00146 rb_vmdebug_stack_dump_raw(rb_thread_t *th, rb_control_frame_t *cfp)
00147 {
00148 #if 0
00149     VALUE *sp = cfp->sp, *bp = cfp->bp;
00150     VALUE *lfp = cfp->lfp;
00151     VALUE *dfp = cfp->dfp;
00152     VALUE *p, *st, *t;
00153 
00154     fprintf(stderr, "-- stack frame ------------\n");
00155     for (p = st = th->stack; p < sp; p++) {
00156         fprintf(stderr, "%04ld (%p): %08"PRIxVALUE, (long)(p - st), p, *p);
00157 
00158         t = (VALUE *)*p;
00159         if (th->stack <= t && t < sp) {
00160             fprintf(stderr, " (= %ld)", (long)((VALUE *)GC_GUARDED_PTR_REF(t) - th->stack));
00161         }
00162 
00163         if (p == lfp)
00164             fprintf(stderr, " <- lfp");
00165         if (p == dfp)
00166             fprintf(stderr, " <- dfp");
00167         if (p == bp)
00168             fprintf(stderr, " <- bp");  /* should not be */
00169 
00170         fprintf(stderr, "\n");
00171     }
00172 #endif
00173 
00174     fprintf(stderr, "-- Control frame information "
00175             "-----------------------------------------------\n");
00176     while ((void *)cfp < (void *)(th->stack + th->stack_size)) {
00177         control_frame_dump(th, cfp);
00178         cfp++;
00179     }
00180     fprintf(stderr, "\n");
00181 }
00182 
00183 void
00184 rb_vmdebug_stack_dump_raw_current(void)
00185 {
00186     rb_thread_t *th = GET_THREAD();
00187     rb_vmdebug_stack_dump_raw(th, th->cfp);
00188 }
00189 
00190 void
00191 rb_vmdebug_env_dump_raw(rb_env_t *env, VALUE *lfp, VALUE *dfp)
00192 {
00193     int i;
00194     fprintf(stderr, "-- env --------------------\n");
00195 
00196     while (env) {
00197         fprintf(stderr, "--\n");
00198         for (i = 0; i < env->env_size; i++) {
00199             fprintf(stderr, "%04d: %08"PRIxVALUE" (%p)", -env->local_size + i, env->env[i],
00200                    (void *)&env->env[i]);
00201             if (&env->env[i] == lfp)
00202                 fprintf(stderr, " <- lfp");
00203             if (&env->env[i] == dfp)
00204                 fprintf(stderr, " <- dfp");
00205             fprintf(stderr, "\n");
00206         }
00207 
00208         if (env->prev_envval != 0) {
00209             GetEnvPtr(env->prev_envval, env);
00210         }
00211         else {
00212             env = 0;
00213         }
00214     }
00215     fprintf(stderr, "---------------------------\n");
00216 }
00217 
00218 void
00219 rb_vmdebug_proc_dump_raw(rb_proc_t *proc)
00220 {
00221     rb_env_t *env;
00222     char *selfstr;
00223     VALUE val = rb_inspect(proc->block.self);
00224     selfstr = StringValueCStr(val);
00225 
00226     fprintf(stderr, "-- proc -------------------\n");
00227     fprintf(stderr, "self: %s\n", selfstr);
00228     GetEnvPtr(proc->envval, env);
00229     rb_vmdebug_env_dump_raw(env, proc->block.lfp, proc->block.dfp);
00230 }
00231 
00232 void
00233 rb_vmdebug_stack_dump_th(VALUE thval)
00234 {
00235     rb_thread_t *th;
00236     GetThreadPtr(thval, th);
00237     rb_vmdebug_stack_dump_raw(th, th->cfp);
00238 }
00239 
00240 #if VMDEBUG > 2
00241 static void
00242 vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
00243 {
00244     int i;
00245 
00246     VALUE rstr;
00247     VALUE *sp = cfp->sp;
00248     VALUE *lfp = cfp->lfp;
00249     VALUE *dfp = cfp->dfp;
00250 
00251     int argc = 0, local_size = 0;
00252     const char *name;
00253     rb_iseq_t *iseq = cfp->iseq;
00254 
00255     if (iseq == 0) {
00256         if (RUBYVM_CFUNC_FRAME_P(cfp)) {
00257             name = rb_id2name(cfp->me->original_id);
00258         }
00259         else {
00260             name = "?";
00261         }
00262     }
00263     else if (RUBY_VM_IFUNC_P(iseq)) {
00264         name = "<ifunc>";
00265     }
00266     else {
00267         argc = iseq->argc;
00268         local_size = iseq->local_size;
00269         name = RSTRING_PTR(iseq->name);
00270     }
00271 
00272     /* stack trace header */
00273 
00274     if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_METHOD ||
00275         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP ||
00276         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK ||
00277         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CLASS ||
00278         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_PROC ||
00279         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA ||
00280         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC ||
00281         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC ||
00282         VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_EVAL) {
00283 
00284         VALUE *ptr = dfp - local_size;
00285 
00286         vm_stack_dump_each(th, cfp + 1);
00287         control_frame_dump(th, cfp);
00288 
00289         if (lfp != dfp) {
00290             local_size++;
00291         }
00292         for (i = 0; i < argc; i++) {
00293             rstr = rb_inspect(*ptr);
00294             fprintf(stderr, "  arg   %2d: %8s (%p)\n", i, StringValueCStr(rstr),
00295                    (void *)ptr++);
00296         }
00297         for (; i < local_size - 1; i++) {
00298             rstr = rb_inspect(*ptr);
00299             fprintf(stderr, "  local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
00300                    (void *)ptr++);
00301         }
00302 
00303         ptr = cfp->bp;
00304         for (; ptr < sp; ptr++, i++) {
00305             if (*ptr == Qundef) {
00306                 rstr = rb_str_new2("undef");
00307             }
00308             else {
00309                 rstr = rb_inspect(*ptr);
00310             }
00311             fprintf(stderr, "  stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
00312                     (ptr - th->stack));
00313         }
00314     }
00315     else if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
00316         if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 2)) {
00317             vm_stack_dump_each(th, cfp + 1);
00318         }
00319         else {
00320             /* SDR(); */
00321         }
00322     }
00323     else {
00324         rb_bug("unsupport frame type: %08lx", VM_FRAME_TYPE(cfp));
00325     }
00326 }
00327 #endif
00328 
00329 void
00330 rb_vmdebug_debug_print_register(rb_thread_t *th)
00331 {
00332     rb_control_frame_t *cfp = th->cfp;
00333     ptrdiff_t pc = -1;
00334     ptrdiff_t lfp = cfp->lfp - th->stack;
00335     ptrdiff_t dfp = cfp->dfp - th->stack;
00336     ptrdiff_t cfpi;
00337 
00338     if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) {
00339         pc = cfp->pc - cfp->iseq->iseq_encoded;
00340     }
00341 
00342     if (lfp < 0 || (size_t)lfp > th->stack_size)
00343         lfp = -1;
00344     if (dfp < 0 || (size_t)dfp > th->stack_size)
00345         dfp = -1;
00346 
00347     cfpi = ((rb_control_frame_t *)(th->stack + th->stack_size)) - cfp;
00348     fprintf(stderr, "  [PC] %04"PRIdPTRDIFF", [SP] %04"PRIdPTRDIFF", [LFP] %04"PRIdPTRDIFF", [DFP] %04"PRIdPTRDIFF", [CFP] %04"PRIdPTRDIFF"\n",
00349             pc, (cfp->sp - th->stack), lfp, dfp, cfpi);
00350 }
00351 
00352 void
00353 rb_vmdebug_thread_dump_regs(VALUE thval)
00354 {
00355     rb_thread_t *th;
00356     GetThreadPtr(thval, th);
00357     rb_vmdebug_debug_print_register(th);
00358 }
00359 
00360 void
00361 rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp)
00362 {
00363     rb_iseq_t *iseq = cfp->iseq;
00364 
00365     if (iseq != 0 && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_FINISH) {
00366         VALUE *seq = iseq->iseq;
00367         ptrdiff_t pc = cfp->pc - iseq->iseq_encoded;
00368 
00369         printf("%3"PRIdPTRDIFF" ", VM_CFP_CNT(th, cfp));
00370         if (pc >= 0) {
00371             rb_iseq_disasm_insn(0, seq, (size_t)pc, iseq, 0);
00372         }
00373     }
00374 
00375 #if VMDEBUG > 3
00376     fprintf(stderr, "        (1)");
00377     rb_vmdebug_debug_print_register(th);
00378 #endif
00379 }
00380 
00381 void
00382 rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
00383 #if OPT_STACK_CACHING
00384                  , VALUE reg_a, VALUE reg_b
00385 #endif
00386     )
00387 {
00388 #if VMDEBUG > 9
00389     SDR2(cfp);
00390 #endif
00391 
00392 #if VMDEBUG > 3
00393     fprintf(stderr, "        (2)");
00394     rb_vmdebug_debug_print_register(th);
00395 #endif
00396     /* stack_dump_raw(th, cfp); */
00397 
00398 #if VMDEBUG > 2
00399     /* stack_dump_thobj(th); */
00400     vm_stack_dump_each(th, th->cfp);
00401 #if OPT_STACK_CACHING
00402     {
00403         VALUE rstr;
00404         rstr = rb_inspect(reg_a);
00405         fprintf(stderr, "  sc reg A: %s\n", StringValueCStr(rstr));
00406         rstr = rb_inspect(reg_b);
00407         fprintf(stderr, "  sc reg B: %s\n", StringValueCStr(rstr));
00408     }
00409 #endif
00410     printf
00411         ("--------------------------------------------------------------\n");
00412 #endif
00413 }
00414 
00415 #ifdef COLLECT_USAGE_ANALYSIS
00416 /* uh = {
00417  *   insn(Fixnum) => ihash(Hash)
00418  * }
00419  * ihash = {
00420  *   -1(Fixnum) => count,      # insn usage
00421  *    0(Fixnum) => ophash,     # operand usage
00422  * }
00423  * ophash = {
00424  *   val(interned string) => count(Fixnum)
00425  * }
00426  */
00427 void
00428 vm_analysis_insn(int insn)
00429 {
00430     ID usage_hash;
00431     ID bigram_hash;
00432     static int prev_insn = -1;
00433 
00434     VALUE uh;
00435     VALUE ihash;
00436     VALUE cv;
00437 
00438     CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
00439     CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM");
00440     uh = rb_const_get(rb_cRubyVM, usage_hash);
00441     if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
00442         ihash = rb_hash_new();
00443         rb_hash_aset(uh, INT2FIX(insn), ihash);
00444     }
00445     if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) {
00446         cv = INT2FIX(0);
00447     }
00448     rb_hash_aset(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1));
00449 
00450     /* calc bigram */
00451     if (prev_insn != -1) {
00452         VALUE bi;
00453         VALUE ary[2];
00454         VALUE cv;
00455 
00456         ary[0] = INT2FIX(prev_insn);
00457         ary[1] = INT2FIX(insn);
00458         bi = rb_ary_new4(2, &ary[0]);
00459 
00460         uh = rb_const_get(rb_cRubyVM, bigram_hash);
00461         if ((cv = rb_hash_aref(uh, bi)) == Qnil) {
00462             cv = INT2FIX(0);
00463         }
00464         rb_hash_aset(uh, bi, INT2FIX(FIX2INT(cv) + 1));
00465     }
00466     prev_insn = insn;
00467 }
00468 
00469 /* from disasm.c */
00470 extern VALUE insn_operand_intern(int insn, int op_no, VALUE op,
00471                                  int len, int pos, VALUE child);
00472 
00473 void
00474 vm_analysis_operand(int insn, int n, VALUE op)
00475 {
00476     ID usage_hash;
00477 
00478     VALUE uh;
00479     VALUE ihash;
00480     VALUE ophash;
00481     VALUE valstr;
00482     VALUE cv;
00483 
00484     CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
00485 
00486     uh = rb_const_get(rb_cRubyVM, usage_hash);
00487     if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
00488         ihash = rb_hash_new();
00489         rb_hash_aset(uh, INT2FIX(insn), ihash);
00490     }
00491     if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) {
00492         ophash = rb_hash_new();
00493         rb_hash_aset(ihash, INT2FIX(n), ophash);
00494     }
00495     /* intern */
00496     valstr = insn_operand_intern(insn, n, op, 0, 0, 0);
00497 
00498     /* set count */
00499     if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) {
00500         cv = INT2FIX(0);
00501     }
00502     rb_hash_aset(ophash, valstr, INT2FIX(FIX2INT(cv) + 1));
00503 }
00504 
00505 void
00506 vm_analysis_register(int reg, int isset)
00507 {
00508     ID usage_hash;
00509     VALUE uh;
00510     VALUE rhash;
00511     VALUE valstr;
00512     static const char regstrs[][5] = {
00513         "pc",                   /* 0 */
00514         "sp",                   /* 1 */
00515         "cfp",                  /* 2 */
00516         "lfp",                  /* 3 */
00517         "dfp",                  /* 4 */
00518         "self",                 /* 5 */
00519         "iseq",                 /* 6 */
00520     };
00521     static const char getsetstr[][4] = {
00522         "get",
00523         "set",
00524     };
00525     static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2];
00526 
00527     VALUE cv;
00528 
00529     CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS");
00530     if (syms[0] == 0) {
00531         char buff[0x10];
00532         int i;
00533 
00534         for (i = 0; i < sizeof(regstrs) / sizeof(regstrs[0]); i++) {
00535             int j;
00536             for (j = 0; j < 2; j++) {
00537                 snfprintf(stderr, buff, 0x10, "%d %s %-4s", i, getsetstr[j],
00538                          regstrs[i]);
00539                 syms[i][j] = ID2SYM(rb_intern(buff));
00540             }
00541         }
00542     }
00543     valstr = syms[reg][isset];
00544 
00545     uh = rb_const_get(rb_cRubyVM, usage_hash);
00546     if ((cv = rb_hash_aref(uh, valstr)) == Qnil) {
00547         cv = INT2FIX(0);
00548     }
00549     rb_hash_aset(uh, valstr, INT2FIX(FIX2INT(cv) + 1));
00550 }
00551 
00552 
00553 #endif
00554 
00555 VALUE
00556 rb_vmdebug_thread_dump_state(VALUE self)
00557 {
00558     rb_thread_t *th;
00559     rb_control_frame_t *cfp;
00560     GetThreadPtr(self, th);
00561     cfp = th->cfp;
00562 
00563     fprintf(stderr, "Thread state dump:\n");
00564     fprintf(stderr, "pc : %p, sp : %p\n", (void *)cfp->pc, (void *)cfp->sp);
00565     fprintf(stderr, "cfp: %p, lfp: %p, dfp: %p\n", (void *)cfp, (void *)cfp->lfp, (void *)cfp->dfp);
00566 
00567     return Qnil;
00568 }
00569 
00570 static int
00571 bugreport_backtrace(void *arg, VALUE file, int line, VALUE method)
00572 {
00573     const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
00574     if (!*(int *)arg) {
00575         fprintf(stderr, "-- Ruby level backtrace information "
00576                 "----------------------------------------\n");
00577         *(int *)arg = 1;
00578     }
00579     if (NIL_P(method)) {
00580         fprintf(stderr, "%s:%d:in unknown method\n", filename, line);
00581     }
00582     else {
00583         fprintf(stderr, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method));
00584     }
00585     return 0;
00586 }
00587 
00588 #if defined(__FreeBSD__) && defined(__OPTIMIZE__)
00589 #undef HAVE_BACKTRACE
00590 #endif
00591 #if HAVE_BACKTRACE
00592 # include <execinfo.h>
00593 #elif defined(_WIN32)
00594 # include <imagehlp.h>
00595 # ifndef SYMOPT_DEBUG
00596 #  define SYMOPT_DEBUG 0x80000000
00597 # endif
00598 # ifndef MAX_SYM_NAME
00599 # define MAX_SYM_NAME 2000
00600 typedef struct {
00601     DWORD64 Offset;
00602     WORD Segment;
00603     ADDRESS_MODE Mode;
00604 } ADDRESS64;
00605 typedef struct {
00606     DWORD64 Thread;
00607     DWORD ThCallbackStack;
00608     DWORD ThCallbackBStore;
00609     DWORD NextCallback;
00610     DWORD FramePointer;
00611     DWORD64 KiCallUserMode;
00612     DWORD64 KeUserCallbackDispatcher;
00613     DWORD64 SystemRangeStart;
00614     DWORD64 KiUserExceptionDispatcher;
00615     DWORD64 StackBase;
00616     DWORD64 StackLimit;
00617     DWORD64 Reserved[5];
00618 } KDHELP64;
00619 typedef struct {
00620     ADDRESS64 AddrPC;
00621     ADDRESS64 AddrReturn;
00622     ADDRESS64 AddrFrame;
00623     ADDRESS64 AddrStack;
00624     ADDRESS64 AddrBStore;
00625     void *FuncTableEntry;
00626     DWORD64 Params[4];
00627     BOOL Far;
00628     BOOL Virtual;
00629     DWORD64 Reserved[3];
00630     KDHELP64 KdHelp;
00631 } STACKFRAME64;
00632 typedef struct {
00633     ULONG SizeOfStruct;
00634     ULONG TypeIndex;
00635     ULONG64 Reserved[2];
00636     ULONG Index;
00637     ULONG Size;
00638     ULONG64 ModBase;
00639     ULONG Flags;
00640     ULONG64 Value;
00641     ULONG64 Address;
00642     ULONG Register;
00643     ULONG Scope;
00644     ULONG Tag;
00645     ULONG NameLen;
00646     ULONG MaxNameLen;
00647     char Name[1];
00648 } SYMBOL_INFO;
00649 typedef struct {
00650     DWORD SizeOfStruct;
00651     void *Key;
00652     DWORD LineNumber;
00653     char *FileName;
00654     DWORD64 Address;
00655 } IMAGEHLP_LINE64;
00656 typedef void *PREAD_PROCESS_MEMORY_ROUTINE64;
00657 typedef void *PFUNCTION_TABLE_ACCESS_ROUTINE64;
00658 typedef void *PGET_MODULE_BASE_ROUTINE64;
00659 typedef void *PTRANSLATE_ADDRESS_ROUTINE64;
00660 # endif
00661 
00662 static void
00663 dump_thread(void *arg)
00664 {
00665     HANDLE dbghelp;
00666     BOOL (WINAPI *pSymInitialize)(HANDLE, const char *, BOOL);
00667     BOOL (WINAPI *pSymCleanup)(HANDLE);
00668     BOOL (WINAPI *pStackWalk64)(DWORD, HANDLE, HANDLE, STACKFRAME64 *, void *, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
00669     DWORD64 (WINAPI *pSymGetModuleBase64)(HANDLE, DWORD64);
00670     BOOL (WINAPI *pSymFromAddr)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *);
00671     BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *);
00672     HANDLE (WINAPI *pOpenThread)(DWORD, BOOL, DWORD);
00673     DWORD tid = *(DWORD *)arg;
00674     HANDLE ph;
00675     HANDLE th;
00676 
00677     dbghelp = LoadLibrary("dbghelp.dll");
00678     if (!dbghelp) return;
00679     pSymInitialize = (BOOL (WINAPI *)(HANDLE, const char *, BOOL))GetProcAddress(dbghelp, "SymInitialize");
00680     pSymCleanup = (BOOL (WINAPI *)(HANDLE))GetProcAddress(dbghelp, "SymCleanup");
00681     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");
00682     pSymGetModuleBase64 = (DWORD64 (WINAPI *)(HANDLE, DWORD64))GetProcAddress(dbghelp, "SymGetModuleBase64");
00683     pSymFromAddr = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD64 *, SYMBOL_INFO *))GetProcAddress(dbghelp, "SymFromAddr");
00684     pSymGetLineFromAddr64 = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD *, IMAGEHLP_LINE64 *))GetProcAddress(dbghelp, "SymGetLineFromAddr64");
00685     pOpenThread = (HANDLE (WINAPI *)(DWORD, BOOL, DWORD))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenThread");
00686     if (pSymInitialize && pSymCleanup && pStackWalk64 && pSymGetModuleBase64 &&
00687         pSymFromAddr && pSymGetLineFromAddr64 && pOpenThread) {
00688         SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES);
00689         ph = GetCurrentProcess();
00690         pSymInitialize(ph, NULL, TRUE);
00691         th = pOpenThread(THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, FALSE, tid);
00692         if (th) {
00693             if (SuspendThread(th) != (DWORD)-1) {
00694                 CONTEXT context;
00695                 memset(&context, 0, sizeof(context));
00696                 context.ContextFlags = CONTEXT_FULL;
00697                 if (GetThreadContext(th, &context)) {
00698                     char libpath[MAX_PATH];
00699                     char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
00700                     SYMBOL_INFO *info = (SYMBOL_INFO *)buf;
00701                     DWORD mac;
00702                     STACKFRAME64 frame;
00703                     memset(&frame, 0, sizeof(frame));
00704 #if defined(_M_AMD64) || defined(__x86_64__)
00705                     mac = IMAGE_FILE_MACHINE_AMD64;
00706                     frame.AddrPC.Mode = AddrModeFlat;
00707                     frame.AddrPC.Offset = context.Rip;
00708                     frame.AddrFrame.Mode = AddrModeFlat;
00709                     frame.AddrFrame.Offset = context.Rbp;
00710                     frame.AddrStack.Mode = AddrModeFlat;
00711                     frame.AddrStack.Offset = context.Rsp;
00712 #elif defined(_M_IA64) || defined(__ia64__)
00713                     mac = IMAGE_FILE_MACHINE_IA64;
00714                     frame.AddrPC.Mode = AddrModeFlat;
00715                     frame.AddrPC.Offset = context.StIIP;
00716                     frame.AddrBStore.Mode = AddrModeFlat;
00717                     frame.AddrBStore.Offset = context.RsBSP;
00718                     frame.AddrStack.Mode = AddrModeFlat;
00719                     frame.AddrStack.Offset = context.IntSp;
00720 #else   /* i386 */
00721                     mac = IMAGE_FILE_MACHINE_I386;
00722                     frame.AddrPC.Mode = AddrModeFlat;
00723                     frame.AddrPC.Offset = context.Eip;
00724                     frame.AddrFrame.Mode = AddrModeFlat;
00725                     frame.AddrFrame.Offset = context.Ebp;
00726                     frame.AddrStack.Mode = AddrModeFlat;
00727                     frame.AddrStack.Offset = context.Esp;
00728 #endif
00729 
00730                     while (pStackWalk64(mac, ph, th, &frame, &context, NULL,
00731                                         NULL, NULL, NULL)) {
00732                         DWORD64 addr = frame.AddrPC.Offset;
00733                         IMAGEHLP_LINE64 line;
00734                         DWORD64 displacement;
00735                         DWORD tmp;
00736 
00737                         if (addr == frame.AddrReturn.Offset || addr == 0 ||
00738                             frame.AddrReturn.Offset == 0)
00739                             break;
00740 
00741                         memset(buf, 0, sizeof(buf));
00742                         info->SizeOfStruct = sizeof(SYMBOL_INFO);
00743                         info->MaxNameLen = MAX_SYM_NAME;
00744                         if (pSymFromAddr(ph, addr, &displacement, info)) {
00745                             if (GetModuleFileName((HANDLE)(uintptr_t)pSymGetModuleBase64(ph, addr), libpath, sizeof(libpath)))
00746                                 fprintf(stderr, "%s", libpath);
00747                             fprintf(stderr, "(%s+0x%I64x)",
00748                                     info->Name, displacement);
00749                         }
00750                         fprintf(stderr, " [0x%p]", (void *)(VALUE)addr);
00751                         memset(&line, 0, sizeof(line));
00752                         line.SizeOfStruct = sizeof(line);
00753                         if (pSymGetLineFromAddr64(ph, addr, &tmp, &line))
00754                             fprintf(stderr, " %s:%lu", line.FileName, line.LineNumber);
00755                         fprintf(stderr, "\n");
00756                     }
00757                 }
00758 
00759                 ResumeThread(th);
00760             }
00761             CloseHandle(th);
00762         }
00763         pSymCleanup(ph);
00764     }
00765     FreeLibrary(dbghelp);
00766 }
00767 #endif
00768 
00769 void
00770 rb_vm_bugreport(void)
00771 {
00772     rb_vm_t *vm = GET_VM();
00773     if (vm) {
00774         int i = 0;
00775         SDR();
00776 
00777         if (rb_backtrace_each(bugreport_backtrace, &i)) {
00778             fputs("\n", stderr);
00779         }
00780     }
00781 
00782 #if HAVE_BACKTRACE || defined(_WIN32)
00783     fprintf(stderr, "-- C level backtrace information "
00784             "-------------------------------------------\n");
00785 
00786     {
00787 #if defined __MACH__ && defined __APPLE__
00788         fprintf(stderr, "\n");
00789         fprintf(stderr, "   See Crash Report log file under "
00790                 "~/Library/Logs/CrashReporter or\n");
00791         fprintf(stderr, "   /Library/Logs/CrashReporter, for "
00792                 "the more detail of.\n");
00793 #elif HAVE_BACKTRACE
00794 #define MAX_NATIVE_TRACE 1024
00795         static void *trace[MAX_NATIVE_TRACE];
00796         int n = backtrace(trace, MAX_NATIVE_TRACE);
00797         char **syms = backtrace_symbols(trace, n);
00798 
00799         if (syms) {
00800 #ifdef USE_ELF
00801             rb_dump_backtrace_with_lines(n, trace, syms);
00802 #else
00803             int i;
00804             for (i=0; i<n; i++) {
00805                 fprintf(stderr, "%s\n", syms[i]);
00806             }
00807 #endif
00808             free(syms);
00809         }
00810 #elif defined(_WIN32)
00811         DWORD tid = GetCurrentThreadId();
00812         HANDLE th = (HANDLE)_beginthread(dump_thread, 0, &tid);
00813         if (th != (HANDLE)-1)
00814             WaitForSingleObject(th, INFINITE);
00815 #endif
00816     }
00817 
00818     fprintf(stderr, "\n");
00819 #endif /* HAVE_BACKTRACE */
00820 
00821     fprintf(stderr, "-- Other runtime information "
00822             "-----------------------------------------------\n\n");
00823     {
00824         int i;
00825 
00826         fprintf(stderr, "* Loaded script: %s\n", StringValueCStr(vm->progname));
00827         fprintf(stderr, "\n");
00828         fprintf(stderr, "* Loaded features:\n\n");
00829         for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
00830             fprintf(stderr, " %4d %s\n", i, StringValueCStr(RARRAY_PTR(vm->loaded_features)[i]));
00831         }
00832         fprintf(stderr, "\n");
00833 
00834 #if __linux__
00835         {
00836             FILE *fp = fopen("/proc/self/maps", "r");
00837             if (fp) {
00838                 fprintf(stderr, "* Process memory map:\n\n");
00839 
00840                 while (!feof(fp)) {
00841                     char buff[0x100];
00842                     size_t rn = fread(buff, 1, 0x100, fp);
00843                     fwrite(buff, 1, rn, stderr);
00844                 }
00845 
00846                 fclose(fp);
00847                 fprintf(stderr, "\n\n");
00848             }
00849         }
00850 #endif /* __linux__ */
00851     }
00852 }
00853