blob: 054ca3df429565c0298292188e15315b78c042a0 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_
#include <iosfwd>
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
#include "partition_alloc/partition_alloc_base/immediate_crash.h"
#include "partition_alloc/partition_alloc_base/log_message.h"
#include "partition_alloc/partition_alloc_base/strings/cstring_builder.h"
#define PA_STRINGIFY_IMPL(s) #s
#define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s)
// This header defines the CHECK, DCHECK, and DPCHECK macros.
//
// CHECK dies with a fatal error if its condition is not true. It is not
// controlled by NDEBUG, so the check will be executed regardless of compilation
// mode.
//
// DCHECK, the "debug mode" check, is enabled depending on NDEBUG and
// DCHECK_ALWAYS_ON, and its severity depends on DCHECK_IS_CONFIGURABLE.
//
// (D)PCHECK is like (D)CHECK, but includes the system error code (c.f.
// perror(3)).
//
// Additional information can be streamed to these macros and will be included
// in the log output if the condition doesn't hold (you may need to include
// <ostream>):
//
// CHECK(condition) << "Additional info.";
//
// The condition is evaluated exactly once. Even in build modes where e.g.
// DCHECK is disabled, the condition and any stream arguments are still
// referenced to avoid warnings about unused variables and functions.
//
// For the (D)CHECK_EQ, etc. macros, see base/check_op.h. However, that header
// is *significantly* larger than check.h, so try to avoid including it in
// header files.
namespace partition_alloc::internal::logging {
// Class used to explicitly ignore an ostream, and optionally a boolean value.
class VoidifyStream {
public:
VoidifyStream() = default;
explicit VoidifyStream(bool ignored) {}
// This operator has lower precedence than << but higher than ?:
void operator&(base::strings::CStringBuilder&) {}
};
// Helper macro which avoids evaluating the arguments to a stream if the
// condition is false.
#define PA_LAZY_CHECK_STREAM(stream, condition) \
!(condition) \
? (void)0 \
: ::partition_alloc::internal::logging::VoidifyStream() & (stream)
// Macro which uses but does not evaluate expr and any stream parameters.
#define PA_EAT_CHECK_STREAM_PARAMS(expr) \
true ? (void)0 \
: ::partition_alloc::internal::logging::VoidifyStream(expr) & \
(*::partition_alloc::internal::logging::g_swallow_stream)
PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)
extern base::strings::CStringBuilder* g_swallow_stream;
class LogMessage;
// Class used for raising a check error upon destruction.
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) CheckError {
public:
// Stream for adding optional details to the error message.
base::strings::CStringBuilder& stream();
PA_NOMERGE ~CheckError();
protected:
CheckError(const char* file,
int line,
LogSeverity severity,
const char* condition);
CheckError(const char* file, int line, LogSeverity severity);
CheckError(const char* file,
int line,
LogSeverity severity,
const char* condition,
SystemErrorCode err_code);
union {
LogMessage log_message_;
#if BUILDFLAG(IS_WIN)
Win32ErrorLogMessage errno_log_message_;
#else
ErrnoLogMessage errno_log_message_;
#endif
};
// |has_errno| describes which union member is used, |log_message_| or
// |errno_log_message_|. If |has_errno| is true, CheckError initializes
// |errno_log_message_| at its constructor and destroys at its destructor.
// (This also means the CheckError is an instance of the parent class of
// PCheck or DPCheck.)
// If false, CheckError initializes and destroys |log_message_|.
const bool has_errno = false;
};
namespace check_error {
// Class used for raising a check error upon destruction.
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) Check : public CheckError {
public:
Check(const char* file, int line, const char* condition);
};
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DCheck : public CheckError {
public:
DCheck(const char* file, int line, const char* condition);
};
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) PCheck : public CheckError {
public:
PCheck(const char* file, int line, const char* condition);
PCheck(const char* file, int line);
};
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DPCheck : public CheckError {
public:
DPCheck(const char* file, int line, const char* condition);
};
class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) NotImplemented
: public CheckError {
public:
NotImplemented(const char* file, int line, const char* function);
};
} // namespace check_error
#if defined(OFFICIAL_BUILD) && !defined(NDEBUG)
#error "Debug builds are not expected to be optimized as official builds."
#endif // defined(OFFICIAL_BUILD) && !defined(NDEBUG)
#if defined(OFFICIAL_BUILD) && !BUILDFLAG(PA_DCHECK_IS_ON)
// Discard log strings to reduce code bloat.
//
// This is not calling BreakDebugger since this is called frequently, and
// calling an out-of-line function instead of a noreturn inline macro prevents
// compiler optimizations.
#define PA_BASE_CHECK(condition) \
PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \
: PA_EAT_CHECK_STREAM_PARAMS()
#define PA_BASE_CHECK_WILL_STREAM() false
#define PA_BASE_PCHECK(condition) \
PA_LAZY_CHECK_STREAM( \
::partition_alloc::internal::logging::check_error::PCheck(__FILE__, \
__LINE__) \
.stream(), \
PA_UNLIKELY(!(condition)))
#else
#define PA_BASE_CHECK(condition) \
PA_LAZY_CHECK_STREAM( \
::partition_alloc::internal::logging::check_error::Check( \
__FILE__, __LINE__, #condition) \
.stream(), \
!PA_ANALYZER_ASSUME_TRUE(condition))
#define PA_BASE_CHECK_WILL_STREAM() true
#define PA_BASE_PCHECK(condition) \
PA_LAZY_CHECK_STREAM( \
::partition_alloc::internal::logging::check_error::PCheck( \
__FILE__, __LINE__, #condition) \
.stream(), \
!PA_ANALYZER_ASSUME_TRUE(condition))
#endif
#if BUILDFLAG(PA_DCHECK_IS_ON)
#define PA_BASE_DCHECK(condition) \
PA_LAZY_CHECK_STREAM( \
::partition_alloc::internal::logging::check_error::DCheck( \
__FILE__, __LINE__, #condition) \
.stream(), \
!PA_ANALYZER_ASSUME_TRUE(condition))
#define PA_BASE_DPCHECK(condition) \
PA_LAZY_CHECK_STREAM( \
::partition_alloc::internal::logging::check_error::DPCheck( \
__FILE__, __LINE__, #condition) \
.stream(), \
!PA_ANALYZER_ASSUME_TRUE(condition))
#else
#define PA_BASE_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
#define PA_BASE_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition))
#endif
// Async signal safe checking mechanism.
[[noreturn]] PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) void RawCheckFailure(
const char* message);
#define PA_RAW_CHECK(condition) \
do { \
if (!(condition)) \
::partition_alloc::internal::logging::RawCheckFailure( \
"Check failed: " #condition "\n"); \
} while (0)
} // namespace partition_alloc::internal::logging
#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_