xref: /llvm-project/libcxx/test/support/count_new.h (revision 748023dc3210533df2c1c6efc8af1b5954493701)
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 [[noreturn]] inline void throw_bad_alloc_helper() {
28 #ifndef TEST_HAS_NO_EXCEPTIONS
29   throw std::bad_alloc();
30 #else
31        std::abort();
32 #endif
33 }
34 }
35 
36 class MemCounter
37 {
38 public:
39     // Make MemCounter super hard to accidentally construct or copy.
40     class MemCounterCtorArg_ {};
41     explicit MemCounter(MemCounterCtorArg_) { reset(); }
42 
43 private:
44     MemCounter(MemCounter const &);
45     MemCounter & operator=(MemCounter const &);
46 
47 public:
48     // All checks return true when disable_checking is enabled.
49     static const bool disable_checking;
50 
51     // Disallow any allocations from occurring. Useful for testing that
52     // code doesn't perform any allocations.
53     bool disable_allocations;
54 
55     // number of allocations to throw after. Default (unsigned)-1. If
56     // throw_after has the default value it will never be decremented.
57     static const unsigned never_throw_value = static_cast<unsigned>(-1);
58     unsigned throw_after;
59 
60     int outstanding_new;
61     int new_called;
62     int delete_called;
63     int aligned_new_called;
64     int aligned_delete_called;
65     std::size_t last_new_size;
66     std::size_t last_new_align;
67     std::size_t last_delete_align;
68 
69     int outstanding_array_new;
70     int new_array_called;
71     int delete_array_called;
72     int aligned_new_array_called;
73     int aligned_delete_array_called;
74     std::size_t last_new_array_size;
75     std::size_t last_new_array_align;
76     std::size_t last_delete_array_align;
77 
78 public:
79     void newCalled(std::size_t s)
80     {
81         assert(disable_allocations == false);
82         if (throw_after == 0) {
83             throw_after = never_throw_value;
84             detail::throw_bad_alloc_helper();
85         } else if (throw_after != never_throw_value) {
86             --throw_after;
87         }
88         ++new_called;
89         ++outstanding_new;
90         last_new_size = s;
91     }
92 
93     void alignedNewCalled(std::size_t s, std::size_t a) {
94       newCalled(s);
95       ++aligned_new_called;
96       last_new_align = a;
97     }
98 
99     void deleteCalled(void * p)
100     {
101       if (p) {
102         --outstanding_new;
103         ++delete_called;
104       }
105     }
106 
107     void alignedDeleteCalled(void *p, std::size_t a) {
108       if (p) {
109         deleteCalled(p);
110         ++aligned_delete_called;
111         last_delete_align = a;
112       }
113     }
114 
115     void newArrayCalled(std::size_t s)
116     {
117         assert(disable_allocations == false);
118         if (throw_after == 0) {
119             throw_after = never_throw_value;
120             detail::throw_bad_alloc_helper();
121         } else {
122             // don't decrement throw_after here. newCalled will end up doing that.
123         }
124         ++outstanding_array_new;
125         ++new_array_called;
126         last_new_array_size = s;
127     }
128 
129     void alignedNewArrayCalled(std::size_t s, std::size_t a) {
130       newArrayCalled(s);
131       ++aligned_new_array_called;
132       last_new_array_align = a;
133     }
134 
135     void deleteArrayCalled(void * p)
136     {
137         assert(p);
138         --outstanding_array_new;
139         ++delete_array_called;
140     }
141 
142     void alignedDeleteArrayCalled(void * p, std::size_t a) {
143       deleteArrayCalled(p);
144       ++aligned_delete_array_called;
145       last_delete_array_align = a;
146     }
147 
148     void disableAllocations()
149     {
150         disable_allocations = true;
151     }
152 
153     void enableAllocations()
154     {
155         disable_allocations = false;
156     }
157 
158     void reset()
159     {
160         disable_allocations = false;
161         throw_after = never_throw_value;
162 
163         outstanding_new = 0;
164         new_called = 0;
165         delete_called = 0;
166         aligned_new_called = 0;
167         aligned_delete_called = 0;
168         last_new_size = 0;
169         last_new_align = 0;
170 
171         outstanding_array_new = 0;
172         new_array_called = 0;
173         delete_array_called = 0;
174         aligned_new_array_called = 0;
175         aligned_delete_array_called = 0;
176         last_new_array_size = 0;
177         last_new_array_align = 0;
178     }
179 
180 public:
181     bool checkOutstandingNewEq(int n) const
182     {
183         return disable_checking || n == outstanding_new;
184     }
185 
186     bool checkOutstandingNewLessThanOrEqual(int n) const
187     {
188         return disable_checking || outstanding_new <= n;
189     }
190 
191     bool checkOutstandingNewNotEq(int n) const
192     {
193         return disable_checking || n != outstanding_new;
194     }
195 
196     bool checkNewCalledEq(int n) const
197     {
198         return disable_checking || n == new_called;
199     }
200 
201     bool checkNewCalledNotEq(int n) const
202     {
203         return disable_checking || n != new_called;
204     }
205 
206     bool checkNewCalledGreaterThan(int n) const
207     {
208         return disable_checking || new_called > n;
209     }
210 
211     bool checkDeleteCalledEq(int n) const
212     {
213         return disable_checking || n == delete_called;
214     }
215 
216     bool checkDeleteCalledNotEq(int n) const
217     {
218         return disable_checking || n != delete_called;
219     }
220 
221     bool checkDeleteCalledGreaterThan(int n) const
222     {
223         return disable_checking || delete_called > n;
224     }
225 
226     bool checkAlignedNewCalledEq(int n) const
227     {
228         return disable_checking || n == aligned_new_called;
229     }
230 
231     bool checkAlignedNewCalledNotEq(int n) const
232     {
233         return disable_checking || n != aligned_new_called;
234     }
235 
236     bool checkAlignedNewCalledGreaterThan(int n) const
237     {
238         return disable_checking || aligned_new_called > n;
239     }
240 
241     bool checkAlignedDeleteCalledEq(int n) const
242     {
243         return disable_checking || n == aligned_delete_called;
244     }
245 
246     bool checkAlignedDeleteCalledNotEq(int n) const
247     {
248         return disable_checking || n != aligned_delete_called;
249     }
250 
251     bool checkLastNewSizeEq(std::size_t n) const
252     {
253         return disable_checking || n == last_new_size;
254     }
255 
256     bool checkLastNewSizeNotEq(std::size_t n) const
257     {
258         return disable_checking || n != last_new_size;
259     }
260 
261     bool checkLastNewSizeGe(std::size_t n) const
262     {
263         return disable_checking || last_new_size >= n;
264     }
265 
266     bool checkLastNewAlignEq(std::size_t n) const
267     {
268         return disable_checking || n == last_new_align;
269     }
270 
271     bool checkLastNewAlignNotEq(std::size_t n) const
272     {
273         return disable_checking || n != last_new_align;
274     }
275 
276     bool checkLastNewAlignGe(std::size_t n) const
277     {
278         return disable_checking || last_new_align >= n;
279     }
280 
281     bool checkLastDeleteAlignEq(std::size_t n) const
282     {
283         return disable_checking || n == last_delete_align;
284     }
285 
286     bool checkLastDeleteAlignNotEq(std::size_t n) const
287     {
288         return disable_checking || n != last_delete_align;
289     }
290 
291     bool checkOutstandingArrayNewEq(int n) const
292     {
293         return disable_checking || n == outstanding_array_new;
294     }
295 
296     bool checkOutstandingArrayNewNotEq(int n) const
297     {
298         return disable_checking || n != outstanding_array_new;
299     }
300 
301     bool checkNewArrayCalledEq(int n) const
302     {
303         return disable_checking || n == new_array_called;
304     }
305 
306     bool checkNewArrayCalledNotEq(int n) const
307     {
308         return disable_checking || n != new_array_called;
309     }
310 
311     bool checkDeleteArrayCalledEq(int n) const
312     {
313         return disable_checking || n == delete_array_called;
314     }
315 
316     bool checkDeleteArrayCalledNotEq(int n) const
317     {
318         return disable_checking || n != delete_array_called;
319     }
320 
321     bool checkAlignedNewArrayCalledEq(int n) const
322     {
323         return disable_checking || n == aligned_new_array_called;
324     }
325 
326     bool checkAlignedNewArrayCalledNotEq(int n) const
327     {
328         return disable_checking || n != aligned_new_array_called;
329     }
330 
331     bool checkAlignedNewArrayCalledGreaterThan(int n) const
332     {
333         return disable_checking || aligned_new_array_called > n;
334     }
335 
336     bool checkAlignedDeleteArrayCalledEq(int n) const
337     {
338         return disable_checking || n == aligned_delete_array_called;
339     }
340 
341     bool checkAlignedDeleteArrayCalledNotEq(int n) const
342     {
343         return disable_checking || n != aligned_delete_array_called;
344     }
345 
346     bool checkLastNewArraySizeEq(std::size_t n) const
347     {
348         return disable_checking || n == last_new_array_size;
349     }
350 
351     bool checkLastNewArraySizeNotEq(std::size_t n) const
352     {
353         return disable_checking || n != last_new_array_size;
354     }
355 
356     bool checkLastNewArrayAlignEq(std::size_t n) const
357     {
358         return disable_checking || n == last_new_array_align;
359     }
360 
361     bool checkLastNewArrayAlignNotEq(std::size_t n) const
362     {
363         return disable_checking || n != last_new_array_align;
364     }
365 };
366 
367 #ifdef DISABLE_NEW_COUNT
368   const bool MemCounter::disable_checking = true;
369 #else
370   const bool MemCounter::disable_checking = false;
371 #endif
372 
373 TEST_DIAGNOSTIC_PUSH
374 TEST_MSVC_DIAGNOSTIC_IGNORED(4640) // '%s' construction of local static object is not thread safe (/Zc:threadSafeInit-)
375 inline MemCounter* getGlobalMemCounter() {
376   static MemCounter counter((MemCounter::MemCounterCtorArg_()));
377   return &counter;
378 }
379 TEST_DIAGNOSTIC_POP
380 
381 MemCounter &globalMemCounter = *getGlobalMemCounter();
382 
383 #ifndef DISABLE_NEW_COUNT
384 // operator new(size_t[, nothrow_t]) and operator delete(size_t[, nothrow_t])
385 void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
386   getGlobalMemCounter()->newCalled(s);
387   if (s == 0)
388     ++s;
389   void* p = std::malloc(s);
390   if (p == nullptr)
391     detail::throw_bad_alloc_helper();
392   return p;
393 }
394 
395 void* operator new(std::size_t s, std::nothrow_t const&) TEST_NOEXCEPT {
396 #  ifdef TEST_HAS_NO_EXCEPTIONS
397   getGlobalMemCounter()->newCalled(s);
398 #  else
399   try {
400     getGlobalMemCounter()->newCalled(s);
401   } catch (std::bad_alloc const&) {
402     return nullptr;
403   }
404 #  endif
405   return std::malloc(s);
406 }
407 
408 void operator delete(void* p) TEST_NOEXCEPT {
409   getGlobalMemCounter()->deleteCalled(p);
410   std::free(p);
411 }
412 
413 void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
414   getGlobalMemCounter()->deleteCalled(p);
415   std::free(p);
416 }
417 
418 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
419 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
420   getGlobalMemCounter()->newArrayCalled(s);
421   if (s == 0)
422     s++;
423   void* p = std::malloc(s);
424   if (p == nullptr)
425     detail::throw_bad_alloc_helper();
426   return p;
427 }
428 
429 void* operator new[](std::size_t s, std::nothrow_t const&) TEST_NOEXCEPT {
430 #  ifdef TEST_HAS_NO_EXCEPTIONS
431   getGlobalMemCounter()->newArrayCalled(s);
432 #  else
433   try {
434     getGlobalMemCounter()->newArrayCalled(s);
435   } catch (std::bad_alloc const&) {
436     return nullptr;
437   }
438 #  endif
439   return std::malloc(s);
440 }
441 
442 void operator delete[](void* p) TEST_NOEXCEPT {
443   getGlobalMemCounter()->deleteArrayCalled(p);
444   std::free(p);
445 }
446 
447 void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
448   getGlobalMemCounter()->deleteArrayCalled(p);
449   std::free(p);
450 }
451 
452 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
453 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
454 #      define USE_ALIGNED_ALLOC
455 #    endif
456 
457 #    if defined(__APPLE__)
458 #      if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&                                                   \
459            __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101500)
460 #        define TEST_HAS_NO_C11_ALIGNED_ALLOC
461 #      endif
462 #    elif defined(__ANDROID__) && __ANDROID_API__ < 28
463 #      define TEST_HAS_NO_C11_ALIGNED_ALLOC
464 #    endif
465 
466 inline void* allocate_aligned_impl(std::size_t size, std::align_val_t align) {
467   const std::size_t alignment = static_cast<std::size_t>(align);
468   void* ret                   = nullptr;
469 #    ifdef USE_ALIGNED_ALLOC
470   ret = _aligned_malloc(size, alignment);
471 #    elif TEST_STD_VER >= 17 && !defined(TEST_HAS_NO_C11_ALIGNED_ALLOC)
472   size_t rounded_size = (size + alignment - 1) & ~(alignment - 1);
473   ret                 = aligned_alloc(alignment, size > rounded_size ? size : rounded_size);
474 #    else
475   assert(posix_memalign(&ret, std::max(alignment, sizeof(void*)), size) != EINVAL);
476 #    endif
477   return ret;
478 }
479 
480 inline void free_aligned_impl(void* ptr, std::align_val_t) {
481   if (ptr) {
482 #    ifdef USE_ALIGNED_ALLOC
483     ::_aligned_free(ptr);
484 #    else
485     ::free(ptr);
486 #    endif
487   }
488 }
489 
490 // operator new(size_t, align_val_t[, nothrow_t]) and operator delete(size_t, align_val_t[, nothrow_t])
491 void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
492   getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
493   void* p = allocate_aligned_impl(s, av);
494   if (p == nullptr)
495     detail::throw_bad_alloc_helper();
496   return p;
497 }
498 
499 void* operator new(std::size_t s, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
500 #    ifdef TEST_HAS_NO_EXCEPTIONS
501   getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
502 #    else
503   try {
504     getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
505   } catch (std::bad_alloc const&) {
506     return nullptr;
507   }
508 #    endif
509   return allocate_aligned_impl(s, av);
510 }
511 
512 void operator delete(void* p, std::align_val_t av) TEST_NOEXCEPT {
513   getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
514   free_aligned_impl(p, av);
515 }
516 
517 void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
518   getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
519   free_aligned_impl(p, av);
520 }
521 
522 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
523 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
524   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
525   void* p = allocate_aligned_impl(s, av);
526   if (p == nullptr)
527     detail::throw_bad_alloc_helper();
528   return p;
529 }
530 
531 void* operator new[](std::size_t s, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
532 #    ifdef TEST_HAS_NO_EXCEPTIONS
533   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
534 #    else
535   try {
536     getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
537   } catch (std::bad_alloc const&) {
538     return nullptr;
539   }
540 #    endif
541   return allocate_aligned_impl(s, av);
542 }
543 
544 void operator delete[](void* p, std::align_val_t av) TEST_NOEXCEPT {
545   getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
546   free_aligned_impl(p, av);
547 }
548 
549 void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
550   getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
551   free_aligned_impl(p, av);
552 }
553 
554 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
555 
556 #endif // DISABLE_NEW_COUNT
557 
558 struct DisableAllocationGuard {
559     explicit DisableAllocationGuard(bool disable = true) : m_disabled(disable)
560     {
561         // Don't re-disable if already disabled.
562         if (globalMemCounter.disable_allocations == true) m_disabled = false;
563         if (m_disabled) globalMemCounter.disableAllocations();
564     }
565 
566     void release() {
567         if (m_disabled) globalMemCounter.enableAllocations();
568         m_disabled = false;
569     }
570 
571     ~DisableAllocationGuard() {
572         release();
573     }
574 
575 private:
576     bool m_disabled;
577 
578     DisableAllocationGuard(DisableAllocationGuard const&);
579     DisableAllocationGuard& operator=(DisableAllocationGuard const&);
580 };
581 
582 #if TEST_STD_VER >= 20
583 
584 struct ConstexprDisableAllocationGuard {
585     TEST_CONSTEXPR_CXX14 explicit ConstexprDisableAllocationGuard(bool disable = true) : m_disabled(disable)
586     {
587         if (!TEST_IS_CONSTANT_EVALUATED) {
588             // Don't re-disable if already disabled.
589             if (globalMemCounter.disable_allocations == true) m_disabled = false;
590             if (m_disabled) globalMemCounter.disableAllocations();
591         } else {
592             m_disabled = false;
593         }
594     }
595 
596     TEST_CONSTEXPR_CXX14 void release() {
597         if (!TEST_IS_CONSTANT_EVALUATED) {
598             if (m_disabled) globalMemCounter.enableAllocations();
599             m_disabled = false;
600         }
601     }
602 
603     TEST_CONSTEXPR_CXX20 ~ConstexprDisableAllocationGuard() {
604         release();
605     }
606 
607 private:
608     bool m_disabled;
609 
610     ConstexprDisableAllocationGuard(ConstexprDisableAllocationGuard const&);
611     ConstexprDisableAllocationGuard& operator=(ConstexprDisableAllocationGuard const&);
612 };
613 
614 #endif
615 
616 struct RequireAllocationGuard {
617     explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
618             : m_req_alloc(RequireAtLeast),
619               m_new_count_on_init(globalMemCounter.new_called),
620               m_outstanding_new_on_init(globalMemCounter.outstanding_new),
621               m_exactly(false)
622     {
623     }
624 
625     void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; }
626     void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; }
627 
628     ~RequireAllocationGuard() {
629         assert(globalMemCounter.checkOutstandingNewEq(static_cast<int>(m_outstanding_new_on_init)));
630         std::size_t Expect = m_new_count_on_init + m_req_alloc;
631         assert(globalMemCounter.checkNewCalledEq(static_cast<int>(Expect)) ||
632                (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(static_cast<int>(Expect))));
633     }
634 
635 private:
636     std::size_t m_req_alloc;
637     const std::size_t m_new_count_on_init;
638     const std::size_t m_outstanding_new_on_init;
639     bool m_exactly;
640     RequireAllocationGuard(RequireAllocationGuard const&);
641     RequireAllocationGuard& operator=(RequireAllocationGuard const&);
642 };
643 
644 #endif /* COUNT_NEW_H */
645