From aba2615658a3f0c635aa87a535e8e83ca710e5dc Mon Sep 17 00:00:00 2001 From: Ryan Honeyager Date: Thu, 29 Feb 2024 16:15:42 -0500 Subject: [PATCH 1/2] Better stacktraces in ioda --- src/engines/ioda/include/ioda/Exception.h | 1 + .../ioda/src/ioda/Engines/HH/HH-util.cpp | 68 ++++++++++++++++++- .../ioda/src/ioda/Engines/HH/HH/HH-util.h | 5 ++ src/engines/ioda/src/ioda/Exception.cpp | 8 +++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/engines/ioda/include/ioda/Exception.h b/src/engines/ioda/include/ioda/Exception.h index 0fcfd2f5..f53c2d41 100644 --- a/src/engines/ioda/include/ioda/Exception.h +++ b/src/engines/ioda/include/ioda/Exception.h @@ -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(), diff --git a/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp b/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp index 1b3cfc76..4c02704c 100644 --- a/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp +++ b/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp @@ -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 { @@ -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 @@ -261,6 +262,71 @@ void attr_update_reference_list(HH_Variable* scale, const std::vector throw Exception("Cannot write REFERENCE_LIST attribute.", ioda_Here()); } + +/// @brief Data passed around when iterating over the HDF5 error stack. +struct NestedExceptions { + std::list> 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(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(err_desc->desc, hdf_here)); + + return 0; + } catch (...) { + return -1; + } +} + +void hdf5_error_stack_recursively_reconstruct( + std::list> &exceptions) { + std::shared_ptr 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); + //throw Exception("Top of HDF5 exception stack", ioda_Here()); + //std::rethrow_exception(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 (...) { + // No stack trace: + // std::rethrow_exception(std::current_exception()); + // With stack trace: + 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()); diff --git a/src/engines/ioda/src/ioda/Engines/HH/HH/HH-util.h b/src/engines/ioda/src/ioda/Engines/HH/HH/HH-util.h index 7afe0013..320cbe23 100644 --- a/src/engines/ioda/src/ioda/Engines/HH/HH/HH-util.h +++ b/src/engines/ioda/src/ioda/Engines/HH/HH/HH-util.h @@ -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. diff --git a/src/engines/ioda/src/ioda/Exception.cpp b/src/engines/ioda/src/ioda/Exception.cpp index 0947c9c9..3c52e150 100644 --- a/src/engines/ioda/src/ioda/Exception.cpp +++ b/src/engines/ioda/src/ioda/Exception.cpp @@ -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; @@ -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(); From 9f7bb4859fffbbd8c9509ae1ee2f5d89a3fd0713 Mon Sep 17 00:00:00 2001 From: Ryan Honeyager Date: Thu, 7 Mar 2024 17:02:08 -0500 Subject: [PATCH 2/2] Apply suggestions from code review --- src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp b/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp index 4c02704c..46d5733c 100644 --- a/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp +++ b/src/engines/ioda/src/ioda/Engines/HH/HH-util.cpp @@ -299,8 +299,6 @@ void hdf5_error_stack_recursively_reconstruct( throwing_cur = true; throw(*cur); - //throw Exception("Top of HDF5 exception stack", ioda_Here()); - //std::rethrow_exception(cur); } catch (...) { if (throwing_cur) std::rethrow_exception(std::current_exception()); std::throw_with_nested(*cur); @@ -320,9 +318,6 @@ void hdf5_error_check() { hdf5_error_stack_recursively_reconstruct(err_data.exceptions); } catch (...) { - // No stack trace: - // std::rethrow_exception(std::current_exception()); - // With stack trace: std::throw_with_nested(Exception("An HDF5 error was encountered.", ioda_Here())); } }