(root)/
gcc-13.2.0/
libsanitizer/
sanitizer_common/
sanitizer_allocator_combined.h
       1  //===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
       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  // Part of the Sanitizer Allocator.
      10  //
      11  //===----------------------------------------------------------------------===//
      12  #ifndef SANITIZER_ALLOCATOR_H
      13  #error This file must be included inside sanitizer_allocator.h
      14  #endif
      15  
      16  // This class implements a complete memory allocator by using two
      17  // internal allocators:
      18  // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
      19  //  When allocating 2^x bytes it should return 2^x aligned chunk.
      20  // PrimaryAllocator is used via a local AllocatorCache.
      21  // SecondaryAllocator can allocate anything, but is not efficient.
      22  template <class PrimaryAllocator,
      23            class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
      24  class CombinedAllocator {
      25   public:
      26    using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
      27    using SecondaryAllocator =
      28        LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
      29                           LargeMmapAllocatorPtrArray,
      30                           typename PrimaryAllocator::AddressSpaceView>;
      31  
      32    void InitLinkerInitialized(s32 release_to_os_interval_ms) {
      33      stats_.InitLinkerInitialized();
      34      primary_.Init(release_to_os_interval_ms);
      35      secondary_.InitLinkerInitialized();
      36    }
      37  
      38    void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
      39      stats_.Init();
      40      primary_.Init(release_to_os_interval_ms, heap_start);
      41      secondary_.Init();
      42    }
      43  
      44    void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
      45      // Returning 0 on malloc(0) may break a lot of code.
      46      if (size == 0)
      47        size = 1;
      48      if (size + alignment < size) {
      49        Report("WARNING: %s: CombinedAllocator allocation overflow: "
      50               "0x%zx bytes with 0x%zx alignment requested\n",
      51               SanitizerToolName, size, alignment);
      52        return nullptr;
      53      }
      54      uptr original_size = size;
      55      // If alignment requirements are to be fulfilled by the frontend allocator
      56      // rather than by the primary or secondary, passing an alignment lower than
      57      // or equal to 8 will prevent any further rounding up, as well as the later
      58      // alignment check.
      59      if (alignment > 8)
      60        size = RoundUpTo(size, alignment);
      61      // The primary allocator should return a 2^x aligned allocation when
      62      // requested 2^x bytes, hence using the rounded up 'size' when being
      63      // serviced by the primary (this is no longer true when the primary is
      64      // using a non-fixed base address). The secondary takes care of the
      65      // alignment without such requirement, and allocating 'size' would use
      66      // extraneous memory, so we employ 'original_size'.
      67      void *res;
      68      if (primary_.CanAllocate(size, alignment))
      69        res = cache->Allocate(&primary_, primary_.ClassID(size));
      70      else
      71        res = secondary_.Allocate(&stats_, original_size, alignment);
      72      if (alignment > 8)
      73        CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
      74      return res;
      75    }
      76  
      77    s32 ReleaseToOSIntervalMs() const {
      78      return primary_.ReleaseToOSIntervalMs();
      79    }
      80  
      81    void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
      82      primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
      83    }
      84  
      85    void ForceReleaseToOS() {
      86      primary_.ForceReleaseToOS();
      87    }
      88  
      89    void Deallocate(AllocatorCache *cache, void *p) {
      90      if (!p) return;
      91      if (primary_.PointerIsMine(p))
      92        cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
      93      else
      94        secondary_.Deallocate(&stats_, p);
      95    }
      96  
      97    void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
      98                     uptr alignment) {
      99      if (!p)
     100        return Allocate(cache, new_size, alignment);
     101      if (!new_size) {
     102        Deallocate(cache, p);
     103        return nullptr;
     104      }
     105      CHECK(PointerIsMine(p));
     106      uptr old_size = GetActuallyAllocatedSize(p);
     107      uptr memcpy_size = Min(new_size, old_size);
     108      void *new_p = Allocate(cache, new_size, alignment);
     109      if (new_p)
     110        internal_memcpy(new_p, p, memcpy_size);
     111      Deallocate(cache, p);
     112      return new_p;
     113    }
     114  
     115    bool PointerIsMine(const void *p) const {
     116      if (primary_.PointerIsMine(p))
     117        return true;
     118      return secondary_.PointerIsMine(p);
     119    }
     120  
     121    bool FromPrimary(const void *p) const { return primary_.PointerIsMine(p); }
     122  
     123    void *GetMetaData(const void *p) {
     124      if (primary_.PointerIsMine(p))
     125        return primary_.GetMetaData(p);
     126      return secondary_.GetMetaData(p);
     127    }
     128  
     129    void *GetBlockBegin(const void *p) {
     130      if (primary_.PointerIsMine(p))
     131        return primary_.GetBlockBegin(p);
     132      return secondary_.GetBlockBegin(p);
     133    }
     134  
     135    // This function does the same as GetBlockBegin, but is much faster.
     136    // Must be called with the allocator locked.
     137    void *GetBlockBeginFastLocked(void *p) {
     138      if (primary_.PointerIsMine(p))
     139        return primary_.GetBlockBegin(p);
     140      return secondary_.GetBlockBeginFastLocked(p);
     141    }
     142  
     143    uptr GetActuallyAllocatedSize(void *p) {
     144      if (primary_.PointerIsMine(p))
     145        return primary_.GetActuallyAllocatedSize(p);
     146      return secondary_.GetActuallyAllocatedSize(p);
     147    }
     148  
     149    uptr TotalMemoryUsed() {
     150      return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
     151    }
     152  
     153    void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
     154  
     155    void InitCache(AllocatorCache *cache) {
     156      cache->Init(&stats_);
     157    }
     158  
     159    void DestroyCache(AllocatorCache *cache) {
     160      cache->Destroy(&primary_, &stats_);
     161    }
     162  
     163    void SwallowCache(AllocatorCache *cache) {
     164      cache->Drain(&primary_);
     165    }
     166  
     167    void GetStats(AllocatorStatCounters s) const {
     168      stats_.Get(s);
     169    }
     170  
     171    void PrintStats() {
     172      primary_.PrintStats();
     173      secondary_.PrintStats();
     174    }
     175  
     176    // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
     177    // introspection API.
     178    void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
     179      primary_.ForceLock();
     180      secondary_.ForceLock();
     181    }
     182  
     183    void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
     184      secondary_.ForceUnlock();
     185      primary_.ForceUnlock();
     186    }
     187  
     188    // Iterate over all existing chunks.
     189    // The allocator must be locked when calling this function.
     190    void ForEachChunk(ForEachChunkCallback callback, void *arg) {
     191      primary_.ForEachChunk(callback, arg);
     192      secondary_.ForEachChunk(callback, arg);
     193    }
     194  
     195   private:
     196    PrimaryAllocator primary_;
     197    SecondaryAllocator secondary_;
     198    AllocatorGlobalStats stats_;
     199  };