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