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 PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_
6 #define PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_
7 
8 #include <iosfwd>
9 
10 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
11 #include "partition_alloc/partition_alloc_base/component_export.h"
12 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
13 #include "partition_alloc/partition_alloc_base/immediate_crash.h"
14 #include "partition_alloc/partition_alloc_base/log_message.h"
15 #include "partition_alloc/partition_alloc_base/strings/cstring_builder.h"
16 
17 #define PA_STRINGIFY_IMPL(s) #s
18 #define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s)
19 
20 // This header defines the CHECK, DCHECK, and DPCHECK macros.
21 //
22 // CHECK dies with a fatal error if its condition is not true. It is not
23 // controlled by NDEBUG, so the check will be executed regardless of compilation
24 // mode.
25 //
26 // DCHECK, the "debug mode" check, is enabled depending on NDEBUG and
27 // DCHECK_ALWAYS_ON, and its severity depends on DCHECK_IS_CONFIGURABLE.
28 //
29 // (D)PCHECK is like (D)CHECK, but includes the system error code (c.f.
30 // perror(3)).
31 //
32 // Additional information can be streamed to these macros and will be included
33 // in the log output if the condition doesn't hold (you may need to include
34 // <ostream>):
35 //
36 //   CHECK(condition) << "Additional info.";
37 //
38 // The condition is evaluated exactly once. Even in build modes where e.g.
39 // DCHECK is disabled, the condition and any stream arguments are still
40 // referenced to avoid warnings about unused variables and functions.
41 //
42 // For the (D)CHECK_EQ, etc. macros, see base/check_op.h. However, that header
43 // is *significantly* larger than check.h, so try to avoid including it in
44 // header files.
45 
46 namespace partition_alloc::internal::logging {
47 
48 // Class used to explicitly ignore an ostream, and optionally a boolean value.
49 class VoidifyStream {
50  public:
51   VoidifyStream() = default;
VoidifyStream(bool ignored)52   explicit VoidifyStream(bool ignored) {}
53 
54   // This operator has lower precedence than << but higher than ?:
55   void operator&(base::strings::CStringBuilder&) {}
56 };
57 
58 // Helper macro which avoids evaluating the arguments to a stream if the
59 // condition is false.
60 #define PA_LAZY_CHECK_STREAM(stream, condition) \
61   !(condition)                                  \
62       ? (void)0                                 \
63       : ::partition_alloc::internal::logging::VoidifyStream() & (stream)
64 
65 // Macro which uses but does not evaluate expr and any stream parameters.
66 #define PA_EAT_CHECK_STREAM_PARAMS(expr)                             \
67   true ? (void)0                                                     \
68        : ::partition_alloc::internal::logging::VoidifyStream(expr) & \
69              (*::partition_alloc::internal::logging::g_swallow_stream)
70 PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)
71 extern base::strings::CStringBuilder* g_swallow_stream;
72 
73 class LogMessage;
74 
75 // Class used for raising a check error upon destruction.
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)76 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) CheckError {
77  public:
78   // Stream for adding optional details to the error message.
79   base::strings::CStringBuilder& stream();
80   PA_NOMERGE ~CheckError();
81 
82  protected:
83   CheckError(const char* file,
84              int line,
85              LogSeverity severity,
86              const char* condition);
87   CheckError(const char* file, int line, LogSeverity severity);
88   CheckError(const char* file,
89              int line,
90              LogSeverity severity,
91              const char* condition,
92              SystemErrorCode err_code);
93 
94   union {
95     LogMessage log_message_;
96 #if BUILDFLAG(IS_WIN)
97     Win32ErrorLogMessage errno_log_message_;
98 #else
99     ErrnoLogMessage errno_log_message_;
100 #endif
101   };
102 
103   // |has_errno| describes which union member is used, |log_message_| or
104   // |errno_log_message_|. If |has_errno| is true, CheckError initializes
105   // |errno_log_message_| at its constructor and destroys at its destructor.
106   // (This also means the CheckError is an instance of the parent class of
107   // PCheck or DPCheck.)
108   // If false, CheckError initializes and destroys |log_message_|.
109   const bool has_errno = false;
110 };
111 
112 namespace check_error {
113 
114 // Class used for raising a check error upon destruction.
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)115 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) Check : public CheckError {
116  public:
117   Check(const char* file, int line, const char* condition);
118 };
119 
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)120 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DCheck : public CheckError {
121  public:
122   DCheck(const char* file, int line, const char* condition);
123 };
124 
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)125 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) PCheck : public CheckError {
126  public:
127   PCheck(const char* file, int line, const char* condition);
128   PCheck(const char* file, int line);
129 };
130 
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)131 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DPCheck : public CheckError {
132  public:
133   DPCheck(const char* file, int line, const char* condition);
134 };
135 
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)136 class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) NotImplemented
137     : public CheckError {
138  public:
139   NotImplemented(const char* file, int line, const char* function);
140 };
141 
142 }  // namespace check_error
143 
144 #if defined(OFFICIAL_BUILD) && !defined(NDEBUG)
145 #error "Debug builds are not expected to be optimized as official builds."
146 #endif  // defined(OFFICIAL_BUILD) && !defined(NDEBUG)
147 
148 #if defined(OFFICIAL_BUILD) && !BUILDFLAG(PA_DCHECK_IS_ON)
149 
150 // Discard log strings to reduce code bloat.
151 //
152 // This is not calling BreakDebugger since this is called frequently, and
153 // calling an out-of-line function instead of a noreturn inline macro prevents
154 // compiler optimizations.
155 #define PA_BASE_CHECK(condition)                   \
156   PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \
157                             : PA_EAT_CHECK_STREAM_PARAMS()
158 
159 #define PA_BASE_CHECK_WILL_STREAM() false
160 
161 #define PA_BASE_PCHECK(condition)                                         \
162   PA_LAZY_CHECK_STREAM(                                                   \
163       ::partition_alloc::internal::logging::check_error::PCheck(__FILE__, \
164                                                                 __LINE__) \
165           .stream(),                                                      \
166       PA_UNLIKELY(!(condition)))
167 
168 #else
169 
170 #define PA_BASE_CHECK(condition)                                \
171   PA_LAZY_CHECK_STREAM(                                         \
172       ::partition_alloc::internal::logging::check_error::Check( \
173           __FILE__, __LINE__, #condition)                       \
174           .stream(),                                            \
175       !PA_ANALYZER_ASSUME_TRUE(condition))
176 
177 #define PA_BASE_CHECK_WILL_STREAM() true
178 
179 #define PA_BASE_PCHECK(condition)                                \
180   PA_LAZY_CHECK_STREAM(                                          \
181       ::partition_alloc::internal::logging::check_error::PCheck( \
182           __FILE__, __LINE__, #condition)                        \
183           .stream(),                                             \
184       !PA_ANALYZER_ASSUME_TRUE(condition))
185 
186 #endif
187 
188 #if BUILDFLAG(PA_DCHECK_IS_ON)
189 
190 #define PA_BASE_DCHECK(condition)                                \
191   PA_LAZY_CHECK_STREAM(                                          \
192       ::partition_alloc::internal::logging::check_error::DCheck( \
193           __FILE__, __LINE__, #condition)                        \
194           .stream(),                                             \
195       !PA_ANALYZER_ASSUME_TRUE(condition))
196 
197 #define PA_BASE_DPCHECK(condition)                                \
198   PA_LAZY_CHECK_STREAM(                                           \
199       ::partition_alloc::internal::logging::check_error::DPCheck( \
200           __FILE__, __LINE__, #condition)                         \
201           .stream(),                                              \
202       !PA_ANALYZER_ASSUME_TRUE(condition))
203 
204 #else
205 
206 #define PA_BASE_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
207 #define PA_BASE_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
208 
209 #endif
210 
211 // Async signal safe checking mechanism.
212 [[noreturn]] PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) void RawCheckFailure(
213     const char* message);
214 #define PA_RAW_CHECK(condition)                              \
215   do {                                                       \
216     if (!(condition))                                        \
217       ::partition_alloc::internal::logging::RawCheckFailure( \
218           "Check failed: " #condition "\n");                 \
219   } while (0)
220 
221 }  // namespace partition_alloc::internal::logging
222 
223 #endif  // PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_
224