Boost Test Framework: Any way to get a backtrace on SIGSEV?


Just starting out with boost test framework. Using Ubuntu + gcc (running on Windows through the new thing if that matters). When I run my tests, I get this:

unknown location(0): fatal error: in "PhraseListTest/everythingEqual8": signal: SIGSEGV, si_code: 0 (memory access violation at address: 0x00000080)
/mnt/c/projects/matching/matching-native/src/test/cpp/phrase_list_test.cpp(70): last checkpoint

*** 1 failure is detected in the test module "burningmime_matching_tests"

How would I go about finding where in my code that happened? The only indication that message gives points to a line in the test. The line in question is just:

BOOST_CHECK(matches(phraseList, bitset));

I'm fairly certain the problem is occurring somewhere inside the matches() function (probably many levels deep), but... where?

In case anyone is reading this, none of the "duplicate" answers work; obviously people closed it without considering the context.

Boost Test Framework replaces the signal handlers with its own (and does it before executing each test). That means at the beginning of each of your tests you need to replace the signal handler. I ended up with something like this...

Some header:

struct SignalToStacktraceScope

Some source file:

#include <cstdio>
#include <csignal>
#include <iostream>
#include <boost/stacktrace.hpp>

        typedef void (*FSignalHandler)(int signum);
        static void stacktraceHandler(int signum, const char* signame, FSignalHandler oldHandler)
            fprintf(stderr, "\n\n===========================================\nReceived signal %s\n", signame);
            fflush(stderr); // flush before stack trace in case getting the stack trace itself produces another segfault
            std::cerr << boost::stacktrace::stacktrace();
            fprintf(stderr, "===========================================\n\n");

            // then raise the signal again to go back to the test framework
            signal(signum, oldHandler);

        #define FOREACH_SIGNAL(F) \
            F(SIGSEGV) \
            F(SIGFPE) \
            F(SIGABRT) \

        #define DEFINE_SIGNAL_WRAPPER(S) \
            static FSignalHandler oldHandler_##S = nullptr; \
            static void stacktraceHandler_##S(int signum) \
            { \
                stacktraceHandler(signum, #S, oldHandler_##S); \

            #define HOOK_SIGNAL_WRAPPER(S) oldHandler_##S = signal(S, &stacktraceHandler_##S);
            #undef HOOK_SIGNAL_WRAPPER

            #define UNHOOK_SIGNAL_WRAPPER(S) { signal(S, oldHandler_##S); oldHandler_##S = nullptr; }
            #undef UNHOOK_SIGNAL_WRAPPER

        #undef FOREACH_SIGNAL
        SignalToStacktraceScope::SignalToStacktraceScope() { }
        SignalToStacktraceScope::~SignalToStacktraceScope() { }

And then put a SignalToStacktraceScope at the top of each test funciton (or wrap the BOOST_TEST_CASE macro to add that). Doing it once at the beginning of the tests won't help, since Boost will keep replacing it.

asked on Stack Overflow Nov 10, 2018 by Robert Fraser • edited Apr 13, 2019 by Robert Fraser

0 Answers

Nobody has answered this question yet.

User contributions licensed under CC BY-SA 3.0