• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 the V8 project authors. All rights reserved.
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 V8_BASE_LOGGING_H_
6 #define V8_BASE_LOGGING_H_
7 
8 #include <cstdint>
9 #include <cstring>
10 #include <sstream>
11 #include <string>
12 
13 #include "src/base/base-export.h"
14 #include "src/base/build_config.h"
15 #include "src/base/compiler-specific.h"
16 #include "src/base/immediate-crash.h"
17 #include "src/base/template-utils.h"
18 
19 V8_BASE_EXPORT V8_NOINLINE void V8_Dcheck(const char* file, int line,
20                                           const char* message);
21 
22 #ifdef DEBUG
23 // In debug, include file, line, and full error message for all
24 // FATAL() calls.
25 [[noreturn]] PRINTF_FORMAT(3, 4) V8_BASE_EXPORT V8_NOINLINE
26     void V8_Fatal(const char* file, int line, const char* format, ...);
27 #define FATAL(...) V8_Fatal(__FILE__, __LINE__, __VA_ARGS__)
28 
29 #else
30 [[noreturn]] PRINTF_FORMAT(1, 2) V8_BASE_EXPORT V8_NOINLINE
31     void V8_Fatal(const char* format, ...);
32 #if !defined(OFFICIAL_BUILD)
33 // In non-official release, include full error message, but drop file & line
34 // numbers. It saves binary size to drop the |file| & |line| as opposed to just
35 // passing in "", 0 for them.
36 #define FATAL(...) V8_Fatal(__VA_ARGS__)
37 #else
38 // FATAL(msg) -> IMMEDIATE_CRASH()
39 // FATAL(msg, ...) -> V8_Fatal(msg, ...)
40 #define FATAL_HELPER(_7, _6, _5, _4, _3, _2, _1, _0, ...) _0
41 #define FATAL_DISCARD_ARG(arg) IMMEDIATE_CRASH()
42 #define FATAL(...)                                                            \
43   FATAL_HELPER(__VA_ARGS__, V8_Fatal, V8_Fatal, V8_Fatal, V8_Fatal, V8_Fatal, \
44                V8_Fatal, FATAL_DISCARD_ARG)                                   \
45   (__VA_ARGS__)
46 #endif  // !defined(OFFICIAL_BUILD)
47 #endif  // DEBUG
48 
49 #define UNIMPLEMENTED() FATAL("unimplemented code")
50 #define UNREACHABLE() FATAL("unreachable code")
51 
52 namespace v8 {
53 namespace base {
54 
55 class CheckMessageStream : public std::ostringstream {};
56 
57 // Overwrite the default function that prints a stack trace.
58 V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
59 
60 // Override the default function that handles DCHECKs.
61 V8_BASE_EXPORT void SetDcheckFunction(void (*dcheck_Function)(const char*, int,
62                                                               const char*));
63 
64 // In official builds, assume all check failures can be debugged given just the
65 // stack trace.
66 #if !defined(DEBUG) && defined(OFFICIAL_BUILD)
67 #define CHECK_FAILED_HANDLER(message) FATAL("ignored")
68 #else
69 #define CHECK_FAILED_HANDLER(message) FATAL("Check failed: %s.", message)
70 #endif
71 
72 // CHECK dies with a fatal error if condition is not true.  It is *not*
73 // controlled by DEBUG, so the check will be executed regardless of
74 // compilation mode.
75 //
76 // We make sure CHECK et al. always evaluates their arguments, as
77 // doing CHECK(FunctionWithSideEffect()) is a common idiom.
78 #define CHECK_WITH_MSG(condition, message) \
79   do {                                     \
80     if (V8_UNLIKELY(!(condition))) {       \
81       CHECK_FAILED_HANDLER(message);       \
82     }                                      \
83   } while (false)
84 #define CHECK(condition) CHECK_WITH_MSG(condition, #condition)
85 
86 #ifdef DEBUG
87 
88 #define DCHECK_WITH_MSG(condition, message)   \
89   do {                                        \
90     if (V8_UNLIKELY(!(condition))) {          \
91       V8_Dcheck(__FILE__, __LINE__, message); \
92     }                                         \
93   } while (false)
94 #define DCHECK(condition) DCHECK_WITH_MSG(condition, #condition)
95 
96 // Helper macro for binary operators.
97 // Don't use this macro directly in your code, use CHECK_EQ et al below.
98 #define CHECK_OP(name, op, lhs, rhs)                                      \
99   do {                                                                    \
100     if (std::string* _msg = ::v8::base::Check##name##Impl<                \
101             typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type,  \
102             typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
103             (lhs), (rhs), #lhs " " #op " " #rhs)) {                       \
104       FATAL("Check failed: %s.", _msg->c_str());                          \
105       delete _msg;                                                        \
106     }                                                                     \
107   } while (false)
108 
109 #define DCHECK_OP(name, op, lhs, rhs)                                     \
110   do {                                                                    \
111     if (std::string* _msg = ::v8::base::Check##name##Impl<                \
112             typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type,  \
113             typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
114             (lhs), (rhs), #lhs " " #op " " #rhs)) {                       \
115       V8_Dcheck(__FILE__, __LINE__, _msg->c_str());                       \
116       delete _msg;                                                        \
117     }                                                                     \
118   } while (false)
119 
120 #else
121 
122 // Make all CHECK functions discard their log strings to reduce code
123 // bloat for official release builds.
124 
125 #define CHECK_OP(name, op, lhs, rhs)                                         \
126   do {                                                                       \
127     bool _cmp = ::v8::base::Cmp##name##Impl<                                 \
128         typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type,         \
129         typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>((lhs),  \
130                                                                      (rhs)); \
131     CHECK_WITH_MSG(_cmp, #lhs " " #op " " #rhs);                             \
132   } while (false)
133 
134 #define DCHECK_WITH_MSG(condition, msg) void(0);
135 
136 #endif
137 
138 namespace detail {
139 template <typename... Ts>
PrintToString(Ts &&...ts)140 std::string PrintToString(Ts&&... ts) {
141   CheckMessageStream oss;
142   int unused_results[]{((oss << std::forward<Ts>(ts)), 0)...};
143   (void)unused_results;  // Avoid "unused variable" warning.
144   return oss.str();
145 }
146 
147 template <typename T>
GetUnderlyingEnumTypeForPrinting(T val)148 auto GetUnderlyingEnumTypeForPrinting(T val) {
149   using underlying_t = typename std::underlying_type<T>::type;
150   // For single-byte enums, return a 16-bit integer to avoid printing the value
151   // as a character.
152   using int_t = typename std::conditional_t<
153       sizeof(underlying_t) != 1, underlying_t,
154       std::conditional_t<std::is_signed<underlying_t>::value, int16_t,
155                          uint16_t> >;
156   return static_cast<int_t>(static_cast<underlying_t>(val));
157 }
158 }  // namespace detail
159 
160 // Define PrintCheckOperand<T> for each T which defines operator<< for ostream.
161 template <typename T>
162 typename std::enable_if<
163     !std::is_function<typename std::remove_pointer<T>::type>::value &&
164         !std::is_enum<T>::value &&
165         has_output_operator<T, CheckMessageStream>::value,
166     std::string>::type
PrintCheckOperand(T val)167 PrintCheckOperand(T val) {
168   return detail::PrintToString(std::forward<T>(val));
169 }
170 
171 // Provide an overload for functions and function pointers. Function pointers
172 // don't implicitly convert to void* but do implicitly convert to bool, so
173 // without this function pointers are always printed as 1 or 0. (MSVC isn't
174 // standards-conforming here and converts function pointers to regular
175 // pointers, so this is a no-op for MSVC.)
176 template <typename T>
177 typename std::enable_if<
178     std::is_function<typename std::remove_pointer<T>::type>::value,
179     std::string>::type
PrintCheckOperand(T val)180 PrintCheckOperand(T val) {
181   return PrintCheckOperand(reinterpret_cast<const void*>(val));
182 }
183 
184 // Define PrintCheckOperand<T> for enums with an output operator.
185 template <typename T>
186 typename std::enable_if<std::is_enum<T>::value &&
187                             has_output_operator<T, CheckMessageStream>::value,
188                         std::string>::type
PrintCheckOperand(T val)189 PrintCheckOperand(T val) {
190   std::string val_str = detail::PrintToString(val);
191   std::string int_str =
192       detail::PrintToString(detail::GetUnderlyingEnumTypeForPrinting(val));
193   // Printing the original enum might have printed a single non-printable
194   // character. Ignore it in that case. Also ignore if it printed the same as
195   // the integral representation.
196   // TODO(clemensb): Can we somehow statically find out if the output operator
197   // is the default one, printing the integral value?
198   if ((val_str.length() == 1 && !std::isprint(val_str[0])) ||
199       val_str == int_str) {
200     return int_str;
201   }
202   return detail::PrintToString(val_str, " (", int_str, ")");
203 }
204 
205 // Define PrintCheckOperand<T> for enums without an output operator.
206 template <typename T>
207 typename std::enable_if<std::is_enum<T>::value &&
208                             !has_output_operator<T, CheckMessageStream>::value,
209                         std::string>::type
PrintCheckOperand(T val)210 PrintCheckOperand(T val) {
211   return detail::PrintToString(detail::GetUnderlyingEnumTypeForPrinting(val));
212 }
213 
214 // Define default PrintCheckOperand<T> for non-printable types.
215 template <typename T>
216 typename std::enable_if<!has_output_operator<T, CheckMessageStream>::value &&
217                             !std::is_enum<T>::value,
218                         std::string>::type
PrintCheckOperand(T val)219 PrintCheckOperand(T val) {
220   return "<unprintable>";
221 }
222 
223 // Define specializations for character types, defined in logging.cc.
224 #define DEFINE_PRINT_CHECK_OPERAND_CHAR(type)                       \
225   template <>                                                       \
226   V8_BASE_EXPORT std::string PrintCheckOperand<type>(type ch);      \
227   template <>                                                       \
228   V8_BASE_EXPORT std::string PrintCheckOperand<type*>(type * cstr); \
229   template <>                                                       \
230   V8_BASE_EXPORT std::string PrintCheckOperand<const type*>(const type* cstr);
231 
232 DEFINE_PRINT_CHECK_OPERAND_CHAR(char)
DEFINE_PRINT_CHECK_OPERAND_CHAR(signed char)233 DEFINE_PRINT_CHECK_OPERAND_CHAR(signed char)
234 DEFINE_PRINT_CHECK_OPERAND_CHAR(unsigned char)
235 #undef DEFINE_PRINT_CHECK_OPERAND_CHAR
236 
237 // Build the error message string.  This is separate from the "Impl"
238 // function template because it is not performance critical and so can
239 // be out of line, while the "Impl" code should be inline. Caller
240 // takes ownership of the returned string.
241 template <typename Lhs, typename Rhs>
242 V8_NOINLINE std::string* MakeCheckOpString(Lhs lhs, Rhs rhs, char const* msg) {
243   std::string lhs_str = PrintCheckOperand<Lhs>(lhs);
244   std::string rhs_str = PrintCheckOperand<Rhs>(rhs);
245   CheckMessageStream ss;
246   ss << msg;
247   constexpr size_t kMaxInlineLength = 50;
248   if (lhs_str.size() <= kMaxInlineLength &&
249       rhs_str.size() <= kMaxInlineLength) {
250     ss << " (" << lhs_str << " vs. " << rhs_str << ")";
251   } else {
252     ss << "\n   " << lhs_str << "\n vs.\n   " << rhs_str << "\n";
253   }
254   return new std::string(ss.str());
255 }
256 
257 // Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
258 // in logging.cc.
259 #define EXPLICIT_CHECK_OP_INSTANTIATION(type)                                \
260   extern template V8_BASE_EXPORT std::string* MakeCheckOpString<type, type>( \
261       type, type, char const*);                                              \
262   extern template V8_BASE_EXPORT std::string PrintCheckOperand<type>(type);
263 
264 EXPLICIT_CHECK_OP_INSTANTIATION(int)
265 EXPLICIT_CHECK_OP_INSTANTIATION(long)       // NOLINT(runtime/int)
266 EXPLICIT_CHECK_OP_INSTANTIATION(long long)  // NOLINT(runtime/int)
267 EXPLICIT_CHECK_OP_INSTANTIATION(unsigned int)
268 EXPLICIT_CHECK_OP_INSTANTIATION(unsigned long)       // NOLINT(runtime/int)
269 EXPLICIT_CHECK_OP_INSTANTIATION(unsigned long long)  // NOLINT(runtime/int)
270 EXPLICIT_CHECK_OP_INSTANTIATION(void const*)
271 #undef EXPLICIT_CHECK_OP_INSTANTIATION
272 
273 // comparison_underlying_type provides the underlying integral type of an enum,
274 // or std::decay<T>::type if T is not an enum. Booleans are converted to
275 // "unsigned int", to allow "unsigned int == bool" comparisons.
276 template <typename T>
277 struct comparison_underlying_type {
278   // std::underlying_type must only be used with enum types, thus use this
279   // {Dummy} type if the given type is not an enum.
280   enum Dummy {};
281   using decay = typename std::decay<T>::type;
282   static constexpr bool is_enum = std::is_enum<decay>::value;
283   using underlying = typename std::underlying_type<
284       typename std::conditional<is_enum, decay, Dummy>::type>::type;
285   using type_or_bool =
286       typename std::conditional<is_enum, underlying, decay>::type;
287   using type =
288       typename std::conditional<std::is_same<type_or_bool, bool>::value,
289                                 unsigned int, type_or_bool>::type;
290 };
291 // Cast a value to its underlying type
292 #define MAKE_UNDERLYING(Type, value) \
293   static_cast<typename comparison_underlying_type<Type>::type>(value)
294 
295 // is_signed_vs_unsigned::value is true if both types are integral, Lhs is
296 // signed, and Rhs is unsigned. False in all other cases.
297 template <typename Lhs, typename Rhs>
298 struct is_signed_vs_unsigned {
299   using lhs_underlying = typename comparison_underlying_type<Lhs>::type;
300   using rhs_underlying = typename comparison_underlying_type<Rhs>::type;
301   static constexpr bool value = std::is_integral<lhs_underlying>::value &&
302                                 std::is_integral<rhs_underlying>::value &&
303                                 std::is_signed<lhs_underlying>::value &&
304                                 std::is_unsigned<rhs_underlying>::value;
305 };
306 // Same thing, other way around: Lhs is unsigned, Rhs signed.
307 template <typename Lhs, typename Rhs>
308 struct is_unsigned_vs_signed : public is_signed_vs_unsigned<Rhs, Lhs> {};
309 
310 // Specialize the compare functions for signed vs. unsigned comparisons.
311 // std::enable_if ensures that this template is only instantiable if both Lhs
312 // and Rhs are integral types, and their signedness does not match.
313 #define MAKE_UNSIGNED(Type, value)         \
314   static_cast<typename std::make_unsigned< \
315       typename comparison_underlying_type<Type>::type>::type>(value)
316 #define DEFINE_SIGNED_MISMATCH_COMP(CHECK, NAME, IMPL)            \
317   template <typename Lhs, typename Rhs>                           \
318   V8_INLINE constexpr                                             \
319       typename std::enable_if<CHECK<Lhs, Rhs>::value, bool>::type \
320           Cmp##NAME##Impl(Lhs lhs, Rhs rhs) {                     \
321     return IMPL;                                                  \
322   }
323 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, EQ,
324                             lhs >= 0 && MAKE_UNSIGNED(Lhs, lhs) ==
325                                             MAKE_UNDERLYING(Rhs, rhs))
326 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, LT,
327                             lhs < 0 || MAKE_UNSIGNED(Lhs, lhs) <
328                                            MAKE_UNDERLYING(Rhs, rhs))
329 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, LE,
330                             lhs <= 0 || MAKE_UNSIGNED(Lhs, lhs) <=
331                                             MAKE_UNDERLYING(Rhs, rhs))
332 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, NE, !CmpEQImpl(lhs, rhs))
333 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, GT, !CmpLEImpl(lhs, rhs))
334 DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, GE, !CmpLTImpl(lhs, rhs))
335 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, EQ, CmpEQImpl(rhs, lhs))
336 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, NE, CmpNEImpl(rhs, lhs))
337 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, LT, CmpGTImpl(rhs, lhs))
338 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, LE, CmpGEImpl(rhs, lhs))
339 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, GT, CmpLTImpl(rhs, lhs))
340 DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, GE, CmpLEImpl(rhs, lhs))
341 #undef MAKE_UNSIGNED
342 #undef DEFINE_SIGNED_MISMATCH_COMP
343 
344 // Helper functions for CHECK_OP macro.
345 // The (float, float) and (double, double) instantiations are explicitly
346 // externalized to ensure proper 32/64-bit comparisons on x86.
347 // The Cmp##NAME##Impl function is only instantiable if one of the two types is
348 // not integral or their signedness matches (i.e. whenever no specialization is
349 // required, see above). Otherwise it is disabled by the enable_if construct,
350 // and the compiler will pick a specialization from above.
351 #define DEFINE_CHECK_OP_IMPL(NAME, op)                                        \
352   template <typename Lhs, typename Rhs>                                       \
353   V8_INLINE constexpr                                                         \
354       typename std::enable_if<!is_signed_vs_unsigned<Lhs, Rhs>::value &&      \
355                                   !is_unsigned_vs_signed<Lhs, Rhs>::value,    \
356                               bool>::type Cmp##NAME##Impl(Lhs lhs, Rhs rhs) { \
357     return lhs op rhs;                                                        \
358   }                                                                           \
359   template <typename Lhs, typename Rhs>                                       \
360   V8_INLINE constexpr std::string* Check##NAME##Impl(Lhs lhs, Rhs rhs,        \
361                                                      char const* msg) {       \
362     using LhsPassT = typename pass_value_or_ref<Lhs>::type;                   \
363     using RhsPassT = typename pass_value_or_ref<Rhs>::type;                   \
364     bool cmp = Cmp##NAME##Impl<LhsPassT, RhsPassT>(lhs, rhs);                 \
365     return V8_LIKELY(cmp)                                                     \
366                ? nullptr                                                      \
367                : MakeCheckOpString<LhsPassT, RhsPassT>(lhs, rhs, msg);        \
368   }
369 DEFINE_CHECK_OP_IMPL(EQ, ==)
370 DEFINE_CHECK_OP_IMPL(NE, !=)
371 DEFINE_CHECK_OP_IMPL(LE, <=)
372 DEFINE_CHECK_OP_IMPL(LT, < )
373 DEFINE_CHECK_OP_IMPL(GE, >=)
374 DEFINE_CHECK_OP_IMPL(GT, > )
375 #undef DEFINE_CHECK_OP_IMPL
376 
377 #define CHECK_EQ(lhs, rhs) CHECK_OP(EQ, ==, lhs, rhs)
378 #define CHECK_NE(lhs, rhs) CHECK_OP(NE, !=, lhs, rhs)
379 #define CHECK_LE(lhs, rhs) CHECK_OP(LE, <=, lhs, rhs)
380 #define CHECK_LT(lhs, rhs) CHECK_OP(LT, <, lhs, rhs)
381 #define CHECK_GE(lhs, rhs) CHECK_OP(GE, >=, lhs, rhs)
382 #define CHECK_GT(lhs, rhs) CHECK_OP(GT, >, lhs, rhs)
383 #define CHECK_NULL(val) CHECK((val) == nullptr)
384 #define CHECK_NOT_NULL(val) CHECK((val) != nullptr)
385 #define CHECK_IMPLIES(lhs, rhs) \
386   CHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs)
387 
388 }  // namespace base
389 }  // namespace v8
390 
391 
392 // The DCHECK macro is equivalent to CHECK except that it only
393 // generates code in debug builds.
394 #ifdef DEBUG
395 #define DCHECK_EQ(lhs, rhs) DCHECK_OP(EQ, ==, lhs, rhs)
396 #define DCHECK_NE(lhs, rhs) DCHECK_OP(NE, !=, lhs, rhs)
397 #define DCHECK_GT(lhs, rhs) DCHECK_OP(GT, >, lhs, rhs)
398 #define DCHECK_GE(lhs, rhs) DCHECK_OP(GE, >=, lhs, rhs)
399 #define DCHECK_LT(lhs, rhs) DCHECK_OP(LT, <, lhs, rhs)
400 #define DCHECK_LE(lhs, rhs) DCHECK_OP(LE, <=, lhs, rhs)
401 #define DCHECK_NULL(val) DCHECK((val) == nullptr)
402 #define DCHECK_NOT_NULL(val) DCHECK((val) != nullptr)
403 #define DCHECK_IMPLIES(lhs, rhs) \
404   DCHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs)
405 #else
406 #define DCHECK(condition)      ((void) 0)
407 #define DCHECK_EQ(v1, v2)      ((void) 0)
408 #define DCHECK_NE(v1, v2)      ((void) 0)
409 #define DCHECK_GT(v1, v2)      ((void) 0)
410 #define DCHECK_GE(v1, v2)      ((void) 0)
411 #define DCHECK_LT(v1, v2)      ((void) 0)
412 #define DCHECK_LE(v1, v2)      ((void) 0)
413 #define DCHECK_NULL(val)       ((void) 0)
414 #define DCHECK_NOT_NULL(val)   ((void) 0)
415 #define DCHECK_IMPLIES(v1, v2) ((void) 0)
416 #endif
417 
418 #endif  // V8_BASE_LOGGING_H_
419