-
Notifications
You must be signed in to change notification settings - Fork 14
WIP: improve exception handling and prepare for code dump #196
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,6 +29,15 @@ | |||||||||||
| #include <string> | ||||||||||||
| #include <tuple> | ||||||||||||
| #include <utility> | ||||||||||||
| #include <cstdio> | ||||||||||||
| #include <unistd.h> | ||||||||||||
|
|
||||||||||||
| #if defined(__APPLE__) && defined(__MACH__) | ||||||||||||
| #include <pthread.h> | ||||||||||||
| #elif defined(__linux__) | ||||||||||||
| #include <sys/syscall.h> | ||||||||||||
| #endif | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| /// Project includes | ||||||||||||
| /// @note: clang-format needs to be off here to preserve the logical grouping of | ||||||||||||
|
|
@@ -47,6 +56,52 @@ | |||||||||||
| namespace metkit::mars2grib { | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| namespace detail { | ||||||||||||
|
|
||||||||||||
| /// @brief Generates a strictly formatted log filename safely (Linux/macOS only). | ||||||||||||
| /// Guarantees no exceptions will escape and crash the simulation. | ||||||||||||
| inline std::string generateEncodingLogFilename() noexcept { | ||||||||||||
| try { | ||||||||||||
| // 1. Get true Process ID | ||||||||||||
| pid_t pid = getpid(); | ||||||||||||
|
|
||||||||||||
| // 2. Get true OS Thread ID | ||||||||||||
| unsigned long long tid = 0; | ||||||||||||
|
|
||||||||||||
| #if defined(__APPLE__) && defined(__MACH__) | ||||||||||||
| uint64_t mac_tid; | ||||||||||||
| pthread_threadid_np(NULL, &mac_tid); | ||||||||||||
| tid = static_cast<unsigned long long>(mac_tid); | ||||||||||||
| #elif defined(__linux__) | ||||||||||||
| // SYS_gettid is universally supported on Linux, even on old glibc versions | ||||||||||||
| tid = static_cast<unsigned long long>(syscall(SYS_gettid)); | ||||||||||||
| #else | ||||||||||||
| // Ultimate fallback if compiled on an unknown UNIX variant | ||||||||||||
| tid = 0; | ||||||||||||
| #endif | ||||||||||||
|
|
||||||||||||
| // 3. Format the string: encodingStack_%06d_%06llu.txt | ||||||||||||
| // %06d ensures at least 6 digits with leading zeros (equivalent to %6.6d) | ||||||||||||
| char buffer[64]; | ||||||||||||
| int written = std::snprintf(buffer, sizeof(buffer), | ||||||||||||
| "encodingStack_%06d_%06llu.txt", | ||||||||||||
| static_cast<int>(pid), tid); | ||||||||||||
|
|
||||||||||||
| if (written > 0 && static_cast<size_t>(written) < sizeof(buffer)) { | ||||||||||||
| return std::string(buffer); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return "encodingStack_fallback.txt"; | ||||||||||||
|
|
||||||||||||
| } catch (...) { | ||||||||||||
| // Catches std::bad_alloc if std::string creation fails due to OOM | ||||||||||||
| return "encodingStack_fallback.txt"; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| } // namespace detail | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| /// | ||||||||||||
| /// @brief Internal engine providing atomic encoding and diagnostic services. | ||||||||||||
| /// | ||||||||||||
|
|
@@ -86,10 +141,10 @@ struct CoreOperations { | |||||||||||
|
|
||||||||||||
| return {activeMars, activePar}; | ||||||||||||
| } | ||||||||||||
| catch (const std::exception& e) { | ||||||||||||
| catch (...) { | ||||||||||||
| // Wrap any exception in a CoreOperations-specific exception to provide context | ||||||||||||
| std::throw_with_nested( | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribGenericException("Error during metadata normalization", Here())); | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribCoreException("Error during metadata normalization", Here())); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -118,10 +173,10 @@ struct CoreOperations { | |||||||||||
| return SpecializedEncoder<MarsDict_t, ParDict_t, OptDict_t, OutDict_t>{std::move(layout)}.encode(mars, misc, | ||||||||||||
| opt); | ||||||||||||
| } | ||||||||||||
| catch (const std::exception& e) { | ||||||||||||
| catch (...) { | ||||||||||||
| // Wrap any exception in a CoreOperations-specific exception to provide context | ||||||||||||
| std::throw_with_nested( | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribGenericException("Error during header encoding", Here())); | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribCoreException("Error during header encoding", Here())); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -143,10 +198,10 @@ struct CoreOperations { | |||||||||||
| metkit::mars2grib::backend::encodeValues(values, misc, opt, *handle); | ||||||||||||
| return handle; | ||||||||||||
| } | ||||||||||||
| catch (const std::exception& e) { | ||||||||||||
| catch (...) { | ||||||||||||
| // Wrap any exception in a CoreOperations-specific exception to provide context | ||||||||||||
| std::throw_with_nested( | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribGenericException("Error during value encoding", Here())); | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribCoreException("Error during value encoding", Here())); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -251,14 +306,10 @@ struct CoreOperations { | |||||||||||
| // 4. Inject Values | ||||||||||||
| return encodeValues(values, activeMisc, options, std::move(gribHeader)); | ||||||||||||
| } | ||||||||||||
| catch (const std::exception& e) { | ||||||||||||
| printExtendedStack(e); | ||||||||||||
| throw; | ||||||||||||
| } | ||||||||||||
| catch (...) { | ||||||||||||
| // Fallback for non-standard exceptions | ||||||||||||
| throw metkit::mars2grib::utils::exceptions::Mars2GribGenericException("Unknown error during encoding", | ||||||||||||
| Here()); | ||||||||||||
| // Wrap any exception in a CoreOperations-specific exception to provide context | ||||||||||||
| std::throw_with_nested( | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribCoreException("Error during encoding", Here())); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -281,12 +332,42 @@ struct CoreOperations { | |||||||||||
|
|
||||||||||||
| return debug_convert_GribHeaderLayoutData_to_json(layout); | ||||||||||||
| } | ||||||||||||
| catch (const std::exception& e) { | ||||||||||||
| catch (...) { | ||||||||||||
| // Wrap any exception in a CoreOperations-specific exception to provide context | ||||||||||||
| std::throw_with_nested( | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribGenericException("Error during header test dump", Here())); | ||||||||||||
| mars2grib::utils::exceptions::Mars2GribCoreException("Error during header test dump", Here())); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| static std::string generateStack( const std::exception& e, const std::string& msg, const eckit::CodeLocation& loc ){ | ||||||||||||
|
|
||||||||||||
| using metkit::mars2grib::utils::exceptions::printExtendedStack; | ||||||||||||
| using metkit::mars2grib::utils::exceptions::lineSize; | ||||||||||||
|
|
||||||||||||
| const std::string logName = detail::generateEncodingLogFilename(); | ||||||||||||
|
|
||||||||||||
| std::ofstream out(logName); | ||||||||||||
|
||||||||||||
| std::ofstream out(logName); | |
| std::ofstream out(logName); | |
| if (!out) { | |
| return {}; | |
| } |
Copilot
AI
Apr 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generateStack() always returns the constant string "test.log", even though it writes to logName. This makes the returned filename incorrect and prevents the API layer from reporting the actual diagnostics file path. Return logName (or the fallback name) instead.
| return "test.log"; | |
| return logName; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,61 +120,145 @@ Mars2Grib::Mars2Grib(const eckit::LocalConfiguration& opts) : opts_{readOptions( | |
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const std::vector<double>& values, | ||
| const eckit::LocalConfiguration& mars, | ||
| const eckit::LocalConfiguration& misc) { | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values}, mars, misc, opts_, language_); | ||
| try { | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
|
Comment on lines
+128
to
+131
|
||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const std::vector<float>& values, | ||
| const eckit::LocalConfiguration& mars, | ||
| const eckit::LocalConfiguration& misc) { | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values}, mars, misc, opts_, language_); | ||
| try { | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const std::vector<double>& values, | ||
| const eckit::LocalConfiguration& mars) { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values}, mars, misc, opts_, language_); | ||
| try { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const std::vector<float>& values, | ||
| const eckit::LocalConfiguration& mars) { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values}, mars, misc, opts_, language_); | ||
| try { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const double* values, size_t length, | ||
| const eckit::LocalConfiguration& mars, | ||
| const eckit::LocalConfiguration& misc) { | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| try { | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const float* values, size_t length, | ||
| const eckit::LocalConfiguration& mars, | ||
| const eckit::LocalConfiguration& misc) { | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| try { | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const double* values, size_t length, | ||
| const eckit::LocalConfiguration& mars) { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| try { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<double, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const double>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::encode(const float* values, size_t length, | ||
| const eckit::LocalConfiguration& mars) { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| try { | ||
| const eckit::LocalConfiguration misc{}; | ||
| return CoreOperations::encode<float, eckit::LocalConfiguration, eckit::LocalConfiguration, Options, | ||
| metkit::codes::CodesHandle>(Span<const float>{values, length}, mars, misc, opts_, | ||
| language_); | ||
| } | ||
| catch (const std::exception& e) { | ||
| std::string logFile = CoreOperations::generateStack( e, "Error during API::encode call", Here() ); | ||
| throw eckit::Exception("Error during encoding", Here()); | ||
| } | ||
| catch (...) { | ||
| // Fallback for non-standard exceptions | ||
| throw eckit::Exception("Unknown error during encoding", Here()); | ||
| } | ||
| } | ||
|
|
||
| } // namespace metkit::mars2grib | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| #include <memory> | ||
| #include <vector> | ||
|
|
||
| #include "metkit/mars2grib/debug/reproducer.h" | ||
| #include "metkit/codes/api/CodesTypes.h" | ||
|
Comment on lines
+1
to
+5
|
||
|
|
||
| // ============================================================================ | ||
| // MockDictionary Definition | ||
| // ============================================================================ | ||
| namespace metkit::mars2grib::debug::reproducer { | ||
|
|
||
|
|
||
| class MockDictionary { | ||
| public: | ||
| std::shared_ptr<Recorder> recorder; | ||
|
|
||
| // Pipeline constructor | ||
| explicit MockDictionary(std::shared_ptr<Recorder> rec) | ||
| : recorder(std::move(rec)) {} | ||
|
|
||
| // Output utility | ||
| void dump(const std::string& filename) const { | ||
| if (recorder) recorder->generate(filename); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void set(const std::string& k, const T& v) { | ||
| recorder->record(k, v); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void set_array(const std::string& k, const std::vector<T>& v) { | ||
| recorder->record_array(k, v.data(), v.size()); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void set_array(const std::string& k, const metkit::codes::Span<T>& v) { | ||
| recorder->record_array(k, v.data(), v.size()); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace metkit::mars2grib::debug::reproducer | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes introduce new observable debugging behavior (per-thread/per-process stack log files and a potential reproducer path), but
src/metkit/mars2grib/docs/doxygen/*currently doesn’t describe this API contract. Please update the mars2grib docs to mention: where the stack files are written, naming scheme, and how users should find/consume the reproducer output.