ARB
arb_signal.cxx
Go to the documentation of this file.
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 
29 static bool dump_backtrace_on_sigsegv = false;
30 static bool suppress_sigsegv = false;
31 static 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 
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 
47 void 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 
58 GB_ERROR GBK_test_address(long *address, long key) {
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 
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 
99  return RUNNING_ON_VALGRIND>0;
100 }
101 
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 
111 bool 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;
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 
172 void 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 }
180 TEST_PUBLISH(TEST_catched_exception);
181 
182 #if 0
183 // this (wrong) example-test triggers ../UNIT_TESTER/UnitTester.cxx@terminate_called
184 struct 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 };
190 void TEST_throw_during_throw() {
191  throw_on_destroy tod(666);
192  tod.do_nothing();
193  throw(668);
194 }
195 #endif
196 
197 #if 0
198 void TEST_modify_std_terminate() {
199  std::set_terminate(TEST_catched_exception); // modify to whatever
200 }
201 #endif
202 
203 #endif // UNIT_TESTS
204 
205 // --------------------------------------------------------------------------------
#define arb_assert(cond)
Definition: arb_assert.h:245
bool GBK_running_on_valgrind()
Definition: arb_signal.cxx:98
const char * GB_ERROR
Definition: arb_core.h:25
static bool & suppress()
Definition: arb_backtrace.h:41
#define implicated(hypothesis, conclusion)
Definition: arb_assert.h:289
#define INSTALL_SIGHANDLER(sig, handler, context)
Definition: SigHandler.h:87
char * address
Definition: f2c.h:11
#define ARB_CRASH_CODE(sig)
Definition: arb_backtrace.h:33
#define RUNNING_ON_VALGRIND
Definition: valgrind.h:3639
bool GBK_raises_SIGSEGV(void(*cb)(void))
Definition: arb_signal.cxx:111
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
#define cb(action)
size_t GBK_getNonfreeableAddress()
Definition: arb_signal.cxx:102
#define TEST_PUBLISH(testfunction)
Definition: test_unit.h:1485
#define TEST_EXPECT(cond)
Definition: test_unit.h:1313
static jmp_buf return_after_segv
Definition: arb_signal.cxx:31
static void error(const char *msg)
Definition: mkptypes.cxx:96
fputc('\n', stderr)
void GBK_install_SIGSEGV_handler(bool dump_backtrace)
Definition: arb_signal.cxx:47
fputs(TRACE_PREFIX, stderr)
static bool dump_backtrace_on_sigsegv
Definition: arb_signal.cxx:29
void GBK_dump_backtrace(FILE *out, const char *message)
Definition: arb_msg.cxx:428
GB_ERROR GBK_test_address(long *address, long key)
Definition: arb_signal.cxx:58
#define NULp
Definition: cxxforward.h:97
#define __ATTR__NORETURN
Definition: attributes.h:56
#define UNINSTALL_SIGHANDLER(sig, handler, old_handler, context)
Definition: SigHandler.h:88
static bool suppress_sigsegv
Definition: arb_signal.cxx:30
static __ATTR__NORETURN void sigsegv_handler(int sig)
Definition: arb_signal.cxx:33
void(* SigHandler)(int)
Definition: SigHandler.h:23