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 )