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