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

Better stacktraces in ioda #14

Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions src/engines/ioda/include/ioda/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class IODA_DL Exception : virtual public std::exception {
mutable std::string emessage_;
void invalidate();
void add_source_location(const ::ioda::source_location& loc);
void add_call_stack();

public:
Exception(const ::ioda::source_location& loc = source_location::current(),
Expand Down
63 changes: 62 additions & 1 deletion src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "./HH/Handles.h"
#include "./HH/HH-attributes.h"
#include "./HH/HH-variables.h"
#include "ioda/Misc/compat/std/source_location_compat.h"
#include "ioda/Exception.h"

namespace ioda {
Expand All @@ -37,7 +38,7 @@ herr_t iterate_find_attr(hid_t loc_id, const char* name, const H5A_info_t* info,
#else
herr_t iterate_find_attr(hid_t loc_id, const char* name, void* op_data) {
H5A_info_t in_info;
H5Aget_info_by_name(loc_id, ".", name, &in_info, H5P_DEFAULT);)
H5Aget_info_by_name(loc_id, ".", name, &in_info, H5P_DEFAULT);
H5A_info_t* info = &in_info;
#endif

Expand Down Expand Up @@ -261,6 +262,66 @@ void attr_update_reference_list(HH_Variable* scale, const std::vector<ds_list_t>
throw Exception("Cannot write REFERENCE_LIST attribute.", ioda_Here());
}


/// @brief Data passed around when iterating over the HDF5 error stack.
struct NestedExceptions {
std::list<std::shared_ptr<Exception>> exceptions;
};

/// @brief Iteration function for the HDF5 error stack.
herr_t hdf5_h5e_walk2(unsigned /*n*/, const H5E_error2_t *err_desc, void *client_data) {
try {
if (!err_desc || !client_data) return -1;
NestedExceptions *err_data = static_cast<NestedExceptions *>(client_data);

detail::compat::source_location::source_location
hdf_here(err_desc->line, 0, err_desc->file_name, err_desc->func_name);
err_data->exceptions.emplace_back(
std::make_shared<Exception>(err_desc->desc, hdf_here));

return 0;
} catch (...) {
return -1;
}
}

void hdf5_error_stack_recursively_reconstruct(
std::list<std::shared_ptr<Exception>> &exceptions) {
std::shared_ptr<Exception> cur;
bool throwing_cur = false;
try {
if (!exceptions.size()) return; // Just in case.

cur = exceptions.back();
exceptions.pop_back();

if (exceptions.size()) hdf5_error_stack_recursively_reconstruct(exceptions);

throwing_cur = true;
throw(*cur);
} catch (...) {
if (throwing_cur) std::rethrow_exception(std::current_exception());
std::throw_with_nested(*cur);
}
}

void hdf5_error_check() {
try {
ssize_t num_errs = H5Eget_num(H5E_DEFAULT);
if (num_errs < 0) throw Exception("Cannot read HDF5 error stack", ioda_Here());
if (num_errs == 0) return;

NestedExceptions err_data;

herr_t res = H5Ewalk2(H5E_DEFAULT, H5E_WALK_DOWNWARD, hdf5_h5e_walk2, &err_data);
if (res < 0) throw Exception("Cannot read HDF5 error stack", ioda_Here());

hdf5_error_stack_recursively_reconstruct(err_data.exceptions);
} catch (...) {
std::throw_with_nested(Exception("An HDF5 error was encountered.", ioda_Here()));
}
}

std::string getNameFromIdentifier(hid_t obj_id) {
ssize_t sz = H5Iget_name(obj_id, nullptr, 0);
if (sz < 0) throw Exception("Cannot get object name", ioda_Here());
Expand Down
5 changes: 5 additions & 0 deletions src/engines/ioda/src/ioda/Engines/HH/HH/HH-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ struct IODA_HIDDEN Vlen_data {
hvl_t& operator[](size_t idx) { return (buf).get()[idx]; }
};

/// @brief Check for any HDF5-related errors and encapsulate these errors as an exception.
/// @throws ioda::Exception if any exception was detected. The contents of the exception will
/// contain the HDF5 error stack.
IODA_HIDDEN void hdf5_error_check();

/// @brief Gets a variable / group / link name from an id. Useful for debugging.
/// @param obj_id is the object.
/// @return One of the possible object names.
Expand Down
8 changes: 8 additions & 0 deletions src/engines/ioda/src/ioda/Exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@
/// \brief Exception classes for IODA

#include "ioda/Exception.h"
#include "oops/util/Stacktrace.h" // in oops/util

namespace ioda {

Exception::Exception(const ::ioda::source_location& loc, const Options& opts) : opts_{opts} {
add_source_location(loc);
add_call_stack();
}

Exception::Exception(const char* msg, const ::ioda::source_location& loc, const Options& opts)
: opts_{opts} {
add("Reason", std::string(msg));
add_source_location(loc);
add_call_stack();
}

Exception::Exception(std::string msg, const ::ioda::source_location& loc, const Options& opts)
: opts_{opts} {
add("Reason", msg);
add_source_location(loc);
add_call_stack();
}

Exception::~Exception() noexcept = default;
Expand All @@ -38,6 +42,10 @@ void Exception::add_source_location(const ::ioda::source_location& loc) {
add("source_column", loc.column());
}

void Exception::add_call_stack() {
add("stacktrace", std::string("\n") + util::stacktrace_current());
}

const char* Exception::what() const noexcept {
try {
if (emessage_.size()) return emessage_.c_str();
Expand Down