Skip to content

StackFrame

Description

A single captured stack frame. Just the IP – symbol resolution happens later in FormatStackTrace.

Usage example (Cross-references)

Usage examples (Cross-references)
            size       padded_size;
            u32        alloc_trace_n;
            StackFrame alloc_trace[DEBUG_ALLOCATOR_MAX_TRACE];
        } DebugRecord;
            u32        alloc_trace_n;
            u32        free_trace_n;
            StackFrame alloc_trace[DEBUG_ALLOCATOR_MAX_TRACE];
            StackFrame free_trace[DEBUG_ALLOCATOR_MAX_TRACE];
        } DebugFreedEntry;
            u32        free_trace_n;
            StackFrame alloc_trace[DEBUG_ALLOCATOR_MAX_TRACE];
            StackFrame free_trace[DEBUG_ALLOCATOR_MAX_TRACE];
        } DebugFreedEntry;
    typedef struct StackFrame {
        void *ip;
    } StackFrame;
    
    ///
    /// TAGS: Backtrace, Type, Frame
    ///
    typedef Vec(StackFrame) StackFrames;
    
    // ---------------------------------------------------------------------------
    /// TAGS: Backtrace, Capture, Trace, Stack
    ///
    size capture_stack_trace_raw(StackFrame *out, size max_frames, size skip_frames);
    
    ///
    /// TAGS: Backtrace, Capture, Trace, CFI, Stack
    ///
    size capture_stack_trace_cfi_raw(StackFrame *out, size max_frames, size skip_frames, SymbolResolver *resolver);
    bool capture_stack_trace_cfi_vec(StackFrames *out, size skip_frames, SymbolResolver *resolver);
    #endif
    /// TAGS: Backtrace, Format, Trace, Stack
    ///
    void format_stack_trace_raw(Str *out, const StackFrame *frames, size count, Allocator *alloc);
    
    ///
    /// TAGS: Backtrace, Format, Trace, Symbol, Stack
    ///
    void format_stack_trace_with_raw(Str *out, const StackFrame *frames, size count, SymbolResolver *resolver);
    void format_stack_trace_with_vec(Str *out, const StackFrames *frames, SymbolResolver *resolver);
    #endif
            // call site context up to Abort(). Skip our own + LogWrite's
            // frame (1 frame).
            StackFrame frames[32];
            size       n     = CaptureStackTrace(frames, 32, 1);
            Str        trace = StrInit(&h);
    }
    
    static void debug_emit_trace(const StackFrame *frames, size count, Zstr label, Allocator *meta) {
        if (!count) {
            LOG_ERROR("    {} trace: (none captured)", label);
            entry.requested_size = live_rec->requested_size;
            entry.alloc_trace_n  = live_rec->alloc_trace_n;
            MemCopy(entry.alloc_trace, live_rec->alloc_trace, (size)live_rec->alloc_trace_n * sizeof(StackFrame));
            entry.free_trace_n = 0;
            if (self->config.capture_traces && self->config.trace_depth > 0) {
    
    typedef struct RawSinkCtx {
        StackFrame *out;
        size        max;
        size        n;
    static bool vec_sink(void *user, void *ip) {
        VecSinkCtx *s = (VecSinkCtx *)user;
        StackFrame  f = {.ip = ip};
        if (!VecPushBackR(s->vec, f)) {
            s->oom = true;
    }
    
    size capture_stack_trace_raw(StackFrame *out, size max_frames, size skip_frames) {
        if (!out || max_frames == 0)
            return 0;
    
    // Shared formatter body (raw view: pointer + length).
    static void format_walk_win(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
        ensure_dbghelp();
        HANDLE proc = GetCurrentProcess();
    }
    
    void format_stack_trace_raw(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
        if (!out || !frames)
            return;
    #    endif
    
    size capture_stack_trace_raw(StackFrame *out, size max_frames, size skip_frames) {
        if (!out || max_frames == 0)
            return 0;
    #    endif
    
    static void format_walk_mac(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
    #    if FEATURE_PARSER_MACHO
        bool       cache_ok = (alloc != NULL);
    }
    
    void format_stack_trace_raw(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
        if (!out || !frames || !alloc)
            return;
    #elif defined(__GNUC__) || defined(__clang__)
    
    size capture_stack_trace_raw(StackFrame *out, size max_frames, size skip_frames) {
        if (!out || max_frames == 0)
            return 0;
    }
    
    static void format_walk_with(Str *out, const StackFrame *frames, size count, SymbolResolver *resolver) {
        for (size i = 0; i < count; ++i) {
            ResolvedSymbol r;
    }
    
    static void format_walk_alloc(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
        SymbolResolver res;
        if (!SymbolResolverInit(&res, alloc)) {
    }
    
    void format_stack_trace_with_raw(Str *out, const StackFrame *frames, size count, SymbolResolver *resolver) {
        if (!out || !frames || !resolver)
            return;
    }
    
    void format_stack_trace_raw(Str *out, const StackFrame *frames, size count, Allocator *alloc) {
        if (!out || !frames || !alloc)
            return;
    }
    
    size capture_stack_trace_cfi_raw(StackFrame *out, size max_frames, size skip_frames, SymbolResolver *resolver) {
        if (!out || max_frames == 0 || !resolver)
            return 0;
    // in v1). aarch64 follows a very similar pattern and is in
    // FUTURE-PLANS.
    size capture_stack_trace_cfi_raw(StackFrame *out, size max_frames, size skip_frames, SymbolResolver *resolver) {
        (void)out;
        (void)max_frames;
        DebugRecord *rec = MapGetFirstPtr(&dbg.live, p);
        u32          n   = ok ? rec->alloc_trace_n : 0;
        StackFrame   snapshot[DEBUG_ALLOCATOR_MAX_TRACE];
        ok = ok && (n > 0);
        if (ok)
        ok = ok && (n > 0);
        if (ok)
            MemCopy(snapshot, rec->alloc_trace, (size)n * sizeof(StackFrame));
    
        AllocatorFree(adbg, p);
    // Named helpers so we can verify a specific symbol shows up in the
    // captured trace.
    static __attribute__((noinline)) size bt_capture_with_helper(StackFrame *frames, size max) {
        return CaptureStackTrace(frames, max, 0);
    }
    }
    
    static __attribute__((noinline)) size bt_capture_outer(StackFrame *frames, size max) {
        return bt_capture_with_helper(frames, max);
    }
    
    bool test_backtrace_capture_non_empty(void) {
        StackFrame frames[32];
        size       n = bt_capture_outer(frames, 32);
        return n >= 2 && n <= 32; // at least helper + outer
        Allocator       *alloc_base = ALLOCATOR_OF(&alloc);
    
        StackFrame frames[32];
        size       n = bt_capture_outer(frames, 32);
        }
    
        StackFrame frames[16];
        size       n = bt_capture_outer(frames, 16);
    // here we still get FP frames (test build flag), but the unwinder is
    // driven by .eh_frame rules in either case.
    static __attribute__((noinline)) size cfi_capture_inner(SymbolResolver *r, StackFrame *frames, size max) {
        return CaptureStackTraceCfi(frames, max, 0, r);
    }
    }
    
    static __attribute__((noinline)) size cfi_capture_outer(SymbolResolver *r, StackFrame *frames, size max) {
        return cfi_capture_inner(r, frames, max);
    }
        }
    
        StackFrame frames[32];
        size       n = cfi_capture_outer(&res, frames, 32);
    // snapshots at a slightly different PC inside its respective capture
    // function.
    static __attribute__((noinline)) size fp_capture_inner(StackFrame *frames, size max) {
        return CaptureStackTrace(frames, max, 0);
    }
    static __attribute__((noinline)) void cfi_vs_fp_inner(
        SymbolResolver *r,
        StackFrame     *fp_frames,
        size           *fp_n,
        StackFrame     *cfi_frames,
        StackFrame     *fp_frames,
        size           *fp_n,
        StackFrame     *cfi_frames,
        size           *cfi_n,
        size            max
        }
    
        StackFrame fp[32], cfi[32];
        size       fp_n = 0, cfi_n = 0;
        cfi_vs_fp_inner(&res, fp, &fp_n, cfi, &cfi_n, 32);
Last updated on