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