Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce new ERROR and ASSERT macros #336

Merged
merged 6 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
ASSERT(gama > 1.0, "Set_Gammas", "Gamma must be greater than one.");
}

/*! \fn double get_time(void)
Expand Down
9 changes: 4 additions & 5 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(
"something went wrong while trying to create the path to the "
"output-dir: %s\n",
outdir.c_str());
chexit(1);
ERROR("Ensure_Outdir_Exists",
"something went wrong while trying to create the path to the "
"output-dir: %s",
outdir.c_str());
}
}
}
Expand Down
77 changes: 64 additions & 13 deletions src/utils/error_handling.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "../utils/error_handling.h"

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

#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 +23,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 +59,12 @@ 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());
ASSERT(is_allowed_bc, "Check_Configuration",
"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,55 @@ 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;

std::string proc_info =
mabruzzo marked this conversation as resolved.
Show resolved Hide resolved
#ifdef MPI_CHOLLA
std::to_string(procID) + " / " + std::to_string(nproc) + " (using MPI)";
#else
"0 / 1 (NOT using MPI)"
#endif

// prepare the formatted message
std::vector<char> msg_buf;
bcaddy marked this conversation as resolved.
Show resolved Hide resolved
if (msg == nullptr) {
msg_buf = std::vector<char>(80);
bcaddy marked this conversation as resolved.
Show resolved Hide resolved
std::snprintf(msg_buf.data(), msg_buf.size(), "{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 msg_len = std::vsnprintf(nullptr, 0, msg, args) + 1;
va_end(args);

msg_buf = std::vector<char>(msg_len);
std::vsnprintf(msg_buf.data(), msg_len, msg, args);
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);
}
49 changes: 47 additions & 2 deletions src/utils/error_handling.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,57 @@
#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, ...);

/*!
* \brief print an error-message (with printf formatting) & abort the program.
*
* This macro should be treated as a function with the signature:
* [[noreturn]] void ERROR(const char* func_name, const char* msg, ...);
*
* - The 1st arg is the name of the function where it's called
bcaddy marked this conversation as resolved.
Show resolved Hide resolved
* - The 2nd 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 ERROR(func_name, ...) Abort_With_Err_(func_name, __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 ASSERT(bool cond, const char* func_name, const char* msg, ...);
*
* - The 1st arg is a boolean condition. When true, this does noth
* - The 2nd arg is the name of the function where it's called
* - The 3rd 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 ASSERT(cond, func_name, ...) \
if (not(cond)) { \
Abort_With_Err_(func_name, __FILE__, __LINE__, __VA_ARGS__); \
}

#endif /*ERROR_HANDLING_CHOLLA_H*/