LCOV - code coverage report
Current view: top level - core - memory_manager.h (source / functions) Coverage Total Hit
Test: hashbrowns Coverage Lines: 92.8 % 83 77
Test Date: 2026-04-10 19:00:18 Functions: 100.0 % 44 44
Legend: Lines: hit not hit

            Line data    Source code
       1              : #ifndef HASHBROWNS_MEMORY_MANAGER_H
       2              : #define HASHBROWNS_MEMORY_MANAGER_H
       3              : 
       4              : #include <cstddef>
       5              : #include <memory>
       6              : #include <atomic>
       7              : #include <unordered_map>
       8              : #include <mutex>
       9              : #include <string>
      10              : #include <vector>
      11              : #include <type_traits>
      12              : 
      13              : namespace hashbrowns {
      14              : 
      15              : /**
      16              :  * @brief Memory tracking and debugging utilities for the benchmarking suite
      17              :  * 
      18              :  * This class provides detailed memory allocation tracking, leak detection,
      19              :  * and performance analysis for custom data structures. It's designed to be
      20              :  * lightweight in production but comprehensive for development and testing.
      21              :  */
      22              : class MemoryTracker {
      23              : public:
      24              :     /**
      25              :      * @brief Statistics about memory allocation patterns
      26              :      */
      27              :     struct Stats {
      28              :         size_t total_allocated{0};    ///< Total bytes allocated
      29              :         size_t total_deallocated{0};  ///< Total bytes deallocated
      30              :         size_t current_usage{0};      ///< Current memory usage
      31              :         size_t peak_usage{0};         ///< Peak memory usage
      32              :         size_t allocation_count{0};   ///< Number of allocations
      33              :         size_t deallocation_count{0}; ///< Number of deallocations
      34              : 
      35              :         /**
      36              :          * @brief Get current memory leakage (allocated - deallocated)
      37              :          */
      38            5 :         size_t memory_leaked() const {
      39            5 :             return total_allocated - total_deallocated;
      40              :         }
      41              : 
      42              :         /**
      43              :          * @brief Get number of outstanding allocations
      44              :          */
      45            2 :         size_t outstanding_allocations() const {
      46            2 :             return allocation_count - deallocation_count;
      47              :         }
      48              :     };
      49              : 
      50              :     /**
      51              :      * @brief Get the singleton instance of the memory tracker
      52              :      */
      53              :     static MemoryTracker& instance();
      54              : 
      55              :     /**
      56              :      * @brief Record a memory allocation
      57              :      * @param ptr Pointer to allocated memory
      58              :      * @param size Size of allocation in bytes
      59              :      * @param file Source file where allocation occurred
      60              :      * @param line Line number where allocation occurred
      61              :      */
      62              :     void record_allocation(void* ptr, size_t size, const char* file = nullptr, int line = 0);
      63              : 
      64              :     /**
      65              :      * @brief Record a memory deallocation
      66              :      * @param ptr Pointer to memory being deallocated
      67              :      */
      68              :     void record_deallocation(void* ptr);
      69              : 
      70              :     /**
      71              :      * @brief Get current memory statistics
      72              :      */
      73          125 :     Stats get_stats() const { 
      74          125 :         std::lock_guard<std::mutex> lock(mutex_);
      75          250 :         return stats_; 
      76          125 :     }
      77              : 
      78              :     /**
      79              :      * @brief Reset all tracking statistics
      80              :      */
      81              :     void reset();
      82              : 
      83              :     /**
      84              :      * @brief Check for memory leaks and print report
      85              :      * @return true if no leaks detected, false otherwise
      86              :      */
      87              :     bool check_leaks() const;
      88              : 
      89              :     /**
      90              :      * @brief Enable or disable detailed tracking (affects performance)
      91              :      */
      92           10 :     void set_detailed_tracking(bool enabled) { detailed_tracking_ = enabled; }
      93              : 
      94              : private:
      95            1 :     MemoryTracker() = default;
      96            1 :     ~MemoryTracker() = default;
      97              :     MemoryTracker(const MemoryTracker&) = delete;
      98              :     MemoryTracker& operator=(const MemoryTracker&) = delete;
      99              : 
     100              :     mutable std::mutex mutex_;
     101              :     Stats stats_;
     102              :     bool detailed_tracking_ = false;
     103              :     std::unordered_map<void*, size_t> allocations_; ///< ptr -> size mapping for leak detection
     104              : };
     105              : 
     106              : /**
     107              :  * @brief Custom allocator that integrates with memory tracking
     108              :  * 
     109              :  * This allocator can be used with STL containers or custom data structures
     110              :  * to provide detailed allocation tracking and analysis.
     111              :  */
     112              : template<typename T>
     113              : class TrackedAllocator {
     114              : public:
     115              :     using value_type = T;
     116              :     using size_type = std::size_t;
     117              :     using difference_type = std::ptrdiff_t;
     118              : 
     119              :     TrackedAllocator() = default;
     120              :     
     121              :     template<typename U>
     122              :     TrackedAllocator(const TrackedAllocator<U>&) noexcept {}
     123              : 
     124              :     /**
     125              :      * @brief Allocate memory for n objects of type T
     126              :      */
     127          246 :     T* allocate(size_type n) {
     128          246 :         if (n > std::numeric_limits<size_type>::max() / sizeof(T)) {
     129            0 :             throw std::bad_alloc();
     130              :         }
     131              :         
     132          246 :         size_t bytes = n * sizeof(T);
     133          246 :         T* ptr = static_cast<T*>(std::malloc(bytes));
     134              :         
     135          246 :         if (!ptr) {
     136            0 :             throw std::bad_alloc();
     137              :         }
     138              :         
     139          246 :         MemoryTracker::instance().record_allocation(ptr, bytes);
     140          246 :         return ptr;
     141              :     }
     142              : 
     143              :     /**
     144              :      * @brief Deallocate memory previously allocated by this allocator
     145              :      */
     146          246 :     void deallocate(T* ptr, size_type) noexcept {
     147          246 :         if (ptr) {
     148          246 :             MemoryTracker::instance().record_deallocation(ptr);
     149          246 :             std::free(ptr);
     150              :         }
     151          246 :     }
     152              : 
     153              :     template<typename U>
     154              :     bool operator==(const TrackedAllocator<U>&) const noexcept { return true; }
     155              :     
     156              :     template<typename U>
     157              :     bool operator!=(const TrackedAllocator<U>&) const noexcept { return false; }
     158              : };
     159              : 
     160              : /**
     161              :  * @brief RAII wrapper for raw memory management
     162              :  * 
     163              :  * This class provides safe, automatic cleanup of raw memory allocations
     164              :  * while integrating with the memory tracking system.
     165              :  */
     166              : template<typename T>
     167              : class unique_array {
     168              : public:
     169              :     /**
     170              :      * @brief Construct from raw pointer (takes ownership)
     171              :      */
     172           62 :     explicit unique_array(T* ptr = nullptr, size_t size = 0) 
     173           62 :         : ptr_(ptr), size_(size) {
     174           62 :         if (ptr_ && size_ > 0) {
     175           62 :             MemoryTracker::instance().record_allocation(ptr_, size_ * sizeof(T));
     176              :         }
     177           62 :     }
     178              : 
     179              :     /**
     180              :      * @brief Destructor - automatically deallocates memory
     181              :      */
     182          123 :     ~unique_array() {
     183          123 :         reset();
     184          123 :     }
     185              : 
     186              :     // Move semantics
     187           61 :     unique_array(unique_array&& other) noexcept 
     188           61 :         : ptr_(other.ptr_), size_(other.size_) {
     189           61 :         other.ptr_ = nullptr;
     190           61 :         other.size_ = 0;
     191           61 :     }
     192              : 
     193              :     unique_array& operator=(unique_array&& other) noexcept {
     194              :         if (this != &other) {
     195              :             reset();
     196              :             ptr_ = other.ptr_;
     197              :             size_ = other.size_;
     198              :             other.ptr_ = nullptr;
     199              :             other.size_ = 0;
     200              :         }
     201              :         return *this;
     202              :     }
     203              : 
     204              :     // Disable copying
     205              :     unique_array(const unique_array&) = delete;
     206              :     unique_array& operator=(const unique_array&) = delete;
     207              : 
     208              :     /**
     209              :      * @brief Access element at index
     210              :      */
     211        62464 :     T& operator[](size_t index) {
     212        62464 :         return ptr_[index];
     213              :     }
     214              : 
     215              :     const T& operator[](size_t index) const {
     216              :         return ptr_[index];
     217              :     }
     218              : 
     219              :     /**
     220              :      * @brief Get raw pointer
     221              :      */
     222              :     T* get() const noexcept { return ptr_; }
     223              : 
     224              :     /**
     225              :      * @brief Get array size
     226              :      */
     227              :     size_t size() const noexcept { return size_; }
     228              : 
     229              :     /**
     230              :      * @brief Release ownership and return raw pointer
     231              :      */
     232              :     T* release() noexcept {
     233              :         T* result = ptr_;
     234              :         ptr_ = nullptr;
     235              :         size_ = 0;
     236              :         return result;
     237              :     }
     238              : 
     239              :     /**
     240              :      * @brief Reset to new pointer (deallocates current)
     241              :      */
     242          123 :     void reset(T* ptr = nullptr, size_t size = 0) {
     243          123 :         if (ptr_ && size_ > 0) {
     244           62 :             MemoryTracker::instance().record_deallocation(ptr_);
     245           62 :             std::free(ptr_);
     246              :         }
     247          123 :         ptr_ = ptr;
     248          123 :         size_ = size;
     249          123 :         if (ptr_ && size_ > 0) {
     250            0 :             MemoryTracker::instance().record_allocation(ptr_, size_ * sizeof(T));
     251              :         }
     252          123 :     }
     253              : 
     254              :     /**
     255              :      * @brief Check if pointer is valid
     256              :      */
     257              :     explicit operator bool() const noexcept {
     258              :         return ptr_ != nullptr;
     259              :     }
     260              : 
     261              : private:
     262              :     T* ptr_;
     263              :     size_t size_;
     264              : };
     265              : 
     266              : /**
     267              :  * @brief Factory function to create tracked arrays
     268              :  */
     269              : template<typename T>
     270           62 : unique_array<T> make_unique_array(size_t count) {
     271           62 :     if (count == 0) {
     272            0 :         return unique_array<T>(nullptr, 0);
     273              :     }
     274              :     
     275           62 :     T* ptr = static_cast<T*>(std::malloc(count * sizeof(T)));
     276           62 :     if (!ptr) {
     277            0 :         throw std::bad_alloc();
     278              :     }
     279              :     
     280           62 :     return unique_array<T>(ptr, count);
     281              : }
     282              : 
     283              : /**
     284              :  * @brief Memory pool for efficient allocation of fixed-size objects
     285              :  * 
     286              :  * This pool reduces allocation overhead and fragmentation for data structures
     287              :  * that frequently allocate/deallocate objects of the same size (e.g., list nodes).
     288              :  */
     289              : template<typename T, size_t ChunkSize = 1024>
     290              : class MemoryPool {
     291              : public:
     292           61 :     MemoryPool() : free_list_(nullptr) {
     293           61 :         add_chunk();
     294           61 :     }
     295              : 
     296              :     /**
     297              :      * @brief Allocate one object from the pool (grows in chunks on demand)
     298              :      */
     299          638 :     T* allocate() {
     300          638 :         std::lock_guard<std::mutex> lock(mutex_);
     301          638 :         if (!free_list_) {
     302            0 :             add_chunk();
     303              :         }
     304              :         // Pop from free list
     305          638 :         Slot* slot = free_list_;
     306          638 :         free_list_ = free_list_->next;
     307          638 :         return reinterpret_cast<T*>(slot);
     308          638 :     }
     309              : 
     310              :     /**
     311              :      * @brief Return an object to the pool
     312              :      */
     313          638 :     void deallocate(T* ptr) {
     314          638 :         if (!ptr) return;
     315          638 :         std::lock_guard<std::mutex> lock(mutex_);
     316          638 :         Slot* slot = reinterpret_cast<Slot*>(ptr);
     317          638 :         slot->next = free_list_;
     318          638 :         free_list_ = slot;
     319          638 :     }
     320              : 
     321              : private:
     322              :     // Internal slot used for free list linking; shares storage with raw storage for T
     323              :     struct Slot { Slot* next; };
     324              :     using Storage = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
     325              : 
     326           61 :     void add_chunk() {
     327           61 :         unique_array<Storage> chunk = make_unique_array<Storage>(ChunkSize);
     328              :         // Link all new slots into the free list
     329              :         // We push in forward order; order doesn't matter
     330        62525 :         for (size_t i = 0; i < ChunkSize; ++i) {
     331        62464 :             Storage* cell = &chunk[i];
     332        62464 :             Slot* slot = reinterpret_cast<Slot*>(cell);
     333        62464 :             slot->next = free_list_;
     334        62464 :             free_list_ = slot;
     335              :         }
     336           61 :         chunks_.push_back(std::move(chunk));
     337          122 :     }
     338              : 
     339              :     std::vector< unique_array<Storage> > chunks_;
     340              :     Slot* free_list_;
     341              :     std::mutex mutex_;
     342              : };
     343              : 
     344              : } // namespace hashbrowns
     345              : 
     346              : // Convenience macros for tracked allocation (optional, for debugging)
     347              : #ifdef HASHBROWNS_TRACK_ALLOCATIONS
     348              : #define TRACKED_NEW(size) hashbrowns::MemoryTracker::instance().record_allocation(std::malloc(size), size, __FILE__, __LINE__)
     349              : #define TRACKED_DELETE(ptr) do { hashbrowns::MemoryTracker::instance().record_deallocation(ptr); std::free(ptr); } while(0)
     350              : #else
     351              : #define TRACKED_NEW(size) std::malloc(size)
     352              : #define TRACKED_DELETE(ptr) std::free(ptr)
     353              : #endif
     354              : 
     355              : #endif // HASHBROWNS_MEMORY_MANAGER_H
        

Generated by: LCOV version 2.0-1