source: branches/stable/CORE/arb_signal.cxx

Last change on this file was 17695, checked in by westram, 5 years ago
  • dump stack-trace for assertions (failing from testcode) into compile-log
    • works only if DEBUG=1, UNIT_TESTS=1 and SANITIZE=1|all
File size: 6.5 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : arb_signal.cxx                                     //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2010   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#include <SuppressOutput.h>
13
14#include <arb_signal.h>
15#include <arb_msg.h>
16
17#include <SigHandler.h>
18#include <setjmp.h>
19#include <valgrind.h>
20#include <arb_backtrace.h>
21
22// AISC_MKPT_PROMOTE:#ifndef ARB_CORE_H
23// AISC_MKPT_PROMOTE:#include <arb_core.h>
24// AISC_MKPT_PROMOTE:#endif
25
26// -----------------------
27//      catch SIGSEGV
28
29static bool    dump_backtrace_on_sigsegv = false;
30static bool    suppress_sigsegv          = false;
31static jmp_buf return_after_segv;
32
33__ATTR__NORETURN static void sigsegv_handler(int sig) {
34    // Note: sigsegv_handler is intentionally global, to show it in backtrace!
35
36    if (suppress_sigsegv) {
37        siglongjmp(return_after_segv, 667); // suppress SIGSEGV (see below)
38    }
39
40    if (dump_backtrace_on_sigsegv) {
41        GBK_dump_backtrace(stderr, GBS_global_string("received signal %i", sig));
42    }
43    fprintf(stderr, "[Terminating with signal %i]\n", sig);
44    exit(ARB_CRASH_CODE(sig));
45}
46
47void GBK_install_SIGSEGV_handler(bool dump_backtrace) {
48    dump_backtrace_on_sigsegv = dump_backtrace;
49    SigHandler old_handler = INSTALL_SIGHANDLER(SIGSEGV, sigsegv_handler, "GBK_install_SIGSEGV_handler");
50    if (old_handler != sigsegv_handler && old_handler != SIG_ERR && old_handler != SIG_DFL) {
51#if defined(DEBUG)
52        fprintf(stderr, "GBK_install_SIGSEGV_handler: Did not install SIGSEGV handler (there's already another one installed)\n");
53#endif // DEBUG
54        UNINSTALL_SIGHANDLER(SIGSEGV, sigsegv_handler, old_handler, "GBK_install_SIGSEGV_handler"); // restore existing signal handler (AISC servers install their own)
55    }
56}
57
58GB_ERROR GBK_test_address(long *address, long key) {
59    arb_assert(!suppress_sigsegv);
60    suppress_sigsegv = true;
61
62    // ----------------------------------------
63    // start of critical section
64    // (need volatile for modified local auto variables, see man longjump / NOTES)
65    volatile bool segv_occurred = false;
66    volatile long found_key;
67    volatile int  trapped       = sigsetjmp(return_after_segv, 1);
68
69    if (!trapped) {                                 // normal execution
70        found_key = *address;
71    }
72    else {
73        segv_occurred = true;
74    }
75    // end of critical section
76    // ----------------------------------------
77
78    suppress_sigsegv = false;
79    arb_assert(implicated(trapped, trapped == 667));
80
81    GB_ERROR error = NULp;
82    if (segv_occurred) {
83        error = GBS_global_string("ARBDB memory manager error: Cannot access address %p", address);
84    }
85    else if (key && found_key != key) {
86        error = GBS_global_string("ARBDB memory manager error: object at address %p has wrong type (found: 0x%lx, expected: 0x%lx)",
87                                  address, found_key, key);
88    }
89
90    if (error) {
91        fputs(error, stderr);
92        fputc('\n', stderr);
93    }
94
95    return error;
96}
97
98bool GBK_running_on_valgrind() {
99    return RUNNING_ON_VALGRIND>0;
100}
101
102size_t GBK_getNonfreeableAddress() {
103    if (suppress_sigsegv) {
104        return 0; // no address -> does not trigger
105    }
106
107    static int i;
108    return reinterpret_cast<size_t>(&i); // return nonfreeable address -> triggers AddressSanitizer
109}
110
111bool GBK_raises_SIGSEGV(void (*cb)(void)) {
112    // test whether 'cb' aborts with SIGSEGV
113    // (Note: never true under valgrind!)
114
115    volatile bool segv_occurred = false;
116    arb_assert(!suppress_sigsegv);
117    SigHandler old_handler = INSTALL_SIGHANDLER(SIGSEGV, sigsegv_handler, "GBK_raises_SIGSEGV");
118
119    suppress_sigsegv = true;
120
121    // ----------------------------------------
122    // start of critical section
123    // (need volatile for modified local auto variables, see man longjump)
124    {
125        // cppcheck-suppress variableScope (false positive)
126        volatile int trapped;
127        {
128            volatile SuppressOutput toConsole;           // comment-out this line to show console output of 'cb'
129
130            int old_suppression       = BackTraceInfo::suppress();
131            BackTraceInfo::suppress() = true;
132
133            trapped = sigsetjmp(return_after_segv, 1);
134
135            if (!trapped) {                     // normal execution
136                cb();
137            }
138            else {
139                segv_occurred = true;
140            }
141            BackTraceInfo::suppress() = old_suppression;
142        }
143        suppress_sigsegv = false;
144        arb_assert(implicated(trapped, trapped == 667));
145    }
146    // end of critical section
147    // ----------------------------------------
148
149    UNINSTALL_SIGHANDLER(SIGSEGV, sigsegv_handler, old_handler, "GBK_raises_SIGSEGV");
150
151    return segv_occurred;
152}
153
154// --------------------------------------------------------------------------------
155
156#ifdef UNIT_TESTS
157#ifndef TEST_UNIT_H
158#include <test_unit.h>
159#endif
160
161// Tests here contain special failure cases concerning C++-exceptions.
162// They exist for tuning/debugging the unit-tester.
163// Most of them should normally be disabled.
164
165#if 0
166// this (wrong) example-test triggers ../UNIT_TESTER/UnitTester.cxx@TEST_THREW
167__ATTR__NORETURN void TEST_exception() {
168    throw(666); // bad! test code should not throw out an exception
169}
170#endif
171
172void TEST_catched_exception() {
173    try {
174        throw(0x815); // throwing is not bad in general, as long as exceptions do not leave the test-code
175        TEST_EXPECT(0);
176    }
177    catch (...) {
178    }
179}
180TEST_PUBLISH(TEST_catched_exception);
181
182#if 0
183// this (wrong) example-test triggers ../UNIT_TESTER/UnitTester.cxx@terminate_called
184struct throw_on_destroy {
185    int j;
186    throw_on_destroy(int i) : j(i) {}
187    ~throw_on_destroy() { if (j == 666) throw(667); } // bad! if another exception was throw, this will call std::terminate()
188    void do_nothing() {}
189};
190void TEST_throw_during_throw() {
191    throw_on_destroy tod(666);
192    tod.do_nothing();
193    throw(668);
194}
195#endif
196
197#if 0
198void TEST_modify_std_terminate() {
199    std::set_terminate(TEST_catched_exception); // modify to whatever
200}
201#endif
202
203#endif // UNIT_TESTS
204
205// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.