| #ifndef QUICHE_COMMON_BUG_UTILS_H_ |
| #define QUICHE_COMMON_BUG_UTILS_H_ |
| |
| // This file contains macros for emitting bug log events when invariants are |
| // violated. |
| // |
| // Each instance of a QUICHE_BUG and friends has an associated id, which can be |
| // helpful for quickly finding the associated source code. |
| // |
| // The IDs are free form, but are expected to be unique. Best practice is to |
| // provide a *short* description of the condition being detected, without |
| // quotes, e.g., |
| // |
| // QUICHE_BUG(http2_decoder_invalid_parse_state) << "..."; |
| // |
| // QUICHE_BUG is defined in platform/api/quiche_bug_tracker.h. |
| // |
| |
| #include <atomic> |
| #include <cstdint> |
| #include <sstream> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| |
| #include "absl/base/log_severity.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "quiche/common/platform/api/quiche_export.h" |
| |
| namespace quiche { |
| namespace internal { |
| |
| class QUICHE_EXPORT GenericBugListener { |
| public: |
| virtual ~GenericBugListener() = default; |
| virtual void OnBug(const char* bug_id, const char* file, int line, |
| absl::string_view bug_message) = 0; |
| }; |
| |
| struct QUICHE_NO_EXPORT GenericBugOptions { |
| explicit GenericBugOptions(absl::LogSeverity log_severity, |
| const char* file_name, int line) |
| : severity(log_severity), file_name(file_name), line(line) {} |
| |
| GenericBugOptions& SetCondition(absl::string_view condition) { |
| this->condition_str = condition; |
| return *this; |
| } |
| |
| GenericBugOptions& SetBugListener(GenericBugListener* listener) { |
| this->bug_listener = listener; |
| return *this; |
| } |
| |
| absl::LogSeverity severity; |
| const char* const file_name; |
| const int line; |
| // !empty() for conditional GENERIC_BUGs. |
| absl::string_view condition_str; |
| // If not nullptr, |bug_listener| will be notified of this GENERIC_BUG hit. |
| // Since GenericBugListener may be a temporary object, this is only safe to |
| // access from GenericBugStreamHandler, whose scope is strictly narrower. |
| GenericBugListener* bug_listener = nullptr; |
| }; |
| |
| QUICHE_EXPORT inline GenericBugOptions DefaultBugOptions(const char* file_name, |
| int line) { |
| return GenericBugOptions(absl::kLogDebugFatal, file_name, line); |
| } |
| |
| // Called if a GENERIC_BUG is hit, but logging is omitted. |
| QUICHE_EXPORT void GenericBugWithoutLog(const char* bug_id, |
| const GenericBugOptions& options); |
| |
| // GenericBugStreamHandler exposes an interface similar to Abseil log objects, |
| // and is used by GENERIC_BUG to trigger a function which can be overridden in |
| // tests. By default, this class performs no action. SetOverrideFunction must be |
| // called to accomplish anything interesting. |
| class QUICHE_EXPORT GenericBugStreamHandler { |
| public: |
| // |prefix| and |bug_id| must be literal strings. They are used in the |
| // destructor. |
| explicit GenericBugStreamHandler(const char* prefix, const char* bug_id, |
| const GenericBugOptions& options); |
| |
| ~GenericBugStreamHandler(); |
| |
| template <typename T, |
| std::enable_if_t<absl::HasAbslStringify<T>::value, bool> = true> |
| GenericBugStreamHandler& operator<<(const T& v) { |
| absl::StrAppend(&str_, v); |
| return *this; |
| } |
| |
| // For types that support only operator<<. There's a better solution in |
| // Abseil, but unfortunately OStringStream is in a namespace marked internal. |
| template <typename T, |
| std::enable_if_t<!absl::HasAbslStringify<T>::value, bool> = true> |
| GenericBugStreamHandler& operator<<(const T& v) { |
| absl::StrAppend(&str_, (std::ostringstream() << v).str()); |
| return *this; |
| } |
| |
| // Interface similar to Abseil log objects. |
| GenericBugStreamHandler& stream() { return *this; } |
| |
| using OverrideFunction = void (*)(absl::LogSeverity severity, |
| const char* file, int line, |
| absl::string_view log_message); |
| |
| // Allows overriding the internal implementation. Call with nullptr to make |
| // this class a no-op. This getter and setter are thread-safe. |
| static void SetOverrideFunction(OverrideFunction override_function); |
| static OverrideFunction GetOverrideFunction(); |
| |
| private: |
| static std::atomic<OverrideFunction> atomic_override_function_; |
| |
| const char* bug_id_; |
| std::string str_; |
| const GenericBugOptions& options_; |
| }; |
| |
| } // namespace internal |
| } // namespace quiche |
| |
| // The GNU compiler emits a warning for code like: |
| // |
| // if (foo) |
| // if (bar) { } else baz; |
| // |
| // because it thinks you might want the else to bind to the first if. This |
| // leads to problems with code like: |
| // |
| // if (do_expr) GENERIC_BUG(bug_id) << "Some message"; |
| // |
| // The "switch (0) case 0:" idiom is used to suppress this. |
| #define GENERIC_BUG_UNBRACED_ELSE_BLOCKER \ |
| switch (0) \ |
| case 0: \ |
| default: |
| |
| #define GENERIC_BUG_IMPL(prefix, bug_id, skip_log_condition, options) \ |
| if (skip_log_condition) { \ |
| ::quiche::internal::GenericBugWithoutLog(#bug_id, (options)); \ |
| } else /* NOLINT */ \ |
| ::quiche::internal::GenericBugStreamHandler(prefix, #bug_id, (options)) \ |
| .stream() |
| |
| #endif // QUICHE_COMMON_BUG_UTILS_H_ |