<p dir="ltr"><br>
On Mar 13, 2013 8:20 AM, "Sergey Matveev" <<a href="mailto:earthdok@google.com">earthdok@google.com</a>> wrote:<br>
><br>
> Hi kcc, glider, samsonov,<br>
><br>
> ForEachChunk() iterates over known chunks, passing each of them to the<br>
> callback.<br>
><br>
> <a href="http://llvm-reviews.chandlerc.com/D539">http://llvm-reviews.chandlerc.com/D539</a><br>
><br>
> Files:<br>
>   lib/sanitizer_common/sanitizer_allocator.h<br>
>   lib/sanitizer_common/tests/sanitizer_allocator_test.cc<br>
><br>
> Index: lib/sanitizer_common/sanitizer_allocator.h<br>
> ===================================================================<br>
> --- lib/sanitizer_common/sanitizer_allocator.h<br>
> +++ lib/sanitizer_common/sanitizer_allocator.h<br>
> @@ -433,6 +433,24 @@<br>
>      }<br>
>    }<br>
><br>
> +  // Iterate over existing chunks. May include chunks that are not currently<br>
> +  // allocated to the user (e.g. freed).<br>
> +  // The caller is expected to do a ForceLock() before calling this function.<br>
> +  void ForEachChunk(void (*callback)(void *p, void *arg), void *argument) {</p>
<p dir="ltr">This is a rather C-ish API. Any reason it's done this way rather than more idiomatically C++ with a template and a Callable? (The immediately obvious benefit being type safety, though beyond that this sort of API would be even more convenient from C++11 (lambdas, std::function, etc))</p>

<p dir="ltr">> +    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {<br>
> +      RegionInfo *region = GetRegionInfo(class_id);<br>
> +      uptr chunk_size = SizeClassMap::Size(class_id);<br>
> +      uptr region_beg = kSpaceBeg + class_id * kRegionSize;<br>
> +      for (uptr p = region_beg;<br>
> +           p < region_beg + region->allocated_user;<br>
> +           p += chunk_size)<br>
> +      {<br>
> +        // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p));<br>
> +        callback((void *)p, argument);<br>
> +      }<br>
> +    }<br>
> +  }<br>
> +<br>
>    typedef SizeClassMap SizeClassMapT;<br>
>    static const uptr kNumClasses = SizeClassMap::kNumClasses;<br>
>    static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;<br>
> @@ -681,6 +699,25 @@<br>
>      }<br>
>    }<br>
><br>
> +  // Iterate over existing chunks. May include chunks that are not currently<br>
> +  // allocated to the user (e.g. freed).<br>
> +  // The caller is expected to do a ForceLock() before calling this function.<br>
> +  void ForEachChunk(void (*callback)(void *p, void *arg), void *argument) {<br>
> +    for (uptr region = 0; region < kNumPossibleRegions; region++)<br>
> +      if (state_->possible_regions[region]) {<br>
> +        uptr chunk_size = SizeClassMap::Size(state_->possible_regions[region]);<br>
> +        uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize);<br>
> +        uptr region_beg = region * kRegionSize;<br>
> +        for (uptr p = region_beg;<br>
> +             p < region_beg + max_chunks_in_region * chunk_size;<br>
> +             p += chunk_size)<br>
> +        {<br>
> +          // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p));<br>
> +          callback((void *)p, argument);<br>
> +        }<br>
> +      }<br>
> +  }<br>
> +<br>
>    void PrintStats() {<br>
>    }<br>
><br>
> @@ -1005,6 +1042,14 @@<br>
>      mutex_.Unlock();<br>
>    }<br>
><br>
> +  // Iterate over existing chunks. May include chunks that are not currently<br>
> +  // allocated to the user (e.g. freed).<br>
> +  // The caller is expected to do a ForceLock() before calling this function.<br>
> +  void ForEachChunk(void (*callback)(void *p, void *arg), void *argument) {<br>
> +    for (uptr i = 0; i < n_chunks_; i++)<br>
> +      callback(GetUser(chunks_[i]), argument);<br>
> +  }<br>
> +<br>
>   private:<br>
>    static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);<br>
>    struct Header {<br>
> @@ -1168,6 +1213,14 @@<br>
>      primary_.ForceUnlock();<br>
>    }<br>
><br>
> +  // Iterate over existing chunks. May include chunks that are not currently<br>
> +  // allocated to the user (e.g. freed).<br>
> +  // The caller is expected to do a ForceLock() before calling this function.<br>
> +  void ForEachChunk(void (*callback)(void *p, void *arg), void *argument) {<br>
> +    primary_.ForEachChunk(callback, argument);<br>
> +    secondary_.ForEachChunk(callback, argument);<br>
> +  }<br>
> +<br>
>   private:<br>
>    PrimaryAllocator primary_;<br>
>    SecondaryAllocator secondary_;<br>
> Index: lib/sanitizer_common/tests/sanitizer_allocator_test.cc<br>
> ===================================================================<br>
> --- lib/sanitizer_common/tests/sanitizer_allocator_test.cc<br>
> +++ lib/sanitizer_common/tests/sanitizer_allocator_test.cc<br>
> @@ -22,6 +22,7 @@<br>
>  #include <pthread.h><br>
>  #include <algorithm><br>
>  #include <vector><br>
> +#include <set><br>
><br>
>  // Too slow for debug build<br>
>  #if TSAN_DEBUG == 0<br>
> @@ -565,4 +566,88 @@<br>
>    }<br>
>  }<br>
><br>
> +void IterationTestCallback(void *chunk, void *argument) {<br>
> +  std::set<void *> *chunks = reinterpret_cast<std::set<void *> *>(argument);<br>
> +  chunks->insert(chunk);<br>
> +}<br>
> +<br>
> +template <class Allocator><br>
> +void TestSizeClassAllocatorIteration() {<br>
> +  Allocator *a = new Allocator;<br>
> +  a->Init();<br>
> +  SizeClassAllocatorLocalCache<Allocator> cache;<br>
> +  memset(&cache, 0, sizeof(cache));<br>
> +  cache.Init(0);<br>
> +<br>
> +  static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,<br>
> +    50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};<br>
> +<br>
> +  std::vector<void *> allocated;<br>
> +<br>
> +  // Allocate a bunch of chunks.<br>
> +  for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) {<br>
> +    uptr size = sizes[s];<br>
> +    if (!a->CanAllocate(size, 1)) continue;<br>
> +    // printf("s = %ld\n", size);<br>
> +    uptr n_iter = std::max((uptr)6, 80000 / size);<br>
> +    // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);<br>
> +    for (uptr j = 0; j < n_iter; j++) {<br>
> +      uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);<br>
> +      void *x = cache.Allocate(a, class_id0);<br>
> +      allocated.push_back(x);<br>
> +    }<br>
> +  }<br>
> +<br>
> +  std::set<void *> reported_chunks;<br>
> +  a->ForceLock();<br>
> +  a->ForEachChunk(IterationTestCallback,<br>
> +                  reinterpret_cast<void *>(&reported_chunks));<br>
> +  a->ForceUnlock();<br>
> +<br>
> +  for (uptr i = 0; i < allocated.size(); i++) {<br>
> +    // Don't use EXPECT_NE. Reporting the first mismatch is enough.<br>
> +    ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());<br>
> +  }<br>
> +<br>
> +  a->TestOnlyUnmap();<br>
> +  delete a;<br>
> +}<br>
> +<br>
> +#if SANITIZER_WORDSIZE == 64<br>
> +TEST(SanitizerCommon, SizeClassAllocator64Iteration) {<br>
> +  TestSizeClassAllocatorIteration<Allocator64>();<br>
> +}<br>
> +#endif<br>
> +<br>
> +TEST(SanitizerCommon, SizeClassAllocator32Iteration) {<br>
> +  TestSizeClassAllocatorIteration<Allocator32Compact>();<br>
> +}<br>
> +<br>
> +<br>
> +TEST(SanitizerCommon, LargeMmapAllocatorIteration) {<br>
> +  LargeMmapAllocator<> a;<br>
> +  a.Init();<br>
> +  AllocatorStats stats;<br>
> +  stats.Init();<br>
> +<br>
> +  static const int kNumAllocs = 1000;<br>
> +  char *allocated[kNumAllocs];<br>
> +  static const uptr size = 40;<br>
> +  // Allocate some.<br>
> +  for (int i = 0; i < kNumAllocs; i++) {<br>
> +    allocated[i] = (char *)a.Allocate(&stats, size, 1);<br>
> +  }<br>
> +<br>
> +  std::set<void *> reported_chunks;<br>
> +  a.ForceLock();<br>
> +  a.ForEachChunk(IterationTestCallback,<br>
> +                  reinterpret_cast<void *>(&reported_chunks));<br>
> +  a.ForceUnlock();<br>
> +<br>
> +  for (uptr i = 0; i < kNumAllocs; i++) {<br>
> +    // Don't use EXPECT_NE. Reporting the first mismatch is enough.<br>
> +    ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());<br>
> +  }<br>
> +}<br>
> +<br>
>  #endif  // #if TSAN_DEBUG==0<br>
><br>
> _______________________________________________<br>
> llvm-commits mailing list<br>
> <a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
> <a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
><br>
</p>