Reference documentation for deal.II version 9.0.0
exceptions.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/base/exceptions.h>
17 #include <deal.II/base/logstream.h>
18 #include <deal.II/base/utilities.h>
19 #include <deal.II/base/mpi.h>
20 
21 #include <string>
22 #include <cstring>
23 #include <cstdlib>
24 #include <iostream>
25 #include <sstream>
26 
27 #ifdef DEAL_II_WITH_MPI
28 # include <mpi.h>
29 #endif
30 
31 #ifdef DEAL_II_HAVE_GLIBC_STACKTRACE
32 # include <execinfo.h>
33 #endif
34 
35 #ifdef DEAL_II_HAVE_LIBSTDCXX_DEMANGLER
36 # include <cxxabi.h>
37 #endif
38 
39 DEAL_II_NAMESPACE_OPEN
40 
41 
42 namespace deal_II_exceptions
43 {
44 
45  std::string additional_assert_output;
46 
47  void set_additional_assert_output (const char *const p)
48  {
49  additional_assert_output = p;
50  }
51 
52  bool show_stacktrace = true;
53 
55  {
56  show_stacktrace = false;
57  }
58 
59  bool abort_on_exception = true;
60 
62  {
63  abort_on_exception = false;
64  }
65 
66 }
67 
68 
69 
71  :
72  file(""),
73  line(0),
74  function(""),
75  cond(""),
76  exc(""),
77  stacktrace (nullptr),
78  n_stacktrace_frames (0),
79  what_str("")
80 {
81 #ifdef DEAL_II_HAVE_GLIBC_STACKTRACE
82  for (unsigned int i=0; i<sizeof(raw_stacktrace)/sizeof(raw_stacktrace[0]); ++i)
83  raw_stacktrace[i] = nullptr;
84 #endif
85 }
86 
87 
88 
90  :
91  file(exc.file),
92  line(exc.line),
93  function(exc.function),
94  cond(exc.cond),
95  exc(exc.exc),
96  stacktrace (nullptr), // don't copy stacktrace to avoid double de-allocation problem
97  n_stacktrace_frames (0),
98  what_str("") // don't copy the error message, it gets generated dynamically by what()
99 {
100 #ifdef DEAL_II_HAVE_GLIBC_STACKTRACE
101  for (unsigned int i=0; i<sizeof(raw_stacktrace)/sizeof(raw_stacktrace[0]); ++i)
102  raw_stacktrace[i] = nullptr;
103 #endif
104 }
105 
106 
107 
109 {
110  free (stacktrace); // free(NULL) is allowed
111  stacktrace = nullptr;
112 }
113 
114 
115 
116 void ExceptionBase::set_fields (const char *f,
117  const int l,
118  const char *func,
119  const char *c,
120  const char *e)
121 {
122  file = f;
123  line = l;
124  function = func;
125  cond = c;
126  exc = e;
127 
128  // If the system supports this, get a stacktrace how we got here:
129  // Note that we defer the symbol lookup done by backtrace_symbols()
130  // to when we need it (see what() below). This is for performance
131  // reasons, as this requires loading libraries and can take in the
132  // order of seconds on some machines.
133 #ifdef DEAL_II_HAVE_GLIBC_STACKTRACE
134  n_stacktrace_frames = backtrace(raw_stacktrace, 25);
135 #endif
136 }
137 
138 const char *ExceptionBase::what() const noexcept
139 {
140  // If no error c_string was generated so far, do it now:
141  if (what_str == "")
142  {
143 #ifdef DEAL_II_HAVE_GLIBC_STACKTRACE
144  // We have deferred the symbol lookup to this point to avoid costly
145  // runtime penalties due to linkage of external libraries by
146  // backtrace_symbols.
147 
148  // first delete old stacktrace if necessary
149  free (stacktrace); // free(NULL) is allowed
150  stacktrace = backtrace_symbols(raw_stacktrace, n_stacktrace_frames);
151 #endif
152 
154  }
155 
156  return what_str.c_str();
157 }
158 
159 
160 const char *ExceptionBase::get_exc_name () const
161 {
162  return exc;
163 }
164 
165 
166 
167 void ExceptionBase::print_exc_data (std::ostream &out) const
168 {
169  // print a header for the exception
170  out << "An error occurred in line <" << line
171  << "> of file <" << file
172  << "> in function" << std::endl
173  << " " << function << std::endl
174  << "The violated condition was: "<< std::endl
175  << " " << cond << std::endl;
176 
177  // print the way the additional information message was generated.
178  // this is useful if the names of local variables appear in the
179  // generation of the error message, because it allows the identification
180  // of parts of the error text with what variables may have cause this
181  //
182  // On the other hand, this is almost never the case for ExcMessage
183  // exceptions which would simply print the same text twice: once for
184  // the way the message was composed, and once for the additional
185  // information. Furthermore, the former of these two is often spread
186  // between numerous "..."-enclosed strings that the preprocessor
187  // collates into a single string, making it awkward to read. Consequently,
188  // elide this text if the message was generated via an ExcMessage object
189  if (std::strstr(cond, "::ExcMessage") != nullptr)
190  out << "The name and call sequence of the exception was:" << std::endl
191  << " " << exc << std::endl;
192 
193  // finally print the additional information the exception provides:
194  out << "Additional information: " << std::endl;
195 }
196 
197 
198 
199 void ExceptionBase::print_info (std::ostream &out) const
200 {
201  out << " (none)" << std::endl;
202 }
203 
204 
205 
206 void ExceptionBase::print_stack_trace (std::ostream &out) const
207 {
208  if (n_stacktrace_frames == 0)
209  return;
210 
211  if (deal_II_exceptions::show_stacktrace == false)
212  return;
213 
214  // if there is a stackframe stored, print it
215  out << std::endl;
216  out << "Stacktrace:" << std::endl
217  << "-----------" << std::endl;
218 
219  // print the stacktrace. first omit all those frames that have
220  // ExceptionBase or deal_II_exceptions in their names, as these
221  // correspond to the exception raising mechanism themselves, rather than
222  // the place where the exception was triggered
223  int frame = 0;
224  while ((frame < n_stacktrace_frames)
225  &&
226  ((std::string(stacktrace[frame]).find ("ExceptionBase") != std::string::npos)
227  ||
228  (std::string(stacktrace[frame]).find ("deal_II_exceptions") != std::string::npos)))
229  ++frame;
230 
231  // output the rest
232  const unsigned int first_significant_frame = frame;
233  for (; frame < n_stacktrace_frames; ++frame)
234  {
235  out << '#' << frame - first_significant_frame
236  << " ";
237 
238  // the stacktrace frame is actually of the format
239  // "filename(functionname+offset) [address]". let's try to get the
240  // mangled functionname out:
241  std::string stacktrace_entry (stacktrace[frame]);
242  const unsigned int pos_start = stacktrace_entry.find('('),
243  pos_end = stacktrace_entry.find('+');
244  std::string functionname = stacktrace_entry.substr (pos_start+1,
245  pos_end-pos_start-1);
246 
247  stacktrace_entry = stacktrace_entry.substr(0, pos_start);
248  stacktrace_entry += ": ";
249 
250  // demangle, and if successful replace old mangled string by
251  // unmangled one (skipping address and offset). treat "main"
252  // differently, since it is apparently demangled as "unsigned int"
253  // for unknown reasons :-) if we can, demangle the function name
254 #ifdef DEAL_II_HAVE_LIBSTDCXX_DEMANGLER
255  int status;
256  char *p = abi::__cxa_demangle(functionname.c_str(), nullptr, nullptr, &status);
257 
258  if ((status == 0) && (functionname != "main"))
259  {
260  std::string realname(p);
261  // in MT mode, one often gets backtraces spanning several lines
262  // because we have so many boost::tuple arguments in the MT
263  // calling functions. most of the trailing arguments of these
264  // tuples are actually unused boost::tuples::null_type, so we
265  // should split them off if they are trailing a template argument
266  // list
267  while (realname.find (", boost::tuples::null_type>")
268  != std::string::npos)
269  realname.erase (realname.find (", boost::tuples::null_type>"),
270  std::string (", boost::tuples::null_type").size());
271 
272  stacktrace_entry += realname;
273  }
274  else
275  stacktrace_entry += functionname;
276 
277  free (p);
278 
279 #else
280 
281  stacktrace_entry += functionname;
282 #endif
283 
284  // then output what we have
285  out << stacktrace_entry
286  << std::endl;
287 
288  // stop if we're in main()
289  if (functionname == "main")
290  break;
291  }
292 }
293 
294 
295 
297 {
298  // build up a c_string with the error message.
299  // Guard this procedure with a try block, we shall not throw at this
300  // place...
301  try
302  {
303  std::ostringstream converter;
304 
305  converter << std::endl
306  << "--------------------------------------------------------"
307  << std::endl;
308 
309  // print out general data
310  print_exc_data (converter);
311  // print out exception specific data
312  print_info (converter);
313  print_stack_trace (converter);
314 
315  if (!deal_II_exceptions::additional_assert_output.empty())
316  {
317  converter << "--------------------------------------------------------"
318  << std::endl
319  << deal_II_exceptions::additional_assert_output
320  << std::endl;
321  }
322 
323  converter << "--------------------------------------------------------"
324  << std::endl;
325 
326  what_str = converter.str();
327  }
328  catch (...)
329  {
330  // On error, resume next. There is nothing better we can do...
331  what_str = "ExceptionBase::generate_message () failed";
332  }
333 }
334 
335 
336 
337 #ifdef DEAL_II_WITH_MPI
338 namespace StandardExceptions
339 {
340  ExcMPI::ExcMPI (const int error_code)
341  :
342  error_code (error_code)
343  {}
344 
345  void ExcMPI::print_info (std::ostream &out) const
346  {
347  char error_name[MPI_MAX_ERROR_STRING];
348  error_name[0] = '\0';
349  int resulting_length = MPI_MAX_ERROR_STRING;
350 
351  bool error_name_known = false;
352  // workaround for Open MPI 1.6.5 not accepting
353  // MPI_ERR_LASTCODE in MPI_Error_class
354  if (error_code < MPI_ERR_LASTCODE)
355  {
356  // get the string name of the error code by first converting it to an
357  // error class.
358  int error_class = 0;
359  int ierr = MPI_Error_class (error_code, &error_class);
360  error_name_known = (ierr == MPI_SUCCESS);
361 
362  // Check the output of the error printing functions. If either MPI
363  // function fails we should just print a less descriptive message.
364  if (error_name_known)
365  {
366  ierr = MPI_Error_string (error_class, error_name, &resulting_length);
367  error_name_known = error_name_known && (ierr == MPI_SUCCESS);
368  }
369  }
370 
371  out << "deal.II encountered an error while calling an MPI function."
372  << std::endl;
373  if (error_name_known)
374  {
375  out << "The description of the error provided by MPI is \""
376  << error_name
377  << "\"."
378  << std::endl;
379  }
380  else
381  {
382  out << "This error code is not equal to any of the standard MPI error codes."
383  << std::endl;
384  }
385  out << "The numerical value of the original error code is "
386  << error_code
387  << "."
388  << std::endl;
389  }
390 }
391 #endif // DEAL_II_WITH_MPI
392 
393 namespace
394 {
395  [[noreturn]]
396  void internal_abort (const ExceptionBase &exc) noexcept
397  {
398  // first print the error
399  std::cerr << exc.what() << std::endl;
400 
401  // then bail out. if in MPI mode, bring down the entire
402  // house by asking the MPI system to do a best-effort
403  // operation at also terminating all of the other MPI
404  // processes. this is useful because if only one process
405  // runs into an assertion, then that may lead to deadlocks
406  // if the others don't recognize this, or at the very least
407  // delay their termination until they realize that their
408  // communication with the job that died times out.
409  //
410  // Unlike std::abort(), MPI_Abort() unfortunately doesn't break when
411  // running inside a debugger like GDB, so only use this strategy if
412  // absolutely necessary and inform the user how to use a debugger.
413 #ifdef DEAL_II_WITH_MPI
414  int is_initialized;
415  MPI_Initialized(&is_initialized);
416  if (is_initialized)
417  {
418  // do the same as in Utilities::MPI::n_mpi_processes() here,
419  // but without error checking to not throw again.
420  int n_proc=1;
421  MPI_Comm_size (MPI_COMM_WORLD, &n_proc);
422  if (n_proc>1)
423  {
424  std::cerr << "Calling MPI_Abort now.\n"
425  << "To break execution in a GDB session, execute 'break MPI_Abort' before "
426  << "running. You can also put the following into your ~/.gdbinit:\n"
427  << " set breakpoint pending on\n"
428  << " break MPI_Abort\n"
429  << " set breakpoint pending auto" << std::endl;
430 
431  MPI_Abort (MPI_COMM_WORLD,
432  /* return code = */ 255);
433  }
434  }
435 #endif
436  std::abort();
437  }
438 }
439 
440 namespace deal_II_exceptions
441 {
442  namespace internals
443  {
444  void do_issue_error_nothrow (const ExceptionBase &exc) noexcept
445  {
446  if (::deal_II_exceptions::abort_on_exception)
447  internal_abort(exc);
448  else
449  {
450  // We are not allowed to throw, and not allowed to abort.
451  // Just print the exception name to deallog and continue normally:
452  deallog << "Exception: " << exc.get_exc_name() << std::endl;
453  deallog << exc.what() << std::endl;
454  }
455  }
456 
457 
458 
459  [[noreturn]]
460  void abort (const ExceptionBase &exc)
461  {
462  if (::deal_II_exceptions::abort_on_exception)
463  internal_abort(exc);
464  else
465  {
466  // We are not allowed to abort, so just throw the error:
467  throw exc;
468  }
469  }
470 
471 
472 
473 #ifdef DEAL_II_WITH_CUDA
474  std::string get_cusparse_error_string(const cusparseStatus_t error_code)
475  {
476  switch (error_code)
477  {
478  case CUSPARSE_STATUS_NOT_INITIALIZED:
479  {
480  return "The cuSPARSE library was not initialized";
481  }
482  case CUSPARSE_STATUS_ALLOC_FAILED:
483  {
484  return "Resource allocation failed inside the cuSPARSE library";
485  }
486  case CUSPARSE_STATUS_INVALID_VALUE:
487  {
488  return "An unsupported value of parameter was passed to the function";
489  }
490  case CUSPARSE_STATUS_ARCH_MISMATCH:
491  {
492  return "The function requires a feature absent from the device architecture";
493  }
494  case CUSPARSE_STATUS_MAPPING_ERROR:
495  {
496  return "An access to GPU memory space failed";
497  }
498  case CUSPARSE_STATUS_EXECUTION_FAILED:
499  {
500  return "The GPU program failed to execute";
501  }
502  case CUSPARSE_STATUS_INTERNAL_ERROR:
503  {
504  return "An internal cuSPARSE operation failed";
505  }
506  case CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED:
507  {
508  return "The matrix type is not supported by this function";
509  }
510  default:
511  {
512  return "Unknown error";
513  }
514  }
515  }
516 
517 
518 
519  std::string get_cusolver_error_string(cusolverStatus_t error_code)
520  {
521  std::string message;
522  switch (error_code)
523  {
524  case CUSOLVER_STATUS_NOT_INITIALIZED:
525  {
526  return "The cuSolver library was not initialized";
527  }
528  case CUSOLVER_STATUS_ALLOC_FAILED:
529  {
530  return "Resource allocation failed inside the cuSolver library";
531  }
532  case CUSOLVER_STATUS_INVALID_VALUE:
533  {
534  return "An unsupported value of a parameter was passed to the function";
535  }
536  case CUSOLVER_STATUS_ARCH_MISMATCH:
537  {
538  return "The function requires a feature absent from the device architecture";
539  }
540  case CUSOLVER_STATUS_EXECUTION_FAILED:
541  {
542  return "The GPU program failed to execute";
543  }
544  case CUSOLVER_STATUS_INTERNAL_ERROR:
545  {
546  return "An internal cuSolver operation failed";
547  }
548  case CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED:
549  {
550  return "The matrix type is not supported by this function";
551  }
552  default:
553  {
554  return "Unknown error";
555  }
556  }
557  }
558 #endif
559 
560  } /*namespace internals*/
561 } /*namespace deal_II_exceptions*/
562 
563 
564 
565 DEAL_II_NAMESPACE_CLOSE
566 
567 
568 
569 // Newer versions of gcc have a very nice feature: you can set a verbose
570 // terminate handler, that not only aborts a program when an exception is
571 // thrown and not caught somewhere, but before aborting it prints that an
572 // exception has been thrown, and possibly what the std::exception::what()
573 // function has to say. Since many people run into the trap of not having a
574 // catch clause in main(), they wonder where that abort may be coming from.
575 // The terminate handler then at least says what is missing in their
576 // program.
577 #ifdef DEAL_II_HAVE_VERBOSE_TERMINATE
578 namespace __gnu_cxx
579 {
580  extern void __verbose_terminate_handler ();
581 }
582 
583 namespace
584 {
585  struct preload_terminate_dummy
586  {
587  preload_terminate_dummy()
588  {
589  std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
590  }
591  };
592 
593  static preload_terminate_dummy dummy;
594 }
595 #endif
virtual const char * what() const noexcept
Definition: exceptions.cc:138
void suppress_stacktrace_in_exceptions()
Definition: exceptions.cc:54
std::string get_cusolver_error_string(const cusolverStatus_t error_code)
Definition: exceptions.cc:519
void set_additional_assert_output(const char *const p)
Definition: exceptions.cc:47
void * raw_stacktrace[25]
Definition: exceptions.h:154
virtual void print_info(std::ostream &out) const
Definition: exceptions.cc:199
void set_fields(const char *file, const int line, const char *function, const char *cond, const char *exc_name)
Definition: exceptions.cc:116
void do_issue_error_nothrow(const ExceptionBase &e) noexcept
Definition: exceptions.cc:444
std::string get_cusparse_error_string(const cusparseStatus_t error_code)
Definition: exceptions.cc:474
std::string what_str
Definition: exceptions.h:167
unsigned int line
Definition: exceptions.h:121
void print_stack_trace(std::ostream &out) const
Definition: exceptions.cc:206
const char * exc
Definition: exceptions.h:136
const char * cond
Definition: exceptions.h:131
const char * file
Definition: exceptions.h:116
void abort(const ExceptionBase &exc)
Definition: exceptions.cc:460
void print_exc_data(std::ostream &out) const
Definition: exceptions.cc:167
void generate_message() const
Definition: exceptions.cc:296
virtual ~ExceptionBase() noexcept
Definition: exceptions.cc:108
char ** stacktrace
Definition: exceptions.h:142
void disable_abort_on_exception()
Definition: exceptions.cc:61
const char * get_exc_name() const
Definition: exceptions.cc:160
int n_stacktrace_frames
Definition: exceptions.h:148