Reference documentation for deal.II version 9.5.0
\(\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\}}\)
Loading...
Searching...
No Matches
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
18#include <deal.II/base/mpi.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
46namespace 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),
129 std::begin(raw_stacktrace));
130#endif
131}
132
133
134
135void
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
160const char *
161ExceptionBase::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
172const char *
174{
175 return exc;
176}
177
178
179
180void
181ExceptionBase::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
212void
213ExceptionBase::print_info(std::ostream &out) const
214{
215 out << " (none)" << std::endl;
216}
217
218
219
220void
221ExceptionBase::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
321void
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
380namespace 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
455namespace 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
void print_stack_trace(std::ostream &out) const
const char * file
Definition exceptions.h:123
std::string what_str
Definition exceptions.h:169
const char * get_exc_name() const
int n_stacktrace_frames
Definition exceptions.h:149
void set_fields(const char *file, const int line, const char *function, const char *cond, const char *exc_name)
virtual void print_info(std::ostream &out) const
virtual const char * what() const noexcept override
const char * exc
Definition exceptions.h:143
const char * function
Definition exceptions.h:133
void print_exc_data(std::ostream &out) const
unsigned int line
Definition exceptions.h:128
void * raw_stacktrace[25]
Definition exceptions.h:155
const char * cond
Definition exceptions.h:138
virtual void print_info(std::ostream &out) const override
ExcMPI(const int error_code)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:472
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:473
LogStream deallog
Definition logstream.cc:37
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:150
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)
std::string get_cusparse_error_string(const cusparseStatus_t error_code)
void abort(const ExceptionBase &exc) noexcept
std::string & get_additional_assert_output()
Definition exceptions.cc:51
void do_issue_error_nothrow(const ExceptionBase &e) noexcept
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