Example #1
0
/*
 * call-seq:
 *   Kernel.each_backtrace_frame( & block )
 *
 * Return array of hashes with object and method frame information for backtrace.
 * Specifying number_of_frames will cause only the last number_of_frames to be returned.
 * Kernel.backtrace returns all frames including the current context (__method__/__callee__).
 */
VALUE rb_RPRuby_Sender_Kernel_each_backtrace_frame(	int		argc,
													VALUE*	args,
													VALUE	rb_self )	{

	rb_thread_t*			c_thread					= GET_THREAD();
	//	Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
	rb_control_frame_t*		c_current_context_frame		= RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
	
	//	c_top_of_control_frame describes the top edge of the stack trace
	//	set c_top_of_control_frame to the first frame in <main>
    rb_control_frame_t*		c_top_of_control_frame	=	RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
	
	VALUE	rb_stored_backtrace_array	=	Qnil;
	
	//	if we were passed a stored backtrace array, use it
	if (	argc == 1
		&&	TYPE( args[ 0 ] ) == T_ARRAY )	{
		rb_stored_backtrace_array	=	args[ 0 ];
	}
				
	//	for each control frame:
    while ( c_current_context_frame < c_top_of_control_frame ) {

		VALUE	rb_frame_hash;
		//	if we are using a stored backtrace we don't need to ask for a new hash
		if ( rb_stored_backtrace_array == Qnil )	{
			rb_frame_hash	=	rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame(	& c_current_context_frame );
		}
		else {
			rb_frame_hash	=	rb_ary_shift( rb_stored_backtrace_array );
		}

		if ( rb_frame_hash == Qnil )	{
			break;
		}
		
		//	if we try to iterate using an Enumerator we will lose our context
		if ( ! rb_block_given_p() )	{
			
			//	we solve this by assuming that the desired context is the moment when each_backtrace_frame is called
			//	this allows us to store the backtrace and iterate it as we want
			//	the only downside is that we have to get the entire backtrace first in order to store it
			rb_stored_backtrace_array	=	rb_RPRuby_Sender_Kernel_backtrace(	0,
																				NULL,
																				rb_self );
		
			RETURN_ENUMERATOR( rb_self, 1, & rb_stored_backtrace_array );
		}
		
		//	otherwise, yield the block
		rb_yield( rb_frame_hash );
		
		//	only move the frame if we are not using a stored backtrace
		if ( rb_stored_backtrace_array == Qnil )	{
			c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );		
		}
	}
	
	return Qnil;
}
Example #2
0
/*
 * call-seq:
 *   Kernel.backtrace( number_of_frames = nil ) -> [ { :object => object, :method => method }, ... ]
 *
 * Return array of hashes with object and method frame information for backtrace.
 * Specifying number_of_frames will cause only the last number_of_frames to be returned.
 * Kernel.backtrace returns all frames including the current context (__method__/__callee__).
 */
VALUE rb_RPRuby_Sender_Kernel_backtrace(	int		argc,
											VALUE*	args,
											VALUE	rb_self )	{

	//	Get max stack level from args if it is there
	int		c_max_stack_level	=	0;
	if ( argc )	{
		c_max_stack_level	=	FIX2INT( args[ 0 ] );
		
		//	if max_stack_level is 0 return empty array
		if ( c_max_stack_level == 0 )	{
			return rb_ary_new();
		}
		//	if max_stack_level < 0, throw error
		else if ( c_max_stack_level < 0 ) {
			rb_raise( rb_eArgError, RPRUBY_SENDER_ERROR_STACK_LEVEL_LESS_THAN_ZERO );
		}
		
	}
	
	rb_thread_t*			c_thread					= GET_THREAD();
	//	Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
	rb_control_frame_t*		c_current_context_frame		= RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp );
	
	//	c_top_of_control_frame describes the top edge of the stack trace
	//	set c_top_of_control_frame to the first frame in <main>
    rb_control_frame_t*		c_top_of_control_frame	=	RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
	
	VALUE	rb_return_array	=	rb_ary_new();
	
	int	c_stack_level	=	0;
	//	for each control frame:
    while (		c_current_context_frame < c_top_of_control_frame
		   &&	(	argc == 0
				 ||	c_stack_level < c_max_stack_level ) ) {
		
		VALUE	rb_frame_hash	=	rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame(	& c_current_context_frame );
	   
		if ( rb_frame_hash == Qnil )	{
			break;
		}
		
		//	push hash to array
		rb_ary_push(	rb_return_array,
						rb_frame_hash );
	   
	   c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
	   c_stack_level++;
   }
   
   return rb_return_array;
}
Example #3
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);
	}
    }
}
Example #4
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;
}
Example #5
0
/*
 * call-seq:
 *   Kernel.backtrace_includes?( method_or_object, ... ) -> true or false
 *   Kernel.backtrace_includes?( number_of_frames, method_or_object, ... ) -> true or false
 *
 * Returns whether specified methods or objects or classes are in the current backtrace context.
 * Kernel.backtrace_includes? begins with the prior frame, so asking if the backtrace includes the current method
 * will only report true if the current method is part of the earlier call chain.
 */
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes(	int		argc,
													VALUE*	args,
													VALUE	rb_self )	{
	
	//	this function is also used for 
	//	* backtrace_includes_one_of?
	//	* backtrace_includes_frame?
	//	* backtrace_includes_one_of_frames?
	
	//	create tracking array
	VALUE	rb_tracking_array	=	rb_ary_new();
	
	//	populate tracking array with methods/objects
	//	optional - if first arg is Qtrue, we are looking for one of the args instead of all of the args
	int		c_which_arg				=	0;
	BOOL	c_requires_all_items	= TRUE;
	if (	args[ 0 ] == Qnil
		||	(	argc > 1
			 &&	args[ 1 ] == Qnil ) )	{
		c_which_arg++;
		c_requires_all_items = FALSE;
	}
	BOOL	c_return_frame	=	FALSE;
	if (	args[ 0 ] == Qfalse
		||	(	argc > 1
			 &&	args[ 1 ] == Qfalse ) )	{
		c_which_arg++;
		c_return_frame = TRUE;
	}
	BOOL	c_return_all_frames	=	FALSE;
	if (	args[ 0 ] == Qtrue
		||	(	argc > 1
			 &&	args[ 1 ] == Qtrue ) )	{
		c_which_arg++;
		c_return_all_frames = TRUE;
	}
	int	c_args_offset	=	c_which_arg;
	for ( ; c_which_arg < argc ; c_which_arg++ )	{
		rb_ary_push(	rb_tracking_array,
						args[ c_which_arg ] );
	}
		
	rb_thread_t*			c_thread					= GET_THREAD();
	//	Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
	rb_control_frame_t*		c_current_context_frame		= RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
	
	//	c_top_of_control_frame describes the top edge of the stack trace
	//	set c_top_of_control_frame to the first frame in <main>
    rb_control_frame_t*		c_top_of_control_frame	=	RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
	
	VALUE	rb_test_index_array	=	rb_ary_new();
	//	:object
	//	instance or class
	rb_ary_push(	rb_test_index_array,
					ID2SYM( rb_intern( "object" ) ) );
	//	:method				
	rb_ary_push(	rb_test_index_array,
					ID2SYM( rb_intern( "method" ) ) );
	//	:file
	rb_ary_push(	rb_test_index_array,
					ID2SYM( rb_intern( "file" ) ) );
	//	:line
	rb_ary_push(	rb_test_index_array,
					ID2SYM( rb_intern( "line" ) ) );
	
	//	only used if c_return_all_frames == TRUE
	VALUE	rb_frame_hashes_array	=	Qnil;
	if ( c_return_all_frames == TRUE )	{
		rb_frame_hashes_array		=	rb_ary_new();
	}
	
	VALUE	rb_frame_hash	=	Qnil;
	
	//	for each control frame:
    while ( c_current_context_frame < c_top_of_control_frame ) {
	   
		//	iterate each array member 
		int	c_which_member;
		for ( c_which_member = 0 ; c_which_member < RARRAY_LEN( rb_tracking_array ) ; c_which_member++ )	{
		   
			VALUE	rb_this_arg	=	args[ c_which_member + c_args_offset ];
		   
			BOOL		matched	=	FALSE;
		   
			rb_frame_hash	=	rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame(	& c_current_context_frame );
			
			//	if we have a hash we are testing multiple items in a frame
			if ( TYPE( rb_this_arg ) == T_HASH )	{

				VALUE	rb_frame_test_array	=	rb_obj_clone( rb_test_index_array );
			
				//	for each element that we could test for
				int	c_which_index;
				int	c_skipped_index_count	=	0;
				for ( c_which_index = 0 ; c_which_index < RARRAY_LEN( rb_frame_test_array ) ; c_which_index++ )	{
					
					VALUE	rb_this_index	=	RARRAY_PTR( rb_frame_test_array )[ c_which_index ];
					
					//	see if our requested test hash includes the potential test element
					if ( rb_hash_lookup(	rb_this_arg,
											rb_this_index ) != Qnil )	{
						
						VALUE	rb_required_element	=	rb_hash_aref(	rb_this_arg,
																		rb_this_index );
						VALUE	rb_frame_element	=	rb_hash_aref(	rb_frame_hash,
																		rb_this_index	);
									 						
						//	if it does, we need to see if the current frame's element matches this element
						VALUE	rb_required_element_klass;
						if ( rb_required_element == rb_frame_element
							//	if we have a string, which is a filename
							||	(	TYPE( rb_required_element ) == T_STRING
								 &&	rb_funcall( rb_frame_element, rb_intern( "==" ), 1, rb_required_element ) == Qtrue )
							//	if we have a class, which is a special case for :object
							||	(	rb_this_index == ID2SYM( rb_intern( "class" ) ) 
								 &&	( rb_required_element_klass = ( ( TYPE( rb_required_element ) == T_CLASS ) ? rb_required_element : rb_funcall( rb_required_element, rb_intern( "class" ), 0 ) ) )
								 &&	rb_required_element_klass == rb_required_element ) )	{

							rb_ary_delete_at(	rb_frame_test_array,
												c_which_index );
							c_which_index--;
						}
					}
					else {
						c_skipped_index_count++;
					}

					if ( RARRAY_LEN( rb_frame_test_array ) == c_skipped_index_count )	{
						if ( c_return_frame == TRUE )	{
							return rb_frame_hash;
						}
						else if ( c_return_all_frames == TRUE )	{
							rb_ary_push(	rb_frame_hashes_array,
											rb_frame_hash );
						}
						else {
							return Qtrue;							
						}
					}					
				}
			}
			else {

				//	:object => <class:instance>
				if	(	TYPE( rb_this_arg ) == T_OBJECT )	{

					if ( rb_hash_aref(	rb_frame_hash,
										ID2SYM( rb_intern( "object" ) ) ) == rb_this_arg )	{
						matched = TRUE;
					}
				}
				//	:object => <class>
				else if	( TYPE( rb_this_arg ) == T_CLASS )	{
					
					VALUE	rb_frame_object			=	rb_hash_aref(	rb_frame_hash,
																		ID2SYM( rb_intern( "object" ) ) );
					VALUE	rb_frame_object_klass	=	TYPE( rb_frame_object ) == T_CLASS ? rb_frame_object : rb_funcall( rb_frame_object, rb_intern( "class" ), 0 );
					if ( rb_frame_object_klass == rb_this_arg )	{
						matched = TRUE;
					}
				}
				//	:method => :method
				else if	( TYPE( rb_this_arg ) == T_SYMBOL )	{
				   
					if ( rb_hash_aref(	rb_frame_hash,
										ID2SYM( rb_intern( "method" ) ) ) == rb_this_arg )	{
						matched = TRUE;
					}
				}
				//	:file => "filename"
				else if ( TYPE( rb_this_arg ) == T_STRING )	{
					VALUE	rb_filename	=	rb_hash_aref(	rb_frame_hash,
															ID2SYM( rb_intern( "file" ) ) );
					VALUE	rb_comparison	=	rb_funcall( rb_filename, rb_intern( "==" ), 1, rb_this_arg );
					if ( rb_comparison == Qtrue )	{
						matched = TRUE;
					}
				}
				//	:line => number
				else if ( TYPE( rb_this_arg ) == T_FIXNUM )	{
					if ( rb_hash_aref(	rb_frame_hash,
										ID2SYM( rb_intern( "line" ) ) ) == rb_this_arg )	{
						matched = TRUE;
					}
				}
			   
				//	if array member exists in frame, remove from array
				if ( matched )	{
					if ( c_requires_all_items == FALSE )	{
						if ( c_return_frame == TRUE )	{
							return rb_frame_hash;
						}
						else {
							return Qtrue;							
						}

					}
					else {
						
						//	delete this index
						rb_ary_delete_at(	rb_tracking_array,
											c_which_member );
						
						//	decrement the loop iterator so that the increase is offset
						//	this is necessary since we just removed an index and are iterating vs. the length of the array
						c_which_member--;
					}
				}
			}
		}
	   
		//	if array is empty, return true
		//	we check here as well as at the end so we can stop iterating the backtrace if we find all our items
		if ( RARRAY_LEN( rb_tracking_array ) == 0 )	{
			if ( c_return_frame == TRUE )	{
				return rb_frame_hash;
			}
			else if ( c_return_all_frames == TRUE )	{
				rb_ary_push(	rb_frame_hashes_array,
								rb_frame_hash );
				return rb_frame_hashes_array;
			}
			else {
				return Qtrue;							
			}
		}
		c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );		
	}
	
	if (	c_return_all_frames == TRUE
		&&	RARRAY_LEN( rb_frame_hashes_array ) > 0 ) {
		return rb_frame_hashes_array;
	}
	//	if we finish iterating frames and still have items in the array, return false
	else if ( RARRAY_LEN( rb_tracking_array ) > 0 )	{
		if ( c_return_frame == TRUE )	{
			return Qnil;
		}
		else {
			return Qfalse;
		}
	}
	//	otherwise, return true
	else if ( c_return_frame == TRUE )	{
		return rb_frame_hash;
	}
	else {
		return Qtrue;							
	}
	//	we don't get here
	return Qnil;
}