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