[PATCH] Add ForEachChunk() to sanitizer allocators.
David Blaikie
dblaikie at gmail.com
Fri Mar 15 10:34:34 PDT 2013
On Thu, Mar 14, 2013 at 5:25 AM, Sergey Matveev <earthdok at google.com> wrote:
> Probably no reason other than my lack of familiarity with C++ idioms. Is
> this what you had in mind?
Yep, that was the idea - few minor (optional) comments left on Phab.
>
>
>
> On Wed, Mar 13, 2013 at 7:28 PM, David Blaikie <dblaikie at gmail.com> wrote:
>>
>>
>> On Mar 13, 2013 8:20 AM, "Sergey Matveev" <earthdok at google.com> wrote:
>> >
>> > Hi kcc, glider, samsonov,
>> >
>> > ForEachChunk() iterates over known chunks, passing each of them to the
>> > callback.
>> >
>> > http://llvm-reviews.chandlerc.com/D539
>> >
>> > Files:
>> > lib/sanitizer_common/sanitizer_allocator.h
>> > lib/sanitizer_common/tests/sanitizer_allocator_test.cc
>> >
>> > Index: lib/sanitizer_common/sanitizer_allocator.h
>> > ===================================================================
>> > --- lib/sanitizer_common/sanitizer_allocator.h
>> > +++ lib/sanitizer_common/sanitizer_allocator.h
>> > @@ -433,6 +433,24 @@
>> > }
>> > }
>> >
>> > + // Iterate over existing chunks. May include chunks that are not
>> > currently
>> > + // allocated to the user (e.g. freed).
>> > + // The caller is expected to do a ForceLock() before calling this
>> > function.
>> > + void ForEachChunk(void (*callback)(void *p, void *arg), void
>> > *argument) {
>>
>> 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))
>>
>> > + 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, argument);
>> > + }
>> > + }
>> > + }
>> > +
>> > typedef SizeClassMap SizeClassMapT;
>> > static const uptr kNumClasses = SizeClassMap::kNumClasses;
>> > static const uptr kNumClassesRounded =
>> > SizeClassMap::kNumClassesRounded;
>> > @@ -681,6 +699,25 @@
>> > }
>> > }
>> >
>> > + // Iterate over existing chunks. May include chunks that are not
>> > currently
>> > + // allocated to the user (e.g. freed).
>> > + // The caller is expected to do a ForceLock() before calling this
>> > function.
>> > + void ForEachChunk(void (*callback)(void *p, void *arg), void
>> > *argument) {
>> > + 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, argument);
>> > + }
>> > + }
>> > + }
>> > +
>> > void PrintStats() {
>> > }
>> >
>> > @@ -1005,6 +1042,14 @@
>> > 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 do a ForceLock() before calling this
>> > function.
>> > + void ForEachChunk(void (*callback)(void *p, void *arg), void
>> > *argument) {
>> > + for (uptr i = 0; i < n_chunks_; i++)
>> > + callback(GetUser(chunks_[i]), argument);
>> > + }
>> > +
>> > private:
>> > static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);
>> > struct Header {
>> > @@ -1168,6 +1213,14 @@
>> > 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 do a ForceLock() before calling this
>> > function.
>> > + void ForEachChunk(void (*callback)(void *p, void *arg), void
>> > *argument) {
>> > + primary_.ForEachChunk(callback, argument);
>> > + secondary_.ForEachChunk(callback, argument);
>> > + }
>> > +
>> > private:
>> > PrimaryAllocator primary_;
>> > SecondaryAllocator secondary_;
>> > Index: lib/sanitizer_common/tests/sanitizer_allocator_test.cc
>> > ===================================================================
>> > --- lib/sanitizer_common/tests/sanitizer_allocator_test.cc
>> > +++ 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,88 @@
>> > }
>> > }
>> >
>> > +void IterationTestCallback(void *chunk, void *argument) {
>> > + std::set<void *> *chunks = reinterpret_cast<std::set<void *>
>> > *>(argument);
>> > + chunks->insert(chunk);
>> > +}
>> > +
>> > +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;
>> > + a->ForceLock();
>> > + a->ForEachChunk(IterationTestCallback,
>> > + reinterpret_cast<void *>(&reported_chunks));
>> > + 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;
>> > + a.ForceLock();
>> > + a.ForEachChunk(IterationTestCallback,
>> > + reinterpret_cast<void *>(&reported_chunks));
>> > + 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
>> >
>> > _______________________________________________
>> > llvm-commits mailing list
>> > llvm-commits at cs.uiuc.edu
>> > http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>> >
>
>
More information about the llvm-commits
mailing list