diff --git a/src/gc-common.c b/src/gc-common.c index 184deec2adf4f..b10d003b19d65 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -968,3 +968,19 @@ static int gc_logging_enabled = 0; JL_DLLEXPORT void jl_enable_gc_logging(int enable) { gc_logging_enabled = enable; } + +JL_DLLEXPORT void jl_save_context_for_conservative_scanning(jl_ptls_t ptls, void *ctx) +{ +#ifdef GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING + if (ctx == NULL) { + // Save the context for the thread as it was running at the time of the call + int r = getcontext(&ptls->ctx_at_the_time_gc_started); + if (r == -1) { + jl_safe_printf("Failed to save context for conservative scanning\n"); + abort(); + } + return; + } + memcpy(&ptls->ctx_at_the_time_gc_started, ctx, sizeof(ucontext_t)); +#endif +} diff --git a/src/gc.c b/src/gc.c index 74a720260e7d5..fbe8b8605a2d3 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2716,6 +2716,7 @@ size_t jl_maxrss(void); // Only one thread should be running in this function static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { + jl_save_context_for_conservative_scanning(ptls, NULL); combine_thread_gc_counts(&gc_num); jl_gc_markqueue_t *mq = &ptls->mark_queue; diff --git a/src/julia_gcext.h b/src/julia_gcext.h index e7cb57d622a78..f7a926fbebf9b 100644 --- a/src/julia_gcext.h +++ b/src/julia_gcext.h @@ -152,6 +152,12 @@ JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, char **active_start, char **active_end, char **total_start, char **total_end); +// Save the context of a task for conservative scanning. +// Requires `GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING` to be defined. +// If ctx is NULL, the registers of the thread at the moment of the call are saved, else +// the registers of the context ctx are saved into the `ctx_at_the_time_gc_started` field of Julia's TLS. +JL_DLLEXPORT void jl_save_context_for_conservative_scanning(jl_ptls_t ptls, void *ctx); + #ifdef __cplusplus } #endif diff --git a/src/julia_threads.h b/src/julia_threads.h index 813d7eaed8145..b74aeb3514166 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -282,6 +282,10 @@ typedef struct _jl_tls_states_t { size_t malloc_sz_since_last_poll; #endif +#ifdef GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING + ucontext_t ctx_at_the_time_gc_started; +#endif + JULIA_DEBUG_SLEEPWAKE( uint64_t uv_run_enter; uint64_t uv_run_leave; diff --git a/src/options.h b/src/options.h index 1ff0f0ce545bd..1cdcf622abfac 100644 --- a/src/options.h +++ b/src/options.h @@ -86,6 +86,13 @@ // GC_SMALL_PAGE allocates objects in 4k pages // #define GC_SMALL_PAGE +// GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING saves the context of the tasks that +// were running right before GC started so that they can be used for conservative +// scanning. Currently only available on x86_64 Linux. +#define GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING +#if !defined(x86_64) || !defined(_OS_LINUX_) +#undef GC_SAVE_CONTEXT_FOR_CONSERVATIVE_SCANNING +#endif // method dispatch profiling -------------------------------------------------- diff --git a/src/signals-unix.c b/src/signals-unix.c index c9a1f00519462..739b798ca7c3b 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -362,6 +362,7 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) return; } if (sig == SIGSEGV && info->si_code == SEGV_ACCERR && jl_addr_is_safepoint((uintptr_t)info->si_addr) && !is_write_fault(context)) { + jl_save_context_for_conservative_scanning(ct->ptls, context); jl_set_gc_and_wait(); // Do not raise sigint on worker thread if (jl_atomic_load_relaxed(&ct->tid) != 0)