bug.cpp
Go to the documentation of this file.
1 //
2 // bug.cpp
3 //
4 // Copyright (C) 1996 Limit Point Systems, Inc.
5 //
6 // Author: Curtis Janssen <cljanss@limitpt.com>
7 // Maintainer: LPS
8 //
9 // This file is part of the SC Toolkit.
10 //
11 // The SC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The SC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27 
28 #include "bug.h"
29 
30 #include <unistd.h>
31 #include <cfenv>
32 #include <csignal>
33 #include <cstdio>
34 #include <cstdlib>
35 #include <cstring>
36 #include <iostream>
37 #include <iterator>
38 #include <sstream>
39 
40 #include "backtrace.h"
41 
42 // usually in signal.h, but not always.
43 #ifndef NSIG
44 #define NSIG 100
45 #endif
46 
47 using namespace std;
48 using namespace ttg;
49 
50 namespace ttg {
51  void initialize_fpe() {
52 #if defined(__APPLE__) && defined(__MACH__)
53 
54  // Public domain polyfill for feenableexcept on OS X
55  // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c
56 
57 #ifndef HAVE_FEENABLEEXCEPT
58  auto feenableexcept = [](int excepts) -> int {
59  static fenv_t fenv;
60  const auto new_excepts = excepts & FE_ALL_EXCEPT;
61 
62  if (fegetenv(&fenv)) {
63  return -1;
64  }
65 #if defined(__x86_64__)
66  // previous masks
67  const unsigned int old_excepts = fenv.__control & FE_ALL_EXCEPT;
68 
69  // unmask
70  fenv.__control &= ~new_excepts;
71  fenv.__mxcsr &= ~(new_excepts << 7);
72 #elif defined(__arm64__)
73  if (new_excepts & FE_INVALID) fenv.__fpcr |= __fpcr_trap_invalid;
74  if (new_excepts & FE_DIVBYZERO) fenv.__fpcr |= __fpcr_trap_divbyzero;
75  if (new_excepts & FE_OVERFLOW) fenv.__fpcr |= __fpcr_trap_overflow;
76  if (new_excepts & FE_UNDERFLOW) fenv.__fpcr |= __fpcr_trap_underflow;
77  if (new_excepts & FE_INEXACT) fenv.__fpcr |= __fpcr_trap_inexact;
78 #else
79 #error "MacOS on unknown architecture"
80 #endif
81  return fesetenv(&fenv);
82  };
83 #define HAVE_FEENABLEEXCEPT 1
84 #endif // not defined HAVE_FEENABLEEXCEPT
85 
86 #ifndef HAVE_FEDISABLEEXCEPT
87  auto fedisableexcept = [](int excepts) -> int {
88  static fenv_t fenv;
89  const auto new_excepts = excepts & FE_ALL_EXCEPT;
90  // all previous masks
91 
92  if (fegetenv(&fenv)) {
93  return -1;
94  }
95 #if defined(__x86_64__)
96  const unsigned int old_excepts = fenv.__control & FE_ALL_EXCEPT;
97 
98  // mask
99  fenv.__control |= new_excepts;
100  fenv.__mxcsr |= new_excepts << 7;
101 #elif defined(__arm64__)
102  if (new_excepts & FE_INVALID) fenv.__fpcr &= ~__fpcr_trap_invalid;
103  if (new_excepts & FE_DIVBYZERO) fenv.__fpcr &= ~__fpcr_trap_divbyzero;
104  if (new_excepts & FE_OVERFLOW) fenv.__fpcr &= ~__fpcr_trap_overflow;
105  if (new_excepts & FE_UNDERFLOW) fenv.__fpcr &= ~__fpcr_trap_underflow;
106  if (new_excepts & FE_INEXACT) fenv.__fpcr &= ~__fpcr_trap_inexact;
107 #else
108 #error "MacOS on unknown architecture"
109 #endif
110 
111  return fesetenv(&fenv);
112  };
113 
114 #define HAVE_FEDISABLEEXCEPT 1
115 #endif // not defined HAVE_FEDISABLEEXCEPT
116 #endif // mac
117 
118 #ifdef HAVE_FEENABLEEXCEPT
119  // this uses a glibc extension to trap on individual exceptions
120  int enable_excepts = 0;
121 #ifdef FE_DIVBYZERO
122  enable_excepts |= FE_DIVBYZERO;
123 #endif
124 #ifdef FE_INVALID
125  enable_excepts |= FE_INVALID;
126 #endif
127 #ifdef FE_OVERFLOW
128  enable_excepts |= FE_OVERFLOW;
129 #endif
130  feenableexcept(enable_excepts);
131 #endif
132 
133 #ifdef HAVE_FEDISABLEEXCEPT
134  // this uses a glibc extension to not trap on individual exceptions
135  int disable_excepts = 0;
136 #ifdef FE_UNDERFLOW
137  disable_excepts |= FE_UNDERFLOW;
138 #endif
139 #ifdef FE_INEXACT
140  disable_excepts |= FE_INEXACT;
141 #endif
142  fedisableexcept(disable_excepts);
143 #endif
144  }
145 }
146 
148 // static variables
149 
150 static Debugger *signals[NSIG];
151 
153 // Debugger class definition
154 
155 std::shared_ptr<Debugger> Debugger::default_debugger_(nullptr);
156 
157 Debugger::Debugger(const char *exec) {
158  init();
159 
160  debug_ = true;
161  traceback_ = true;
162  exit_on_signal_ = true;
163  sleep_ = false;
164  wait_for_debugger_ = true;
165  default_cmd();
166  prefix_ = "";
167  handle_sigint_ = false;
168  handle_defaults();
169 
170  set_exec(exec);
171  resolve_cmd_alias();
172 }
173 
174 Debugger::~Debugger() {
175  for (int i = 0; i < NSIG; i++) {
176  if (mysigs_[i]) signals[i] = nullptr;
177  }
178  delete[] mysigs_;
179 }
180 
181 void Debugger::init() {
182  exec_.resize(0);
183  prefix_.resize(0);
184  cmd_.resize(0);
185  sleep_ = 0;
186 
187  exit_on_signal_ = 1;
188  traceback_ = 1;
189  debug_ = 1;
190  wait_for_debugger_ = 1;
191 
192  mysigs_ = new int[NSIG];
193  for (int i = 0; i < NSIG; i++) {
194  mysigs_[i] = 0;
195  }
196 }
197 
198 namespace {
199  static void handler(int sig) {
200  if (signals[sig]) signals[sig]->got_signal(sig);
201  }
202 } // namespace
203 
204 void Debugger::handle(int sig) {
205  if (sig >= NSIG) return;
206  typedef void (*handler_type)(int);
207  signal(sig, (handler_type)handler);
208  signals[sig] = this;
209  mysigs_[sig] = 1;
210 }
211 
212 void Debugger::release(int sig) {
213  if (sig >= NSIG) return;
214  signal(sig, SIG_DFL);
215  signals[sig] = nullptr;
216  mysigs_[sig] = 0;
217 }
218 
219 void Debugger::handle_defaults() {
220 #ifdef SIGSEGV
221  handle(SIGSEGV);
222 #endif
223 #ifdef SIGFPE
224  handle(SIGFPE);
225 #endif
226 #ifdef SIGQUIT
227  handle(SIGQUIT);
228 #endif
229 #ifdef SIGIOT
230  handle(SIGIOT);
231 #endif
232 #ifdef SIGINT
233  if (handle_sigint_) handle(SIGINT);
234 #endif
235 #ifdef SIGHUP
236  handle(SIGHUP);
237 #endif
238 #ifdef SIGBUS
239  handle(SIGBUS);
240 #endif
241 #ifdef SIGABRT
242  handle(SIGABRT);
243 #endif
244 #ifdef SIGTRAP
245  handle(SIGTRAP);
246 #endif
247 }
248 
249 void Debugger::set_exec(const char *exec) {
250  if (exec) {
251  exec_ = exec;
252  } else {
253  exec_.resize(0);
254  }
255 }
256 
257 void Debugger::set_prefix(const char *p) {
258  if (p) {
259  prefix_ = p;
260  } else {
261  prefix_.resize(0);
262  }
263 }
264 
265 void Debugger::set_prefix(int i) {
266  char p[128];
267  sprintf(p, "%3d: ", i);
268  set_prefix(p);
269 }
270 
271 void Debugger::default_cmd() {
272  int has_x11_display = (getenv("DISPLAY") != 0);
273 
274  if (has_x11_display) {
275  set_cmd("gdb_xterm");
276  } else {
277  set_cmd(0);
278  }
279 }
280 
281 void Debugger::resolve_cmd_alias() {
282  if (cmd_ == "gdb_xterm") {
283  cmd_ = "xterm -title \"$(PREFIX)$(EXEC)\" -e gdb -ex \"set variable debugger_ready_=1\" --pid=$(PID) $(EXEC) &";
284  } else if (cmd_ == "lldb_xterm") {
285  cmd_ = "xterm -title \"$(PREFIX)$(EXEC)\" -e lldb -p $(PID) -o \"expr debugger_ready_=1\" &";
286  }
287 }
288 
289 void Debugger::set_cmd(const char *cmd) {
290  if (cmd) {
291  cmd_ = cmd;
292  resolve_cmd_alias();
293  } else {
294  cmd_.resize(0);
295  }
296 }
297 
298 void Debugger::debug(const char *reason) {
299  std::cout << prefix_ << "Debugger::debug: ";
300  if (reason)
301  std::cout << reason;
302  else
303  std::cout << "no reason given";
304  std::cout << endl;
305 
306  if (!cmd_.empty()) {
307  int pid = getpid();
308  // contruct the command name
309  std::string cmd = cmd_;
310  std::string::size_type pos;
311  std::string pidvar("$(PID)");
312  while ((pos = cmd.find(pidvar)) != std::string::npos) {
313  std::string pidstr;
314  pidstr += std::to_string(pid);
315  cmd.replace(pos, pidvar.size(), pidstr);
316  }
317  std::string execvar("$(EXEC)");
318  while ((pos = cmd.find(execvar)) != std::string::npos) {
319  cmd.replace(pos, execvar.size(), exec_);
320  }
321  std::string prefixvar("$(PREFIX)");
322  while ((pos = cmd.find(prefixvar)) != std::string::npos) {
323  cmd.replace(pos, prefixvar.size(), prefix_);
324  }
325 
326  // start the debugger
327  // before starting the debugger de-register signal handler for SIGTRAP to let the debugger take over
328  release(SIGTRAP);
329  std::cout << prefix_ << "Debugger: starting \"" << cmd << "\"" << endl;
330  debugger_ready_ = 0;
331  const auto system_retvalue = system(cmd.c_str());
332  if (system_retvalue != 0) { // call to system() failed
333  std::cout << prefix_ << "Failed debugger launch: system() did not succeed ..." << endl;
334  } else { // call to system() succeeded
335  // wait until the debugger is ready
336  if (sleep_) {
337  std::cout << prefix_ << "Sleeping " << sleep_ << " seconds to wait for debugger ..." << endl;
338  sleep(sleep_);
339  }
340  if (wait_for_debugger_) {
341  std::string make_ready_message;
342  if (cmd_.find(" gdb ") != std::string::npos || cmd_.find(" lldb ") != std::string::npos) {
343  make_ready_message =
344  " configure debugging session (set breakpoints/watchpoints, etc.) then type 'c' to continue running";
345  }
346 
347  std::cout << prefix_ << ": waiting for the user ..." << make_ready_message << endl;
348  while (!debugger_ready_)
349  ;
350  }
351  }
352  } else { // empty command = wait for the user to attach manually
353  std::cout << prefix_ << ": waiting for the user to attach a debugger to process " << getpid() << " ... " << endl;
354  debugger_ready_ = 0;
355  while (!debugger_ready_)
356  ;
357  }
358 }
359 
360 void Debugger::got_signal(int sig) {
361  const char *signame;
362  if (sig == SIGSEGV)
363  signame = "SIGSEGV";
364  else if (sig == SIGFPE)
365  signame = "SIGFPE";
366  else if (sig == SIGHUP)
367  signame = "SIGHUP";
368  else if (sig == SIGINT)
369  signame = "SIGINT";
370  else if (sig == SIGABRT)
371  signame = "SIGABRT";
372 #ifdef SIGBUS
373  else if (sig == SIGBUS)
374  signame = "SIGBUS";
375 #endif
376  else if (sig == SIGTRAP)
377  signame = "SIGTRAP";
378  else
379  signame = "UNKNOWN SIGNAL";
380 
381  if (traceback_) {
382  traceback(signame);
383  }
384  if (debug_) {
385  debug(signame);
386  }
387 
388  if (exit_on_signal_) {
389  std::cout << prefix_ << "Debugger: exiting" << endl;
390  exit(1);
391  } else {
392  std::cout << prefix_ << "Debugger: continuing" << endl;
393  }
394 
395  // handle(sig);
396 }
397 
398 void Debugger::set_debug_on_signal(int v) { debug_ = v; }
399 
400 void Debugger::set_traceback_on_signal(int v) { traceback_ = v; }
401 
402 void Debugger::set_wait_for_debugger(int v) { wait_for_debugger_ = v; }
403 
404 void Debugger::set_exit_on_signal(int v) { exit_on_signal_ = v; }
405 
406 void Debugger::set_default_debugger(const std::shared_ptr<Debugger> &d) { default_debugger_ = d; }
407 
408 std::shared_ptr<Debugger> Debugger::default_debugger() { return default_debugger_; }
409 
410 #define SIMPLE_STACK (defined(linux) && defined(i386)) || (defined(__OSF1__) && defined(i860))
411 
412 void Debugger::traceback(const char *reason) { Debugger::__traceback(prefix_, reason); }
413 
414 void Debugger::__traceback(const std::string &prefix, const char *reason) {
415  detail::Backtrace result(prefix);
416  const size_t nframes_to_skip = 2;
417 #if defined(HAVE_LIBUNWIND)
418  std::cout << prefix << "Debugger::traceback(using libunwind):";
419 #elif defined(HAVE_BACKTRACE) // !HAVE_LIBUNWIND
420  std::cout << prefix << "Debugger::traceback(using backtrace):";
421 #else // !HAVE_LIBUNWIND && !HAVE_BACKTRACE
422 #if defined(SIMPLE_STACK)
423  std::cout << prefix << "Debugger::traceback:";
424 #else
425  std::cout << prefix << "traceback not available for this arch" << endl;
426  return;
427 #endif // SIMPLE_STACK
428 #endif // HAVE_LIBUNWIND, HAVE_BACKTRACE
429 
430  if (reason)
431  std::cout << reason;
432  else
433  std::cout << "no reason given";
434  std::cout << endl;
435 
436  if (result.empty())
437  std::cout << prefix << "backtrace returned no state information" << std::endl;
438  else
439  std::cout << result.str(nframes_to_skip) << std::endl;
440 }
441 
443 
444 namespace ttg {
445  void launch_debugger(int rank, const char *exec_name, const char *cmd) {
446  using ttg::Debugger;
447  auto debugger = std::make_shared<Debugger>();
448  Debugger::set_default_debugger(debugger);
449  debugger->set_exec(exec_name);
450  debugger->set_prefix(rank);
451  debugger->set_cmd("lldb_xterm");
452  debugger->debug("start");
453 
454  // initialize_watchpoints();
455  }
456 
457  void launch_lldb(int rank, const char *exec_name) { launch_debugger(rank, exec_name, "lldb_xterm"); }
458  void launch_gdb(int rank, const char *exec_name) { launch_debugger(rank, exec_name, "gdb_xterm"); }
459 } // namespace ttg
460 
462 // Local Variables:
463 // mode: c++
464 // c-file-style: "CLJ"
465 // End:
#define NSIG
Definition: bug.cpp:44
virtual void got_signal(int sig)
Called when signal sig is received. This is mainly for internal use.
Definition: bug.cpp:360
bool empty() const
Definition: backtrace.h:61
std::string str(const size_t nframes_to_skip=0) const
Definition: backtrace.cpp:154
top-level TTG namespace contains runtime-neutral functionality
Definition: keymap.h:8
void launch_lldb(int rank, const char *exec_name)
Definition: bug.cpp:457
void launch_debugger(int rank, const char *exec_name, const char *cmd)
Definition: bug.cpp:445
int rank(World world=default_execution_context())
Definition: run.h:85
void initialize_fpe()
Initializes the floating point exceptions.
Definition: bug.cpp:51
void launch_gdb(int rank, const char *exec_name)
Definition: bug.cpp:458