diff options
| author | Kostya Serebryany <kcc@google.com> | 2013-03-15 11:39:41 +0000 | 
|---|---|---|
| committer | Kostya Serebryany <kcc@google.com> | 2013-03-15 11:39:41 +0000 | 
| commit | b941a2fca425eedecf1f0095afed732d68f60057 (patch) | |
| tree | 7ba7c0adde4dedf63bd2ca43924e915608c84551 /compiler-rt | |
| parent | cdd46d9ccc646a1847e38dea4722f22dfaa4c535 (diff) | |
| download | bcm5719-llvm-b941a2fca425eedecf1f0095afed732d68f60057.tar.gz bcm5719-llvm-b941a2fca425eedecf1f0095afed732d68f60057.zip  | |
[asan] Add ForEachChunk() to sanitizer allocators. Patch by Sergey Matveev
llvm-svn: 177147
Diffstat (limited to 'compiler-rt')
| -rw-r--r-- | compiler-rt/lib/sanitizer_common/sanitizer_allocator.h | 55 | ||||
| -rw-r--r-- | compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc | 91 | 
2 files changed, 146 insertions, 0 deletions
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h index eb7e45bd40c..45c93da07ef 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h @@ -433,6 +433,24 @@ class SizeClassAllocator64 {      }    } +  // Iterate over existing chunks. May include chunks that are not currently +  // allocated to the user (e.g. freed). +  // The caller is expected to call ForceLock() before calling this function. +  template<typename Callable> +  void ForEachChunk(const Callable &callback) { +    for (uptr class_id = 1; class_id < kNumClasses; class_id++) { +      RegionInfo *region = GetRegionInfo(class_id); +      uptr chunk_size = SizeClassMap::Size(class_id); +      uptr region_beg = kSpaceBeg + class_id * kRegionSize; +      for (uptr p = region_beg; +           p < region_beg + region->allocated_user; +           p += chunk_size) { +        // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p)); +        callback((void *)p); +      } +    } +  } +    typedef SizeClassMap SizeClassMapT;    static const uptr kNumClasses = SizeClassMap::kNumClasses;    static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; @@ -681,6 +699,25 @@ class SizeClassAllocator32 {      }    } +  // Iterate over existing chunks. May include chunks that are not currently +  // allocated to the user (e.g. freed). +  // The caller is expected to call ForceLock() before calling this function. +  template<typename Callable> +  void ForEachChunk(const Callable &callback) { +    for (uptr region = 0; region < kNumPossibleRegions; region++) +      if (state_->possible_regions[region]) { +        uptr chunk_size = SizeClassMap::Size(state_->possible_regions[region]); +        uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); +        uptr region_beg = region * kRegionSize; +        for (uptr p = region_beg; +             p < region_beg + max_chunks_in_region * chunk_size; +             p += chunk_size) { +          // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p)); +          callback((void *)p); +        } +      } +  } +    void PrintStats() {    } @@ -1005,6 +1042,15 @@ class LargeMmapAllocator {      mutex_.Unlock();    } +  // Iterate over existing chunks. May include chunks that are not currently +  // allocated to the user (e.g. freed). +  // The caller is expected to call ForceLock() before calling this function. +  template<typename Callable> +  void ForEachChunk(const Callable &callback) { +    for (uptr i = 0; i < n_chunks_; i++) +      callback(GetUser(chunks_[i])); +  } +   private:    static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);    struct Header { @@ -1168,6 +1214,15 @@ class CombinedAllocator {      primary_.ForceUnlock();    } +  // Iterate over existing chunks. May include chunks that are not currently +  // allocated to the user (e.g. freed). +  // The caller is expected to call ForceLock() before calling this function. +  template<typename Callable> +  void ForEachChunk(const Callable &callback) { +    primary_.ForEachChunk(callback); +    secondary_.ForEachChunk(callback); +  } +   private:    PrimaryAllocator primary_;    SecondaryAllocator secondary_; diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc index 40cf8a5190b..dedafa355d4 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -22,6 +22,7 @@  #include <pthread.h>  #include <algorithm>  #include <vector> +#include <set>  // Too slow for debug build  #if TSAN_DEBUG == 0 @@ -565,4 +566,94 @@ TEST(Allocator, ScopedBuffer) {    }  } +class IterationTestCallback { + public: +  explicit IterationTestCallback(std::set<void *> *chunks) +    : chunks_(chunks) {} +  void operator()(void *chunk) const { +    chunks_->insert(chunk); +  } + private: +  std::set<void *> *chunks_; +}; + +template <class Allocator> +void TestSizeClassAllocatorIteration() { +  Allocator *a = new Allocator; +  a->Init(); +  SizeClassAllocatorLocalCache<Allocator> cache; +  memset(&cache, 0, sizeof(cache)); +  cache.Init(0); + +  static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, +    50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; + +  std::vector<void *> allocated; + +  // Allocate a bunch of chunks. +  for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { +    uptr size = sizes[s]; +    if (!a->CanAllocate(size, 1)) continue; +    // printf("s = %ld\n", size); +    uptr n_iter = std::max((uptr)6, 80000 / size); +    // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); +    for (uptr j = 0; j < n_iter; j++) { +      uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); +      void *x = cache.Allocate(a, class_id0); +      allocated.push_back(x); +    } +  } + +  std::set<void *> reported_chunks; +  IterationTestCallback callback(&reported_chunks); +  a->ForceLock(); +  a->template ForEachChunk<IterationTestCallback>(callback); +  a->ForceUnlock(); + +  for (uptr i = 0; i < allocated.size(); i++) { +    // Don't use EXPECT_NE. Reporting the first mismatch is enough. +    ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end()); +  } + +  a->TestOnlyUnmap(); +  delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64Iteration) { +  TestSizeClassAllocatorIteration<Allocator64>(); +} +#endif + +TEST(SanitizerCommon, SizeClassAllocator32Iteration) { +  TestSizeClassAllocatorIteration<Allocator32Compact>(); +} + + +TEST(SanitizerCommon, LargeMmapAllocatorIteration) { +  LargeMmapAllocator<> a; +  a.Init(); +  AllocatorStats stats; +  stats.Init(); + +  static const int kNumAllocs = 1000; +  char *allocated[kNumAllocs]; +  static const uptr size = 40; +  // Allocate some. +  for (int i = 0; i < kNumAllocs; i++) { +    allocated[i] = (char *)a.Allocate(&stats, size, 1); +  } + +  std::set<void *> reported_chunks; +  IterationTestCallback callback(&reported_chunks); +  a.ForceLock(); +  a.ForEachChunk<IterationTestCallback>(callback); +  a.ForceUnlock(); + +  for (uptr i = 0; i < kNumAllocs; i++) { +    // Don't use EXPECT_NE. Reporting the first mismatch is enough. +    ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end()); +  } +} +  #endif  // #if TSAN_DEBUG==0  | 

