Repository: JuliaLang/julia
File: src/stackwalk.c (C)

Code before:
        event->DllBase = (uintptr_t)NotificationData->Unloaded.DllBase;
    }

    event->NotificationReason = NotificationReason;
    event->next = jl_atomic_exchange_release(&dll_notify_queue, event);

    if (uv_mutex_trylock(&jl_in_stackwalk) != 0)
        return; // lock unavailable, process later

    jl_profile_process_dll_events();
    uv_mutex_unlock(&jl_in_stackwalk);
}

// Initialize stackwalk infrastructure (DLL tracking and profiling)
void jl_init_stackwalk(void)
{
    uv_mutex_init(&jl_in_stackwalk);
    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_IGNORE_CVREC);
    if (!SymInitialize(GetCurrentProcess(), "", 1))
        jl_safe_printf("WARNING: failed to initialize stack walk info\n");
    LdrRegisterDllNotification(0, dll_notification_callback, NULL, &dll_notification_cookie);
}

// Finalize stackwalk infrastructure
void jl_fin_stackwalk(void)
{
    if (dll_notification_cookie) {
        LdrUnregisterDllNotification(dll_notification_cookie);
        dll_notification_cookie = NULL;
    }
}

// Set the abort_profile_ptr in TLS
#ifdef _CPU_X86_64_
JL_DLLEXPORT void jl_set_profile_abort_ptr(_Atomic(int) *abort_ptr) JL_NOTSAFEPOINT
{
    abort_profile_ptr = abort_ptr;
}
#endif

static int jl_unw_init(bt_cursor_t *cursor, bt_context_t *Context)
{
    int result;
#if !defined(_CPU_X86_64_)
    memset(&cursor->stackframe, 0, sizeof(cursor->stackframe));
    cursor->stackframe.AddrPC.Offset = Context->Eip;
    cursor->stackframe.AddrStack.Offset = Context->Esp;
    cursor->stackframe.AddrFrame.Offset = Context->Ebp;
    cursor->stackframe.AddrPC.Mode = AddrModeFlat;
    cursor->stackframe.AddrStack.Mode = AddrModeFlat;
    cursor->stackframe.AddrFrame.Mode = AddrModeFlat;

Review comment (bug):
This is technically now a data race, since we requested that the profile thread concurrently read this object while we just got notified that it is about to be unmapped. Is there a way we can safely stall here (inside the close library lock) to cause profiling to stop looking at the earlier entry?

Code after:

        dll_notification_event_t *next = event->next;
        free(event);
        event = next;
    }
    dll_notify_queue = NULL;
}

// Callback for LdrRegisterDllNotification
static VOID CALLBACK dll_notification_callback(
    ULONG NotificationReason,
    PCLDR_DLL_NOTIFICATION_DATA NotificationData,
    PVOID Context)
{
    (void)Context;

    dll_notification_event_t *event = (dll_notification_event_t*)malloc(sizeof(dll_notification_event_t));
    if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
        const LDR_DLL_LOADED_NOTIFICATION_DATA *data = &NotificationData->Loaded;
        event->FullDllName = _wcsdup(data->FullDllName->Buffer);
        event->BaseDllName = _wcsdup(data->BaseDllName->Buffer);
        event->DllBase = (uintptr_t)data->DllBase;
        event->SizeOfImage = data->SizeOfImage;
    }
    else if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED) {
        event->DllBase = (uintptr_t)NotificationData->Unloaded.DllBase;
    }

    // This lock guards both the data structure, as well as the possibility
    // that a DLL is about to unloaded that is concurrently being processed
    // by `SymLoadModuleExW`
    uv_mutex_lock(&jl_dll_notify_lock);

    event->NotificationReason = NotificationReason;
    event->next = dll_notify_queue;
    dll_notify_queue = event;

    uv_mutex_unlock(&jl_dll_notify_lock);
    return; // process later
}

// Initialize stackwalk infrastructure (DLL tracking and profiling)
void jl_init_stackwalk(void)
{
    uv_mutex_init(&jl_in_stackwalk);
    uv_mutex_init(&jl_dll_notify_lock);
    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_IGNORE_CVREC);
    if (!SymInitialize(GetCurrentProcess(), "", 1))
        jl_safe_printf("WARNING: failed to initialize stack walk info\n");
    LdrRegisterDllNotification(0, dll_notification_callback, NULL, &dll_notification_cookie);
}