deal.II version GIT relicensing-2169-gec1b43f35b 2024-11-22 07:10:00+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 std::string demangled_stacktrace_entry =
289 stacktrace_entry.substr(0, pos_start);
290 demangled_stacktrace_entry += ": ";
291
292 // demangle, and if successful replace old mangled string by
293 // unmangled one (skipping address and offset). treat "main"
294 // differently, since it is apparently demangled as "unsigned int"
295 // for unknown reasons :-) if we can, demangle the function name
296#ifdef DEAL_II_HAVE_LIBSTDCXX_DEMANGLER
297 int status;
298 char *p =
299 abi::__cxa_demangle(functionname.c_str(), nullptr, nullptr, &status);
300
301 if ((status == 0) && (functionname != "main"))
302 {
303 std::string realname(p);
304 // in MT mode, one often gets backtraces spanning several lines
305 // because we have so many boost::tuple arguments in the MT
306 // calling functions. most of the trailing arguments of these
307 // tuples are actually unused boost::tuples::null_type, so we
308 // should split them off if they are trailing a template argument
309 // list
310 while (realname.find(", boost::tuples::null_type>") !=
311 std::string::npos)
312 realname.erase(realname.find(", boost::tuples::null_type>"),
313 std::string(", boost::tuples::null_type").size());
314
315 demangled_stacktrace_entry += realname;
316 }
317 else
318 demangled_stacktrace_entry += functionname;
319
320 free(p);
321
322#else
323
324 demangled_stacktrace_entry += functionname;
325#endif
326
327 // then output what we have
328 out << demangled_stacktrace_entry << std::endl;
329
330 // stop if we're in main()
331 if (functionname == "main")
332 break;
333 }
334
335 free(stacktrace); // free(nullptr) is allowed
336 stacktrace = nullptr;
337}
338
339
340
341void
343{
344 // build up a c_string with the error message.
345 // Guard this procedure with a try block, we shall not throw at this
346 // place...
347 try
348 {
349 std::ostringstream converter;
350
351 converter << std::endl
352 << "--------------------------------------------------------"
353 << std::endl;
354
355 // Print out general data
356 print_exc_data(converter);
357
358 // Print out exception specific data. Put this into another stringstream
359 // object for now so that we can break long lines and print them in a
360 // more easily read way
361 {
362 std::ostringstream message;
363 print_info(message);
364
365 const auto message_in_lines =
366 Utilities::break_text_into_lines(message.str(), 70);
367
368 // Put the message into the stream that will be output.
369 for (const auto &line : message_in_lines)
370 converter << " " << line << '\n';
371 }
372
373
374 print_stack_trace(converter);
375
377 .empty())
378 {
379 converter
380 << "--------------------------------------------------------"
381 << std::endl
383 << std::endl;
384 }
385
386 converter << "--------------------------------------------------------"
387 << std::endl;
388
389 what_str = converter.str();
390 }
391 catch (...)
392 {
393 // On error, resume next. There is nothing better we can do...
394 what_str = "ExceptionBase::generate_message () failed";
395 }
396}
397
398
399
400namespace StandardExceptions
401{
402#ifdef DEAL_II_WITH_MPI
403 ExcMPI::ExcMPI(const int error_code)
404 : error_code(error_code)
405 {}
406
407 void
408 ExcMPI::print_info(std::ostream &out) const
409 {
410 char error_name[MPI_MAX_ERROR_STRING];
411 error_name[0] = '\0';
412 int resulting_length = MPI_MAX_ERROR_STRING;
413
414 bool error_name_known = false;
415 // workaround for Open MPI 1.6.5 not accepting
416 // MPI_ERR_LASTCODE in MPI_Error_class
417 if (error_code < MPI_ERR_LASTCODE)
418 {
419 // get the string name of the error code by first converting it to an
420 // error class.
421 int error_class = 0;
422 int ierr = MPI_Error_class(error_code, &error_class);
423 error_name_known = (ierr == MPI_SUCCESS);
424
425 // Check the output of the error printing functions. If either MPI
426 // function fails we should just print a less descriptive message.
427 if (error_name_known)
428 {
429 ierr = MPI_Error_string(error_class, error_name, &resulting_length);
430 error_name_known = error_name_known && (ierr == MPI_SUCCESS);
431 }
432 }
433
434 out << "deal.II encountered an error while calling an MPI function."
435 << std::endl;
436 if (error_name_known)
437 {
438 out << "The description of the error provided by MPI is \""
439 << error_name << "\"." << std::endl;
440 }
441 else
442 {
443 out
444 << "This error code is not equal to any of the standard MPI error codes."
445 << std::endl;
446 }
447 out << "The numerical value of the original error code is " << error_code
448 << '.' << std::endl;
449 }
450#endif // DEAL_II_WITH_MPI
451
452
453
454#ifdef DEAL_II_TRILINOS_WITH_SEACAS
455 ExcExodusII::ExcExodusII(const int error_code)
456 : error_code(error_code)
457 {
458 // To avoid including yet another header in exceptions.h we assume that
459 // EX_NOERR is zero. Check that here:
460 static_assert(EX_NOERR == 0,
461 "EX_NOERR is assumed to be zero in all versions of ExodusII");
462 }
463
464
465
466 void
467 ExcExodusII::print_info(std::ostream &out) const
468 {
469 out << "Error code is " << error_code << '\n';
470 out << "String description: " << ex_strerror(error_code) << '\n';
471 }
472#endif // DEAL_II_TRILINOS_WITH_SEACAS
473} // namespace StandardExceptions
474
475namespace deal_II_exceptions
476{
477 namespace internals
478 {
479 [[noreturn]] void
480 abort(const ExceptionBase &exc) noexcept
481 {
482 // first print the error
483 std::cerr << exc.what() << std::endl;
484
485 // then bail out. if in MPI mode, bring down the entire
486 // house by asking the MPI system to do a best-effort
487 // operation at also terminating all of the other MPI
488 // processes. this is useful because if only one process
489 // runs into an assertion, then that may lead to deadlocks
490 // if the others don't recognize this, or at the very least
491 // delay their termination until they realize that their
492 // communication with the job that died times out.
493 //
494 // Unlike std::abort(), MPI_Abort() unfortunately doesn't break when
495 // running inside a debugger like GDB, so only use this strategy if
496 // absolutely necessary and inform the user how to use a debugger.
497#ifdef DEAL_II_WITH_MPI
498 int is_initialized;
499 MPI_Initialized(&is_initialized);
500 if (is_initialized != 0)
501 {
502 // do the same as in Utilities::MPI::n_mpi_processes() here,
503 // but without error checking to not throw again.
504 const int n_proc = Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
505 if (n_proc > 1)
506 {
507 std::cerr
508 << "Calling MPI_Abort now.\n"
509 << "To break execution in a GDB session, execute 'break MPI_Abort' before "
510 << "running. You can also put the following into your ~/.gdbinit:\n"
511 << " set breakpoint pending on\n"
512 << " break MPI_Abort\n"
513 << " set breakpoint pending auto" << std::endl;
514
515 MPI_Abort(MPI_COMM_WORLD,
516 /* return code = */ 255);
517 }
518 }
519#endif
520
521 // Let's abort the program here. On the host, we need to call
522 // std::abort, on devices we need to do something different.
523 // Kokkos::abort() does the right thing in all circumstances.
524
525#if KOKKOS_VERSION < 30200
526 if constexpr (std::is_same_v<Kokkos::DefaultExecutionSpace,
527 Kokkos::DefaultHostExecutionSpace>)
528 {
529 // FIXME_KOKKOS Older Kokkos versions don't declare Kokkos::abort as
530 // [[noreturn]]. In case Kokkos is only configured with host backends,
531 // we can just use std::abort instead.
532 std::abort();
533 }
534 else
535#endif
536 {
537 Kokkos::abort(
538 "Abort() was called during dealing with an assertion or exception.");
539 }
540 }
541
542
543
544 void
546 {
548 abort(exc);
549 else
550 {
551 // We are not allowed to throw, and not allowed to abort.
552 // Just print the exception name to deallog and continue normally:
553 deallog << "Exception: " << exc.get_exc_name() << std::endl;
554 deallog << exc.what() << std::endl;
555 }
556 }
557
558 } /*namespace internals*/
559} /*namespace deal_II_exceptions*/
560
561
562
void generate_message() const
void print_stack_trace(std::ostream &out) const
const char * file
Definition exceptions.h:124
std::string what_str
Definition exceptions.h:170
const char * get_exc_name() const
int n_stacktrace_frames
Definition exceptions.h:150
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:144
const char * function
Definition exceptions.h:134
void print_exc_data(std::ostream &out) const
unsigned int line
Definition exceptions.h:129
void * raw_stacktrace[25]
Definition exceptions.h:156
const char * cond
Definition exceptions.h:139
virtual void print_info(std::ostream &out) const override
ExcMPI(const int error_code)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:498
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:499
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
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