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