• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_CHECK_H_
6 #define BASE_CHECK_H_
7 
8 #include <iosfwd>
9 
10 #include "base/base_export.h"
11 #include "base/compiler_specific.h"
12 #include "base/dcheck_is_on.h"
13 #include "base/debug/debugging_buildflags.h"
14 #include "base/immediate_crash.h"
15 #include "base/location.h"
16 
17 // This header defines the CHECK, DCHECK, and DPCHECK macros.
18 //
19 // CHECK dies with a fatal error if its condition is not true. It is not
20 // controlled by NDEBUG, so the check will be executed regardless of compilation
21 // mode.
22 //
23 // DCHECK, the "debug mode" check, is enabled depending on NDEBUG and
24 // DCHECK_ALWAYS_ON, and its severity depends on DCHECK_IS_CONFIGURABLE.
25 //
26 // (D)PCHECK is like (D)CHECK, but includes the system error code (c.f.
27 // perror(3)).
28 //
29 // Additional information can be streamed to these macros and will be included
30 // in the log output if the condition doesn't hold (you may need to include
31 // <ostream>):
32 //
33 //   CHECK(condition) << "Additional info.";
34 //
35 // The condition is evaluated exactly once. Even in build modes where e.g.
36 // DCHECK is disabled, the condition and any stream arguments are still
37 // referenced to avoid warnings about unused variables and functions.
38 //
39 // For the (D)CHECK_EQ, etc. macros, see base/check_op.h. However, that header
40 // is *significantly* larger than check.h, so try to avoid including it in
41 // header files.
42 
43 namespace logging {
44 
45 // Class used to explicitly ignore an ostream, and optionally a boolean value.
46 class VoidifyStream {
47  public:
48   VoidifyStream() = default;
VoidifyStream(bool)49   explicit VoidifyStream(bool) {}
50 
51   // This operator has lower precedence than << but higher than ?:
52   void operator&(std::ostream&) {}
53 };
54 
55 // Macro which uses but does not evaluate expr and any stream parameters.
56 #define EAT_CHECK_STREAM_PARAMS(expr) \
57   true ? (void)0                      \
58        : ::logging::VoidifyStream(expr) & (*::logging::g_swallow_stream)
59 BASE_EXPORT extern std::ostream* g_swallow_stream;
60 
61 class LogMessage;
62 
63 // Class used for raising a check error upon destruction.
64 class BASE_EXPORT CheckError {
65  public:
66   // Used by CheckOp. Takes ownership of `log_message`.
CheckError(LogMessage * log_message)67   explicit CheckError(LogMessage* log_message) : log_message_(log_message) {}
68 
69   static CheckError Check(const char* file, int line, const char* condition);
70 
71   static CheckError DCheck(
72       const char* condition,
73       const base::Location& location = base::Location::Current());
74 
75   static CheckError PCheck(const char* file, int line, const char* condition);
76   static CheckError PCheck(const char* file, int line);
77 
78   static CheckError DPCheck(
79       const char* condition,
80       const base::Location& location = base::Location::Current());
81 
82   static CheckError NotImplemented(const char* file,
83                                    int line,
84                                    const char* function);
85 
86   // Stream for adding optional details to the error message.
87   std::ostream& stream();
88 
89   // Try really hard to get the call site and callee as separate stack frames in
90   // crash reports.
91   NOMERGE NOINLINE NOT_TAIL_CALLED ~CheckError();
92 
93   CheckError(const CheckError&) = delete;
94   CheckError& operator=(const CheckError&) = delete;
95 
96   template <typename T>
97   std::ostream& operator<<(T&& streamed_type) {
98     return stream() << streamed_type;
99   }
100 
101  protected:
102   LogMessage* const log_message_;
103 };
104 
105 class BASE_EXPORT NotReachedError : public CheckError {
106  public:
107   static NotReachedError NotReached(
108       const base::Location& location = base::Location::Current());
109 
110   // Used to trigger a NOTREACHED() without providing file or line while also
111   // discarding log-stream arguments. See base/notreached.h.
112   NOMERGE NOINLINE NOT_TAIL_CALLED static void TriggerNotReached();
113 
114   // TODO(crbug.com/851128): Mark [[noreturn]] once this is CHECK-fatal on all
115   // builds.
116   NOMERGE NOINLINE NOT_TAIL_CALLED ~NotReachedError();
117 
118  private:
119   using CheckError::CheckError;
120 };
121 
122 // TODO(crbug.com/851128): This should take the name of the above class once all
123 // callers of NOTREACHED() have migrated to the CHECK-fatal version.
124 class BASE_EXPORT NotReachedNoreturnError : public CheckError {
125  public:
126   NotReachedNoreturnError(const char* file, int line);
127 
128   [[noreturn]] NOMERGE NOINLINE NOT_TAIL_CALLED ~NotReachedNoreturnError();
129 };
130 
131 // The 'switch' is used to prevent the 'else' from being ambiguous when the
132 // macro is used in an 'if' clause such as:
133 // if (a == 1)
134 //   CHECK(Foo());
135 //
136 // TODO(crbug.com/1380930): Remove the const bool when the blink-gc plugin has
137 // been updated to accept `if (LIKELY(!field_))` as well as `if (!field_)`.
138 #define CHECK_FUNCTION_IMPL(check_failure_invocation, condition)   \
139   switch (0)                                                       \
140   case 0:                                                          \
141   default:                                                         \
142     if (const bool checky_bool_lol = static_cast<bool>(condition); \
143         LIKELY(ANALYZER_ASSUME_TRUE(checky_bool_lol)))             \
144       ;                                                            \
145     else                                                           \
146       check_failure_invocation
147 
148 #if defined(OFFICIAL_BUILD) && !defined(NDEBUG)
149 #error "Debug builds are not expected to be optimized as official builds."
150 #endif  // defined(OFFICIAL_BUILD) && !defined(NDEBUG)
151 
152 #if defined(OFFICIAL_BUILD) && !DCHECK_IS_ON()
153 // Note that this uses IMMEDIATE_CRASH_ALWAYS_INLINE to force-inline in debug
154 // mode as well. See LoggingTest.CheckCausesDistinctBreakpoints.
CheckFailure()155 [[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void CheckFailure() {
156   base::ImmediateCrash();
157 }
158 
159 // Discard log strings to reduce code bloat.
160 //
161 // This is not calling BreakDebugger since this is called frequently, and
162 // calling an out-of-line function instead of a noreturn inline macro prevents
163 // compiler optimizations.
164 #define CHECK(condition) \
165   UNLIKELY(!(condition)) ? logging::CheckFailure() : EAT_CHECK_STREAM_PARAMS()
166 
167 #define CHECK_WILL_STREAM() false
168 
169 // Strip the conditional string from official builds.
170 #define PCHECK(condition)                                                \
171   CHECK_FUNCTION_IMPL(::logging::CheckError::PCheck(__FILE__, __LINE__), \
172                       condition)
173 
174 #else
175 
176 #define CHECK_WILL_STREAM() true
177 
178 #define CHECK(condition) \
179   CHECK_FUNCTION_IMPL(   \
180       ::logging::CheckError::Check(__FILE__, __LINE__, #condition), condition)
181 
182 #define PCHECK(condition)                                            \
183   CHECK_FUNCTION_IMPL(                                               \
184       ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition), \
185       condition)
186 
187 #endif
188 
189 #if DCHECK_IS_ON()
190 
191 #define DCHECK(condition) \
192   CHECK_FUNCTION_IMPL(::logging::CheckError::DCheck(#condition), condition)
193 #define DPCHECK(condition) \
194   CHECK_FUNCTION_IMPL(::logging::CheckError::DPCheck(#condition), condition)
195 
196 #else
197 
198 #define DCHECK(condition) EAT_CHECK_STREAM_PARAMS(!(condition))
199 #define DPCHECK(condition) EAT_CHECK_STREAM_PARAMS(!(condition))
200 
201 #endif
202 
203 // Async signal safe checking mechanism.
204 BASE_EXPORT void RawCheck(const char* message);
205 BASE_EXPORT void RawError(const char* message);
206 #define RAW_CHECK(condition)                                 \
207   do {                                                       \
208     if (UNLIKELY(!(condition))) {                            \
209       ::logging::RawCheck("Check failed: " #condition "\n"); \
210     }                                                        \
211   } while (0)
212 
213 }  // namespace logging
214 
215 #endif  // BASE_CHECK_H_
216