v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
logging.h
Go to the documentation of this file.
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 <iterator>
11#include <sstream>
12#include <string>
13#include <utility>
14#include <vector>
15
16#include "src/base/abort-mode.h"
22
23V8_BASE_EXPORT V8_NOINLINE void V8_Dcheck(const char* file, int line,
24 const char* message);
25
26#ifdef DEBUG
27// In debug, include file, line, and full error message for all
28// FATAL() calls.
30 void V8_Fatal(const char* file, int line, const char* format, ...);
31#define FATAL(...) V8_Fatal(__FILE__, __LINE__, __VA_ARGS__)
32
33// The following can be used instead of FATAL() to prevent calling
34// IMMEDIATE_CRASH in official mode. Please only use if needed for testing.
35// See v8:13945
36#define GRACEFUL_FATAL(...) FATAL(__VA_ARGS__)
37
38#else
40 void V8_Fatal(const char* format, ...);
41#define GRACEFUL_FATAL(...) V8_Fatal(__VA_ARGS__)
42
43#if !defined(OFFICIAL_BUILD)
44// In non-official release, include full error message, but drop file & line
45// numbers. It saves binary size to drop the |file| & |line| as opposed to just
46// passing in "", 0 for them.
47#define FATAL(...) V8_Fatal(__VA_ARGS__)
48#else
49// FATAL(msg) -> IMMEDIATE_CRASH()
50// FATAL(msg, ...) -> V8_Fatal(msg, ...)
51#define FATAL_HELPER(_7, _6, _5, _4, _3, _2, _1, _0, ...) _0
52#define FATAL_DISCARD_ARG(arg) IMMEDIATE_CRASH()
53#define FATAL(...) \
54 FATAL_HELPER(__VA_ARGS__, V8_Fatal, V8_Fatal, V8_Fatal, V8_Fatal, V8_Fatal, \
55 V8_Fatal, FATAL_DISCARD_ARG) \
56 (__VA_ARGS__)
57#endif // !defined(OFFICIAL_BUILD)
58#endif // DEBUG
59
60namespace v8::base {
61// These string constants are pattern-matched by fuzzers.
62constexpr const char* kUnimplementedCodeMessage = "unimplemented code";
63constexpr const char* kUnreachableCodeMessage = "unreachable code";
64} // namespace v8::base
65
66#define UNIMPLEMENTED() FATAL(::v8::base::kUnimplementedCodeMessage)
67#define UNREACHABLE() FATAL(::v8::base::kUnreachableCodeMessage)
68// g++ versions <= 8 cannot use UNREACHABLE() in a constexpr function.
69// TODO(miladfarca): Remove once all compilers handle this properly.
70#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ <= 8)
71#define CONSTEXPR_UNREACHABLE() abort()
72#else
73#define CONSTEXPR_UNREACHABLE() UNREACHABLE()
74#endif
75
76namespace v8 {
77namespace base {
78
79class CheckMessageStream : public std::ostringstream {};
80
81// Overwrite the default function that prints a stack trace.
82V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
83
84// Override the default function that handles DCHECKs.
85V8_BASE_EXPORT void SetDcheckFunction(void (*dcheck_Function)(const char*, int,
86 const char*));
87
88// Override the default function invoked during V8_Fatal.
89V8_BASE_EXPORT void SetFatalFunction(void (*fatal_Function)(const char*, int,
90 const char*));
91
92enum class OOMType {
93 // We ran out of memory in the JavaScript heap.
95 // The process ran out of memory.
97};
98
99// A simpler version of V8::FatalProcessOutOfMemory that is available in
100// src/base. Will simply terminate the process with an OOM message that is
101// recognizes as such by fuzzers and other tooling.
102[[noreturn]] V8_BASE_EXPORT void FatalOOM(OOMType type, const char* msg);
103
104// In official builds, assume all check failures can be debugged given just the
105// stack trace.
106#if !defined(DEBUG) && defined(OFFICIAL_BUILD)
107#define CHECK_FAILED_HANDLER(message) FATAL("ignored")
108#else
109#define CHECK_FAILED_HANDLER(message) FATAL("Check failed: %s.", message)
110#endif
111
112// CHECK dies with a fatal error if condition is not true. It is *not*
113// controlled by DEBUG, so the check will be executed regardless of
114// compilation mode.
115//
116// We make sure CHECK et al. always evaluates their arguments, as
117// doing CHECK(FunctionWithSideEffect()) is a common idiom.
118#define CHECK_WITH_MSG(condition, message) \
119 do { \
120 if (V8_UNLIKELY(!(condition))) { \
121 CHECK_FAILED_HANDLER(message); \
122 } \
123 } while (false)
124#define CHECK(condition) CHECK_WITH_MSG(condition, #condition)
125
126#ifdef DEBUG
127
128#define DCHECK_WITH_MSG_AND_LOC(condition, message, loc) \
129 do { \
130 if (V8_UNLIKELY(!(condition))) { \
131 V8_Dcheck(loc.FileName(), static_cast<int>(loc.Line()), message); \
132 } \
133 } while (false)
134#define DCHECK_WITH_MSG(condition, message) \
135 do { \
136 if (V8_UNLIKELY(!(condition))) { \
137 V8_Dcheck(__FILE__, __LINE__, message); \
138 } \
139 } while (false)
140#define DCHECK_WITH_LOC(condition, loc) \
141 DCHECK_WITH_MSG_AND_LOC(condition, #condition, loc)
142#define DCHECK(condition) DCHECK_WITH_MSG(condition, #condition)
143
144// Helper macro for binary operators.
145// Don't use this macro directly in your code, use CHECK_EQ et al below.
146#define CHECK_OP(name, op, lhs, rhs) \
147 do { \
148 if (std::string* _msg = ::v8::base::Check##name##Impl< \
149 typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type, \
150 typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
151 (lhs), (rhs), #lhs " " #op " " #rhs)) { \
152 FATAL("Check failed: %s.", _msg->c_str()); \
153 delete _msg; \
154 } \
155 } while (false)
156
157#define DCHECK_OP(name, op, lhs, rhs) \
158 do { \
159 if (std::string* _msg = ::v8::base::Check##name##Impl< \
160 typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type, \
161 typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
162 (lhs), (rhs), #lhs " " #op " " #rhs)) { \
163 V8_Dcheck(__FILE__, __LINE__, _msg->c_str()); \
164 delete _msg; \
165 } \
166 } while (false)
167
168#else
169
170// Make all CHECK functions discard their log strings to reduce code
171// bloat for official release builds.
172
173#define CHECK_OP(name, op, lhs, rhs) \
174 do { \
175 bool _cmp = ::v8::base::Cmp##name##Impl< \
176 typename ::v8::base::pass_value_or_ref<decltype(lhs)>::type, \
177 typename ::v8::base::pass_value_or_ref<decltype(rhs)>::type>((lhs), \
178 (rhs)); \
179 CHECK_WITH_MSG(_cmp, #lhs " " #op " " #rhs); \
180 } while (false)
181
182#define DCHECK_WITH_MSG(condition, msg) void(0);
183
184#endif
185
186namespace detail {
187template <typename... Ts>
188std::string PrintToString(Ts&&... ts) {
190 (..., (oss << std::forward<Ts>(ts)));
191 return oss.str();
192}
193
194template <typename T>
196 auto underlying_val = static_cast<std::underlying_type_t<T>>(val);
197 // For single-byte enums, return a 16-bit integer to avoid printing the value
198 // as a character.
199 if constexpr (sizeof(underlying_val) == 1) {
200 constexpr bool kIsSigned = std::is_signed_v<decltype(underlying_val)>;
201 using int_t = std::conditional_t<kIsSigned, int16_t, uint16_t>;
202 return static_cast<int_t>(underlying_val);
203 } else {
204 return underlying_val;
205 }
206}
207
208} // namespace detail
209
210// Define default PrintCheckOperand<T> for non-printable types.
211template <typename T>
212std::string PrintCheckOperand(T val) {
213 return "<unprintable>";
214}
215
216// Define PrintCheckOperand<T> for each T which defines operator<< for ostream,
217// except types explicitly specialized below.
218template <typename T>
219 requires(!std::is_function_v<typename std::remove_pointer<T>::type> &&
220 !std::is_enum_v<T> && has_output_operator<T, CheckMessageStream>)
221std::string PrintCheckOperand(T val) {
222 return detail::PrintToString(std::forward<T>(val));
223}
224
225// Provide an overload for functions and function pointers. Function pointers
226// don't implicitly convert to void* but do implicitly convert to bool, so
227// without this function pointers are always printed as 1 or 0. (MSVC isn't
228// standards-conforming here and converts function pointers to regular
229// pointers, so this is a no-op for MSVC.)
230template <typename T>
231 requires(std::is_function_v<typename std::remove_pointer_t<T>>)
232std::string PrintCheckOperand(T val) {
233 return PrintCheckOperand(reinterpret_cast<const void*>(val));
234}
235
236// Define PrintCheckOperand<T> for enums.
237template <typename T>
238 requires(std::is_enum_v<T>)
239std::string PrintCheckOperand(T val) {
240 std::string int_str =
243 std::string val_str = detail::PrintToString(val);
244 // Printing the original enum might have printed a single non-printable
245 // character. Ignore it in that case. Also ignore if it printed the same as
246 // the integral representation.
247 // TODO(clemensb): Can we somehow statically find out if the output operator
248 // is the default one, printing the integral value?
249 if ((val_str.length() == 1 && !std::isprint(val_str[0])) ||
250 val_str == int_str) {
251 return int_str;
252 }
253 return detail::PrintToString(val_str, " (", int_str, ")");
254 } else {
255 return int_str;
256 }
257}
258
259// Define PrintCheckOperand<T> for forward iterable containers without an output
260// operator.
261template <typename T>
262 requires(!has_output_operator<T, CheckMessageStream> &&
263 requires(T t) {
264 { t.begin() } -> std::forward_iterator;
265 })
266std::string PrintCheckOperand(T container) {
268 oss << "{";
269 bool first = true;
270 for (const auto& val : container) {
271 if (!first) {
272 oss << ",";
273 } else {
274 first = false;
275 }
276 oss << PrintCheckOperand(val);
277 }
278 oss << "}";
279 return oss.str();
280}
281
282// Define specializations for character types, defined in logging.cc.
283#define DEFINE_PRINT_CHECK_OPERAND_CHAR(type) \
284 template <> \
285 V8_BASE_EXPORT std::string PrintCheckOperand<type>(type ch); \
286 template <> \
287 V8_BASE_EXPORT std::string PrintCheckOperand<type*>(type * cstr); \
288 template <> \
289 V8_BASE_EXPORT std::string PrintCheckOperand<const type*>(const type* cstr);
290
294#undef DEFINE_PRINT_CHECK_OPERAND_CHAR
295
296// Build the error message string. This is separate from the "Impl"
297// function template because it is not performance critical and so can
298// be out of line, while the "Impl" code should be inline. Caller
299// takes ownership of the returned string.
300template <typename Lhs, typename Rhs>
301V8_NOINLINE std::string* MakeCheckOpString(Lhs lhs, Rhs rhs, char const* msg) {
302 std::string lhs_str = PrintCheckOperand<Lhs>(lhs);
303 std::string rhs_str = PrintCheckOperand<Rhs>(rhs);
305 ss << msg;
306 constexpr size_t kMaxInlineLength = 50;
307 if (lhs_str.size() <= kMaxInlineLength &&
308 rhs_str.size() <= kMaxInlineLength) {
309 ss << " (" << lhs_str << " vs. " << rhs_str << ")";
310 } else {
311 ss << "\n " << lhs_str << "\n vs.\n " << rhs_str << "\n";
312 }
313 return new std::string(ss.str());
314}
315
316// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
317// in logging.cc.
318#define EXPLICIT_CHECK_OP_INSTANTIATION(type) \
319 extern template V8_BASE_EXPORT std::string* MakeCheckOpString<type, type>( \
320 type, type, char const*); \
321 extern template V8_BASE_EXPORT std::string PrintCheckOperand<type>(type);
322
324EXPLICIT_CHECK_OP_INSTANTIATION(long) // NOLINT(runtime/int)
325EXPLICIT_CHECK_OP_INSTANTIATION(long long) // NOLINT(runtime/int)
327EXPLICIT_CHECK_OP_INSTANTIATION(unsigned long) // NOLINT(runtime/int)
328EXPLICIT_CHECK_OP_INSTANTIATION(unsigned long long) // NOLINT(runtime/int)
330#undef EXPLICIT_CHECK_OP_INSTANTIATION
331
332// comparison_underlying_type provides the underlying integral type of an enum,
333// or std::decay<T>::type if T is not an enum. Booleans are converted to
334// "unsigned int", to allow "unsigned int == bool" comparisons.
335template <typename T>
337 // std::underlying_type must only be used with enum types, thus use this
338 // {Dummy} type if the given type is not an enum.
339 enum Dummy {};
340 using decay = typename std::decay<T>::type;
341 static constexpr bool is_enum = std::is_enum_v<decay>;
342 using underlying = typename std::underlying_type<
343 typename std::conditional<is_enum, decay, Dummy>::type>::type;
345 typename std::conditional<is_enum, underlying, decay>::type;
346 using type = typename std::conditional<std::is_same_v<type_or_bool, bool>,
347 unsigned int, type_or_bool>::type;
348};
349// Cast a value to its underlying type
350#define MAKE_UNDERLYING(Type, value) \
351 static_cast<typename comparison_underlying_type<Type>::type>(value)
352
353// is_signed_vs_unsigned::value is true if both types are integral, Lhs is
354// signed, and Rhs is unsigned. False in all other cases.
355template <typename Lhs, typename Rhs>
359 static constexpr bool value = std::is_integral_v<lhs_underlying> &&
360 std::is_integral_v<rhs_underlying> &&
361 std::is_signed_v<lhs_underlying> &&
362 std::is_unsigned_v<rhs_underlying>;
363};
364// Same thing, other way around: Lhs is unsigned, Rhs signed.
365template <typename Lhs, typename Rhs>
366struct is_unsigned_vs_signed : public is_signed_vs_unsigned<Rhs, Lhs> {};
367
374
375// Define the default implementation of Cmp##NAME##Impl to be used by
376// CHECK##NAME##Impl.
377// Note the specializations below for integral types with mismatching
378// signedness.
379#define DEFINE_CMP_IMPL(NAME, op) \
380 template <typename Lhs, typename Rhs> \
381 V8_INLINE constexpr bool Cmp##NAME##Impl(Lhs lhs, Rhs rhs) { \
382 return lhs op rhs; \
383 }
384DEFINE_CMP_IMPL(EQ, ==)
385DEFINE_CMP_IMPL(NE, !=)
386DEFINE_CMP_IMPL(LE, <=)
388DEFINE_CMP_IMPL(GE, >=)
389DEFINE_CMP_IMPL(GT, >)
390#undef DEFINE_CMP_IMPL
391
392// Specialize the compare functions for signed vs. unsigned comparisons (via the
393// `requires` clause).
394#define MAKE_UNSIGNED(Type, value) \
395 static_cast<typename std::make_unsigned< \
396 typename comparison_underlying_type<Type>::type>::type>(value)
397#define DEFINE_SIGNED_MISMATCH_COMP(CHECK, NAME, IMPL) \
398 template <typename Lhs, typename Rhs> \
399 requires(CHECK<Lhs, Rhs>::value) \
400 V8_INLINE constexpr bool Cmp##NAME##Impl(Lhs lhs, Rhs rhs) { \
401 return IMPL; \
402 }
404 lhs >= 0 && MAKE_UNSIGNED(Lhs, lhs) ==
405 MAKE_UNDERLYING(Rhs, rhs))
407 lhs < 0 || MAKE_UNSIGNED(Lhs, lhs) <
408 MAKE_UNDERLYING(Rhs, rhs))
410 lhs <= 0 || MAKE_UNSIGNED(Lhs, lhs) <=
411 MAKE_UNDERLYING(Rhs, rhs))
412DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, NE, !CmpEQImpl(lhs, rhs))
413DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, GT, !CmpLEImpl(lhs, rhs))
414DEFINE_SIGNED_MISMATCH_COMP(is_signed_vs_unsigned, GE, !CmpLTImpl(lhs, rhs))
421#undef MAKE_UNSIGNED
422#undef DEFINE_SIGNED_MISMATCH_COMP
423
424// Define the implementation of Check##NAME##Impl, using Cmp##NAME##Impl defined
425// above.
426#define DEFINE_CHECK_OP_IMPL(NAME) \
427 template <typename Lhs, typename Rhs> \
428 V8_INLINE constexpr std::string* Check##NAME##Impl(Lhs lhs, Rhs rhs, \
429 char const* msg) { \
430 using LhsPassT = typename pass_value_or_ref<Lhs>::type; \
431 using RhsPassT = typename pass_value_or_ref<Rhs>::type; \
432 bool cmp = Cmp##NAME##Impl<LhsPassT, RhsPassT>(lhs, rhs); \
433 return V8_LIKELY(cmp) \
434 ? nullptr \
435 : MakeCheckOpString<LhsPassT, RhsPassT>(lhs, rhs, msg); \
436 }
443#undef DEFINE_CHECK_OP_IMPL
444
445#define CHECK_EQ(lhs, rhs) CHECK_OP(EQ, ==, lhs, rhs)
446#define CHECK_NE(lhs, rhs) CHECK_OP(NE, !=, lhs, rhs)
447#define CHECK_LE(lhs, rhs) CHECK_OP(LE, <=, lhs, rhs)
448#define CHECK_LT(lhs, rhs) CHECK_OP(LT, <, lhs, rhs)
449#define CHECK_GE(lhs, rhs) CHECK_OP(GE, >=, lhs, rhs)
450#define CHECK_GT(lhs, rhs) CHECK_OP(GT, >, lhs, rhs)
451#define CHECK_NULL(val) CHECK((val) == nullptr)
452#define CHECK_NOT_NULL(val) CHECK((val) != nullptr)
453#define CHECK_IMPLIES(lhs, rhs) \
454 CHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs)
455// Performs a single (unsigned) comparison to check that {index} is
456// in range [0, limit).
457#define CHECK_BOUNDS(index, limit) \
458 CHECK_LT(static_cast<std::make_unsigned_t<decltype(index)>>(index), \
459 static_cast<std::make_unsigned_t<decltype(limit)>>(limit))
460
461} // namespace base
462} // namespace v8
463
464
465// The DCHECK macro is equivalent to CHECK except that it only
466// generates code in debug builds.
467#ifdef DEBUG
468#define DCHECK_EQ(lhs, rhs) DCHECK_OP(EQ, ==, lhs, rhs)
469#define DCHECK_NE(lhs, rhs) DCHECK_OP(NE, !=, lhs, rhs)
470#define DCHECK_GT(lhs, rhs) DCHECK_OP(GT, >, lhs, rhs)
471#define DCHECK_GE(lhs, rhs) DCHECK_OP(GE, >=, lhs, rhs)
472#define DCHECK_LT(lhs, rhs) DCHECK_OP(LT, <, lhs, rhs)
473#define DCHECK_LE(lhs, rhs) DCHECK_OP(LE, <=, lhs, rhs)
474#define DCHECK_NULL(val) DCHECK((val) == nullptr)
475#define DCHECK_NOT_NULL(val) DCHECK((val) != nullptr)
476#define DCHECK_IMPLIES(lhs, rhs) \
477 DCHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs)
478#define DCHECK_BOUNDS(index, limit) \
479 DCHECK_LT(static_cast<std::make_unsigned_t<decltype(index)>>(index), \
480 static_cast<std::make_unsigned_t<decltype(limit)>>(limit))
481#else
482#define DCHECK(condition) ((void) 0)
483#define DCHECK_WITH_LOC(condition, location) ((void)0)
484#define DCHECK_WITH_MSG_AND_LOC(condition, message, location) ((void)0)
485#define DCHECK_EQ(v1, v2) ((void) 0)
486#define DCHECK_NE(v1, v2) ((void) 0)
487#define DCHECK_GT(v1, v2) ((void) 0)
488#define DCHECK_GE(v1, v2) ((void) 0)
489#define DCHECK_LT(v1, v2) ((void) 0)
490#define DCHECK_LE(v1, v2) ((void) 0)
491#define DCHECK_NULL(val) ((void) 0)
492#define DCHECK_NOT_NULL(val) ((void) 0)
493#define DCHECK_IMPLIES(v1, v2) ((void) 0)
494#define DCHECK_BOUNDS(index, limit) ((void)0)
495#endif
496
497#endif // V8_BASE_LOGGING_H_
#define V8_BASE_EXPORT
Definition base-export.h:26
void V8_Fatal(const char *format,...)
Definition logging.cc:170
#define DEFINE_PRINT_CHECK_OPERAND_CHAR(type)
Definition logging.cc:106
std::string PrintToString(Ts &&... ts)
Definition logging.h:188
auto GetUnderlyingEnumTypeForPrinting(T val)
Definition logging.h:195
void SetPrintStackTrace(void(*print_stack_trace)())
Definition logging.cc:68
V8_NOINLINE std::string * MakeCheckOpString(Lhs lhs, Rhs rhs, char const *msg)
Definition logging.h:301
std::string PrintCheckOperand(T val)
Definition logging.h:212
void FatalOOM(OOMType type, const char *msg)
Definition logging.cc:80
constexpr const char * kUnreachableCodeMessage
Definition logging.h:63
void SetDcheckFunction(void(*dcheck_function)(const char *, int, const char *))
Definition logging.cc:72
constexpr const char * kUnimplementedCodeMessage
Definition logging.h:62
void SetFatalFunction(void(*fatal_function)(const char *, int, const char *))
Definition logging.cc:76
#define PRINTF_FORMAT(format_param, dots_param)
#define DEFINE_SIGNED_MISMATCH_COMP(CHECK, NAME, IMPL)
Definition logging.h:397
#define DEFINE_CMP_IMPL(NAME, op)
Definition logging.h:379
#define MAKE_UNSIGNED(Type, value)
Definition logging.h:394
#define DEFINE_CHECK_OP_IMPL(NAME)
#define EXPLICIT_CHECK_OP_INSTANTIATION(type)
Definition logging.h:318
#define MAKE_UNDERLYING(Type, value)
Definition logging.h:350
V8_BASE_EXPORT V8_NOINLINE void V8_Dcheck(const char *file, int line, const char *message)
Definition logging.cc:218
typename std::decay< T >::type decay
Definition logging.h:340
static constexpr bool is_enum
Definition logging.h:341
typename std::conditional< std::is_same_v< type_or_bool, bool >, unsigned int, type_or_bool >::type type
Definition logging.h:346
typename std::underlying_type< typename std::conditional< is_enum, decay, Dummy >::type >::type underlying
Definition logging.h:342
typename std::conditional< is_enum, underlying, decay >::type type_or_bool
Definition logging.h:344
typename comparison_underlying_type< Rhs >::type rhs_underlying
Definition logging.h:358
typename comparison_underlying_type< Lhs >::type lhs_underlying
Definition logging.h:357
#define V8_NOINLINE
Definition v8config.h:586