Skip to content

Commit

Permalink
Merge pull request #336 from mabruzzo/error-macro
Browse files Browse the repository at this point in the history
Introduce new ``ERROR`` and ``ASSERT`` macros
  • Loading branch information
evaneschneider authored Sep 22, 2023
2 parents 9209462 + 71727de commit ac06fe6
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 19 deletions.
4 changes: 3 additions & 1 deletion src/global/global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

#include <set>

#include "../io/io.h" //defines chprintf
#include "../io/io.h" //defines chprintf
#include "../utils/error_handling.h" // defines ASSERT

/* Global variables */
Real gama; // Ratio of specific heats
Expand All @@ -33,6 +34,7 @@ void Set_Gammas(Real gamma_in)
{
// set gamma
gama = gamma_in;
CHOLLA_ASSERT(gama > 1.0, "Gamma must be greater than one.");
}

/*! \fn double get_time(void)
Expand Down
5 changes: 2 additions & 3 deletions src/io/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2788,11 +2788,10 @@ void Ensure_Outdir_Exists(std::string outdir)
// to a directory (it's unclear from docs whether err-code is set in that
// case)
if (err_code or not std::filesystem::is_directory(without_file_prefix)) {
chprintf(
CHOLLA_ERROR(
"something went wrong while trying to create the path to the "
"output-dir: %s\n",
"output-dir: %s",
outdir.c_str());
chexit(1);
}
}
}
Expand Down
84 changes: 71 additions & 13 deletions src/utils/error_handling.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#include "../utils/error_handling.h"

#include <cassert>
#include <cstdarg>
#include <cstdio>
#include <iostream>
#include <string>

#ifdef MPI_CHOLLA
#include <mpi.h>
void chexit(int code)
#include "../mpi/mpi_routines.h"
[[noreturn]] void chexit(int code)
{
if (code == 0) {
/*exit normally*/
Expand All @@ -20,14 +22,14 @@ void chexit(int code)
}
}
#else /*MPI_CHOLLA*/
void chexit(int code)
[[noreturn]] void chexit(int code)
{
/*exit using code*/
exit(code);
}
#endif /*MPI_CHOLLA*/

void Check_Configuration(parameters const &P)
void Check_Configuration(parameters const& P)
{
// General Checks
// ==============
Expand Down Expand Up @@ -56,13 +58,13 @@ void Check_Configuration(parameters const &P)
#endif // Only one integrator check

// Check the boundary conditions
auto Check_Boundary = [](int const &boundary, std::string const &direction) {
auto Check_Boundary = [](int const& boundary, std::string const& direction) {
bool is_allowed_bc = boundary >= 0 and boundary <= 4;
std::string const error_message =
"WARNING: Possibly invalid boundary conditions for direction: " + direction +
" flag: " + std::to_string(boundary) +
". Must select between 0 (no boundary), 1 (periodic), 2 (reflective), 3 (transmissive), 4 (custom), 5 (mpi).";
assert(is_allowed_bc && error_message.c_str());
CHOLLA_ASSERT(is_allowed_bc,
"WARNING: Possibly invalid boundary conditions for direction: %s flag: %d. Must "
"select between 0 (no boundary), 1 (periodic), 2 (reflective), 3 (transmissive), "
"4 (custom), 5 (mpi).",
direction.c_str(), boundary);
};
Check_Boundary(P.xl_bcnd, "xl_bcnd");
Check_Boundary(P.xu_bcnd, "xu_bcnd");
Expand All @@ -82,9 +84,6 @@ void Check_Configuration(parameters const &P)
#endif //! PRECISION
static_assert(PRECISION == 2, "PRECISION must be 2. Single precision is not currently supported");

// Check that gamma, the ratio of specific heats, is greater than 1
assert(::gama <= 1.0 and "Gamma must be greater than one.");

// MHD Checks
// ==========
#ifdef MHD
Expand Down Expand Up @@ -126,3 +125,62 @@ void Check_Configuration(parameters const &P)

#endif // MHD
}

// NOLINTNEXTLINE(cert-dcl50-cpp)
[[noreturn]] void Abort_With_Err_(const char* func_name, const char* file_name, int line_num, const char* msg, ...)
{
// considerations when using MPI:
// - all processes must execute this function to catch errors that happen on
// just one process
// - to handle cases where all processes encounter the same error, we
// pre-buffer the error message (so that the output remains legible)

// since we are aborting, it's OK that this isn't the most optimized

// prepare some info for the error message header
const char* santized_func_name = (func_name == nullptr) ? "{unspecified}" : func_name;

#ifdef MPI_CHOLLA
std::string proc_info = std::to_string(procID) + " / " + std::to_string(nproc) + " (using MPI)";
#else
std::string proc_info = "0 / 1 (NOT using MPI)";
#endif

// prepare the formatted message
std::string msg_buf;
if (msg == nullptr) {
msg_buf = "{nullptr encountered instead of error message}";
} else {
std::va_list args, args_copy;
va_start(args, msg);
va_copy(args_copy, args);

std::size_t bufsize_without_terminator = std::vsnprintf(nullptr, 0, msg, args);
va_end(args);

// NOTE: starting in C++17 it's possible to mutate msg_buf by mutating msg_buf.data()

// we initialize a msg_buf with size == bufsize_without_terminator (filled with ' ' chars)
// - msg_buf.data() returns a ptr with msg_buf.size() + 1 characters. We are allowed to
// mutate any of the first msg_buf.size() characters. The entry at
// msg_buf.data()[msg_buf.size()] is initially '\0' (& it MUST remain equal to '\0')
// - the 2nd argument of std::vsnprintf is the size of the output buffer. We NEED to
// include the terminator character in this argument, otherwise the formatted message
// will be truncated
msg_buf = std::string(bufsize_without_terminator, ' ');
std::vsnprintf(msg_buf.data(), bufsize_without_terminator + 1, msg, args_copy);
va_end(args_copy);
}

// now write the error and exit
std::fprintf(stderr,
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
"Error occurred in %s on line %d\n"
"Function: %s\n"
"Rank: %s\n"
"Message: %s\n"
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n",
file_name, line_num, santized_func_name, proc_info.data(), msg_buf.data());
std::fflush(stderr); // may be unnecessary for stderr
chexit(1);
}
61 changes: 59 additions & 2 deletions src/utils/error_handling.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,69 @@
#include <stdlib.h>

#include "../global/global.h"
void chexit(int code);
[[noreturn]] void chexit(int code);

/*!
* \brief Check that the Cholla configuration and parameters don't have any significant errors. Mostly compile time
* checks.
*
*/
void Check_Configuration(parameters const &P);
void Check_Configuration(parameters const& P);

/*!
* \brief helper function that prints an error message & aborts the program (in
* an MPI-safe way). Commonly invoked through a macro.
*
*/
[[noreturn]] void Abort_With_Err_(const char* func_name, const char* file_name, int line_num, const char* msg, ...);

/* __CHOLLA_PRETTY_FUNC__ is a magic constant like __LINE__ or __FILE__ that
* provides the name of the current function.
* - The C++11 standard requires that __func__ is provided on all platforms, but
* that only provides limited information (just the name of the function).
* - Where available, we prefer to use compiler-specific features that provide
* more information about the function (like the scope of the function & the
* the function signature).
*/
#ifdef __GNUG__
#define __CHOLLA_PRETTY_FUNC__ __PRETTY_FUNCTION__
#else
#define __CHOLLA_PRETTY_FUNC__ __func__
#endif

/*!
* \brief print an error-message (with printf formatting) & abort the program.
*
* This macro should be treated as a function with the signature:
* [[noreturn]] void CHOLLA_ERROR(const char* msg, ...);
*
* - The 1st arg is printf-style format argument specifying the error message
* - The remaining args arguments are used to format error message
*
* \note
* the ``msg`` string is part of the variadic args so that there is always
* at least 1 variadic argument (even in cases when ``msg`` doesn't format
* any arguments). There is no way around this until C++ 20.
*/
#define CHOLLA_ERROR(...) Abort_With_Err_(__CHOLLA_PRETTY_FUNC__, __FILE__, __LINE__, __VA_ARGS__)

/*!
* \brief if the condition is false, print an error-message (with printf
* formatting) & abort the program.
*
* This macro should be treated as a function with the signature:
* [[noreturn]] void CHOLLA_ASSERT(bool cond, const char* msg, ...);
*
* - The 1st arg is a boolean condition. When true, this does noth
* - The 2nd arg is printf-style format argument specifying the error message
* - The remaining args arguments are used to format error message
*
* \note
* the behavior is independent of the ``NDEBUG`` macro
*/
#define CHOLLA_ASSERT(cond, ...) \
if (not(cond)) { \
Abort_With_Err_(__CHOLLA_PRETTY_FUNC__, __FILE__, __LINE__, __VA_ARGS__); \
}

#endif /*ERROR_HANDLING_CHOLLA_H*/

0 comments on commit ac06fe6

Please sign in to comment.