1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef COUNT_NEW_H
10 #define COUNT_NEW_H
11
12 #include <algorithm>
13 #include <cassert>
14 #include <cerrno>
15 #include <cstdlib>
16 #include <new>
17 #include <type_traits>
18
19 #include "test_macros.h"
20
21 #if defined(TEST_HAS_SANITIZERS)
22 #define DISABLE_NEW_COUNT
23 #endif
24
25 namespace detail
26 {
27 TEST_NORETURN
throw_bad_alloc_helper()28 inline void throw_bad_alloc_helper() {
29 #ifndef TEST_HAS_NO_EXCEPTIONS
30 throw std::bad_alloc();
31 #else
32 std::abort();
33 #endif
34 }
35 }
36
37 class MemCounter
38 {
39 public:
40 // Make MemCounter super hard to accidentally construct or copy.
41 class MemCounterCtorArg_ {};
MemCounter(MemCounterCtorArg_)42 explicit MemCounter(MemCounterCtorArg_) { reset(); }
43
44 private:
45 MemCounter(MemCounter const &);
46 MemCounter & operator=(MemCounter const &);
47
48 public:
49 // All checks return true when disable_checking is enabled.
50 static const bool disable_checking;
51
52 // Disallow any allocations from occurring. Useful for testing that
53 // code doesn't perform any allocations.
54 bool disable_allocations;
55
56 // number of allocations to throw after. Default (unsigned)-1. If
57 // throw_after has the default value it will never be decremented.
58 static const unsigned never_throw_value = static_cast<unsigned>(-1);
59 unsigned throw_after;
60
61 int outstanding_new;
62 int new_called;
63 int delete_called;
64 int aligned_new_called;
65 int aligned_delete_called;
66 std::size_t last_new_size;
67 std::size_t last_new_align;
68 std::size_t last_delete_align;
69
70 int outstanding_array_new;
71 int new_array_called;
72 int delete_array_called;
73 int aligned_new_array_called;
74 int aligned_delete_array_called;
75 std::size_t last_new_array_size;
76 std::size_t last_new_array_align;
77 std::size_t last_delete_array_align;
78
79 public:
newCalled(std::size_t s)80 void newCalled(std::size_t s)
81 {
82 assert(disable_allocations == false);
83 if (throw_after == 0) {
84 throw_after = never_throw_value;
85 detail::throw_bad_alloc_helper();
86 } else if (throw_after != never_throw_value) {
87 --throw_after;
88 }
89 ++new_called;
90 ++outstanding_new;
91 last_new_size = s;
92 }
93
alignedNewCalled(std::size_t s,std::size_t a)94 void alignedNewCalled(std::size_t s, std::size_t a) {
95 newCalled(s);
96 ++aligned_new_called;
97 last_new_align = a;
98 }
99
deleteCalled(void * p)100 void deleteCalled(void * p)
101 {
102 assert(p);
103 --outstanding_new;
104 ++delete_called;
105 }
106
alignedDeleteCalled(void * p,std::size_t a)107 void alignedDeleteCalled(void *p, std::size_t a) {
108 deleteCalled(p);
109 ++aligned_delete_called;
110 last_delete_align = a;
111 }
112
newArrayCalled(std::size_t s)113 void newArrayCalled(std::size_t s)
114 {
115 assert(disable_allocations == false);
116 if (throw_after == 0) {
117 throw_after = never_throw_value;
118 detail::throw_bad_alloc_helper();
119 } else {
120 // don't decrement throw_after here. newCalled will end up doing that.
121 }
122 ++outstanding_array_new;
123 ++new_array_called;
124 last_new_array_size = s;
125 }
126
alignedNewArrayCalled(std::size_t s,std::size_t a)127 void alignedNewArrayCalled(std::size_t s, std::size_t a) {
128 newArrayCalled(s);
129 ++aligned_new_array_called;
130 last_new_array_align = a;
131 }
132
deleteArrayCalled(void * p)133 void deleteArrayCalled(void * p)
134 {
135 assert(p);
136 --outstanding_array_new;
137 ++delete_array_called;
138 }
139
alignedDeleteArrayCalled(void * p,std::size_t a)140 void alignedDeleteArrayCalled(void * p, std::size_t a) {
141 deleteArrayCalled(p);
142 ++aligned_delete_array_called;
143 last_delete_array_align = a;
144 }
145
disableAllocations()146 void disableAllocations()
147 {
148 disable_allocations = true;
149 }
150
enableAllocations()151 void enableAllocations()
152 {
153 disable_allocations = false;
154 }
155
reset()156 void reset()
157 {
158 disable_allocations = false;
159 throw_after = never_throw_value;
160
161 outstanding_new = 0;
162 new_called = 0;
163 delete_called = 0;
164 aligned_new_called = 0;
165 aligned_delete_called = 0;
166 last_new_size = 0;
167 last_new_align = 0;
168
169 outstanding_array_new = 0;
170 new_array_called = 0;
171 delete_array_called = 0;
172 aligned_new_array_called = 0;
173 aligned_delete_array_called = 0;
174 last_new_array_size = 0;
175 last_new_array_align = 0;
176 }
177
178 public:
checkOutstandingNewEq(int n)179 bool checkOutstandingNewEq(int n) const
180 {
181 return disable_checking || n == outstanding_new;
182 }
183
checkOutstandingNewNotEq(int n)184 bool checkOutstandingNewNotEq(int n) const
185 {
186 return disable_checking || n != outstanding_new;
187 }
188
checkNewCalledEq(int n)189 bool checkNewCalledEq(int n) const
190 {
191 return disable_checking || n == new_called;
192 }
193
checkNewCalledNotEq(int n)194 bool checkNewCalledNotEq(int n) const
195 {
196 return disable_checking || n != new_called;
197 }
198
checkNewCalledGreaterThan(int n)199 bool checkNewCalledGreaterThan(int n) const
200 {
201 return disable_checking || new_called > n;
202 }
203
checkDeleteCalledEq(int n)204 bool checkDeleteCalledEq(int n) const
205 {
206 return disable_checking || n == delete_called;
207 }
208
checkDeleteCalledNotEq(int n)209 bool checkDeleteCalledNotEq(int n) const
210 {
211 return disable_checking || n != delete_called;
212 }
213
checkDeleteCalledGreaterThan(int n)214 bool checkDeleteCalledGreaterThan(int n) const
215 {
216 return disable_checking || delete_called > n;
217 }
218
checkAlignedNewCalledEq(int n)219 bool checkAlignedNewCalledEq(int n) const
220 {
221 return disable_checking || n == aligned_new_called;
222 }
223
checkAlignedNewCalledNotEq(int n)224 bool checkAlignedNewCalledNotEq(int n) const
225 {
226 return disable_checking || n != aligned_new_called;
227 }
228
checkAlignedNewCalledGreaterThan(int n)229 bool checkAlignedNewCalledGreaterThan(int n) const
230 {
231 return disable_checking || aligned_new_called > n;
232 }
233
checkAlignedDeleteCalledEq(int n)234 bool checkAlignedDeleteCalledEq(int n) const
235 {
236 return disable_checking || n == aligned_delete_called;
237 }
238
checkAlignedDeleteCalledNotEq(int n)239 bool checkAlignedDeleteCalledNotEq(int n) const
240 {
241 return disable_checking || n != aligned_delete_called;
242 }
243
checkLastNewSizeEq(std::size_t n)244 bool checkLastNewSizeEq(std::size_t n) const
245 {
246 return disable_checking || n == last_new_size;
247 }
248
checkLastNewSizeNotEq(std::size_t n)249 bool checkLastNewSizeNotEq(std::size_t n) const
250 {
251 return disable_checking || n != last_new_size;
252 }
253
checkLastNewSizeGe(std::size_t n)254 bool checkLastNewSizeGe(std::size_t n) const
255 {
256 return disable_checking || last_new_size >= n;
257 }
258
checkLastNewAlignEq(std::size_t n)259 bool checkLastNewAlignEq(std::size_t n) const
260 {
261 return disable_checking || n == last_new_align;
262 }
263
checkLastNewAlignNotEq(std::size_t n)264 bool checkLastNewAlignNotEq(std::size_t n) const
265 {
266 return disable_checking || n != last_new_align;
267 }
268
checkLastNewAlignGe(std::size_t n)269 bool checkLastNewAlignGe(std::size_t n) const
270 {
271 return disable_checking || last_new_align >= n;
272 }
273
checkLastDeleteAlignEq(std::size_t n)274 bool checkLastDeleteAlignEq(std::size_t n) const
275 {
276 return disable_checking || n == last_delete_align;
277 }
278
checkLastDeleteAlignNotEq(std::size_t n)279 bool checkLastDeleteAlignNotEq(std::size_t n) const
280 {
281 return disable_checking || n != last_delete_align;
282 }
283
checkOutstandingArrayNewEq(int n)284 bool checkOutstandingArrayNewEq(int n) const
285 {
286 return disable_checking || n == outstanding_array_new;
287 }
288
checkOutstandingArrayNewNotEq(int n)289 bool checkOutstandingArrayNewNotEq(int n) const
290 {
291 return disable_checking || n != outstanding_array_new;
292 }
293
checkNewArrayCalledEq(int n)294 bool checkNewArrayCalledEq(int n) const
295 {
296 return disable_checking || n == new_array_called;
297 }
298
checkNewArrayCalledNotEq(int n)299 bool checkNewArrayCalledNotEq(int n) const
300 {
301 return disable_checking || n != new_array_called;
302 }
303
checkDeleteArrayCalledEq(int n)304 bool checkDeleteArrayCalledEq(int n) const
305 {
306 return disable_checking || n == delete_array_called;
307 }
308
checkDeleteArrayCalledNotEq(int n)309 bool checkDeleteArrayCalledNotEq(int n) const
310 {
311 return disable_checking || n != delete_array_called;
312 }
313
checkAlignedNewArrayCalledEq(int n)314 bool checkAlignedNewArrayCalledEq(int n) const
315 {
316 return disable_checking || n == aligned_new_array_called;
317 }
318
checkAlignedNewArrayCalledNotEq(int n)319 bool checkAlignedNewArrayCalledNotEq(int n) const
320 {
321 return disable_checking || n != aligned_new_array_called;
322 }
323
checkAlignedNewArrayCalledGreaterThan(int n)324 bool checkAlignedNewArrayCalledGreaterThan(int n) const
325 {
326 return disable_checking || aligned_new_array_called > n;
327 }
328
checkAlignedDeleteArrayCalledEq(int n)329 bool checkAlignedDeleteArrayCalledEq(int n) const
330 {
331 return disable_checking || n == aligned_delete_array_called;
332 }
333
checkAlignedDeleteArrayCalledNotEq(int n)334 bool checkAlignedDeleteArrayCalledNotEq(int n) const
335 {
336 return disable_checking || n != aligned_delete_array_called;
337 }
338
checkLastNewArraySizeEq(std::size_t n)339 bool checkLastNewArraySizeEq(std::size_t n) const
340 {
341 return disable_checking || n == last_new_array_size;
342 }
343
checkLastNewArraySizeNotEq(std::size_t n)344 bool checkLastNewArraySizeNotEq(std::size_t n) const
345 {
346 return disable_checking || n != last_new_array_size;
347 }
348
checkLastNewArrayAlignEq(std::size_t n)349 bool checkLastNewArrayAlignEq(std::size_t n) const
350 {
351 return disable_checking || n == last_new_array_align;
352 }
353
checkLastNewArrayAlignNotEq(std::size_t n)354 bool checkLastNewArrayAlignNotEq(std::size_t n) const
355 {
356 return disable_checking || n != last_new_array_align;
357 }
358 };
359
360 #ifdef DISABLE_NEW_COUNT
361 const bool MemCounter::disable_checking = true;
362 #else
363 const bool MemCounter::disable_checking = false;
364 #endif
365
366 TEST_DIAGNOSTIC_PUSH
367 TEST_MSVC_DIAGNOSTIC_IGNORED(4640) // '%s' construction of local static object is not thread safe (/Zc:threadSafeInit-)
getGlobalMemCounter()368 inline MemCounter* getGlobalMemCounter() {
369 static MemCounter counter((MemCounter::MemCounterCtorArg_()));
370 return &counter;
371 }
372 TEST_DIAGNOSTIC_POP
373
374 MemCounter &globalMemCounter = *getGlobalMemCounter();
375
376 #ifndef DISABLE_NEW_COUNT
new(std::size_t s)377 void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
378 {
379 getGlobalMemCounter()->newCalled(s);
380 void* ret = std::malloc(s);
381 if (ret == nullptr)
382 detail::throw_bad_alloc_helper();
383 return ret;
384 }
385
delete(void * p)386 void operator delete(void* p) TEST_NOEXCEPT
387 {
388 getGlobalMemCounter()->deleteCalled(p);
389 std::free(p);
390 }
391
TEST_THROW_SPEC(std::bad_alloc)392 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
393 {
394 getGlobalMemCounter()->newArrayCalled(s);
395 return operator new(s);
396 }
397
398 void operator delete[](void* p) TEST_NOEXCEPT
399 {
400 getGlobalMemCounter()->deleteArrayCalled(p);
401 operator delete(p);
402 }
403
404 #ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
405 #if defined(_LIBCPP_MSVCRT_LIKE) || \
406 (!defined(_LIBCPP_VERSION) && defined(_WIN32))
407 #define USE_ALIGNED_ALLOC
408 #endif
409
new(std::size_t s,std::align_val_t av)410 void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
411 const std::size_t a = static_cast<std::size_t>(av);
412 getGlobalMemCounter()->alignedNewCalled(s, a);
413 void *ret = nullptr;
414 #ifdef USE_ALIGNED_ALLOC
415 ret = _aligned_malloc(s, a);
416 #else
417 assert(posix_memalign(&ret, std::max(a, sizeof(void*)), s) != EINVAL);
418 #endif
419 if (ret == nullptr)
420 detail::throw_bad_alloc_helper();
421 return ret;
422 }
423
delete(void * p,std::align_val_t av)424 void operator delete(void *p, std::align_val_t av) TEST_NOEXCEPT {
425 const std::size_t a = static_cast<std::size_t>(av);
426 getGlobalMemCounter()->alignedDeleteCalled(p, a);
427 if (p) {
428 #ifdef USE_ALIGNED_ALLOC
429 ::_aligned_free(p);
430 #else
431 ::free(p);
432 #endif
433 }
434 }
435
TEST_THROW_SPEC(std::bad_alloc)436 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
437 const std::size_t a = static_cast<std::size_t>(av);
438 getGlobalMemCounter()->alignedNewArrayCalled(s, a);
439 return operator new(s, av);
440 }
441
442 void operator delete[](void *p, std::align_val_t av) TEST_NOEXCEPT {
443 const std::size_t a = static_cast<std::size_t>(av);
444 getGlobalMemCounter()->alignedDeleteArrayCalled(p, a);
445 return operator delete(p, av);
446 }
447
448 #endif // TEST_HAS_NO_ALIGNED_ALLOCATION
449
450 #endif // DISABLE_NEW_COUNT
451
452 struct DisableAllocationGuard {
m_disabledDisableAllocationGuard453 explicit DisableAllocationGuard(bool disable = true) : m_disabled(disable)
454 {
455 // Don't re-disable if already disabled.
456 if (globalMemCounter.disable_allocations == true) m_disabled = false;
457 if (m_disabled) globalMemCounter.disableAllocations();
458 }
459
releaseDisableAllocationGuard460 void release() {
461 if (m_disabled) globalMemCounter.enableAllocations();
462 m_disabled = false;
463 }
464
~DisableAllocationGuardDisableAllocationGuard465 ~DisableAllocationGuard() {
466 release();
467 }
468
469 private:
470 bool m_disabled;
471
472 DisableAllocationGuard(DisableAllocationGuard const&);
473 DisableAllocationGuard& operator=(DisableAllocationGuard const&);
474 };
475
476 #if TEST_STD_VER >= 20
477
478 struct ConstexprDisableAllocationGuard {
m_disabledConstexprDisableAllocationGuard479 TEST_CONSTEXPR_CXX14 explicit ConstexprDisableAllocationGuard(bool disable = true) : m_disabled(disable)
480 {
481 if (!TEST_IS_CONSTANT_EVALUATED) {
482 // Don't re-disable if already disabled.
483 if (globalMemCounter.disable_allocations == true) m_disabled = false;
484 if (m_disabled) globalMemCounter.disableAllocations();
485 } else {
486 m_disabled = false;
487 }
488 }
489
releaseConstexprDisableAllocationGuard490 TEST_CONSTEXPR_CXX14 void release() {
491 if (!TEST_IS_CONSTANT_EVALUATED) {
492 if (m_disabled) globalMemCounter.enableAllocations();
493 m_disabled = false;
494 }
495 }
496
~ConstexprDisableAllocationGuardConstexprDisableAllocationGuard497 TEST_CONSTEXPR_CXX20 ~ConstexprDisableAllocationGuard() {
498 release();
499 }
500
501 private:
502 bool m_disabled;
503
504 ConstexprDisableAllocationGuard(ConstexprDisableAllocationGuard const&);
505 ConstexprDisableAllocationGuard& operator=(ConstexprDisableAllocationGuard const&);
506 };
507
508 #endif
509
510 struct RequireAllocationGuard {
511 explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
m_req_allocRequireAllocationGuard512 : m_req_alloc(RequireAtLeast),
513 m_new_count_on_init(globalMemCounter.new_called),
514 m_outstanding_new_on_init(globalMemCounter.outstanding_new),
515 m_exactly(false)
516 {
517 }
518
requireAtLeastRequireAllocationGuard519 void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; }
requireExactlyRequireAllocationGuard520 void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; }
521
~RequireAllocationGuardRequireAllocationGuard522 ~RequireAllocationGuard() {
523 assert(globalMemCounter.checkOutstandingNewEq(static_cast<int>(m_outstanding_new_on_init)));
524 std::size_t Expect = m_new_count_on_init + m_req_alloc;
525 assert(globalMemCounter.checkNewCalledEq(static_cast<int>(Expect)) ||
526 (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(static_cast<int>(Expect))));
527 }
528
529 private:
530 std::size_t m_req_alloc;
531 const std::size_t m_new_count_on_init;
532 const std::size_t m_outstanding_new_on_init;
533 bool m_exactly;
534 RequireAllocationGuard(RequireAllocationGuard const&);
535 RequireAllocationGuard& operator=(RequireAllocationGuard const&);
536 };
537
538 #endif /* COUNT_NEW_H */
539