Mjolnir Core
Core functionality of the Mjolnir API
new_delete_counter.h
Go to the documentation of this file.
1 
7 
8 #pragma once
9 
10 
11 // === MACROS =========================================================================================================
12 
13 #if defined(_MSC_VER)
14 # include <malloc.h>
15 # define ALIGNED_ALLOC(alignment, size) _aligned_malloc(size, alignment)
16 # define ALIGNED_FREE _aligned_free
17 #elif defined(__GNUC__)
18 # include <cstdlib>
19 # define ALIGNED_ALLOC(alignment, size) std::aligned_alloc(alignment, size) // NOLINT(cppcoreguidelines-macro-usage)
20 # define ALIGNED_FREE std::free // NOLINT(cppcoreguidelines-macro-usage)
21 #else
22 static_assert(false, "Incompatible compiler");
23 #endif
24 
25 
26 #ifndef DISABLE_NEW_DELETE_COUNTER
27 
28 
43 # define ASSERT_NUM_DELETE_EQ(num_delete_exp) ASSERT_EQ(new_delete_counter.get_num_delete_calls(), num_delete_exp)
44 
45 
46 // --------------------------------------------------------------------------------------------------------------------
47 
64 # define ASSERT_NUM_NEW_AND_DELETE_EQ(num_new_exp, num_delete_exp) \
65  ASSERT_NUM_NEW_EQ(num_new_exp); \
66  ASSERT_NUM_DELETE_EQ(num_delete_exp)
67 
68 
69 // --------------------------------------------------------------------------------------------------------------------
70 
84 # define ASSERT_NUM_NEW_EQ(num_new_exp) ASSERT_EQ(new_delete_counter.get_num_new_calls(), num_new_exp)
85 
86 
87 // --------------------------------------------------------------------------------------------------------------------
88 
94 # define COUNT_NEW_AND_DELETE auto new_delete_counter = NewDeleteCounter()
95 
96 
97 // --------------------------------------------------------------------------------------------------------------------
98 
117 # define EXPECT_NUM_DELETE_EQ(num_delete_exp) EXPECT_EQ(new_delete_counter.get_num_delete_calls(), num_delete_exp)
118 
119 
120 // --------------------------------------------------------------------------------------------------------------------
121 
142 # define EXPECT_NUM_NEW_AND_DELETE_EQ(num_new_exp, num_delete_exp) \
143  EXPECT_NUM_NEW_EQ(num_new_exp); \
144  EXPECT_NUM_DELETE_EQ(num_delete_exp)
145 
146 
147 // --------------------------------------------------------------------------------------------------------------------
148 
166 # define EXPECT_NUM_NEW_EQ(num_new_exp) EXPECT_EQ(new_delete_counter.get_num_new_calls(), num_new_exp)
167 
168 
169 #else
170 # define ASSERT_NUM_DELETE_EQ(num_delete_exp)
171 # define ASSERT_NUM_NEW_AND_DELETE_EQ(num_new_exp, num_delete_exp)
172 # define ASSERT_NUM_NEW_EQ(num_new_exp)
173 # define COUNT_NEW_AND_DELETE
174 # define EXPECT_NUM_DELETE_EQ(num_delete_exp)
175 # define EXPECT_NUM_NEW_AND_DELETE_EQ(num_new_exp, num_delete_exp)
176 # define EXPECT_NUM_NEW_EQ(num_new_exp)
177 #endif
178 
179 
180 // === DECLARATIONS ===================================================================================================
181 
182 
184 
185 #include <atomic>
186 #include <iostream>
187 
188 
189 namespace mjolnir
190 {
193 
194 
205 {
206 public:
208  NewDeleteCounter(const NewDeleteCounter&) = delete;
210  ~NewDeleteCounter() = default;
211 
212  auto operator=(const NewDeleteCounter&) -> NewDeleteCounter& = delete;
213  auto operator=(NewDeleteCounter&&) -> NewDeleteCounter& = delete;
214 
215 
221  [[nodiscard]] auto get_num_delete_calls() const noexcept -> I32;
222 
223 
229  [[nodiscard]] auto get_num_new_calls() const noexcept -> I32;
230 
231 
237  [[nodiscard]] static auto get_total_num_delete_calls() noexcept -> I32;
238 
239 
245  [[nodiscard]] static auto get_total_num_new_calls() noexcept -> I32;
246 
247 
250  void print_num_calls() const noexcept;
251 
252 
253 private:
256  static void increase_total_delete_calls() noexcept;
257 
258 
261  static void increase_total_new_calls() noexcept;
262 
263 
272  [[nodiscard]] static auto return_value([[maybe_unused]] I32 value) noexcept -> I32;
273 
274 
275  std::atomic<I32> m_num_new_at_construction = -1;
276  std::atomic<I32> m_num_del_at_construction = -1;
277 
278  inline static std::atomic<I32> m_num_new_global = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
279  inline static std::atomic<I32> m_num_del_global = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
280 
281 
282 #ifndef DISABLE_NEW_DELETE_COUNTER
283  friend auto ::operator new(std::size_t size) -> void*;
284  friend auto ::operator new(std::size_t size, std::align_val_t al) -> void*;
285  friend auto ::operator new(std::size_t size, const std::nothrow_t&) noexcept -> void*;
286  friend auto ::operator new(std::size_t size, std::align_val_t al, const std::nothrow_t&) noexcept -> void*;
287  friend auto ::operator new[](std::size_t size) -> void*;
288  friend auto ::operator new[](std::size_t size, std::align_val_t al) -> void*;
289  friend auto ::operator new[](std::size_t size, const std::nothrow_t&) noexcept -> void*;
290  friend auto ::operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t&) noexcept -> void*;
291  friend void ::operator delete(void* ptr) noexcept;
292  friend void ::operator delete(void* ptr, const std::nothrow_t&) noexcept;
293  friend void ::operator delete[](void* ptr) noexcept;
294  friend void ::operator delete[](void* ptr, const std::nothrow_t&) noexcept;
295  friend void ::operator delete(void* ptr, std::align_val_t al) noexcept;
296  friend void ::operator delete[](void* ptr, std::align_val_t al) noexcept;
297  friend void ::operator delete(void* ptr, std::align_val_t al, const std::nothrow_t& tag) noexcept;
298  friend void ::operator delete[](void* ptr, std::align_val_t al, const std::nothrow_t& tag) noexcept;
299 # ifdef __cpp_sized_deallocation
300  friend void ::operator delete(void* ptr, std::size_t sz) noexcept;
301  friend void ::operator delete[](void* ptr, std::size_t sz) noexcept;
302  friend void ::operator delete(void* ptr, std::size_t sz, std::align_val_t al) noexcept;
303  friend void ::operator delete[](void* ptr, std::size_t sz, std::align_val_t al) noexcept;
304 # endif // __cpp_sized_deallocation
305 #endif // DISABLE_NEW_DELETE_COUNTER
306 };
307 
308 
310 } // namespace mjolnir
311 
312 
313 // === DEFINITIONS ====================================================================================================
314 
315 
316 namespace mjolnir
317 {
318 // --------------------------------------------------------------------------------------------------------------------
319 
320 inline NewDeleteCounter::NewDeleteCounter()
321  : m_num_new_at_construction(m_num_new_global.load())
322  , m_num_del_at_construction(m_num_del_global.load())
323 {
324 }
325 
326 
327 // --------------------------------------------------------------------------------------------------------------------
328 
329 [[nodiscard]] inline auto NewDeleteCounter::get_num_delete_calls() const noexcept -> I32
330 {
331  return return_value(m_num_del_global - m_num_del_at_construction);
332 }
333 
334 
335 // --------------------------------------------------------------------------------------------------------------------
336 
337 [[nodiscard]] inline auto NewDeleteCounter::get_num_new_calls() const noexcept -> I32
338 {
339  return return_value(m_num_new_global - m_num_new_at_construction);
340 }
341 
342 
343 // --------------------------------------------------------------------------------------------------------------------
344 
345 [[nodiscard]] inline auto NewDeleteCounter::get_total_num_delete_calls() noexcept -> I32
346 {
347  return return_value(m_num_del_global);
348 }
349 
350 
351 // --------------------------------------------------------------------------------------------------------------------
352 
353 [[nodiscard]] inline auto NewDeleteCounter::get_total_num_new_calls() noexcept -> I32
354 {
355  return return_value(m_num_new_global);
356 }
357 
358 
359 // --------------------------------------------------------------------------------------------------------------------
360 
361 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
362 inline void NewDeleteCounter::print_num_calls() const noexcept
363 {
364 #ifndef DISABLE_NEW_DELETE_COUNTER
365  std::cout << "Number of new calls : " << get_num_new_calls() << std::endl;
366  std::cout << "Number of delete calls : " << get_num_delete_calls() << std::endl;
367 #else
368  std::cout << "Global new counter disabled." << std::endl;
369 #endif // DISABLE_NEW_DELETE_COUNTER
370 }
371 
372 
373 // --------------------------------------------------------------------------------------------------------------------
374 
375 inline void NewDeleteCounter::increase_total_delete_calls() noexcept
376 {
377  ++m_num_del_global;
378 }
379 
380 
381 // --------------------------------------------------------------------------------------------------------------------
382 
383 inline void NewDeleteCounter::increase_total_new_calls() noexcept
384 {
385  ++m_num_new_global;
386 }
387 
388 
389 // --------------------------------------------------------------------------------------------------------------------
390 
391 [[nodiscard]] inline auto NewDeleteCounter::return_value([[maybe_unused]] I32 value) noexcept -> I32
392 {
393 #ifndef DISABLE_NEW_DELETE_COUNTER
394  return value;
395 #else
396  return -1;
397 #endif // DISABLE_NEW_DELETE_COUNTER
398 }
399 
400 
401 // --------------------------------------------------------------------------------------------------------------------
402 
403 
404 } // namespace mjolnir
405 
406 
407 // === Overloads of new ===============================================================================================
408 
409 
411 
412 #ifndef DISABLE_NEW_DELETE_COUNTER
413 auto operator new(std::size_t size) -> void*
414 {
415  void* p = malloc(size);
416  if (! p)
417  throw std::bad_alloc(); // LCOV_EXCL_LINE
418 
419  mjolnir::NewDeleteCounter::increase_total_new_calls();
420  return p;
421 }
422 
423 auto operator new[](std::size_t size) -> void*
424 {
425  void* p = malloc(size);
426  if (! p)
427  throw std::bad_alloc(); // LCOV_EXCL_LINE
428 
429  mjolnir::NewDeleteCounter::increase_total_new_calls();
430  return p;
431 }
432 
433 auto operator new(std::size_t size, const std::nothrow_t&) noexcept -> void*
434 {
435  mjolnir::NewDeleteCounter::increase_total_new_calls();
436  return malloc(size);
437 }
438 
439 auto operator new[](std::size_t size, const std::nothrow_t&) noexcept -> void*
440 {
441  mjolnir::NewDeleteCounter::increase_total_new_calls();
442  return malloc(size);
443 }
444 
445 auto operator new(std::size_t size, std::align_val_t al) -> void*
446 {
447  void* p = ALIGNED_ALLOC(static_cast<size_t>(al), size);
448  if (! p)
449  throw std::bad_alloc(); // LCOV_EXCL_LINE
450 
451  mjolnir::NewDeleteCounter::increase_total_new_calls();
452  return p;
453 }
454 
455 auto operator new[](std::size_t size, std::align_val_t al) -> void*
456 {
457  void* p = ALIGNED_ALLOC(static_cast<size_t>(al), size);
458  if (! p)
459  throw std::bad_alloc(); // LCOV_EXCL_LINE
460 
461  mjolnir::NewDeleteCounter::increase_total_new_calls();
462  return p;
463 }
464 
465 auto operator new(std::size_t size, std::align_val_t al, const std::nothrow_t&) noexcept -> void*
466 {
467  mjolnir::NewDeleteCounter::increase_total_new_calls();
468  return ALIGNED_ALLOC(static_cast<size_t>(al), size);
469 }
470 
471 auto operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t&) noexcept -> void*
472 {
473  mjolnir::NewDeleteCounter::increase_total_new_calls();
474  return ALIGNED_ALLOC(static_cast<size_t>(al), size);
475 }
476 
477 void operator delete(void* ptr) noexcept
478 {
479  mjolnir::NewDeleteCounter::increase_total_delete_calls();
480  free(ptr);
481 }
482 
483 void operator delete[](void* ptr) noexcept
484 {
485  mjolnir::NewDeleteCounter::increase_total_delete_calls();
486  free(ptr);
487 }
488 
489 void operator delete(void* ptr, const std::nothrow_t&) noexcept
490 {
491  mjolnir::NewDeleteCounter::increase_total_delete_calls();
492  free(ptr);
493 }
494 
495 void operator delete[](void* ptr, const std::nothrow_t&) noexcept
496 {
497  mjolnir::NewDeleteCounter::increase_total_delete_calls();
498  free(ptr);
499 }
500 
501 
502 # ifdef __cpp_sized_deallocation
503 void operator delete(void* ptr, [[maybe_unused]] std::size_t sz) noexcept
504 {
505  mjolnir::NewDeleteCounter::increase_total_delete_calls();
506  free(ptr);
507 }
508 
509 void operator delete[](void* ptr, [[maybe_unused]] std::size_t sz) noexcept
510 {
511  mjolnir::NewDeleteCounter::increase_total_delete_calls();
512  free(ptr);
513 }
514 
515 void operator delete(void* ptr, [[maybe_unused]] std::size_t sz, [[maybe_unused]] std::align_val_t al) noexcept
516 {
517  mjolnir::NewDeleteCounter::increase_total_delete_calls();
518  free(ptr);
519 }
520 
521 void operator delete[](void* ptr, [[maybe_unused]] std::size_t sz, [[maybe_unused]] std::align_val_t al) noexcept
522 {
523  mjolnir::NewDeleteCounter::increase_total_delete_calls();
524  free(ptr);
525 }
526 # endif // __cpp_sized_deallocation
527 
528 void operator delete(void* ptr, [[maybe_unused]] std::align_val_t al) noexcept
529 {
530  mjolnir::NewDeleteCounter::increase_total_delete_calls();
531  ALIGNED_FREE(ptr);
532 }
533 
534 void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t al) noexcept
535 {
536  mjolnir::NewDeleteCounter::increase_total_delete_calls();
537  ALIGNED_FREE(ptr);
538 }
539 
540 void operator delete(void* ptr,
541  [[maybe_unused]] std::align_val_t al,
542  [[maybe_unused]] const std::nothrow_t& tag) noexcept
543 {
544  mjolnir::NewDeleteCounter::increase_total_delete_calls();
545  ALIGNED_FREE(ptr);
546 }
547 
548 void operator delete[](void* ptr,
549  [[maybe_unused]] std::align_val_t al,
550  [[maybe_unused]] const std::nothrow_t& tag) noexcept
551 {
552  mjolnir::NewDeleteCounter::increase_total_delete_calls();
553  ALIGNED_FREE(ptr);
554 }
555 
556 #endif // DISABLE_NEW_DELETE_COUNTER
557 
static auto get_total_num_delete_calls() noexcept -> I32
Gets the total number of delete calls of the program.
Definition: new_delete_counter.h:345
static auto get_total_num_new_calls() noexcept -> I32
Gets the total number of new calls of the program.
Definition: new_delete_counter.h:353
auto get_num_new_calls() const noexcept -> I32
Gets the number of new calls since the construction of the instance.
Definition: new_delete_counter.h:337
auto get_num_delete_calls() const noexcept -> I32
Gets the number of delete calls since the construction of the instance.
Definition: new_delete_counter.h:329
Counts the number of new and delete calls since the instance was created.
Definition: new_delete_counter.h:205
void print_num_calls() const noexcept
Prints the number of new and delete calls since the construction of the class instance.
Definition: new_delete_counter.h:362
Defines the fundamental data types.
std::int32_t I32
32 bit signed integer type
Definition: fundamental_types.h:22