Skip to content

Commit c78895b

Browse files
Satoshi Tagomoritagomoris
authored andcommitted
Add "Namespace detection information" section in bug reports
* To show environments stack when the current namespace is unexpected or namespace detection is broken * It is displayed only when RUBY_BUGREPORT_NAMESPACE_ENV=1 is specified
1 parent c938c11 commit c78895b

File tree

2 files changed

+247
-0
lines changed

2 files changed

+247
-0
lines changed

vm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ rb_vm_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame
108108
static const VALUE *
109109
VM_EP_RUBY_LEP(const rb_execution_context_t *ec, const rb_control_frame_t *current_cfp)
110110
{
111+
// rb_vmdebug_namespace_env_dump_raw() simulates this function
111112
const VALUE *ep = current_cfp->ep;
112113
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
113114
const rb_control_frame_t *cfp = NULL, *checkpoint_cfp = current_cfp;

vm_dump.c

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
162162
}
163163
kprintf("s:%04"PRIdPTRDIFF" ", cfp->sp - ec->vm_stack);
164164
kprintf(ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
165+
kprintf("l:%s ", VM_ENV_LOCAL_P(cfp->ep) ? "y" : "n");
165166
if (ns) {
166167
kprintf("n:%04ld ", ns->ns_id % 10000);
167168
}
@@ -220,6 +221,247 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
220221
return false;
221222
}
222223

224+
static inline const rb_control_frame_t *
225+
vmdebug_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE * const ep)
226+
{
227+
if (!ep) {
228+
return NULL;
229+
}
230+
else {
231+
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
232+
233+
while (cfp < eocfp) {
234+
if (cfp->ep == ep) {
235+
return cfp;
236+
}
237+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
238+
}
239+
240+
return NULL;
241+
}
242+
}
243+
244+
static rb_callable_method_entry_t *
245+
vmdebug_env_method_entry_unchecked(VALUE obj, int can_be_svar)
246+
{
247+
if (obj == Qfalse) return NULL;
248+
249+
switch (imemo_type(obj)) {
250+
case imemo_ment:
251+
return (rb_callable_method_entry_t *)obj;
252+
case imemo_cref:
253+
return NULL;
254+
case imemo_svar:
255+
if (can_be_svar) {
256+
return vmdebug_env_method_entry_unchecked(((struct vm_svar *)obj)->cref_or_me, FALSE);
257+
}
258+
default:
259+
return NULL;
260+
}
261+
}
262+
263+
static const rb_callable_method_entry_t *
264+
vmdebug_frame_method_entry_unchecked(const VALUE *ep)
265+
{
266+
rb_callable_method_entry_t *me;
267+
268+
while (!VM_ENV_LOCAL_P_UNCHECKED(ep)) {
269+
if ((me = vmdebug_env_method_entry_unchecked(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
270+
ep = VM_ENV_PREV_EP_UNCHECKED(ep);
271+
}
272+
273+
return vmdebug_env_method_entry_unchecked(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
274+
}
275+
276+
static bool
277+
namespace_env_dump(const rb_execution_context_t *ec, const VALUE *env, const rb_control_frame_t *checkpoint_cfp, FILE *errout)
278+
{
279+
ptrdiff_t pc = -1;
280+
ptrdiff_t ep = env - ec->vm_stack;
281+
char ep_in_heap = ' ';
282+
char posbuf[MAX_POSBUF+1];
283+
int line = 0;
284+
const char *magic, *iseq_name = "-";
285+
VALUE tmp;
286+
const rb_iseq_t *iseq = NULL;
287+
const rb_namespace_t *ns = NULL;
288+
const rb_control_frame_t *cfp = vmdebug_search_cf_from_ep(ec, checkpoint_cfp, env);
289+
const rb_callable_method_entry_t *me = vmdebug_frame_method_entry_unchecked(env);
290+
291+
if (ep < 0 || (size_t)ep > ec->vm_stack_size) {
292+
if (cfp) {
293+
ep = (ptrdiff_t)cfp->ep;
294+
ep_in_heap = 'p';
295+
}
296+
}
297+
298+
switch (VM_ENV_FLAGS_UNCHECKED(env, VM_FRAME_MAGIC_MASK)) {
299+
case VM_FRAME_MAGIC_TOP:
300+
magic = "TOP";
301+
ns = VM_ENV_NAMESPACE_UNCHECKED(env);
302+
break;
303+
case VM_FRAME_MAGIC_METHOD:
304+
magic = "METHOD";
305+
if (me) {
306+
ns = me->def->ns;
307+
}
308+
break;
309+
case VM_FRAME_MAGIC_CLASS:
310+
magic = "CLASS";
311+
ns = VM_ENV_NAMESPACE_UNCHECKED(env);
312+
break;
313+
case VM_FRAME_MAGIC_BLOCK:
314+
magic = "BLOCK";
315+
break;
316+
case VM_FRAME_MAGIC_CFUNC:
317+
magic = "CFUNC";
318+
if (me) {
319+
ns = me->def->ns;
320+
}
321+
break;
322+
case VM_FRAME_MAGIC_IFUNC:
323+
magic = "IFUNC";
324+
break;
325+
case VM_FRAME_MAGIC_EVAL:
326+
magic = "EVAL";
327+
break;
328+
case VM_FRAME_MAGIC_RESCUE:
329+
magic = "RESCUE";
330+
break;
331+
case VM_FRAME_MAGIC_DUMMY:
332+
magic = "DUMMY";
333+
break;
334+
case 0:
335+
magic = "------";
336+
break;
337+
default:
338+
magic = "(none)";
339+
break;
340+
}
341+
342+
if (cfp && cfp->iseq != 0) {
343+
#define RUBY_VM_IFUNC_P(ptr) IMEMO_TYPE_P(ptr, imemo_ifunc)
344+
if (RUBY_VM_IFUNC_P(cfp->iseq)) {
345+
iseq_name = "<ifunc>";
346+
}
347+
else if (SYMBOL_P((VALUE)cfp->iseq)) {
348+
tmp = rb_sym2str((VALUE)cfp->iseq);
349+
iseq_name = RSTRING_PTR(tmp);
350+
snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name);
351+
line = -1;
352+
}
353+
else {
354+
if (cfp->pc) {
355+
iseq = cfp->iseq;
356+
pc = cfp->pc - ISEQ_BODY(iseq)->iseq_encoded;
357+
iseq_name = RSTRING_PTR(ISEQ_BODY(iseq)->location.label);
358+
if (pc >= 0 && pc <= ISEQ_BODY(iseq)->iseq_size) {
359+
line = rb_vm_get_sourceline(cfp);
360+
}
361+
if (line) {
362+
snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(rb_iseq_path(iseq)), line);
363+
}
364+
}
365+
else {
366+
iseq_name = "<dummy_frame>";
367+
}
368+
}
369+
}
370+
else if (me != NULL && IMEMO_TYPE_P(me, imemo_ment)) {
371+
iseq_name = rb_id2name(me->def->original_id);
372+
snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name);
373+
line = -1;
374+
}
375+
376+
if (cfp) {
377+
kprintf("c:%04"PRIdPTRDIFF" ",
378+
((rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp));
379+
}
380+
else {
381+
kprintf("c:---- ");
382+
}
383+
kprintf(ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "E:%06"PRIxPTRDIFF" ", ep % 10000);
384+
kprintf("l:%s ", VM_ENV_LOCAL_P(env) ? "y" : "n");
385+
if (ns) {
386+
kprintf("n:%04ld ", ns->ns_id % 10000);
387+
}
388+
else {
389+
kprintf("n:---- ");
390+
}
391+
kprintf("%-6s", magic);
392+
if (line) {
393+
kprintf(" %s", posbuf);
394+
}
395+
if (VM_ENV_FLAGS_UNCHECKED(env, VM_FRAME_FLAG_FINISH) != 0) {
396+
kprintf(" [FINISH]");
397+
}
398+
kprintf("\n");
399+
return true;
400+
error:
401+
return false;
402+
}
403+
404+
static bool
405+
namespace_env_dump_unchecked(const rb_execution_context_t *ec, const VALUE *env, const rb_control_frame_t *checkpoint_cfp, FILE *errout)
406+
{
407+
if (env == NULL) {
408+
kprintf("c:---- e:000000 l:- n:---- (none)\n");
409+
return true;
410+
}
411+
else {
412+
return namespace_env_dump(ec, env, checkpoint_cfp, errout);
413+
}
414+
error:
415+
return false;
416+
}
417+
418+
bool
419+
rb_vmdebug_namespace_env_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *current_cfp, FILE *errout)
420+
{
421+
// See VM_EP_RUBY_LEP for the original logic
422+
const VALUE *ep = current_cfp->ep;
423+
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
424+
const rb_control_frame_t *cfp = NULL, *checkpoint_cfp = current_cfp;
425+
426+
kprintf("-- Namespace detection information "
427+
"-----------------------------------------\n");
428+
429+
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
430+
431+
while (!VM_ENV_LOCAL_P(ep) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
432+
while (!VM_ENV_LOCAL_P(ep)) {
433+
ep = VM_ENV_PREV_EP(ep);
434+
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
435+
}
436+
while (VM_ENV_FLAGS(ep, VM_FRAME_FLAG_CFRAME) != 0) {
437+
if (!cfp) {
438+
cfp = vmdebug_search_cf_from_ep(ec, checkpoint_cfp, ep);
439+
}
440+
if (!cfp) {
441+
goto stop;
442+
}
443+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
444+
if (cfp >= eocfp) {
445+
kprintf("[PREVIOUS CONTROL FRAME IS OUT OF BOUND]\n");
446+
goto stop;
447+
}
448+
ep = cfp->ep;
449+
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
450+
if (!ep) {
451+
goto stop;
452+
}
453+
}
454+
checkpoint_cfp = cfp;
455+
cfp = NULL;
456+
}
457+
stop:
458+
kprintf("\n");
459+
return true;
460+
461+
error:
462+
return false;
463+
}
464+
223465
bool
224466
rb_vmdebug_stack_dump_raw(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, FILE *errout)
225467
{
@@ -1132,6 +1374,7 @@ rb_dump_machine_register(FILE *errout, const ucontext_t *ctx)
11321374
bool
11331375
rb_vm_bugreport(const void *ctx, FILE *errout)
11341376
{
1377+
const char *ns_env = getenv("RUBY_BUGREPORT_NAMESPACE_ENV");
11351378
const char *cmd = getenv("RUBY_ON_BUG");
11361379
if (cmd) {
11371380
char buf[0x100];
@@ -1175,6 +1418,9 @@ rb_vm_bugreport(const void *ctx, FILE *errout)
11751418

11761419
if (vm && ec) {
11771420
rb_vmdebug_stack_dump_raw(ec, ec->cfp, errout);
1421+
if (ns_env) {
1422+
rb_vmdebug_namespace_env_dump_raw(ec, ec->cfp, errout);
1423+
}
11781424
rb_backtrace_print_as_bugreport(errout);
11791425
kputs("\n");
11801426
// If we get here, hopefully things are intact enough that

0 commit comments

Comments
 (0)