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 <csignal>
32 #include <cstdio>
33 #include <cstdlib>
34 #include <cstring>
35 #include <iostream>
36 #include <iterator>
37 #include <sstream>
38 
39 #include "backtrace.h"
40 
41 // usually in signal.h, but not always.
42 #ifndef NSIG
43 #define NSIG 100
44 #endif
45 
46 namespace TiledArray {
47 
49 // static variables
50 
51 static Debugger *signals[NSIG];
52 
54 // Debugger class definition
55 
56 std::shared_ptr<Debugger> Debugger::default_debugger_(nullptr);
57 
58 Debugger::Debugger(const char *exec) {
59  init();
60 
61  debug_ = true;
62  traceback_ = true;
63  exit_on_signal_ = true;
64  sleep_ = false;
65  wait_for_debugger_ = true;
66  default_cmd();
67  prefix_ = "";
68  handle_sigint_ = false;
70 
71  set_exec(exec);
72  resolve_cmd_alias();
73 }
74 
76  for (int i = 0; i < NSIG; i++) {
77  if (mysigs_[i]) signals[i] = nullptr;
78  }
79  delete[] mysigs_;
80 }
81 
83  exec_.resize(0);
84  prefix_.resize(0);
85  cmd_.resize(0);
86  sleep_ = 0;
87 
88  exit_on_signal_ = 1;
89  traceback_ = 1;
90  debug_ = 1;
92 
93  mysigs_ = new int[NSIG];
94  for (int i = 0; i < NSIG; i++) {
95  mysigs_[i] = 0;
96  }
97 }
98 
99 namespace {
100 static void handler(int sig) {
101  if (signals[sig]) signals[sig]->got_signal(sig);
102 }
103 } // namespace
104 
105 void Debugger::handle(int sig) {
106  if (sig >= NSIG) return;
107  typedef void (*handler_type)(int);
108  signal(sig, (handler_type)handler);
109  signals[sig] = this;
110  mysigs_[sig] = 1;
111 }
112 
113 void Debugger::release(int sig) {
114  if (sig >= NSIG) return;
115  signal(sig, SIG_DFL);
116  signals[sig] = nullptr;
117  mysigs_[sig] = 0;
118 }
119 
121 #ifdef SIGSEGV
122  handle(SIGSEGV);
123 #endif
124 #ifdef SIGFPE
125  handle(SIGFPE);
126 #endif
127 #ifdef SIGQUIT
128  handle(SIGQUIT);
129 #endif
130 #ifdef SIGIOT
131  handle(SIGIOT);
132 #endif
133 #ifdef SIGINT
134  if (handle_sigint_) handle(SIGINT);
135 #endif
136 #ifdef SIGHUP
137  handle(SIGHUP);
138 #endif
139 #ifdef SIGBUS
140  handle(SIGBUS);
141 #endif
142 #ifdef SIGABRT
143  handle(SIGABRT);
144 #endif
145 #ifdef SIGTRAP
146  handle(SIGTRAP);
147 #endif
148 }
149 
150 void Debugger::set_exec(const char *exec) {
151  if (exec) {
152  exec_ = exec;
153  } else {
154  exec_.resize(0);
155  }
156 }
157 
158 void Debugger::set_prefix(const char *p) {
159  if (p) {
160  prefix_ = p;
161  } else {
162  prefix_.resize(0);
163  }
164 }
165 
166 void Debugger::set_prefix(int i) {
167  char p[128];
168  sprintf(p, "%3d: ", i);
169  set_prefix(p);
170 }
171 
173  int has_x11_display = (getenv("DISPLAY") != 0);
174 
175  if (has_x11_display) {
176  set_cmd("gdb_xterm");
177  } else {
178  set_cmd(0);
179  }
180 }
181 
182 void Debugger::resolve_cmd_alias() {
183  if (cmd_ == "gdb_xterm") {
184  cmd_ =
185  "xterm -title \"$(PREFIX)$(EXEC)\" -e gdb -ex \"set variable "
186  "debugger_ready_=1\" --pid=$(PID) $(EXEC) &";
187  } else if (cmd_ == "lldb_xterm") {
188  cmd_ =
189  "xterm -title \"$(PREFIX)$(EXEC)\" -e lldb -p $(PID) -o \"expr "
190  "debugger_ready_=1\" &";
191  }
192 }
193 
194 void Debugger::set_cmd(const char *cmd) {
195  if (cmd) {
196  cmd_ = cmd;
197  resolve_cmd_alias();
198  } else {
199  cmd_.resize(0);
200  }
201 }
202 
203 void Debugger::debug(const char *reason) {
204  std::cout << prefix_ << "Debugger::debug: ";
205  if (reason)
206  std::cout << reason;
207  else
208  std::cout << "no reason given";
209  std::cout << std::endl;
210 
211  if (!cmd_.empty()) {
212  int pid = getpid();
213  // contruct the command name
214  std::string cmd = cmd_;
215  std::string::size_type pos;
216  std::string pidvar("$(PID)");
217  while ((pos = cmd.find(pidvar)) != std::string::npos) {
218  std::string pidstr;
219  pidstr += std::to_string(pid);
220  cmd.replace(pos, pidvar.size(), pidstr);
221  }
222  std::string execvar("$(EXEC)");
223  while ((pos = cmd.find(execvar)) != std::string::npos) {
224  cmd.replace(pos, execvar.size(), exec_);
225  }
226  std::string prefixvar("$(PREFIX)");
227  while ((pos = cmd.find(prefixvar)) != std::string::npos) {
228  cmd.replace(pos, prefixvar.size(), prefix_);
229  }
230 
231  // start the debugger
232  // before starting the debugger de-register signal handler for SIGTRAP to
233  // let the debugger take over
234  release(SIGTRAP);
235  std::cout << prefix_ << "Debugger: starting \"" << cmd << "\"" << std::endl;
236  debugger_ready_ = 0;
237  const auto system_retvalue = system(cmd.c_str());
238  if (system_retvalue != 0) { // call to system() failed
239  std::cout << prefix_
240  << "Failed debugger launch: system() did not succeed ..."
241  << std::endl;
242  } else { // call to system() succeeded
243  // wait until the debugger is ready
244  if (sleep_) {
245  std::cout << prefix_ << "Sleeping " << sleep_
246  << " seconds to wait for debugger ..." << std::endl;
247  sleep(sleep_);
248  }
249  if (wait_for_debugger_) {
250  std::string make_ready_message;
251  if (cmd_.find(" gdb ") != std::string::npos ||
252  cmd_.find(" lldb ") != std::string::npos) {
253  make_ready_message =
254  " configure debugging session (set breakpoints/watchpoints, "
255  "etc.) then type 'c' to continue running";
256  }
257 
258  std::cout << prefix_ << ": waiting for the user ..."
259  << make_ready_message << std::endl;
260  while (!debugger_ready_)
261  ;
262  }
263  }
264  }
265 }
266 
267 void Debugger::got_signal(int sig) {
268  const char *signame;
269  if (sig == SIGSEGV)
270  signame = "SIGSEGV";
271  else if (sig == SIGFPE)
272  signame = "SIGFPE";
273  else if (sig == SIGHUP)
274  signame = "SIGHUP";
275  else if (sig == SIGINT)
276  signame = "SIGINT";
277  else if (sig == SIGABRT)
278  signame = "SIGABRT";
279 #ifdef SIGBUS
280  else if (sig == SIGBUS)
281  signame = "SIGBUS";
282 #endif
283  else if (sig == SIGTRAP)
284  signame = "SIGTRAP";
285  else
286  signame = "UNKNOWN SIGNAL";
287 
288  if (traceback_) {
289  traceback(signame);
290  }
291  if (debug_) {
292  debug(signame);
293  }
294 
295  if (exit_on_signal_) {
296  std::cout << prefix_ << "Debugger: exiting" << std::endl;
297  exit(1);
298  } else {
299  std::cout << prefix_ << "Debugger: continuing" << std::endl;
300  }
301 
302  // handle(sig);
303 }
304 
306 
308 
310 
312 
313 void Debugger::set_default_debugger(const std::shared_ptr<Debugger> &d) {
314  default_debugger_ = d;
315 }
316 
317 std::shared_ptr<Debugger> Debugger::default_debugger() {
318  return default_debugger_;
319 }
320 
321 #define SIMPLE_STACK \
322  (defined(linux) && defined(i386)) || (defined(__OSF1__) && defined(i860))
323 
324 void Debugger::traceback(const char *reason) {
326 }
327 
328 void Debugger::__traceback(const std::string &prefix, const char *reason) {
329  detail::Backtrace result(prefix);
330  const size_t nframes_to_skip = 2;
331 #if defined(HAVE_LIBUNWIND)
332  std::cout << prefix << "Debugger::traceback(using libunwind):";
333 #elif defined(HAVE_BACKTRACE) // !HAVE_LIBUNWIND
334  std::cout << prefix << "Debugger::traceback(using backtrace):";
335 #else // !HAVE_LIBUNWIND && !HAVE_BACKTRACE
336 #if defined(SIMPLE_STACK)
337  std::cout << prefix << "Debugger::traceback:";
338 #else
339  std::cout << prefix << "traceback not available for this arch" << std::endl;
340  return;
341 #endif // SIMPLE_STACK
342 #endif // HAVE_LIBUNWIND, HAVE_BACKTRACE
343 
344  if (reason)
345  std::cout << reason;
346  else
347  std::cout << "no reason given";
348  std::cout << std::endl;
349 
350  if (result.empty())
351  std::cout << prefix << "backtrace returned no state information"
352  << std::endl;
353  else
354  std::cout << result.str(nframes_to_skip) << std::endl;
355 }
356 
358  auto debugger = std::make_shared<TiledArray::Debugger>();
359  debugger->debug("Starting gdb ...");
360 }
361 
363  auto debugger = std::make_shared<TiledArray::Debugger>();
364  debugger->set_cmd("xterm -title \"$(PREFIX)$(EXEC)\" -e lldb -p $(PID) &");
365  debugger->debug("Starting lldb ...");
366 }
367 
368 } // namespace TiledArray
369 
371 // Local Variables:
372 // mode: c++
373 // c-file-style: "CLJ"
374 // End:
std::string exec_
Definition: bug.h:283
void launch_gdb_xterm()
Use this to launch GNU debugger in xterm.
Definition: bug.cpp:357
virtual void set_cmd(const char *)
Definition: bug.cpp:194
static std::shared_ptr< Debugger > default_debugger()
Return the global default debugger.
Definition: bug.cpp:317
virtual void set_traceback_on_signal(int)
Turn on or off traceback on a signel. The default is on.
Definition: bug.cpp:307
bool exit_on_signal_
Definition: bug.h:289
virtual void set_prefix(const char *p)
This sets a prefix which preceeds all messages printing by Debugger.
Definition: bug.cpp:158
std::string cmd_
Definition: bug.h:284
volatile int debugger_ready_
Definition: bug.h:285
virtual void set_debug_on_signal(int)
Turn on or off debugging on a signel. The default is on.
Definition: bug.cpp:305
static std::shared_ptr< Debugger > default_debugger_
Definition: bug.h:297
virtual void set_exit_on_signal(int)
Turn on or off exit after a signel. The default is on.
Definition: bug.cpp:311
std::string str(const size_t nframes_to_skip=0) const
Definition: backtrace.cpp:163
virtual void debug(const char *reason)
Definition: bug.cpp:203
virtual void set_exec(const char *)
Definition: bug.cpp:150
virtual void traceback(const char *reason)
Definition: bug.cpp:324
virtual void default_cmd()
Calls set_cmd with a hopefully suitable default.
Definition: bug.cpp:172
virtual ~Debugger()
Definition: bug.cpp:75
virtual void handle(int sig)
The Debugger will be activated when sig is caught.
Definition: bug.cpp:105
static void __traceback(const std::string &prefix, const char *reason=nullptr)
Definition: bug.cpp:328
bool wait_for_debugger_
Definition: bug.h:291
bool handle_sigint_
Definition: bug.h:292
virtual void release(int sig)
Definition: bug.cpp:113
#define NSIG
Definition: bug.cpp:43
Debugger(const char *exec=nullptr)
Programmatic construction of Debugger.
Definition: bug.cpp:58
virtual void got_signal(int sig)
Called when signal sig is received. This is mainly for internal use.
Definition: bug.cpp:267
std::string prefix_
Definition: bug.h:282
virtual void set_wait_for_debugger(int)
Definition: bug.cpp:309
void launch_lldb_xterm()
Use this to launch LLVM debugger in xterm.
Definition: bug.cpp:362
static void set_default_debugger(const std::shared_ptr< Debugger > &)
Set the global default debugger. The initial value is null.
Definition: bug.cpp:313
virtual void handle_defaults()
This calls handle(int) with all of the major signals.
Definition: bug.cpp:120