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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_ 7 8 #include <cstdint> 9 10 #include "base/allocator/partition_allocator/page_allocator_constants.h" 11 #include "base/allocator/partition_allocator/partition_alloc_base/check.h" 12 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" 13 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" 14 #include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" 15 #include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" 16 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" 17 #include "build/build_config.h" 18 19 #define PA_STRINGIFY_IMPL(s) #s 20 #define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s) 21 22 // When PartitionAlloc is used as the default allocator, we cannot use the 23 // regular (D)CHECK() macros, as they allocate internally. When an assertion is 24 // triggered, they format strings, leading to reentrancy in the code, which none 25 // of PartitionAlloc is designed to support (and especially not for error 26 // paths). 27 // 28 // As a consequence: 29 // - When PartitionAlloc is not malloc(), use the regular macros 30 // - Otherwise, crash immediately. This provides worse error messages though. 31 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 32 // For official build discard log strings to reduce binary bloat. 33 #if !CHECK_WILL_STREAM() 34 // See base/check.h for implementation details. 35 #define PA_CHECK(condition) \ 36 PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \ 37 : PA_EAT_CHECK_STREAM_PARAMS() 38 #else 39 // PartitionAlloc uses async-signal-safe RawCheck() for error reporting. 40 // Async-signal-safe functions are guaranteed to not allocate as otherwise they 41 // could operate with inconsistent allocator state. 42 #define PA_CHECK(condition) \ 43 PA_UNLIKELY(!(condition)) \ 44 ? ::partition_alloc::internal::logging::RawCheck( \ 45 __FILE__ "(" PA_STRINGIFY(__LINE__) ") Check failed: " #condition) \ 46 : PA_EAT_CHECK_STREAM_PARAMS() 47 #endif // !CHECK_WILL_STREAM() 48 49 #if BUILDFLAG(PA_DCHECK_IS_ON) 50 #define PA_DCHECK(condition) PA_CHECK(condition) 51 #else 52 #define PA_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) 53 #endif // BUILDFLAG(PA_DCHECK_IS_ON) 54 55 #define PA_PCHECK(condition) \ 56 if (!(condition)) { \ 57 int error = errno; \ 58 ::partition_alloc::internal::base::debug::Alias(&error); \ 59 PA_IMMEDIATE_CRASH(); \ 60 } 61 62 #if BUILDFLAG(PA_DCHECK_IS_ON) 63 #define PA_DPCHECK(condition) PA_PCHECK(condition) 64 #else 65 #define PA_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) 66 #endif // BUILDFLAG(PA_DCHECK_IS_ON) 67 68 #else 69 #define PA_CHECK(condition) PA_BASE_CHECK(condition) 70 #define PA_DCHECK(condition) PA_BASE_DCHECK(condition) 71 #define PA_PCHECK(condition) PA_BASE_PCHECK(condition) 72 #define PA_DPCHECK(condition) PA_BASE_DPCHECK(condition) 73 #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 74 75 // Expensive dchecks that run within *Scan. These checks are only enabled in 76 // debug builds with dchecks enabled. 77 #if !defined(NDEBUG) 78 #define PA_SCAN_DCHECK_IS_ON() BUILDFLAG(PA_DCHECK_IS_ON) 79 #else 80 #define PA_SCAN_DCHECK_IS_ON() 0 81 #endif 82 83 #if PA_SCAN_DCHECK_IS_ON() 84 #define PA_SCAN_DCHECK(expr) PA_DCHECK(expr) 85 #else 86 #define PA_SCAN_DCHECK(expr) PA_EAT_CHECK_STREAM_PARAMS(!(expr)) 87 #endif 88 89 #if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR) 90 91 // Use this macro to assert on things that are conditionally constexpr as 92 // determined by PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR or 93 // PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR. Where fixed at compile time, this 94 // is a static_assert. Where determined at run time, this is a PA_CHECK. 95 // Therefore, this macro must only be used where both a static_assert and a 96 // PA_CHECK would be viable, that is, within a function, and ideally a function 97 // that executes only once, early in the program, such as during initialization. 98 #define STATIC_ASSERT_OR_PA_CHECK(condition, message) \ 99 static_assert(condition, message) 100 101 #else 102 103 #define STATIC_ASSERT_OR_PA_CHECK(condition, message) \ 104 do { \ 105 PA_CHECK(condition) << (message); \ 106 } while (false) 107 108 #endif 109 110 // alignas(16) DebugKv causes breakpad_unittests and sandbox_linux_unittests 111 // failures on android-marshmallow-x86-rel because of SIGSEGV. 112 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY) && \ 113 defined(ARCH_CPU_32_BITS) 114 #define PA_DEBUGKV_ALIGN alignas(8) 115 #else 116 #define PA_DEBUGKV_ALIGN alignas(16) 117 #endif 118 119 namespace partition_alloc::internal { 120 121 // Used for PA_DEBUG_DATA_ON_STACK, below. 122 struct PA_DEBUGKV_ALIGN DebugKv { 123 // 16 bytes object aligned on 16 bytes, to make it easier to see in crash 124 // reports. 125 char k[8] = {}; // Not necessarily 0-terminated. 126 uint64_t v = 0; 127 DebugKvDebugKv128 DebugKv(const char* key, uint64_t value) : v(value) { 129 // Fill with ' ', so that the stack dump is nicer to read. Not using 130 // memset() on purpose, this header is included from *many* places. 131 for (int index = 0; index < 8; index++) { 132 k[index] = ' '; 133 } 134 135 for (int index = 0; index < 8; index++) { 136 k[index] = key[index]; 137 if (key[index] == '\0') { 138 break; 139 } 140 } 141 } 142 }; 143 144 } // namespace partition_alloc::internal 145 146 #define PA_CONCAT(x, y) x##y 147 #define PA_CONCAT2(x, y) PA_CONCAT(x, y) 148 #define PA_DEBUG_UNIQUE_NAME PA_CONCAT2(kv, __LINE__) 149 150 // Puts a key-value pair on the stack for debugging. `base::debug::Alias()` 151 // makes sure a local variable is saved on the stack, but the variables can be 152 // hard to find in crash reports, particularly if the frame pointer is not 153 // present / invalid. 154 // 155 // This puts a key right before the value on the stack. The key has to be a C 156 // string, which gets truncated if it's longer than 8 characters. 157 // Example use: 158 // PA_DEBUG_DATA_ON_STACK("size", 0x42) 159 // 160 // Sample output in lldb: 161 // (lldb) x 0x00007fffffffd0d0 0x00007fffffffd0f0 162 // 0x7fffffffd0d0: 73 69 7a 65 00 00 00 00 42 00 00 00 00 00 00 00 163 // size............ 164 // 165 // With gdb, one can use: 166 // x/8g <STACK_POINTER> 167 // to see the data. With lldb, "x <STACK_POINTER> <FRAME_POJNTER>" can be used. 168 #define PA_DEBUG_DATA_ON_STACK(name, value) \ 169 ::partition_alloc::internal::DebugKv PA_DEBUG_UNIQUE_NAME{name, value}; \ 170 ::partition_alloc::internal::base::debug::Alias(&PA_DEBUG_UNIQUE_NAME); 171 172 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_ 173