1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "error.hpp"
13 #include "globals.hpp"
14 #include "array.hpp"
15 #include <cstdlib>
16 #include <iostream>
17 
18 #ifdef MFEM_USE_LIBUNWIND
19 #define UNW_LOCAL_ONLY
20 #define UNW_NAME_LEN 512
21 #include <libunwind.h>
22 #include <cxxabi.h>
23 #if defined(__APPLE__) || defined(__linux__)
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27 #include <dlfcn.h>
28 #endif
29 #endif // MFEM_USE_LIBUNWIND
30 
31 #ifdef MFEM_USE_MPI
32 #include <mpi.h>
33 #endif
34 
35 namespace mfem
36 {
37 
38 #ifdef MFEM_USE_EXCEPTIONS
what() const39 const char* ErrorException::what() const throw()
40 {
41    return msg.c_str();
42 }
43 
44 static ErrorAction mfem_error_action = MFEM_ERROR_THROW;
45 #else
46 static ErrorAction mfem_error_action = MFEM_ERROR_ABORT;
47 #endif
48 
set_error_action(ErrorAction action)49 void set_error_action(ErrorAction action)
50 {
51    // Check if 'action' is valid.
52    switch (action)
53    {
54       case MFEM_ERROR_ABORT: break;
55       case MFEM_ERROR_THROW:
56 #ifdef MFEM_USE_EXCEPTIONS
57          break;
58 #else
59          mfem_error("set_error_action: MFEM_ERROR_THROW requires the build "
60                     "option MFEM_USE_EXCEPTIONS=YES");
61          return;
62 #endif
63       default:
64          mfem::err << "\n\nset_error_action: invalid action: " << action
65                    << '\n';
66          mfem_error();
67          return;
68    }
69    mfem_error_action = action;
70 }
71 
get_error_action()72 ErrorAction get_error_action()
73 {
74    return mfem_error_action;
75 }
76 
77 namespace internal
78 {
79 // defined in globals.cpp
80 extern bool mfem_out_initialized, mfem_err_initialized;
81 }
82 
mfem_backtrace(int mode,int depth)83 void mfem_backtrace(int mode, int depth)
84 {
85 #ifdef MFEM_USE_LIBUNWIND
86    char name[UNW_NAME_LEN];
87    unw_cursor_t cursor;
88    unw_context_t uc;
89    unw_word_t ip, offp;
90    std::ostream &merr = internal::mfem_err_initialized ? mfem::err : std::cerr;
91 
92    int err = unw_getcontext(&uc);
93    err = err ? err : unw_init_local(&cursor, &uc);
94 
95    Array<unw_word_t> addrs(MemoryType::HOST);
96    while (unw_step(&cursor) > 0 && addrs.Size() != depth)
97    {
98       err = err ? err : unw_get_proc_name(&cursor, name, UNW_NAME_LEN, &offp);
99       err = err ? err : unw_get_reg(&cursor, UNW_REG_IP, &ip);
100       if (err) { break; }
101       char *name_p = name;
102       int demangle_status;
103 
104       // __cxa_demangle is not standard, but works with GCC, Intel, PGI, Clang
105       char *name_demangle =
106          abi::__cxa_demangle(name, NULL, NULL, &demangle_status);
107       if (demangle_status == 0) // use mangled name if something goes wrong
108       {
109          name_p = name_demangle;
110       }
111 
112       merr << addrs.Size() << ") [0x" << std::hex << ip - 1 << std::dec
113            << "]: " << name_p << std::endl;
114       addrs.Append(ip - 1);
115 
116       if (demangle_status == 0)
117       {
118          free(name_demangle);
119       }
120    }
121 #if defined(__APPLE__) || defined(__linux__)
122    if (addrs.Size() > 0 && (mode & 1))
123    {
124       merr << "\nLookup backtrace source lines:";
125       const char *fname = NULL;
126       for (int i = 0; i < addrs.Size(); i++)
127       {
128          Dl_info info;
129          err = !dladdr((void*)addrs[i], &info);
130          if (err)
131          {
132             fname = "<exe>";
133          }
134          else if (fname != info.dli_fname)
135          {
136             fname = info.dli_fname;
137             merr << '\n';
138 #ifdef __linux__
139             merr << "addr2line -C -e " << fname;
140 #else
141             merr << "atos -o " << fname << " -l "
142                  << (err ? 0 : info.dli_fbase);
143 #endif
144          }
145          merr << " 0x" << std::hex << addrs[i] << std::dec;
146       }
147       merr << '\n';
148    }
149 #endif
150 #endif // MFEM_USE_LIBUNWIND
151 }
152 
mfem_error(const char * msg)153 void mfem_error(const char *msg)
154 {
155    std::ostream &merr = internal::mfem_err_initialized ? mfem::err : std::cerr;
156    if (msg)
157    {
158       // NOTE: By default, each call of the "operator <<" method of the
159       // mfem::err object results in flushing the I/O stream, which can be a
160       // very bad thing if all your processors try to do it at the same time.
161       merr << "\n\n" << msg << "\n";
162    }
163 
164 #ifdef MFEM_USE_LIBUNWIND
165    merr << "Backtrace:" << std::endl;
166    mfem_backtrace(1, -1);
167    merr << std::endl;
168 #endif
169 
170 #ifdef MFEM_USE_EXCEPTIONS
171    if (mfem_error_action == MFEM_ERROR_THROW)
172    {
173       throw ErrorException(msg);
174    }
175 #endif
176 
177 #ifdef MFEM_USE_MPI
178    int init_flag, fin_flag;
179    MPI_Initialized(&init_flag);
180    MPI_Finalized(&fin_flag);
181    if (init_flag && !fin_flag) { MPI_Abort(GetGlobalMPI_Comm(), 1); }
182 #endif
183    std::abort(); // force crash by calling abort
184 }
185 
mfem_warning(const char * msg)186 void mfem_warning(const char *msg)
187 {
188    std::ostream &mout = internal::mfem_out_initialized ? mfem::out : std::cout;
189    if (msg)
190    {
191       mout << "\n\n" << msg << std::endl;
192    }
193 }
194 
195 }
196