backtrace(3)

πŸ’– backtrace(3) β€” self-debugging era πŸ’–

okay bestie, sit down, hydrate, because we're about to talk about the ✨ Library Functions Manual ✨ pages for backtrace, backtrace_symbols, and backtrace_symbols_fd. these three are literally the "let my program take a picture of its own call stack" squad, and they live in the Standard C library (libc, linked with -lc). no cap. 🫢

🌸 NAME 🌸

backtrace, backtrace_symbols, backtrace_symbols_fd β€” support for application self-debugging ✨ (yes, your app is doing its own emotional introspection, and honestly? valid.)

πŸ“š LIBRARY πŸ“š

Standard C library (libc, -lc)

✨ SYNOPSIS ✨

#include <execinfo.h>

int backtrace(int size;
              void *buffer[size], int size);

char **backtrace_symbols(int size;
              void *const buffer[size], int size);
void backtrace_symbols_fd(int size;
              void *const buffer[size], int size, int fd);

πŸ¦‹ DESCRIPTION πŸ¦‹

backtrace() β€” the vibe-capture function

backtrace() returns a backtrace for the calling program, dropped into the array that buffer points at. and what even is a backtrace? it's the series of currently active function calls for the program β€” like a receipts trail of "how did we even get here". πŸ’…

every item in the array that buffer points to is of type void *, and it's the return address from the corresponding stack frame. the size argument says how many addresses max can fit in buffer. if the backtrace is bigger than size, then you get the addresses for the size most recent function calls β€” like, the freshest tea only. πŸ“ so if you want the complete backtrace, make sure buffer and size are big enough. periodt.

backtrace_symbols() β€” translate the addresses into something readable

given the set of addresses that backtrace() returned in buffer, backtrace_symbols() translates those addresses into an array of strings that describe the addresses symbolically. size = the number of addresses in buffer. 🧚

each address's symbolic representation is made of:

  • the function name (if that can be determined),
  • a hexadecimal offset into the function,
  • and the actual return address (in hexadecimal).

the address of the array of string pointers gets returned as the function result of backtrace_symbols(). this array is malloc(3)ed by backtrace_symbols(), and the caller must free it. πŸ™ˆ (the strings pointed to by the array of pointers don't need to be freed β€” and should not be. don't do it bestie.)

backtrace_symbols_fd() β€” same energy, no malloc

backtrace_symbols_fd() takes the same buffer and size arguments as backtrace_symbols(), but instead of handing you back an array of strings, it writes the strings, one per line, to the file descriptor fd. πŸ‘‘

and the iconic part: backtrace_symbols_fd() does not call malloc(3), so it can be used in situations where the other function might fail β€” but see NOTES, because there's a plot twist. 😭


πŸ’« RETURN VALUE πŸ’«

backtrace() returns the number of addresses returned in buffer, which is not greater than size.

  • if the return value is less than size, then the full backtrace was stored. slay. βœ…
  • if it is equal to size, then it may have been truncated β€” in which case the addresses of the oldest stack frames are not returned. πŸ’€

on success, backtrace_symbols() returns a pointer to the array malloc(3)ed by the call; on error, NULL is returned.


πŸŽ€ ATTRIBUTES πŸŽ€

for an explanation of the terms used in this section, see attributes(7).

  • backtrace(), backtrace_symbols(), backtrace_symbols_fd() β€” Thread safety β†’ MT-Safe πŸ‘‘

🌈 STANDARDS 🌈

GNU.

⭐ HISTORY ⭐

glibc 2.1.


πŸ”₯ NOTES πŸ”₯

these functions make some assumptions about how a function's return address is stored on the stack. note the following, because this is the part where it gets real:

  • Omission of the frame pointers (as implied by any of gcc(1)'s nonzero optimization levels) may cause these assumptions to be violated. πŸ™ƒ
  • Inlined functions do not have stack frames. (they simply do not have the range, bestie.)
  • Tail-call optimization causes one stack frame to replace another.
  • backtrace() and backtrace_symbols_fd() don't call malloc() explicitly, but they are part of libgcc, which gets loaded dynamically when first used. dynamic loading usually triggers a call to malloc(3). πŸ’€ so if you need certain calls to these two functions to not allocate memory (in signal handlers, for example), you need to make sure libgcc is loaded beforehand. plan ahead, it's giving main character preparation energy.

also: the symbol names may be unavailable without the use of special linker options. for systems using the GNU linker, it is necessary to use the -rdynamic linker option. ⚠️ and note that names of "static" functions are not exposed, and won't be available in the backtrace. they're private like that. 🀫


🧚 EXAMPLES 🧚

the program below demonstrates the use of backtrace() and backtrace_symbols(). the following shell session shows what we might see when running the program:

$ cc -rdynamic prog.c -o prog
$ ./prog 3
backtrace() returned 8 addresses
./prog(myfunc3+0x5c) [0x80487f0]
./prog [0x8048871]
./prog(myfunc+0x21) [0x8048894]
./prog(myfunc+0x1a) [0x804888d]
./prog(myfunc+0x1a) [0x804888d]
./prog(main+0x65) [0x80488fb]
/lib/libc.so.6(__libc_start_main+0xdc) [0xb7e38f9c]
./prog [0x8048711]

🌸 Program source 🌸

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BT_BUF_SIZE 100

void
myfunc3(void)
{
    int nptrs;
    void *buffer[BT_BUF_SIZE];
    char **strings;

    nptrs = backtrace(buffer, BT_BUF_SIZE);
    printf("backtrace() returned %d addresses\n", nptrs);

    /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
       would produce similar output to the following: */

    strings = backtrace_symbols(buffer, nptrs);
    if (strings == NULL) {
        perror("backtrace_symbols");
        exit(EXIT_FAILURE);
    }

    for (size_t j = 0; j < nptrs; j++)
        printf("%s\n", strings[j]);

    free(strings);
}

static void   /* "static" means don't export the symbol.  */
myfunc2(void)
{
    myfunc3();
}

void
myfunc(int ncalls)
{
    if (ncalls > 1)
        myfunc(ncalls - 1);
    else
        myfunc2();
}

int
main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "%s num-calls\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    myfunc(atoi(argv[1]));
    exit(EXIT_SUCCESS);
}

🫢 SEE ALSO 🫢

addr2line(1), gcc(1), gdb(1), ld(1), dlopen(3), malloc(3)


go off bestie, you just read a whole manpage πŸ’… remember: free the array from backtrace_symbols(), don't free the strings, and pass -rdynamic unless you want mystery addresses. iykyk. πŸŽ€

this has been Linux man-pages 6.20, dated 2026-02-08, page backtrace(3) β€” thanks for scrolling with me, mwah πŸ’–βœ¨πŸ‘‘

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms