PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
error_handling.cc
Go to the documentation of this file.
1 /* Copyright (C) 2014, 2015, 2016, 2017, 2021, 2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include "pism/util/error_handling.hh"
21 #include <petsc.h>
22 
23 #include <stdexcept>
24 #include <stdarg.h>
25 
26 namespace pism {
27 
29  : filename(NULL), line_number(0) {
30  // empty
31 }
32 
33 ErrorLocation::ErrorLocation(const char *name, int line)
34  : filename(name), line_number(line) {
35  // empty
36 }
37 
39 
40 RuntimeError::RuntimeError(const ErrorLocation &location, const std::string &message)
41  : std::runtime_error(message), m_location(location) {
42  if (sm_hook != NULL) {
43  sm_hook(this);
44  }
45 }
46 
47 RuntimeError RuntimeError::formatted(const ErrorLocation &location, const char format[], ...) {
48  char buffer[8192];
49  va_list argp;
50 
51  va_start(argp, format);
52  vsnprintf(buffer, sizeof(buffer), format, argp);
53  va_end(argp);
54 
55  return RuntimeError(location, buffer);
56 }
57 
58 void RuntimeError::set_hook(Hook new_hook) {
59  sm_hook = new_hook;
60 }
61 
63  // empty
64 }
65 
66 void RuntimeError::add_context(const std::string &message) {
67  m_context.push_back(message);
68 }
69 
70 void RuntimeError::add_context(const char format[], ...) {
71  char buffer[8192];
72  va_list argp;
73 
74  va_start(argp, format);
75  vsnprintf(buffer, sizeof(buffer), format, argp);
76  va_end(argp);
77 
78  // convert to std::string to avoid recursion
79  this->add_context(std::string(buffer));
80 }
81 
82 void RuntimeError::print(MPI_Comm com) {
83  PetscErrorCode ierr = 0;
84  std::string error = "PISM ERROR: ";
85  std::string message = this->what();
86 
87  std::string padding = std::string(error.size(), ' ');
88 
89  // replace newlines with newlines plus padding
90  size_t k = message.find('\n', 0);
91  while (k != std::string::npos) {
92  message.insert(k+1, padding);
93  k = message.find('\n', k+1);
94  }
95 
96  // print the error message with "PISM ERROR:" in front:
97  ierr = PetscPrintf(com,
98  "%s%s\n", error.c_str(), message.c_str()); CHKERRCONTINUE(ierr);
99 
100  // compute how much padding we need to align things:
101  std::string while_str = std::string(error.size(), ' ') + "while ";
102  padding = std::string(while_str.size() + 1, ' '); // 1 extra space
103 
104  // loop over "context" messages
105  for (const auto &j : m_context) {
106  message = j;
107 
108  // replace newlines with newlines plus padding
109  k = message.find('\n', 0);
110  while (k != std::string::npos) {
111  message.insert(k+1, padding);
112  k = message.find('\n', k+1);
113  }
114 
115  // print a "context" message
116  ierr = PetscPrintf(com,
117  "%s%s\n", while_str.c_str(), message.c_str()); CHKERRCONTINUE(ierr);
118  }
119 
120  if (m_location.filename != NULL) {
121  padding = std::string(error.size(), ' ');
122  ierr = PetscPrintf(com,
123  "%sError location: %s, line %d\n",
124  padding.c_str(), m_location.filename, m_location.line_number); CHKERRCONTINUE(ierr);
125  }
126 }
127 
128 /** Handle fatal PISM errors by printing an informative error message.
129  *
130  * (Since these are fatal there is nothing else that can be done.)
131  *
132  * Should be called from a catch(...) block *only*.
133  */
134 void handle_fatal_errors(MPI_Comm com) {
135  PetscErrorCode ierr;
136  try {
137  throw; // re-throw the current exception
138  }
139  catch (RuntimeError &e) {
140  e.print(com);
141  }
142  catch (std::exception &e) {
143  ierr = PetscPrintf(PETSC_COMM_SELF,
144  "\n"
145  "PISM ERROR: Caught a C++ standard library exception: \"%s\".\n"
146  " This is probably a bug in PISM.\n"
147  " Please send a report to uaf-pism@alaska.edu\n"
148  "\n",
149  e.what()); CHKERRCONTINUE(ierr);
150  } catch (...) {
151  ierr = PetscPrintf(PETSC_COMM_SELF,
152  "\n"
153  "PISM ERROR: Caught an unexpected exception.\n"
154  " This is probably a bug in PISM.\n"
155  " Please send a report to uaf-pism@alaska.edu\n"
156  "\n");
157  CHKERRCONTINUE(ierr);
158  }
159 }
160 
161 void check_c_call(int errcode, int success,
162  const char* function_name, const char *file, int line) {
163  if (errcode != success) {
164  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "External library function %s failed at %s:%d",
165  function_name, file, line);
166  }
167 }
168 
169 void check_petsc_call(int errcode,
170  const char* function_name, const char *file, int line) {
171  // tell PETSc to print the error message
172  CHKERRCONTINUE(errcode);
173  check_c_call(errcode, 0, function_name, file, line);
174 }
175 
177  : m_failed(false), m_com(com) {
178  // empty
179 }
180 
181 //! @brief Indicates a failure of a parallel section.
182 /*!
183  * This should be called from a `catch (...) { ... }` block **only**.
184  */
186  int rank = 0;
187  MPI_Comm_rank(m_com, &rank);
188 
189  PetscFPrintf(MPI_COMM_SELF, stderr,
190  "PISM ERROR: Rank %d failed with the following message.\n", rank);
191 
192  handle_fatal_errors(MPI_COMM_SELF);
193 
194  m_failed = true;
195 }
196 
198  m_failed = false;
199 }
200 
202 #if (Pism_DEBUG==1)
203  int success_flag = m_failed ? 0 : 1;
204  int success_flag_global = 0;
205 
206  MPI_Allreduce(&success_flag, &success_flag_global, 1, MPI_INT, MPI_LAND, m_com);
207 
208  if (success_flag_global == 0) {
209  throw RuntimeError(PISM_ERROR_LOCATION, "Failure in a parallel section. See error messages above for more.");
210  }
211 #endif
212 }
213 
214 } // end of namespace pism
const char * filename
ParallelSection(MPI_Comm com)
void failed()
Indicates a failure of a parallel section.
ErrorLocation m_location
void(* Hook)(RuntimeError *)
RuntimeError(const ErrorLocation &location, const std::string &message)
static RuntimeError static void set_hook(Hook new_hook)
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
std::vector< std::string > m_context
void void print(MPI_Comm com)
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
#define PISM_ERROR_LOCATION
static const double k
Definition: exactTestP.cc:42
void check_c_call(int errcode, int success, const char *function_name, const char *file, int line)
void handle_fatal_errors(MPI_Comm com)
void check_petsc_call(int errcode, const char *function_name, const char *file, int line)