Skip to content

Commit 791b194

Browse files
authored
Add jl_active_task_stack to gcext API (#36823)
* 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.
1 parent 7344d19 commit 791b194

File tree

3 files changed

+78
-25
lines changed

3 files changed

+78
-25
lines changed

src/julia_gcext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,18 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p);
125125
// the size of that stack buffer upon return. Also, if task is a thread's
126126
// current task, that thread's id will be stored in *tid; otherwise,
127127
// *tid will be set to -1.
128+
//
129+
// DEPRECATED: use jl_active_task_stack() instead.
128130
JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *tid);
129131

132+
// Query the active and total stack range for the given task, and set
133+
// *active_start and *active_end respectively *total_start and *total_end
134+
// accordingly. The range for the active part is a best-effort approximation
135+
// and may not be tight.
136+
JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
137+
char **active_start, char **active_end,
138+
char **total_start, char **total_end);
139+
130140
#ifdef __cplusplus
131141
}
132142
#endif

src/task.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,57 @@ JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *tid)
247247
return (void *)((char *)task->stkbuf + off);
248248
}
249249

250+
JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task,
251+
char **active_start, char **active_end,
252+
char **total_start, char **total_end)
253+
{
254+
if (!task->started) {
255+
*total_start = *active_start = 0;
256+
*total_end = *active_end = 0;
257+
return;
258+
}
259+
260+
int16_t tid = task->tid;
261+
jl_ptls_t ptls2 = (tid != -1) ? jl_all_tls_states[tid] : 0;
262+
263+
if (task->copy_stack && ptls2 && task == ptls2->current_task) {
264+
*total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize;
265+
*total_end = *active_end = (char*)ptls2->stackbase;
266+
}
267+
else if (task->stkbuf) {
268+
*total_start = *active_start = (char*)task->stkbuf;
269+
#ifndef _OS_WINDOWS_
270+
if (jl_all_tls_states[0]->root_task == task) {
271+
// See jl_init_root_task(). The root task of the main thread
272+
// has its buffer enlarged by an artificial 3000000 bytes, but
273+
// that means that the start of the buffer usually points to
274+
// inaccessible memory. We need to correct for this.
275+
*active_start += ROOT_TASK_STACK_ADJUSTMENT;
276+
*total_start += ROOT_TASK_STACK_ADJUSTMENT;
277+
}
278+
#endif
279+
280+
*total_end = *active_end = (char*)task->stkbuf + task->bufsz;
281+
#ifdef COPY_STACKS
282+
// save_stack stores the stack of an inactive task in stkbuf, and the
283+
// actual number of used bytes in copy_stack.
284+
if (task->copy_stack > 1)
285+
*active_end = (char*)task->stkbuf + task->copy_stack;
286+
#endif
287+
}
288+
else {
289+
// no stack allocated yet
290+
*total_start = *active_start = 0;
291+
*total_end = *active_end = 0;
292+
return;
293+
}
294+
295+
if (task == jl_current_task) {
296+
// scan up to current `sp` for current thread and task
297+
*active_start = (char*)jl_get_frame_addr();
298+
}
299+
}
300+
250301
// Marked noinline so we can consistently skip the associated frame.
251302
// `skip` is number of additional frames to skip.
252303
NOINLINE static void record_backtrace(jl_ptls_t ptls, int skip) JL_NOTSAFEPOINT

test/gcext/gcext.c

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -478,37 +478,29 @@ static int stack_grows_down(void) {
478478

479479
void task_scanner(jl_task_t *task, int root_task)
480480
{
481+
int var_on_frame;
482+
481483
// The task scanner is not necessary for liveness, as the
482484
// corresponding task stack is already part of the stack.
483485
// Its purpose is simply to test that the task scanner
484486
// doing actual work does not trigger a problem.
485-
size_t size;
486-
int tid;
487-
void *stack = jl_task_stack_buffer(task, &size, &tid);
488-
if (tid >= 0) {
489-
// this is the live stack of a thread. Is it ours?
490-
if (stack && tid == jl_threadid()) {
491-
// only scan the live portion of the stack.
492-
char *end_stack = (char *) stack + size;
493-
if (lt_ptr(stack, &size) && lt_ptr(&size, (char *)stack + size)) {
494-
if (stack_grows_down()) {
495-
size = end_stack - (char *)&size;
496-
stack = (void *)&size;
497-
}
498-
else {
499-
size = (char *) end_stack - (char *) &size;
500-
}
501-
} else {
502-
// error, current stack frame must be on the live stack.
503-
jl_error("stack frame not part of the current task");
504-
}
487+
char *start_stack;
488+
char *end_stack;
489+
char *total_start_stack;
490+
char *total_end_stack;
491+
jl_active_task_stack(task, &start_stack, &end_stack, &total_start_stack, &total_end_stack);
492+
493+
// this is the live stack of a thread. Is it ours?
494+
if (start_stack && task == (jl_task_t *)jl_get_current_task()) {
495+
if (!(lt_ptr(start_stack, &var_on_frame) && lt_ptr(&var_on_frame, end_stack))) {
496+
// error, current stack frame must be on the live stack.
497+
jl_error("stack frame not part of the current task");
505498
}
506-
else
507-
stack = NULL;
508499
}
509-
if (stack) {
510-
void **start = (void **) stack;
511-
void **end = start + size / sizeof(void *);
500+
501+
if (start_stack) {
502+
void **start = (void **)start_stack;
503+
void **end = (void **)end_stack;
512504
while (start < end) {
513505
void *p = *start++;
514506
void *q = jl_gc_internal_obj_base_ptr(p);

0 commit comments

Comments
 (0)