Exception: make source location print-out optional
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 15 May 2024 23:29:55 +0000 (23:29 +0000)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 27 Jun 2024 15:09:21 +0000 (11:09 -0400)
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 <jeremie.galarneau@efficios.com>
src/bin/lttng/exception.cpp
src/bin/lttng/exception.hpp
src/common/exception.cpp
src/common/exception.hpp
src/common/random.cpp
src/common/random.hpp

index 8995d5eb9f9b9639b4f2864eb443c65e517068f0..2a2db974a73663a6c1d4fda8e674833d83764e96 100644 (file)
 
 #include <sstream>
 
-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)
 {
 }
index 00448f92eb3ab488dc36c6e0ece267407f363e9f..3b24d2e887fadf7112533985b37d02578178ce51 100644 (file)
 #include <stdexcept>
 #include <string>
 
-#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 */
index 028d253f2d885a6c9d9c29a4dc8c875a40b698f2..219e80fb18053ef8167e1d5d975f4f351f90370d 100644 (file)
 
 #include <sstream>
 
-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)
 {
 }
index 1880978e76997c3ee9df65f556516fd5f16b9581..f878b9cf541707e5c425e2ea746125662bbfa52b 100644 (file)
 #ifndef LTTNG_EXCEPTION_H_
 #define LTTNG_EXCEPTION_H_
 
+#include <common/string-utils/c-string-view.hpp>
+
 #include <lttng/lttng-error.h>
 
 #include <stdexcept>
 #include <string>
 #include <system_error>
 
+#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<lttng::source_location> : formatter<std::string> {
+       template <typename FormatContextType>
+       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_ */
index ba9d937ba32b95f43f877348ac0f358ea9c7e071..178a228c9e74591731f3f947c3f643270e36cd63 100644 (file)
@@ -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)
 {
 }
 
index 7ba6ede5022985ceb069c2a8659f0d5e6c444df1..2dac4d3afb60fe93943ab2657dc07e11652101ae 100644 (file)
@@ -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);
 };
 
 /*
This page took 0.030506 seconds and 4 git commands to generate.