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