Exemplo n.º 1
0
static void
backtrace_each(rb_thread_t *th,
	       void (*init)(void *arg, size_t size),
	       void (*iter_iseq)(void *arg, const rb_control_frame_t *cfp),
	       void (*iter_cfunc)(void *arg, const rb_control_frame_t *cfp, ID mid),
	       void *arg)
{
    rb_control_frame_t *last_cfp = th->cfp;
    rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(th);
    rb_control_frame_t *cfp;
    ptrdiff_t size, i;

    /*                <- start_cfp (end control frame)
     *  top frame (dummy)
     *  top frame (dummy)
     *  top frame     <- start_cfp
     *  top frame
     *  ...
     *  2nd frame     <- lev:0
     *  current frame <- th->cfp
     */

    start_cfp =
      RUBY_VM_NEXT_CONTROL_FRAME(
	  RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */

    if (start_cfp < last_cfp) {
	size = 0;
    }
    else {
	size = start_cfp - last_cfp + 1;
    }

    init(arg, size);

    /* SDR(); */
    for (i=0, cfp = start_cfp; i<size; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
	/* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(th->stack + th->stack_size) - cfp); */
	if (cfp->iseq) {
	    if (cfp->pc) {
		iter_iseq(arg, cfp);
	    }
	}
	else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
	    ID mid = cfp->me->def ? cfp->me->def->original_id : cfp->me->called_id;

	    iter_cfunc(arg, cfp, mid);
	}
    }
}
Exemplo n.º 2
0
static int
vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg)
{
    const rb_control_frame_t *limit_cfp = th->cfp;
    const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size);
    VALUE file = Qnil;
    int line_no = 0;

    cfp -= 2;
    while (lev-- >= 0) {
	if (++limit_cfp > cfp) {
	    return FALSE;
	}
    }
    if (init) (*init)(arg);
    limit_cfp = RUBY_VM_NEXT_CONTROL_FRAME(limit_cfp);
    if (th->vm->progname) file = th->vm->progname;
    while (cfp > limit_cfp) {
	if (cfp->iseq != 0) {
	    if (cfp->pc != 0) {
		rb_iseq_t *iseq = cfp->iseq;

		line_no = rb_vm_get_sourceline(cfp);
		file = iseq->filename;
		if ((*iter)(arg, file, line_no, iseq->name)) break;
	    }
	}
	else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
	    ID id;
	    extern VALUE ruby_engine_name;

	    if (NIL_P(file)) file = ruby_engine_name;
	    if (cfp->me->def)
		id = cfp->me->def->original_id;
	    else
		id = cfp->me->called_id;
	    if (id != ID_ALLOCATOR && (*iter)(arg, file, line_no, rb_id2str(id)))
		break;
	}
	cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
    }
    return TRUE;
}
Exemplo n.º 3
0
static void
vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
{
    int i;

    VALUE rstr;
    VALUE *sp = cfp->sp;
    VALUE *lfp = cfp->lfp;
    VALUE *dfp = cfp->dfp;

    int argc = 0, local_size = 0;
    const char *name;
    rb_iseq_t *iseq = cfp->iseq;

    if (iseq == 0) {
	if (RUBYVM_CFUNC_FRAME_P(cfp)) {
	    name = rb_id2name(cfp->me->original_id);
	}
	else {
	    name = "?";
	}
    }
    else if (RUBY_VM_IFUNC_P(iseq)) {
	name = "<ifunc>";
    }
    else {
	argc = iseq->argc;
	local_size = iseq->local_size;
	name = RSTRING_PTR(iseq->name);
    }

    /* stack trace header */

    if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_METHOD ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_TOP ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CLASS ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_PROC ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC ||
	VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_EVAL) {

	VALUE *ptr = dfp - local_size;

	vm_stack_dump_each(th, cfp + 1);
	control_frame_dump(th, cfp);

	if (lfp != dfp) {
	    local_size++;
	}
	for (i = 0; i < argc; i++) {
	    rstr = rb_inspect(*ptr);
	    fprintf(stderr, "  arg   %2d: %8s (%p)\n", i, StringValueCStr(rstr),
		   (void *)ptr++);
	}
	for (; i < local_size - 1; i++) {
	    rstr = rb_inspect(*ptr);
	    fprintf(stderr, "  local %2d: %8s (%p)\n", i, StringValueCStr(rstr),
		   (void *)ptr++);
	}

	ptr = cfp->bp;
	for (; ptr < sp; ptr++, i++) {
	    if (*ptr == Qundef) {
		rstr = rb_str_new2("undef");
	    }
	    else {
		rstr = rb_inspect(*ptr);
	    }
	    fprintf(stderr, "  stack %2d: %8s (%"PRIdPTRDIFF")\n", i, StringValueCStr(rstr),
		    (ptr - th->stack));
	}
    }
    else if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
	if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 2)) {
	    vm_stack_dump_each(th, cfp + 1);
	}
	else {
	    /* SDR(); */
	}
    }
    else {
	rb_bug("unsupport frame type: %08lx", VM_FRAME_TYPE(cfp));
    }
}
Exemplo n.º 4
0
static VALUE stacktrace(int argc, VALUE* argv, rb_thread_t *th)
{
  VALUE ary = rb_ary_new();
  
  int start = 0;
  int finish = -1;
  int stack_size = 0;
  const rb_control_frame_t *cfp = th->cfp;
  const rb_control_frame_t *tcfp;
  const rb_control_frame_t *limit_cfp = (void *)(th->stack + th->stack_size);
  VALUE file = Qnil;
  int line = 0;
  rb_iseq_t *iseq = 0;
  ID id;
  VALUE frame;
  extern VALUE ruby_engine_name;
  int flags = ST_F_ALL;
  
  if (argc > 0) {
    start = NUM2INT(argv[0]);
  }

  if (argc > 1) {
    finish = NUM2INT(argv[1]);
  } 

  if (argc > 2) {
    flags = NUM2INT(argv[2]);
  }

  cfp += 1;
  limit_cfp -= 2;

  if (finish > 0) {
    finish--;
  }

  if (start < 0 || finish < 0) {
    tcfp = cfp; 
    while (tcfp < limit_cfp) 
    {
	    if (tcfp->iseq != 0 && cfp->pc != 0) {
        stack_size++;
      }
      else if (RUBYVM_CFUNC_FRAME_P(tcfp)) {
        stack_size++;
      }
      tcfp++;
    }

    if (start < 0) {
      start = stack_size + start;
    }
    if (finish < 0) {
      finish = stack_size + finish;
    }
  }

   // rb_warn("flags: %i", flags & ST_F_KLASS);
    // rb_warn("test %i %i cfp: %i lcfp %i ss %i", start, finish, cfp, limit_cfp, stack_size);

  while (cfp < limit_cfp) {
    VALUE hash = 0; 
	  if (cfp->iseq != 0 && cfp->pc != 0) {
        if (start-- > 0) {cfp++; continue;}
        if (finish-- < 0) break;
        iseq = cfp->iseq;
        frame = rb_class_new_instance(0, 0, c_StackFrame); 
        if (iseq->defined_method_id && ((flags & ST_F_KLASS) == ST_F_KLASS)) {
          rb_iv_set(frame, "@klass", iseq->klass);
        }
        if ((flags & ST_F_METHOD) == ST_F_METHOD) {
          rb_iv_set(frame, "@method", iseq->name);
        }
        if ((flags & ST_F_FILENAME) == ST_F_FILENAME) {
          rb_iv_set(frame, "@filename", iseq->filename);
        }
        if ((flags & ST_F_LINENUMBER) == ST_F_LINENUMBER) {
          line = rb_vm_get_sourceline(cfp);
          rb_iv_set(frame, "@line_number", INT2FIX(line));
        }
        rb_ary_push(ary, frame);
	  }
    else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
         if (start-- > 0) {cfp++; continue;}
         if (finish-- < 0) break;
         if (NIL_P(file)) file = ruby_engine_name;
         if (cfp->me->def)
           id = cfp->me->def->original_id;
         else
           id = cfp->me->called_id;
         if (id != ID_ALLOCATOR) {
            frame = rb_class_new_instance(0, 0, c_StackFrame); 
            if ((flags & ST_F_KLASS) == ST_F_KLASS) {
              rb_iv_set(frame, "@klass", cfp->me->klass);
            }
            if ((flags & ST_F_METHOD) == ST_F_METHOD) {
              rb_iv_set(frame, "@method", rb_id2str(id)); 
            }
            rb_ary_push(ary,frame);
           
         } 
    }
	  cfp += 1;
  }
  return ary;
}
Exemplo n.º 5
0
VALUE rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame(	rb_control_frame_t**		c_current_frame )	{

	const char*		c_method_name		=	NULL;
	int				c_sourcefile_line	=	0;
	
	//	create new hash for this frame
	VALUE	rb_frame_hash			=	rb_hash_new();
		
	VALUE	rb_sourcefile_name		=	Qnil;
	VALUE	rb_sourcefile_line		=	Qnil;
	VALUE	rb_method_name			=	Qnil;
	VALUE	rb_object_for_frame		=	Qnil;
	
	if ( ( *c_current_frame )->iseq != 0 ) {
		
		if ( ( *c_current_frame )->pc != 0 ) {
			
			rb_iseq_t *iseq			= ( *c_current_frame )->iseq;
			
			//	get sourcefile name and set in hash
			rb_sourcefile_name		=	iseq->filename;
			
			//	get sourcefile line and set in hash
			c_sourcefile_line		=	rb_vm_get_sourceline( *c_current_frame );
			rb_sourcefile_line		=	INT2FIX( c_sourcefile_line );
			
			//	get name of instruction sequence
			rb_method_name			=	ID2SYM( rb_intern( StringValuePtr( iseq->name ) ) );				
			rb_object_for_frame	=	( *c_current_frame )->self;
		}
	}
	else if ( RUBYVM_CFUNC_FRAME_P( *c_current_frame ) ) {
		
		//	get name of method

		#if RUBY_PATCHLEVEL >= -1
		//	For 1.9.2:
		const rb_method_entry_t*	c_method_for_frame	=	( *c_current_frame )->me;
		c_method_name				=	rb_id2name( c_method_for_frame->called_id );
		#else
		//	For 1.9.1:
		c_method_name				=	rb_id2name( ( *c_current_frame )->method_id );
		#endif
		
		rb_method_name				=	( c_method_name == NULL ? Qnil : ID2SYM( rb_intern( c_method_name ) ) );
		rb_object_for_frame	=	( *c_current_frame )->self;		
	}
	//	we have to test this case - it works for blocks but there may be other cases too
	else if (	( *c_current_frame )->block_iseq != 0
			 &&	( *c_current_frame )->pc == 0)	{
	
		//	If we got here we have a fiber
		//	There doesn't seem to be much that we can tell about a fiber's context
			
		VALUE			rb_current_fiber	=	rb_fiber_current();
		rb_fiber_t*		c_current_fiber		=	NULL;

		GetFiberPtr(	rb_current_fiber, 
						c_current_fiber);
						
		rb_context_t*	c_context			=	& c_current_fiber->cont;
		
//		rb_block_t*	c_blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP( *c_current_frame );
		 
		rb_object_for_frame	=	( *c_current_frame )->self;
		
		//	get sourcefile name and set in hash
		rb_sourcefile_name		=	Qnil;

		//	get sourcefile line and set in hash
		rb_sourcefile_line		=	Qnil;
		
		//	get name of instruction sequence
		rb_method_name			=	rb_str_new2( "<Fiber>" );		
		
		//	if we have a fiber we also include its ruby reference since we have so little other context
		rb_hash_aset(	rb_frame_hash,
						ID2SYM( rb_intern( "fiber" ) ),
						c_context->self );
		
		//	The one time that we know a fiber is in use in the Ruby base is with Enumerators
		//	For now we will handle that with a special case
		
		VALUE	rb_enumerator_class	=	rb_const_get(	rb_cObject,
														rb_intern( "Enumerator" ) );
		
		VALUE	rb_object_for_frame_klass	=	( ( TYPE( rb_object_for_frame ) == T_CLASS ) ? rb_object_for_frame : rb_funcall( rb_object_for_frame, rb_intern( "class" ), 0 ) );

		VALUE	rb_ancestors	=	rb_funcall(	rb_object_for_frame_klass,
												rb_intern( "ancestors" ),
												0 );
		
		if ( rb_ary_includes(	rb_ancestors,
								rb_enumerator_class ) )	{
			
			struct enumerator* c_enumerator		=	enumerator_ptr( rb_object_for_frame );
			
			rb_object_for_frame	=	c_enumerator->obj;
			rb_method_name		=	ID2SYM( c_enumerator->meth );			
		}
		
	}
	else if (	( *c_current_frame )->block_iseq == 0
			 &&	( *c_current_frame )->pc == 0)	{
		//	this happens after we had a fiber and we try to go up - which doesn't make sense with a fiber
		//	not sure what we want to do here, if anything
		return Qnil;
	}
	else {
		//	The third possibility is that we have an iseq frame with nil params for what we want
		//	In that case we can simply return the next frame
		*c_current_frame	=	RUBY_VM_PREVIOUS_CONTROL_FRAME( *c_current_frame );
		
		//	in theory this could crash because we are going forward a frame when we don't know what's there
		//	in practice I think we are ok, since we are only jumping forward from nil frames which should never be at the end
		//	at least - I don't think they should... we shall see.
		//	
		//	a fix would be to check the next frame, but that requires access to the thread or the limit cfp, 
		//	which requires passing more context; so for now, I'm leaving it there
		
		return rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( c_current_frame );
	}

	//	Push values to return hash

	rb_hash_aset(	rb_frame_hash,
					ID2SYM( rb_intern( "object" ) ),
					rb_object_for_frame );
					
	rb_hash_aset(	rb_frame_hash,
					ID2SYM( rb_intern( "file" ) ),
					rb_sourcefile_name );

	rb_hash_aset(	rb_frame_hash,
					ID2SYM( rb_intern( "line" ) ),
					rb_sourcefile_line );

	rb_hash_aset(	rb_frame_hash,
					ID2SYM( rb_intern( "method" ) ),
					rb_method_name );
	
	return rb_frame_hash;
}