diff --git a/src/gc-alloc-profiler.cpp b/src/gc-alloc-profiler.cpp index c799e10143478..ce6adc4c8b787 100644 --- a/src/gc-alloc-profiler.cpp +++ b/src/gc-alloc-profiler.cpp @@ -47,15 +47,24 @@ jl_combined_results g_combined_results; // Will live forever. // === stack stuff === jl_raw_backtrace_t get_raw_backtrace() JL_NOTSAFEPOINT { - // A single large buffer to record backtraces onto - static jl_bt_element_t static_bt_data[JL_MAX_BT_SIZE]; + // We first record the backtrace onto a MAX-sized buffer, so that we don't have to + // allocate the buffer until we know the size. To ensure thread-safety, we use a + // per-thread backtrace buffer. + jl_ptls_t ptls = jl_current_task->ptls; + jl_bt_element_t *shared_bt_data_buffer = ptls->profiling_bt_buffer; + if (shared_bt_data_buffer == NULL) { + size_t size = sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1); + shared_bt_data_buffer = (jl_bt_element_t*) malloc_s(size); + ptls->profiling_bt_buffer = shared_bt_data_buffer; + } - size_t bt_size = rec_backtrace(static_bt_data, JL_MAX_BT_SIZE, 2); + size_t bt_size = rec_backtrace(shared_bt_data_buffer, JL_MAX_BT_SIZE, 2); // Then we copy only the needed bytes out of the buffer into our profile. size_t bt_bytes = bt_size * sizeof(jl_bt_element_t); - jl_bt_element_t *bt_data = (jl_bt_element_t*) malloc(bt_bytes); - memcpy(bt_data, static_bt_data, bt_bytes); + jl_bt_element_t *bt_data = (jl_bt_element_t*) malloc_s(bt_bytes); + memcpy(bt_data, shared_bt_data_buffer, bt_bytes); + return jl_raw_backtrace_t{ bt_data, diff --git a/src/julia_threads.h b/src/julia_threads.h index 57270832123c3..549981c2137c2 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -238,6 +238,8 @@ typedef struct _jl_tls_states_t { // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. struct _jl_bt_element_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long size_t bt_size; // Size for backtrace in transit in bt_data + // Temporary backtrace buffer used only for allocations profiler. + struct _jl_bt_element_t *profiling_bt_buffer; // Atomically set by the sender, reset by the handler. volatile _Atomic(sig_atomic_t) signal_request; // TODO: no actual reason for this to be _Atomic // Allow the sigint to be raised asynchronously