• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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