123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- //===-- local_cache.h -------------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef SCUDO_LOCAL_CACHE_H_
- #define SCUDO_LOCAL_CACHE_H_
- #include "internal_defs.h"
- #include "list.h"
- #include "platform.h"
- #include "report.h"
- #include "stats.h"
- #include "string_utils.h"
- namespace scudo {
- template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
- typedef typename SizeClassAllocator::SizeClassMap SizeClassMap;
- typedef typename SizeClassAllocator::CompactPtrT CompactPtrT;
- void init(GlobalStats *S, SizeClassAllocator *A) {
- DCHECK(isEmpty());
- Stats.init();
- if (LIKELY(S))
- S->link(&Stats);
- Allocator = A;
- initCache();
- }
- void destroy(GlobalStats *S) {
- drain();
- if (LIKELY(S))
- S->unlink(&Stats);
- }
- void *allocate(uptr ClassId) {
- DCHECK_LT(ClassId, NumClasses);
- PerClass *C = &PerClassArray[ClassId];
- if (C->Count == 0) {
- // Refill half of the number of max cached.
- DCHECK_GT(C->MaxCount / 2, 0U);
- if (UNLIKELY(!refill(C, ClassId, C->MaxCount / 2)))
- return nullptr;
- DCHECK_GT(C->Count, 0);
- }
- // We read ClassSize first before accessing Chunks because it's adjacent to
- // Count, while Chunks might be further off (depending on Count). That keeps
- // the memory accesses in close quarters.
- const uptr ClassSize = C->ClassSize;
- CompactPtrT CompactP = C->Chunks[--C->Count];
- Stats.add(StatAllocated, ClassSize);
- Stats.sub(StatFree, ClassSize);
- return Allocator->decompactPtr(ClassId, CompactP);
- }
- bool deallocate(uptr ClassId, void *P) {
- CHECK_LT(ClassId, NumClasses);
- PerClass *C = &PerClassArray[ClassId];
- // If the cache is full, drain half of blocks back to the main allocator.
- const bool NeedToDrainCache = C->Count == C->MaxCount;
- if (NeedToDrainCache)
- drain(C, ClassId);
- // See comment in allocate() about memory accesses.
- const uptr ClassSize = C->ClassSize;
- C->Chunks[C->Count++] =
- Allocator->compactPtr(ClassId, reinterpret_cast<uptr>(P));
- Stats.sub(StatAllocated, ClassSize);
- Stats.add(StatFree, ClassSize);
- return NeedToDrainCache;
- }
- bool isEmpty() const {
- for (uptr I = 0; I < NumClasses; ++I)
- if (PerClassArray[I].Count)
- return false;
- return true;
- }
- void drain() {
- // Drain BatchClassId last as it may be needed while draining normal blocks.
- for (uptr I = 0; I < NumClasses; ++I) {
- if (I == BatchClassId)
- continue;
- while (PerClassArray[I].Count > 0)
- drain(&PerClassArray[I], I);
- }
- while (PerClassArray[BatchClassId].Count > 0)
- drain(&PerClassArray[BatchClassId], BatchClassId);
- DCHECK(isEmpty());
- }
- void *getBatchClassBlock() {
- void *B = allocate(BatchClassId);
- if (UNLIKELY(!B))
- reportOutOfMemory(SizeClassAllocator::getSizeByClassId(BatchClassId));
- return B;
- }
- LocalStats &getStats() { return Stats; }
- void getStats(ScopedString *Str) {
- bool EmptyCache = true;
- for (uptr I = 0; I < NumClasses; ++I) {
- if (PerClassArray[I].Count == 0)
- continue;
- EmptyCache = false;
- // The size of BatchClass is set to 0 intentionally. See the comment in
- // initCache() for more details.
- const uptr ClassSize = I == BatchClassId
- ? SizeClassAllocator::getSizeByClassId(I)
- : PerClassArray[I].ClassSize;
- // Note that the string utils don't support printing u16 thus we cast it
- // to a common use type uptr.
- Str->append(" %02zu (%6zu): cached: %4zu max: %4zu\n", I, ClassSize,
- static_cast<uptr>(PerClassArray[I].Count),
- static_cast<uptr>(PerClassArray[I].MaxCount));
- }
- if (EmptyCache)
- Str->append(" No block is cached.\n");
- }
- static u16 getMaxCached(uptr Size) {
- return Min(SizeClassMap::MaxNumCachedHint,
- SizeClassMap::getMaxCachedHint(Size));
- }
- private:
- static const uptr NumClasses = SizeClassMap::NumClasses;
- static const uptr BatchClassId = SizeClassMap::BatchClassId;
- struct alignas(SCUDO_CACHE_LINE_SIZE) PerClass {
- u16 Count;
- u16 MaxCount;
- // Note: ClassSize is zero for the transfer batch.
- uptr ClassSize;
- CompactPtrT Chunks[2 * SizeClassMap::MaxNumCachedHint];
- };
- PerClass PerClassArray[NumClasses] = {};
- LocalStats Stats;
- SizeClassAllocator *Allocator = nullptr;
- NOINLINE void initCache() {
- for (uptr I = 0; I < NumClasses; I++) {
- PerClass *P = &PerClassArray[I];
- const uptr Size = SizeClassAllocator::getSizeByClassId(I);
- P->MaxCount = static_cast<u16>(2 * getMaxCached(Size));
- if (I != BatchClassId) {
- P->ClassSize = Size;
- } else {
- // ClassSize in this struct is only used for malloc/free stats, which
- // should only track user allocations, not internal movements.
- P->ClassSize = 0;
- }
- }
- }
- void destroyBatch(uptr ClassId, void *B) {
- if (ClassId != BatchClassId)
- deallocate(BatchClassId, B);
- }
- NOINLINE bool refill(PerClass *C, uptr ClassId, u16 MaxRefill) {
- const u16 NumBlocksRefilled =
- Allocator->popBlocks(this, ClassId, C->Chunks, MaxRefill);
- DCHECK_LE(NumBlocksRefilled, MaxRefill);
- C->Count = static_cast<u16>(C->Count + NumBlocksRefilled);
- return NumBlocksRefilled != 0;
- }
- NOINLINE void drain(PerClass *C, uptr ClassId) {
- const u16 Count = Min(static_cast<u16>(C->MaxCount / 2), C->Count);
- Allocator->pushBlocks(this, ClassId, &C->Chunks[0], Count);
- // u16 will be promoted to int by arithmetic type conversion.
- C->Count = static_cast<u16>(C->Count - Count);
- for (u16 I = 0; I < C->Count; I++)
- C->Chunks[I] = C->Chunks[I + Count];
- }
- };
- } // namespace scudo
- #endif // SCUDO_LOCAL_CACHE_H_
|