xref: /llvm-project/libcxx/test/support/count_new.h (revision 4e112e5c1c8511056030294af3264da35f95d93c)
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   void* p = std::malloc(s);
389   if (p == nullptr)
390     detail::throw_bad_alloc_helper();
391   return p;
392 }
393 
394 void* operator new(std::size_t s, std::nothrow_t const&) TEST_NOEXCEPT {
395 #  ifdef TEST_HAS_NO_EXCEPTIONS
396   getGlobalMemCounter()->newCalled(s);
397 #  else
398   try {
399     getGlobalMemCounter()->newCalled(s);
400   } catch (std::bad_alloc const&) {
401     return nullptr;
402   }
403 #  endif
404   return std::malloc(s);
405 }
406 
407 void operator delete(void* p) TEST_NOEXCEPT {
408   getGlobalMemCounter()->deleteCalled(p);
409   std::free(p);
410 }
411 
412 void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
413   getGlobalMemCounter()->deleteCalled(p);
414   std::free(p);
415 }
416 
417 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
418 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
419   getGlobalMemCounter()->newArrayCalled(s);
420   void* p = std::malloc(s);
421   if (p == nullptr)
422     detail::throw_bad_alloc_helper();
423   return p;
424 }
425 
426 void* operator new[](std::size_t s, std::nothrow_t const&) TEST_NOEXCEPT {
427 #  ifdef TEST_HAS_NO_EXCEPTIONS
428   getGlobalMemCounter()->newArrayCalled(s);
429 #  else
430   try {
431     getGlobalMemCounter()->newArrayCalled(s);
432   } catch (std::bad_alloc const&) {
433     return nullptr;
434   }
435 #  endif
436   return std::malloc(s);
437 }
438 
439 void operator delete[](void* p) TEST_NOEXCEPT {
440   getGlobalMemCounter()->deleteArrayCalled(p);
441   std::free(p);
442 }
443 
444 void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
445   getGlobalMemCounter()->deleteArrayCalled(p);
446   std::free(p);
447 }
448 
449 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
450 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
451 #      define USE_ALIGNED_ALLOC
452 #    endif
453 
454 inline void* alocate_aligned_impl(std::size_t size, std::align_val_t align) {
455   const std::size_t alignment = static_cast<std::size_t>(align);
456   void* ret                   = nullptr;
457 #    ifdef USE_ALIGNED_ALLOC
458   ret = _aligned_malloc(size, alignment);
459 #    else
460   assert(posix_memalign(&ret, std::max(alignment, sizeof(void*)), size) != EINVAL);
461 #    endif
462   return ret;
463 }
464 
465 inline void free_aligned_impl(void* ptr, std::align_val_t) {
466   if (ptr) {
467 #    ifdef USE_ALIGNED_ALLOC
468     ::_aligned_free(ptr);
469 #    else
470     ::free(ptr);
471 #    endif
472   }
473 }
474 
475 // operator new(size_t, align_val_t[, nothrow_t]) and operator delete(size_t, align_val_t[, nothrow_t])
476 void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
477   getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
478   void* p = alocate_aligned_impl(s, av);
479   if (p == nullptr)
480     detail::throw_bad_alloc_helper();
481   return p;
482 }
483 
484 void* operator new(std::size_t s, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
485 #    ifdef TEST_HAS_NO_EXCEPTIONS
486   getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
487 #    else
488   try {
489     getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
490   } catch (std::bad_alloc const&) {
491     return nullptr;
492   }
493 #    endif
494   return alocate_aligned_impl(s, av);
495 }
496 
497 void operator delete(void* p, std::align_val_t av) TEST_NOEXCEPT {
498   getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
499   free_aligned_impl(p, av);
500 }
501 
502 void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
503   getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
504   free_aligned_impl(p, av);
505 }
506 
507 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
508 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
509   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
510   void* p = alocate_aligned_impl(s, av);
511   if (p == nullptr)
512     detail::throw_bad_alloc_helper();
513   return p;
514 }
515 
516 void* operator new[](std::size_t s, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
517 #    ifdef TEST_HAS_NO_EXCEPTIONS
518   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
519 #    else
520   try {
521     getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
522   } catch (std::bad_alloc const&) {
523     return nullptr;
524   }
525 #    endif
526   return alocate_aligned_impl(s, av);
527 }
528 
529 void operator delete[](void* p, std::align_val_t av) TEST_NOEXCEPT {
530   getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
531   free_aligned_impl(p, av);
532 }
533 
534 void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST_NOEXCEPT {
535   getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
536   free_aligned_impl(p, av);
537 }
538 
539 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
540 
541 #endif // DISABLE_NEW_COUNT
542 
543 struct DisableAllocationGuard {
544     explicit DisableAllocationGuard(bool disable = true) : m_disabled(disable)
545     {
546         // Don't re-disable if already disabled.
547         if (globalMemCounter.disable_allocations == true) m_disabled = false;
548         if (m_disabled) globalMemCounter.disableAllocations();
549     }
550 
551     void release() {
552         if (m_disabled) globalMemCounter.enableAllocations();
553         m_disabled = false;
554     }
555 
556     ~DisableAllocationGuard() {
557         release();
558     }
559 
560 private:
561     bool m_disabled;
562 
563     DisableAllocationGuard(DisableAllocationGuard const&);
564     DisableAllocationGuard& operator=(DisableAllocationGuard const&);
565 };
566 
567 #if TEST_STD_VER >= 20
568 
569 struct ConstexprDisableAllocationGuard {
570     TEST_CONSTEXPR_CXX14 explicit ConstexprDisableAllocationGuard(bool disable = true) : m_disabled(disable)
571     {
572         if (!TEST_IS_CONSTANT_EVALUATED) {
573             // Don't re-disable if already disabled.
574             if (globalMemCounter.disable_allocations == true) m_disabled = false;
575             if (m_disabled) globalMemCounter.disableAllocations();
576         } else {
577             m_disabled = false;
578         }
579     }
580 
581     TEST_CONSTEXPR_CXX14 void release() {
582         if (!TEST_IS_CONSTANT_EVALUATED) {
583             if (m_disabled) globalMemCounter.enableAllocations();
584             m_disabled = false;
585         }
586     }
587 
588     TEST_CONSTEXPR_CXX20 ~ConstexprDisableAllocationGuard() {
589         release();
590     }
591 
592 private:
593     bool m_disabled;
594 
595     ConstexprDisableAllocationGuard(ConstexprDisableAllocationGuard const&);
596     ConstexprDisableAllocationGuard& operator=(ConstexprDisableAllocationGuard const&);
597 };
598 
599 #endif
600 
601 struct RequireAllocationGuard {
602     explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
603             : m_req_alloc(RequireAtLeast),
604               m_new_count_on_init(globalMemCounter.new_called),
605               m_outstanding_new_on_init(globalMemCounter.outstanding_new),
606               m_exactly(false)
607     {
608     }
609 
610     void requireAtLeast(std::size_t N) { m_req_alloc = N; m_exactly = false; }
611     void requireExactly(std::size_t N) { m_req_alloc = N; m_exactly = true; }
612 
613     ~RequireAllocationGuard() {
614         assert(globalMemCounter.checkOutstandingNewEq(static_cast<int>(m_outstanding_new_on_init)));
615         std::size_t Expect = m_new_count_on_init + m_req_alloc;
616         assert(globalMemCounter.checkNewCalledEq(static_cast<int>(Expect)) ||
617                (!m_exactly && globalMemCounter.checkNewCalledGreaterThan(static_cast<int>(Expect))));
618     }
619 
620 private:
621     std::size_t m_req_alloc;
622     const std::size_t m_new_count_on_init;
623     const std::size_t m_outstanding_new_on_init;
624     bool m_exactly;
625     RequireAllocationGuard(RequireAllocationGuard const&);
626     RequireAllocationGuard& operator=(RequireAllocationGuard const&);
627 };
628 
629 #endif /* COUNT_NEW_H */
630