Alien-LibJIT

 view release on metacpan or  search on metacpan

libjit/jit/jit-debugger.c  view on Meta::CPAN

@code{jit_debugger_step}, etc, to control the debug process.
@end enumerate

These two threads should be set to "unbreakable" with a call to
@code{jit_debugger_set_breakable}.  This prevents them from accidentally
stopping at a breakpoint, which would cause a system deadlock.
Other housekeeping threads, such as a finalization thread, should
also be set to "unbreakable" for the same reason.

@noindent
Events have the following members:

@table @code
@item type
The type of event (see the next table for details).

@item thread
The thread that the event occurred on.

@item function
The function that the breakpoint occurred within.

@item data1
@itemx data2
The data values at the breakpoint.  These values are inserted into
the function's code with @code{jit_insn_mark_breakpoint}.

@item id
The identifier for the breakpoint.

@item trace
The stack trace corresponding to the location where the breakpoint
occurred.  This value is automatically freed upon the next call
to @code{jit_debugger_wait_event}.  If you wish to preserve the
value, then you must call @code{jit_stack_trace_copy}.
@end table

@noindent
The following event types are currently supported:

@table @code
@item JIT_DEBUGGER_TYPE_QUIT
A thread called @code{jit_debugger_quit}, indicating that it wanted the
event thread to terminate.

@item JIT_DEBUGGER_TYPE_HARD_BREAKPOINT
A thread stopped at a hard breakpoint.  That is, a breakpoint defined
by a call to @code{jit_debugger_add_breakpoint}.

@item JIT_DEBUGGER_TYPE_SOFT_BREAKPOINT
A thread stopped at a breakpoint that wasn't explicitly defined by
a call to @code{jit_debugger_add_breakpoint}.  This typicaly results
from a call to a "step" function like @code{jit_debugger_step}, where
execution stopped at the next line but there isn't an explicit breakpoint
on that line.

@item JIT_DEBUGGER_TYPE_USER_BREAKPOINT
A thread stopped because of a call to @code{jit_debugger_break}.

@item JIT_DEBUGGER_TYPE_ATTACH_THREAD
A thread called @code{jit_debugger_attach_self}.  The @code{data1} field
of the event is set to the value of @code{stop_immediately} for the call.

@item JIT_DEBUGGER_TYPE_DETACH_THREAD
A thread called @code{jit_debugger_detach_self}.
@end table

@deftypefun int jit_insn_mark_breakpoint_variable (jit_function_t @var{func}, jit_value_t @var{data1}, jit_value_t @var{data2})
This function is similar to @code{jit_insn_mark_breakpoint} except that values
in @var{data1} and @var{data2} can be computed at runtime. You can use this
function for example to get address of local variable.
@end deftypefun

@*/

/*
 * Linked event, for the debugger event queue.
 */
typedef struct jit_debugger_linked_event
{
	jit_debugger_event_t				event;
	struct jit_debugger_linked_event   *next;

} jit_debugger_linked_event_t;

/*
 * Run types.
 */
#define	JIT_RUN_TYPE_STOPPED		0
#define	JIT_RUN_TYPE_CONTINUE		1
#define	JIT_RUN_TYPE_STEP			2
#define	JIT_RUN_TYPE_NEXT			3
#define	JIT_RUN_TYPE_FINISH			4
#define	JIT_RUN_TYPE_DETACHED		5

/*
 * Information about a thread that is under the control of the debugger.
 */
typedef struct jit_debugger_thread
{
	struct jit_debugger_thread *next;
	jit_debugger_thread_id_t	id;
	jit_thread_id_t				native_id;
	int				   volatile	run_type;
	jit_function_t				find_func;
	jit_nint					last_data1;
	jit_nint					last_func_data1;
	int							breakable;

} *jit_debugger_thread_t;

/*
 * Structure of a debugger instance.
 */
struct jit_debugger
{
	jit_monitor_t				  queue_lock;
	jit_monitor_t				  run_lock;
	jit_context_t				  context;
	jit_debugger_linked_event_t * volatile events;
	jit_debugger_linked_event_t * volatile last_event;

libjit/jit/jit-debugger.c  view on Meta::CPAN

/*
 * Get the information block for the current thread.
 */
static jit_debugger_thread_t get_current_thread(jit_debugger_t dbg)
{
	/* TODO */
	return 0;
}

/*
 * Get the information block for a specific thread.
 */
static jit_debugger_thread_t get_specific_thread
		(jit_debugger_t dbg, jit_debugger_thread_id_t thread)
{
	/* TODO */
	return 0;
}

/*
 * Allocate space for a new event on the event queue.
 */
#define	alloc_event()		\
		((jit_debugger_event_t *)jit_cnew(jit_debugger_linked_event_t))

/*
 * Add an event that was previously allocated with "alloc_event"
 * to a debugger's event queue.
 */
static void add_event(jit_debugger_t dbg, jit_debugger_event_t *_event)
{
	jit_debugger_linked_event_t *event = (jit_debugger_linked_event_t *)_event;
	event->next = 0;
	jit_monitor_lock(&(dbg->queue_lock));
	if(dbg->last_event)
	{
		dbg->last_event->next = event;
	}
	else
	{
		dbg->events = event;
	}
	dbg->last_event = event;
	jit_monitor_signal(&(dbg->queue_lock));
	jit_monitor_unlock(&(dbg->queue_lock));
}

/*@
 * @deftypefun int jit_debugging_possible (void)
 * Determine if debugging is possible.  i.e. that threading is available
 * and compatible with the debugger's requirements.
 * @end deftypefun
@*/
int jit_debugging_possible(void)
{
	return JIT_THREADS_SUPPORTED;
}

/*@
 * @deftypefun jit_debugger_t jit_debugger_create (jit_context_t @var{context})
 * Create a new debugger instance and attach it to a JIT @var{context}.
 * If the context already has a debugger associated with it, then this
 * function will return the previous debugger.
 * @end deftypefun
@*/
jit_debugger_t jit_debugger_create(jit_context_t context)
{
	jit_debugger_t dbg;
	if(context)
	{
		if(context->debugger)
		{
			return context->debugger;
		}
		dbg = jit_cnew(struct jit_debugger);
		if(!dbg)
		{
			return 0;
		}
		dbg->context = context;
		context->debugger = dbg;
		jit_monitor_create(&(dbg->queue_lock));
		jit_monitor_create(&(dbg->run_lock));
		return dbg;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun void jit_debugger_destroy (jit_debugger_t @var{dbg})
 * Destroy a debugger instance.
 * @end deftypefun
@*/
void jit_debugger_destroy(jit_debugger_t dbg)
{
	/* TODO */
}

/*@
 * @deftypefun jit_context_t jit_debugger_get_context (jit_debugger_t @var{dbg})
 * Get the JIT context that is associated with a debugger instance.
 * @end deftypefun
@*/
jit_context_t jit_debugger_get_context(jit_debugger_t dbg)
{
	if(dbg)
	{
		return dbg->context;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_debugger_t jit_debugger_from_context (jit_context_t @var{context})
 * Get the debugger that is currently associated with a JIT @var{context},

libjit/jit/jit-debugger.c  view on Meta::CPAN

		(jit_debugger_t dbg, const void *native_thread)
{
	/* TODO */
	return 0;
}

/*@
 * @deftypefun int jit_debugger_get_native_thread (jit_debugger_t @var{dbg}, jit_debugger_thread_id_t @var{thread}, void *@var{native_thread})
 * Get the native thread handle associated with a debugger thread identifier.
 * Returns non-zero if OK, or zero if the debugger thread identifier is not
 * yet associated with a native thread handle.
 * @end deftypefun
@*/
int jit_debugger_get_native_thread
		(jit_debugger_t dbg, jit_debugger_thread_id_t thread,
		 void *native_thread)
{
	jit_debugger_thread_t th;
	lock_debugger(dbg);
	th = get_specific_thread(dbg, thread);
	if(th)
	{
		jit_memcpy(native_thread, &(th->native_id), sizeof(th->native_id));
		unlock_debugger(dbg);
		return 1;
	}
	else
	{
		unlock_debugger(dbg);
		return 0;
	}
}

/*@
 * @deftypefun void jit_debugger_set_breakable (jit_debugger_t @var{dbg}, const void *@var{native_thread}, int @var{flag})
 * Set a flag that indicates if a native thread can stop at breakpoints.
 * If set to 1 (the default), breakpoints will be active on the thread.
 * If set to 0, breakpoints will be ignored on the thread.  Typically
 * this is used to mark threads associated with the debugger's user
 * interface, or the virtual machine's finalization thread, so that they
 * aren't accidentally suspended by the debugger (which might cause a
 * deadlock).
 * @end deftypefun
@*/
void jit_debugger_set_breakable
		(jit_debugger_t dbg, const void *native_thread, int flag)
{
	jit_debugger_thread_t th;
	jit_debugger_thread_id_t id;
	id = jit_debugger_get_thread(dbg, native_thread);
	lock_debugger(dbg);
	th = get_specific_thread(dbg, id);
	if(th)
	{
		th->breakable = flag;
	}
	unlock_debugger(dbg);
}

/*@
 * @deftypefun void jit_debugger_attach_self (jit_debugger_t @var{dbg}, int @var{stop_immediately})
 * Attach the current thread to a debugger.  If @var{stop_immediately}
 * is non-zero, then the current thread immediately suspends, waiting for
 * the user to start it with @code{jit_debugger_run}.  This function is
 * typically called in a thread's startup code just before any "real work"
 * is performed.
 * @end deftypefun
@*/
void jit_debugger_attach_self(jit_debugger_t dbg, int stop_immediately)
{
	jit_debugger_event_t *event;
	jit_debugger_thread_t th;
	lock_debugger(dbg);
	th = get_current_thread(dbg);
	if(th)
	{
		event = alloc_event();
		if(event)
		{
			event->type = JIT_DEBUGGER_TYPE_ATTACH_THREAD;
			event->thread = th->id;
			event->data1 = (jit_nint)stop_immediately;
			add_event(dbg, event);
			th->find_func = 0;
			th->last_data1 = 0;
			th->last_func_data1 = 0;
			if(stop_immediately)
			{
				th->run_type = JIT_RUN_TYPE_STOPPED;
				suspend_thread(dbg, th);
			}
			else
			{
				th->run_type = JIT_RUN_TYPE_CONTINUE;
			}
		}
	}
	unlock_debugger(dbg);
}

/*@
 * @deftypefun void jit_debugger_detach_self (jit_debugger_t @var{dbg})
 * Detach the current thread from the debugger.  This is typically
 * called just before the thread exits.
 * @end deftypefun
@*/
void jit_debugger_detach_self(jit_debugger_t dbg)
{
	jit_debugger_event_t *event;
	jit_debugger_thread_t th;
	lock_debugger(dbg);
	th = get_current_thread(dbg);
	if(th)
	{
		event = alloc_event();
		if(event)
		{
			event->type = JIT_DEBUGGER_TYPE_DETACH_THREAD;
			event->thread = th->id;
			add_event(dbg, event);
			th->run_type = JIT_RUN_TYPE_DETACHED;
		}
	}
	unlock_debugger(dbg);
}

/*@
 * @deftypefun int jit_debugger_wait_event (jit_debugger_t @var{dbg}, jit_debugger_event_t *@var{event}, jit_nint @var{timeout})
 * Wait for the next debugger event to arrive.  Debugger events typically

libjit/jit/jit-debugger.c  view on Meta::CPAN

 * prototype:
 *
 * @example
 * void hook(jit_function_t func, jit_nint data1, jit_nint data2);
 * @end example
 *
 * The @code{func} argument indicates the function that the breakpoint
 * occurred within.  The @code{data1} and @code{data2} arguments are
 * those supplied to @code{jit_insn_mark_breakpoint}.  The debugger can use
 * these values to indicate information about the breakpoint's type
 * and location.
 *
 * Hook functions can be used for other purposes besides breakpoint
 * debugging.  For example, a program could be instrumented with hooks
 * that tally up the number of times that each function is called,
 * or which profile the amount of time spent in each function.
 *
 * By convention, @code{data1} values less than 10000 are intended for
 * use by user-defined hook functions.  Values of 10000 and greater are
 * reserved for the full-blown debugger system described earlier.
 * @end deftypefun
@*/
jit_debugger_hook_func jit_debugger_set_hook
		(jit_context_t context, jit_debugger_hook_func hook)
{
	jit_debugger_hook_func prev;
	if(context)
	{
		prev = context->debug_hook;
		context->debug_hook = hook;
		return prev;
	}
	else
	{
		return 0;
	}
}

void _jit_debugger_hook(jit_function_t func, jit_nint data1, jit_nint data2)
{
	jit_context_t context;
	jit_debugger_t dbg;
	jit_debugger_thread_t th;
	jit_debugger_event_t *event;
	int stop;

	/* Find the context and look for a user-supplied debug hook */
	context = func->context;
	if(context->debug_hook)
	{
		(*(context->debug_hook))(func, data1, data2);
	}

	/* Ignore breakpoints with data1 values less than 10000.  These are
	   presumed to be handled by a user-supplied debug hook instead */
	if(data1 < JIT_DEBUGGER_DATA1_FIRST)
	{
		return;
	}

	/* Determine if there is a debugger attached to the context */
	dbg = context->debugger;
	if(!dbg)
	{
		return;
	}

	/* Lock down the debugger while we do this */
	lock_debugger(dbg);

	/* Get the current thread's information block */
	th = get_current_thread(dbg);
	if(!th || !(th->breakable))
	{
		unlock_debugger(dbg);
		return;
	}

	/* Determine if there is a hard breakpoint at this location */
	/* TODO */

	/* Determine if we are looking for a soft breakpoint */
	stop = 0;
	switch(th->run_type)
	{
		case JIT_RUN_TYPE_STEP:
		{
			/* Stop at all breakpoints */
			stop = 1;
		}
		break;

		case JIT_RUN_TYPE_NEXT:
		{
			/* Stop only if we are in the same function as the last stopping
			   point, or if we might have already left the function */
			if(func == th->find_func || th->find_func == 0 ||
			   th->last_func_data1 == JIT_DEBUGGER_DATA1_LEAVE ||
			   th->last_data1 == JIT_DEBUGGER_DATA1_THROW)
			{
				stop = 1;
			}
			if(func == th->find_func)
			{
				th->last_func_data1 = data1;
			}
		}
		break;

		case JIT_RUN_TYPE_FINISH:
		{
			/* Stop if we are at a leave point, or we saw an exception */
			if((func == th->find_func && data1 == JIT_DEBUGGER_DATA1_LEAVE) ||
			   th->last_data1 == JIT_DEBUGGER_DATA1_THROW ||
			   th->find_func == 0)
			{
				stop = 1;
			}
		}
		break;
	}



( run in 0.685 second using v1.01-cache-2.11-cpan-e1769b4cff6 )