Skip to content
mhahnFr edited this page Jul 18, 2024 · 25 revisions

Here you can find all information about the CallstackLibrary as well as the documentation of the source code.

The features

This library can translate backtraces obtained by the function backtrace into a human-readable format. It will use the debug symbols of the application.
Additional C++ functionality can be enabled while compiling the library.

There is also a wrapper class for simplified usage of this library in C++ code.

Additionally, there is an exception class capable of printing its construction stacktrace.

How to use

Tip

Example:

#include <stdio.h> // For printf(...)

#include <callstack.h>

int main(void) {
    struct callstack* callstack = callstack_new();
    struct callstack_frame* frames = callstack_toArray(callstack);
    
    printf("The current callstack:\n");
    for (size_t i = 0; i < callstack_getFrameCount(callstack); ++i) {
        printf("In: (%s) %s (%s:%ld)\n", callstack_frame_getShortestName(&frames[i]), 
                                         frames[i].function == NULL ? "???" : frames[i].function,
                                         callstack_frame_getShortestSourceFileOr(&frames[i], "???"),
                                         frames[i].sourceLine);
    }
    
    callstack_delete(callstack);
}

C++ support

With the C++ wrapper class the lifecycle management of callstack objects is simplified.
To use it, you need to do nothing - it is automatically included if you compile your code with a C++ compiler.

The move semantic is also supported by the wrapper if compiled using a compiler supporting C++11 or newer.

In order to get demangled C++ function names, the library needs to be compiled with the C++ exclusive functions enabled, see below.

Tip

Example:

#include <iostream>

#include <callstack.h>

int main() {
    lcs::callstack callstack;
    callstack_frame* frames = callstack_toArray(callstack);
    
    std::cout << "The current callstack:" << std::endl;
    for (size_t i = 0; i < callstack_getFrameCount(callstack); ++i) {
        std::cout << "In: (" << callstack_frame_getShortestName(&frames[i])
                  << ") "    << (frames[i].function == NULL ? "???" : frames[i].function)
                  << " ("    << callstack_frame_getShortestSourceFileOr(&frames[i], "???")
                  << ":"     << frames[i].sourceLine
                  << ")"     << std::endl;
    }
}

Callstack exceptions

With the callstack exception an exception capable of creating and printing its construction stacktrace is available.

It can be thrown directly:

// main.cpp

#include <iostream>

#include <callstack_exception.hpp>

void bar() {
    throw lcs::exception("Callstack exception with a message");
}

void foo() { bar(); }

int main() {
    try {
        foo();
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

Compiled and linked on macOS using c++ -g main.cpp -I<path/to/library>/include -L<path/to/library> -lcallstack and after enabling C++ functions of the library creates the following output:

lcs::exception: "Callstack exception with a message", stacktrace:
At: (a.out) lcs::callstack::callstack(bool) (include/callstack.hpp:81)
in: (a.out) lcs::callstack::callstack(bool) (include/callstack.hpp:79)
in: (a.out) lcs::exception::exception(char const*, bool) (include/callstack_exception.hpp:126)
in: (a.out) lcs::exception::exception(char const*, bool) (include/callstack_exception.hpp:127)
in: (a.out) bar() (main.cpp:8)
in: (a.out) foo() (main.cpp:11)
in: (a.out) main (main.cpp:15)
in: (/usr/lib/dyld) start + 1942

Extending the callstack exception

The callstack exception can easily serve as base class for other exceptions:

// main.cpp

#include <iostream>

#include <callstack_exception.hpp>

class CustomStacktraceException: public lcs::exception {};

void foo() {
    throw CustomStacktraceException();
}

void bar() { foo(); }

int main() {
    try {
        bar();
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

Compiled and linked on Debian using g++ -g main.cpp -I<path/to/library>/include -L<path/to/library> -lcallstack and after enabling C++ functions of the library creates the following output:

CustomStacktraceException, stacktrace:
At: (a.out) lcs::callstack::callstack(bool) (include/callstack.hpp:81)
in: (a.out) lcs::exception::exception(bool) (include/callstack_exception.hpp:116)
in: (a.out) CustomStacktraceException::CustomStacktraceException() (main.cpp:7)
in: (a.out) foo() (main.cpp:10)
in: (a.out) bar() (main.cpp:13)
in: (a.out) main (main.cpp:17)
in: (/usr/lib/x86_64-linux-gnu/libc.so.6) << Unknown >>
in: (/usr/lib/x86_64-linux-gnu/libc.so.6) __libc_start_main + 133
in: (a.out) _start + 33

Enabling additional C++ exclusive functions

The C++ name demangler and a few other optional functions are disabled by default. To enable them while compiling the library, pass CXX_FUNCTIONS=true to make:

make CXX_FUNCTIONS=true

When linking statically against the CallstackLibrary with C++ functions enabled the application additionally needs to be linked against the C++ standard library of your compiler (this is usually already the case when linking C++ code).