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