|  | #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_ |