Skip to content

Commit

Permalink
Add jl_active_task_stack to gcext API (#36823)
Browse files Browse the repository at this point in the history
* Add jl_active_task_stack to gcext API

This is meant as a successor for `jl_task_stack_buffer`, which this
patch marks as deprecated in a comment.

* Let jl_active_task_stack also provide total stack range

This may be useful for debugging and other future applications, while
costing us little here; jl_active_task_stack is not supposed to be
used in hot paths either.
  • Loading branch information
fingolfin authored Oct 2, 2020
1 parent 7344d19 commit 791b194
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 25 deletions.
10 changes: 10 additions & 0 deletions src/julia_gcext.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,18 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p);
// the size of that stack buffer upon return. Also, if task is a thread's
// current task, that thread's id will be stored in *tid; otherwise,
// *tid will be set to -1.
//
// DEPRECATED: use jl_active_task_stack() instead.
JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *tid);

// Query the active and total stack range for the given task, and set
// *active_start and *active_end respectively *total_start and *total_end
// accordingly. The range for the active part is a best-effort approximation
// and may not be tight.
JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
char **active_start, char **active_end,
char **total_start, char **total_end);

#ifdef __cplusplus
}
#endif
Expand Down
51 changes: 51 additions & 0 deletions src/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,57 @@ JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *tid)
return (void *)((char *)task->stkbuf + off);
}

JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
char **active_start, char **active_end,
char **total_start, char **total_end)
{
if (!task->started) {
*total_start = *active_start = 0;
*total_end = *active_end = 0;
return;
}

int16_t tid = task->tid;
jl_ptls_t ptls2 = (tid != -1) ? jl_all_tls_states[tid] : 0;

if (task->copy_stack && ptls2 && task == ptls2->current_task) {
*total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize;
*total_end = *active_end = (char*)ptls2->stackbase;
}
else if (task->stkbuf) {
*total_start = *active_start = (char*)task->stkbuf;
#ifndef _OS_WINDOWS_
if (jl_all_tls_states[0]->root_task == task) {
// See jl_init_root_task(). The root task of the main thread
// has its buffer enlarged by an artificial 3000000 bytes, but
// that means that the start of the buffer usually points to
// inaccessible memory. We need to correct for this.
*active_start += ROOT_TASK_STACK_ADJUSTMENT;
*total_start += ROOT_TASK_STACK_ADJUSTMENT;
}
#endif

*total_end = *active_end = (char*)task->stkbuf + task->bufsz;
#ifdef COPY_STACKS
// save_stack stores the stack of an inactive task in stkbuf, and the
// actual number of used bytes in copy_stack.
if (task->copy_stack > 1)
*active_end = (char*)task->stkbuf + task->copy_stack;
#endif
}
else {
// no stack allocated yet
*total_start = *active_start = 0;
*total_end = *active_end = 0;
return;
}

if (task == jl_current_task) {
// scan up to current `sp` for current thread and task
*active_start = (char*)jl_get_frame_addr();
}
}

// Marked noinline so we can consistently skip the associated frame.
// `skip` is number of additional frames to skip.
NOINLINE static void record_backtrace(jl_ptls_t ptls, int skip) JL_NOTSAFEPOINT
Expand Down
42 changes: 17 additions & 25 deletions test/gcext/gcext.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,37 +478,29 @@ static int stack_grows_down(void) {

void task_scanner(jl_task_t *task, int root_task)
{
int var_on_frame;

// The task scanner is not necessary for liveness, as the
// corresponding task stack is already part of the stack.
// Its purpose is simply to test that the task scanner
// doing actual work does not trigger a problem.
size_t size;
int tid;
void *stack = jl_task_stack_buffer(task, &size, &tid);
if (tid >= 0) {
// this is the live stack of a thread. Is it ours?
if (stack && tid == jl_threadid()) {
// only scan the live portion of the stack.
char *end_stack = (char *) stack + size;
if (lt_ptr(stack, &size) && lt_ptr(&size, (char *)stack + size)) {
if (stack_grows_down()) {
size = end_stack - (char *)&size;
stack = (void *)&size;
}
else {
size = (char *) end_stack - (char *) &size;
}
} else {
// error, current stack frame must be on the live stack.
jl_error("stack frame not part of the current task");
}
char *start_stack;
char *end_stack;
char *total_start_stack;
char *total_end_stack;
jl_active_task_stack(task, &start_stack, &end_stack, &total_start_stack, &total_end_stack);

// this is the live stack of a thread. Is it ours?
if (start_stack && task == (jl_task_t *)jl_get_current_task()) {
if (!(lt_ptr(start_stack, &var_on_frame) && lt_ptr(&var_on_frame, end_stack))) {
// error, current stack frame must be on the live stack.
jl_error("stack frame not part of the current task");
}
else
stack = NULL;
}
if (stack) {
void **start = (void **) stack;
void **end = start + size / sizeof(void *);

if (start_stack) {
void **start = (void **)start_stack;
void **end = (void **)end_stack;
while (start < end) {
void *p = *start++;
void *q = jl_gc_internal_obj_base_ptr(p);
Expand Down

0 comments on commit 791b194

Please sign in to comment.