This is an archive of the discontinued LLVM Phabricator instance.

[sanitizer] MmapAlignedOrDie changes to reduce fragmentation
ClosedPublic

Authored by cryptoad on Jun 13 2017, 9:11 AM.

Details

Summary

The reasoning behind this change is explained in D33454, which unfortunately
broke the Windows version (due to the platform not supporting partial unmapping
of a memory region).

This new approach changes MmapAlignedOrDie to allow for the specification of
a padding_chunk. If non-null, and the initial allocation is aligned, this
padding chunk will hold the address of the extra memory (of alignment bytes).
This allows AllocateRegion to get 2 regions if the memory is aligned
properly, and thus help reduce fragmentation (and saves on unmapping
operations). As with the initial D33454, we use a stash in the 32-bit Primary
to hold those extra regions and return them on the fast-path.

The Windows version of MmapAlignedOrDie will always return a 0
padding_chunk if one was requested.

Event Timeline

cryptoad created this revision.Jun 13 2017, 9:11 AM
cryptoad edited the summary of this revision. (Show Details)Jun 13 2017, 9:25 AM
cryptoad updated this revision to Diff 102353.Jun 13 2017, 9:27 AM

Correct the top level comment that wasn't accurate anymore.

cryptoad updated this revision to Diff 102354.Jun 13 2017, 9:28 AM

Explicitely initialize num_stashed_regions to 0.

alekseyshl added inline comments.Jun 13 2017, 10:19 AM
lib/sanitizer_common/sanitizer_common.h
98

How about this:

Since the predominant use case of this function is "size == alignment" and the nature of the
way the alignment requirement is satisfied (by allocating size+alignment bytes of memory), there's a potential
of address space fragmentation. The padding_chunk parameter provides the opportunity
to return the contiguous padding of "size" bytes of the allocated chunk if the initial allocation happened to be
// perfectly aligned and the platform supports partial unmapping of the mapped region.

lib/sanitizer_common/sanitizer_posix.cc
159

Just realized that it should be

if (is_aligned && padding_chunk && size == alignment) {

...

The 'second chunk' logic does not work when size != alignment. With some improvements, it might help with size < alignment case, but, fortunately, our only use case for now is size == alignment, let's not complicate it more than necessary.

lib/sanitizer_common/tests/sanitizer_common_test.cc
130

Since the structure of the test is the same, maybe we should move #if#else#endinf into the loop?I mean, ifdef'ing only lines 100-103 and 118-129?

      EXPECT_EQ(0U, res % (alignment * PageSize));
      internal_memset((void*)res, 1, size * PageSize);
#if SANITIZER_WINDOWS
      // padding_chunk is not supported on Windows.
      EXPECT_EQ(0U, padding_chunk);
      UnmapOrDie((void*)res, size * PageSize);
#else
      if (size == 1 && alignment == 1) {
        // mmap returns PageSize aligned chunks, so this is a specific case
        // where we can check that padding_chunk will never be 0.
        EXPECT_NE(0U, padding_chunk);
      }
      if (padding_chunk) {
        EXPECT_EQ(res + size * PageSize, padding_chunk);
        internal_memset((void*)padding_chunk, 1, alignment * PageSize);
        UnmapOrDie((void*)padding_chunk, alignment * PageSize);
      }
#endif
kcc added inline comments.Jun 13 2017, 10:48 AM
lib/sanitizer_common/tests/sanitizer_common_test.cc
130

Try to avoid #if at all costs, even in tests.
In this case, I am pretty sure you can do

if(SANITIZER_WINDOWS)

instead.

cryptoad updated this revision to Diff 102369.Jun 13 2017, 11:26 AM
cryptoad marked 4 inline comments as done.

Addressing review comments:

  • modify MmapAlignedOrDie's comment in sanitizer_common.h;
  • require size == alignment for a padding_chunk to be returned, adjust the test accordingly;
  • change the structure of the new test with an if (SANITIZER_WINDOWS).
alekseyshl accepted this revision.Jun 13 2017, 12:15 PM
This revision is now accepted and ready to land.Jun 13 2017, 12:15 PM
cryptoad closed this revision.Jun 14 2017, 8:32 AM