From 9f4d1ef361e6f02cad87c78f5b1260bc9c3ebc08 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Wed, 15 May 2024 23:29:55 +0000 Subject: [PATCH] Exception: make source location print-out optional MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The project-specific exceptions are changed to not include the source location as part of the exception's message (return of what()) since it is unsuitable in some contexts (the output of the lttng client for example). Moreover, the exceptions are modified to provide their own message formatting instead of relying on the user doing it at the site at which it is thrown since that formatting is often repetitive. The sites at which the exceptions are thrown can still augment the exceptions with more context. The intention of our exceptions is that they still contain the requisite context to change the print-out based on their members; that context should not be baked into the message string at the site at which the exception is thrown. Change-Id: I2733b58d4daf6b9fffc1221c9d03ffef2e246893 Signed-off-by: Jérémie Galarneau --- src/bin/lttng/exception.cpp | 10 +-- src/bin/lttng/exception.hpp | 9 +- src/common/exception.cpp | 64 ++++---------- src/common/exception.hpp | 169 ++++++++++++++++++++++++++++-------- src/common/random.cpp | 9 +- src/common/random.hpp | 4 +- 6 files changed, 160 insertions(+), 105 deletions(-) diff --git a/src/bin/lttng/exception.cpp b/src/bin/lttng/exception.cpp index 8995d5eb9..2a2db974a 100644 --- a/src/bin/lttng/exception.cpp +++ b/src/bin/lttng/exception.cpp @@ -12,13 +12,9 @@ #include -lttng::cli::no_default_session_error::no_default_session_error(const char *file_name, - const char *function_name, - unsigned int line_number) : - runtime_error(lttng::format("No default session found in `{}/.lttngrc`", +lttng::cli::no_default_session_error::no_default_session_error(const lttng::source_location &location) : + runtime_error(fmt::format("No default session found in `{}/.lttngrc`", utils_get_home_dir() ?: "LTTNG_HOME"), - file_name, - function_name, - line_number) + location) { } diff --git a/src/bin/lttng/exception.hpp b/src/bin/lttng/exception.hpp index 00448f92e..3b24d2e88 100644 --- a/src/bin/lttng/exception.hpp +++ b/src/bin/lttng/exception.hpp @@ -15,16 +15,15 @@ #include #include -#define LTTNG_THROW_CLI_NO_DEFAULT_SESSION() \ - throw lttng::cli::no_default_session_error(__FILE__, __func__, __LINE__) +#define LTTNG_THROW_CLI_NO_DEFAULT_SESSION() \ + throw lttng::cli::no_default_session_error( \ + LTTNG_SOURCE_LOCATION()) namespace lttng { namespace cli { class no_default_session_error : public runtime_error { public: - explicit no_default_session_error(const char *file_name, - const char *function_name, - unsigned int line_number); + explicit no_default_session_error(const lttng::source_location& source_location); }; } /* namespace cli */ }; /* namespace lttng */ diff --git a/src/common/exception.cpp b/src/common/exception.cpp index 028d253f2..219e80fb1 100644 --- a/src/common/exception.cpp +++ b/src/common/exception.cpp @@ -11,79 +11,47 @@ #include -namespace { -std::string -format_throw_location(const char *file_name, const char *function_name, unsigned int line_number) -{ - std::stringstream location; - - location << "[" << function_name << "()" - << " " << file_name << ":" << line_number << "]"; - - return location.str(); -} -} /* namespace */ - lttng::ctl::error::error(const std::string& msg, lttng_error_code error_code, - const char *file_name, - const char *function_name, - unsigned int line_number) : - runtime_error(msg + ": " + std::string(error_get_str(error_code)), - file_name, - function_name, - line_number), - _error_code{ error_code } + const lttng::source_location& location) : + runtime_error(msg, location), _error_code{ error_code } { } lttng::posix_error::posix_error(const std::string& msg, - int errno_code, - const char *file_name, - const char *function_name, - unsigned int line_number) : - std::system_error(errno_code, - std::generic_category(), - msg + " " + format_throw_location(file_name, function_name, line_number)) + unsigned int errno_code, + const lttng::source_location& location) : + std::system_error(errno_code, std::generic_category()), + lttng::runtime_error(msg, location) { } lttng::runtime_error::runtime_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - std::runtime_error(msg + " " + format_throw_location(file_name, function_name, line_number)) + const lttng::source_location& location) : + std::runtime_error(msg), source_location(location) { } lttng::unsupported_error::unsupported_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - std::runtime_error(msg + " " + format_throw_location(file_name, function_name, line_number)) + const lttng::source_location& location) : + lttng::runtime_error(msg, location) { } lttng::communication_error::communication_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - runtime_error(msg, file_name, function_name, line_number) + const lttng::source_location& location) : + runtime_error(msg, location) { } lttng::protocol_error::protocol_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - communication_error(msg, file_name, function_name, line_number) + const lttng::source_location& location) : + communication_error(msg, location) { } lttng::invalid_argument_error::invalid_argument_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - runtime_error(msg, file_name, function_name, line_number) + const lttng::source_location& location) : + runtime_error(msg, location) { } diff --git a/src/common/exception.hpp b/src/common/exception.hpp index 1880978e7..f878b9cf5 100644 --- a/src/common/exception.hpp +++ b/src/common/exception.hpp @@ -8,52 +8,117 @@ #ifndef LTTNG_EXCEPTION_H_ #define LTTNG_EXCEPTION_H_ +#include + #include #include #include #include +#define LTTNG_SOURCE_LOCATION() lttng::source_location(__FILE__, __func__, __LINE__) + #define LTTNG_THROW_CTL(msg, error_code) \ - throw lttng::ctl::error(msg, error_code, __FILE__, __func__, __LINE__) + throw lttng::ctl::error(msg, error_code, LTTNG_SOURCE_LOCATION()) #define LTTNG_THROW_POSIX(msg, errno_code) \ - throw lttng::posix_error(msg, errno_code, __FILE__, __func__, __LINE__) -#define LTTNG_THROW_ERROR(msg) throw lttng::runtime_error(msg, __FILE__, __func__, __LINE__) + throw lttng::posix_error(msg, errno_code, LTTNG_SOURCE_LOCATION()) +#define LTTNG_THROW_ERROR(msg) throw lttng::runtime_error(msg, LTTNG_SOURCE_LOCATION()) #define LTTNG_THROW_UNSUPPORTED_ERROR(msg) \ - throw lttng::unsupported_error(msg, __FILE__, __func__, __LINE__) + throw lttng::unsupported_error(msg, LTTNG_SOURCE_LOCATION()) #define LTTNG_THROW_COMMUNICATION_ERROR(msg) \ - throw lttng::communication_error(msg, __FILE__, __func__, __LINE__) -#define LTTNG_THROW_PROTOCOL_ERROR(msg) \ - throw lttng::protocol_error(msg, __FILE__, __func__, __LINE__) + throw lttng::communication_error(msg, LTTNG_SOURCE_LOCATION()) +#define LTTNG_THROW_PROTOCOL_ERROR(msg) throw lttng::protocol_error(msg, LTTNG_SOURCE_LOCATION()) #define LTTNG_THROW_INVALID_ARGUMENT_ERROR(msg) \ - throw lttng::invalid_argument_error(msg, __FILE__, __func__, __LINE__) + throw lttng::invalid_argument_error(msg, LTTNG_SOURCE_LOCATION()) namespace lttng { +/** + * @class source_location + * @brief Represents the location in the source code where an exception was thrown. + * + * The source_location class captures the file name, function name, and line number + * of the source code where an exception occurs. This information is useful for + * debugging and logging purposes. + * + * @details + * This class provides: + * - The name of the source file (file_name). + * - The name of the function (function_name). + * - The line number in the source file (line_number). + * + * Example usage: + * @code + * try { + * // Code that may throw an exception. + * } catch (const lttng::runtime_error& ex) { + * // Handle the exception, possibly logging location information. + * ERR_FMT("{} [{}]", ex.what(), ex.source_location); + * } + * @endcode + */ +class source_location { +public: + source_location(lttng::c_string_view file_name_, + lttng::c_string_view function_name_, + unsigned int line_number_) : + file_name(file_name_), function_name(function_name_), line_number(line_number_) + { + } + + lttng::c_string_view file_name; + lttng::c_string_view function_name; + unsigned int line_number; +}; + +/** + * @class runtime_error + * @brief Base type for all LTTng exceptions. + * + * Exceptions in the project provide an error message (through the usual what() method), but that + * message may not include the whole context of the error. For example, it is not always desirable + * to include the source location in a user-facing message. + * + * As such, exception handlers should mind the type of the exception being thrown and consider + * what context is suitable to extract (e.g. some context may only be relevant at the DEBUG logging + * level, while the error message may be user-facing). + * + * Since 'what()' is marked as noexcept, derived classes should format their generic message during + * their construction and pass it to the runtime_error constructor. + */ class runtime_error : public std::runtime_error { public: - explicit runtime_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + runtime_error(const std::string& msg, const lttng::source_location& source_location); + + lttng::source_location source_location; }; -class unsupported_error : public std::runtime_error { +/** + * @class unsupported_error + * @brief Represents an error for unsupported features. + * + * This error may occur due to the current configuration making a feature unavailable + * (e.g. when using an older kernel or tracer release). + */ +class unsupported_error : public lttng::runtime_error { public: explicit unsupported_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); }; namespace ctl { -/* Wrap lttng_error_code errors which may be reported through liblttng-ctl's interface. */ +/** + * @class error + * @brief Wraps lttng_error_code errors for reporting through liblttng-ctl's interface. + * + * There is typically a better way to report errors than using this type of exception. However, it + * is sometimes useful to transition legacy code to use RAII facilities and exceptions without + * revisiting every caller. + */ class error : public runtime_error { public: explicit error(const std::string& msg, lttng_error_code error_code, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); lttng_error_code code() const noexcept { @@ -65,39 +130,69 @@ private: }; } /* namespace ctl */ -class posix_error : public std::system_error { +/** + * @class posix_error + * @brief Wraps a POSIX system error, including the location where the error occurred. + */ +class posix_error : public std::system_error, lttng::runtime_error { public: explicit posix_error(const std::string& msg, - int errno_code, - const char *file_name, - const char *function_name, - unsigned int line_number); + unsigned int errno_code, + const lttng::source_location& source_location); }; -class communication_error : public runtime_error { +/** + * @class communication_error + * @brief Base class for communication errors between components. + */ +class communication_error : public lttng::runtime_error { public: explicit communication_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); }; +/** + * @class protocol_error + * @brief Base class for protocol layer communication errors (encoding or decoding problems). + */ class protocol_error : public communication_error { public: explicit protocol_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); }; -class invalid_argument_error : public runtime_error { +/** + * @class invalid_argument_error + * @brief Represents an error for invalid arguments. + */ +class invalid_argument_error : public lttng::runtime_error { public: explicit invalid_argument_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); }; -}; /* namespace lttng */ +} /* namespace lttng */ + +/* + * Specialize fmt::formatter for lttng::source_location + * + * Due to a bug in g++ < 7.1, this specialization must be enclosed in the fmt namespace, + * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480. + */ +namespace fmt { +template <> +struct formatter : formatter { + template + typename FormatContextType::iterator format(const lttng::source_location& location, + FormatContextType& ctx) + { + return format_to(ctx.out(), + "{}() {}:{}", + location.function_name.data(), + location.file_name.data(), + location.line_number); + } +}; +} /* namespace fmt */ #endif /* LTTNG_EXCEPTION_H_ */ diff --git a/src/common/random.cpp b/src/common/random.cpp index ba9d937ba..178a228c9 100644 --- a/src/common/random.cpp +++ b/src/common/random.cpp @@ -22,7 +22,8 @@ #endif #define LTTNG_THROW_RANDOM_PRODUCTION_ERROR(msg) \ - throw lttng::random::production_error(msg, __FILE__, __func__, __LINE__) + throw lttng::random::production_error( \ + msg, lttng::source_location(__FILE__, __func__, __LINE__)) namespace { /* getrandom is available in Linux >= 3.17. */ @@ -162,10 +163,8 @@ lttng::random::seed_t produce_random_seed_from_urandom() } /* namespace */ lttng::random::production_error::production_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number) : - lttng::runtime_error(msg, file_name, function_name, line_number) + const lttng::source_location& location) : + lttng::runtime_error(msg, location) { } diff --git a/src/common/random.hpp b/src/common/random.hpp index 7ba6ede50..2dac4d3af 100644 --- a/src/common/random.hpp +++ b/src/common/random.hpp @@ -21,9 +21,7 @@ using seed_t = unsigned int; class production_error : public ::lttng::runtime_error { public: explicit production_error(const std::string& msg, - const char *file_name, - const char *function_name, - unsigned int line_number); + const lttng::source_location& source_location); }; /* -- 2.34.1