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