diff options
author | Kostya Serebryany <kcc@google.com> | 2012-07-06 13:46:49 +0000 |
---|---|---|
committer | Kostya Serebryany <kcc@google.com> | 2012-07-06 13:46:49 +0000 |
commit | d1e6094f9ed22ab29b154bb7a331345a41edb4af (patch) | |
tree | 625380338dca615a7a68f740b7372f33157e8066 /compiler-rt | |
parent | dc211a8db4e6be4dbb4e3180dcbeb4c56abe0143 (diff) | |
download | bcm5719-llvm-d1e6094f9ed22ab29b154bb7a331345a41edb4af.tar.gz bcm5719-llvm-d1e6094f9ed22ab29b154bb7a331345a41edb4af.zip |
[tsan] implement SizeClassAllocatorLocalCache (part of tsan allocator)
llvm-svn: 159823
Diffstat (limited to 'compiler-rt')
-rw-r--r-- | compiler-rt/lib/sanitizer_common/sanitizer_allocator64.h | 87 | ||||
-rw-r--r-- | compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc | 60 |
2 files changed, 117 insertions, 30 deletions
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator64.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator64.h index 0d227756df8..679e07dfa63 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator64.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator64.h @@ -79,6 +79,13 @@ class DefaultSizeClassMap { } }; +struct AllocatorListNode { + AllocatorListNode *next; +}; + +typedef IntrusiveList<AllocatorListNode> AllocatorFreeList; + + // Space: a portion of address space of kSpaceSize bytes starting at // a fixed address (kSpaceBeg). Both constants are powers of two and // kSpaceBeg is kSpaceSize-aligned. @@ -114,6 +121,30 @@ class SizeClassAllocator64 { CHECK(PointerIsMine(p)); DeallocateBySizeClass(p, GetSizeClass(p)); } + + // Allocate several chunks of the given class_id. + void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + if (region->free_list.empty()) { + PopulateFreeList(class_id, region); + } + CHECK(!region->free_list.empty()); + // Just take as many chunks as we have in the free list now. + // FIXME: this might be too much. + free_list->append_front(®ion->free_list); + CHECK(region->free_list.empty()); + } + + // Swallow the entire free_list for the given class_id. + void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + region->free_list.append_front(free_list); + } + bool PointerIsMine(void *p) { return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize; } @@ -140,8 +171,9 @@ class SizeClassAllocator64 { UnmapOrDie(reinterpret_cast<void*>(AllocBeg()), AllocSize()); } - private: static const uptr kNumClasses = 256; // Power of two <= 256 + + private: COMPILER_CHECK(kNumClasses <= SizeClassMap::kNumClasses); static const uptr kRegionSize = kSpaceSize / kNumClasses; COMPILER_CHECK((kRegionSize >> 32) > 0); // kRegionSize must be >= 2^32. @@ -149,18 +181,12 @@ class SizeClassAllocator64 { // or with one element if its size is greater. static const uptr kPopulateSize = 1 << 18; - struct ListNode { - ListNode *next; - }; - - typedef IntrusiveList<ListNode> FreeList; - struct RegionInfo { SpinMutex mutex; - FreeList free_list; + AllocatorFreeList free_list; uptr allocated_user; // Bytes allocated for user memory. uptr allocated_meta; // Bytes allocated for metadata. - char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(FreeList)]; + char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)]; }; COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize); @@ -196,7 +222,7 @@ class SizeClassAllocator64 { uptr i = 0; do { // do-while loop because we need to put at least one item. uptr p = region_beg + idx; - region->free_list.push_front(reinterpret_cast<ListNode*>(p)); + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); idx += size; i++; } while (idx < end_idx); @@ -213,7 +239,7 @@ class SizeClassAllocator64 { PopulateFreeList(class_id, region); } CHECK(!region->free_list.empty()); - ListNode *node = region->free_list.front(); + AllocatorListNode *node = region->free_list.front(); region->free_list.pop_front(); return reinterpret_cast<void*>(node); } @@ -221,8 +247,45 @@ class SizeClassAllocator64 { void DeallocateBySizeClass(void *p, uptr class_id) { RegionInfo *region = GetRegionInfo(class_id); SpinMutexLock l(®ion->mutex); - region->free_list.push_front(reinterpret_cast<ListNode*>(p)); + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); + } +}; + +// Objects of this type should be used as local caches for SizeClassAllocator64. +// Since the typical use of this class is to have one object per thread in TLS, +// is has to be POD. +template<const uptr kNumClasses, class SizeClassAllocator> +struct SizeClassAllocatorLocalCache { + // Don't need to call Init if the object is a global (i.e. zero-initialized). + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void *Allocate(SizeClassAllocator *allocator, uptr class_id) { + CHECK_LT(class_id, kNumClasses); + AllocatorFreeList *free_list = &free_lists_[class_id]; + if (free_list->empty()) + allocator->BulkAllocate(class_id, free_list); + CHECK(!free_list->empty()); + void *res = free_list->front(); + free_list->pop_front(); + return res; + } + + void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { + CHECK_LT(class_id, kNumClasses); + free_lists_[class_id].push_front(reinterpret_cast<AllocatorListNode*>(p)); } + + void Drain(SizeClassAllocator *allocator) { + for (uptr i = 0; i < kNumClasses; i++) { + allocator->BulkDeallocate(i, &free_lists_[i]); + CHECK(free_lists_[i].empty()); + } + } + + // private: + AllocatorFreeList free_lists_[kNumClasses]; }; // This class can (de)allocate only large chunks of memory using mmap/unmap. diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc index e8da03eb9ec..62b4329b08b 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc @@ -14,9 +14,14 @@ #include <algorithm> #include <vector> -TEST(SanitizerCommon, DefaultSizeClassMap) { - typedef DefaultSizeClassMap SCMap; +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x10000000000; // 1T. + +typedef DefaultSizeClassMap SCMap; +typedef + SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 16, SCMap> Allocator; +TEST(SanitizerCommon, DefaultSizeClassMap) { #if 0 for (uptr i = 0; i < SCMap::kNumClasses; i++) { // printf("% 3ld: % 5ld (%4lx); ", i, SCMap::Size(i), SCMap::Size(i)); @@ -47,14 +52,7 @@ TEST(SanitizerCommon, DefaultSizeClassMap) { } } -static const uptr kAllocatorSpace = 0x600000000000ULL; -static const uptr kAllocatorSize = 0x10000000000; // 1T. - TEST(SanitizerCommon, SizeClassAllocator64) { - typedef DefaultSizeClassMap SCMap; - typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - 16, SCMap> Allocator; - Allocator a; a.Init(); @@ -101,9 +99,6 @@ TEST(SanitizerCommon, SizeClassAllocator64) { TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { - typedef DefaultSizeClassMap SCMap; - typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - 16, SCMap> Allocator; Allocator a; a.Init(); static volatile void *sink; @@ -128,9 +123,6 @@ TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { } void FailInAssertionOnOOM() { - typedef DefaultSizeClassMap SCMap; - typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - 16, SCMap> Allocator; Allocator a; a.Init(); const uptr size = 1 << 20; @@ -188,9 +180,7 @@ TEST(SanitizerCommon, LargeMmapAllocator) { } TEST(SanitizerCommon, CombinedAllocator) { - typedef DefaultSizeClassMap SCMap; - typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - 16, SCMap> PrimaryAllocator; + typedef Allocator PrimaryAllocator; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, SecondaryAllocator> Allocator; @@ -225,3 +215,37 @@ TEST(SanitizerCommon, CombinedAllocator) { } a.TestOnlyUnmap(); } + +typedef SizeClassAllocatorLocalCache<Allocator::kNumClasses, Allocator> + AllocatorCache; +static THREADLOCAL AllocatorCache static_allocator_cache; + +TEST(SanitizerCommon, SizeClassAllocatorLocalCache) { + static_allocator_cache.Init(); + + Allocator a; + AllocatorCache cache; + + a.Init(); + cache.Init(); + + const uptr kNumAllocs = 10000; + const int kNumIter = 100; + uptr saved_total = 0; + for (int i = 0; i < kNumIter; i++) { + void *allocated[kNumAllocs]; + for (uptr i = 0; i < kNumAllocs; i++) { + allocated[i] = cache.Allocate(&a, 0); + } + for (uptr i = 0; i < kNumAllocs; i++) { + cache.Deallocate(&a, 0, allocated[i]); + } + cache.Drain(&a); + uptr total_allocated = a.TotalMemoryUsed(); + if (saved_total) + CHECK_EQ(saved_total, total_allocated); + saved_total = total_allocated; + } + + a.TestOnlyUnmap(); +} |