From c6338ac943099db46e700a03fb7e7c90e2ffd4d8 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Wed, 21 Jan 2015 02:05:31 +0000 Subject: [PATCH] [asan] use MADV_NOHUGEPAGE for shadow to reduce the actual memory usage llvm-svn: 226636 --- compiler-rt/lib/asan/asan_internal.h | 2 + compiler-rt/lib/asan/asan_poisoning.h | 3 +- compiler-rt/lib/asan/asan_rtl.cc | 5 +- .../lib/sanitizer_common/sanitizer_common.h | 1 + .../lib/sanitizer_common/sanitizer_flags.inc | 2 + .../sanitizer_common/sanitizer_posix_libcdep.cc | 6 ++ compiler-rt/lib/sanitizer_common/sanitizer_win.cc | 4 + compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc | 7 +- .../test/asan/TestCases/Linux/nohugepage_test.cc | 91 ++++++++++++++++++++++ 9 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 compiler-rt/test/asan/TestCases/Linux/nohugepage_test.cc diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h index 33a3894..a8e23ce 100644 --- a/compiler-rt/lib/asan/asan_internal.h +++ b/compiler-rt/lib/asan/asan_internal.h @@ -108,6 +108,8 @@ void AppendToErrorMessageBuffer(const char *buffer); void *AsanDlSymNext(const char *sym); +void ReserveShadowMemoryRange(uptr beg, uptr end); + // Platform-specific options. #if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); diff --git a/compiler-rt/lib/asan/asan_poisoning.h b/compiler-rt/lib/asan/asan_poisoning.h index 73ae12f..3fc9464 100644 --- a/compiler-rt/lib/asan/asan_poisoning.h +++ b/compiler-rt/lib/asan/asan_poisoning.h @@ -64,8 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, if (page_end != shadow_end) { REAL(memset)((void *)page_end, 0, shadow_end - page_end); } - void *res = MmapFixedNoReserve(page_beg, page_end - page_beg); - CHECK_EQ(page_beg, res); + ReserveShadowMemoryRange(page_beg, page_end - 1); } } } diff --git a/compiler-rt/lib/asan/asan_rtl.cc b/compiler-rt/lib/asan/asan_rtl.cc index 63cbb3f..953935b 100644 --- a/compiler-rt/lib/asan/asan_rtl.cc +++ b/compiler-rt/lib/asan/asan_rtl.cc @@ -86,7 +86,8 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. -static void ReserveShadowMemoryRange(uptr beg, uptr end) { +// We need to use inclusive range because end+1 may not be representable. +void ReserveShadowMemoryRange(uptr beg, uptr end) { CHECK_EQ((beg % GetPageSizeCached()), 0); CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; @@ -97,6 +98,8 @@ static void ReserveShadowMemoryRange(uptr beg, uptr end) { "Perhaps you're using ulimit -v\n", size); Abort(); } + if (common_flags()->no_huge_pages_for_shadow) + NoHugePagesInRegion(beg, size); } // --------------- LowLevelAllocateCallbac ---------- {{{1 diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h index d1b8ab7..07641b6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -76,6 +76,7 @@ void FlushUnneededShadowMemory(uptr addr, uptr size); void IncreaseTotalMmap(uptr size); void DecreaseTotalMmap(uptr size); uptr GetRSS(); +void NoHugePagesInRegion(uptr addr, uptr length); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc index c39e7a6..cc4232fb 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -139,3 +139,5 @@ COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", "Format string used to render stack frames. " "See sanitizer_stacktrace_printer.h for the format description. " "Use DEFAULT to get default format.") +COMMON_FLAG(bool, no_huge_pages_for_shadow, true, + "If true, the shadow is not allowed to use huge pages. ") diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc index ed1e372..1ecbffe 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -44,6 +44,12 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) { madvise((void*)addr, size, MADV_DONTNEED); } +void NoHugePagesInRegion(uptr addr, uptr size) { +#ifdef MADV_NOHUGEPAGE // May not be defined on old systems. + madvise((void *)addr, size, MADV_NOHUGEPAGE); +#endif // MADV_NOHUGEPAGE +} + static rlim_t getlim(int res) { rlimit rlim; CHECK_EQ(0, getrlimit(res, &rlim)); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_win.cc b/compiler-rt/lib/sanitizer_common/sanitizer_win.cc index 6c98e1d..c9a8f70 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_win.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_win.cc @@ -132,6 +132,10 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) { // FIXME: add madvice-analog when we move to 64-bits. } +void NoHugePagesInRegion(uptr addr, uptr size) { + // FIXME: probably similar to FlushUnneededShadowMemory. +} + bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc index 4dcfa55..f786ec8 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc @@ -215,10 +215,9 @@ void InitializeShadowMemory() { // Frequently a thread uses only a small part of stack and similarly // a program uses a small part of large mmap. On some programs // we see 20% memory usage reduction without huge pages for this range. -#ifdef MADV_NOHUGEPAGE - madvise((void*)MemToShadow(0x7f0000000000ULL), - 0x10000000000ULL * kShadowMultiplier, MADV_NOHUGEPAGE); -#endif + // FIXME: don't use constants here. + NoHugePagesInRegion(MemToShadow(0x7f0000000000ULL), + 0x10000000000ULL * kShadowMultiplier); DPrintf("memory shadow: %zx-%zx (%zuGB)\n", kShadowBeg, kShadowEnd, (kShadowEnd - kShadowBeg) >> 30); diff --git a/compiler-rt/test/asan/TestCases/Linux/nohugepage_test.cc b/compiler-rt/test/asan/TestCases/Linux/nohugepage_test.cc new file mode 100644 index 0000000..b549f3b --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Linux/nohugepage_test.cc @@ -0,0 +1,91 @@ +// Regression test for +// https://code.google.com/p/chromium/issues/detail?id=446692 +// where asan consumed too much RAM due to transparent hugetables. +// +// RUN: %clangxx_asan -g %s -o %t +// RUN: ASAN_OPTIONS=no_huge_pages_for_shadow=1 %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s +// +// Would be great to run the test with no_huge_pages_for_shadow=0, but +// the result will depend on the OS version and settings... +// +// REQUIRES: x86_64-supported-target, asan-64-bits +// +// WARNING: this test is very subtle and may nto work on some systems. +// If this is the case we'll need to futher improve it or disable it. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char FileContents[1 << 14]; + +void FileToString(const char *path) { + FileContents[0] = 0; + int fd = open(path, 0); + if (fd < 0) return; + ssize_t res = read(fd, FileContents, sizeof(FileContents) - 1); + if (res >= 0) + FileContents[res] = 0; +} + +long ReadShadowRss() { + const char *path = "/proc/self/smaps"; + FileToString(path); + char *s = strstr(FileContents, "2008fff7000-10007fff8000"); + if (!s) return 0; + + s = strstr(s, "Rss:"); + if (!s) return 0; + s = s + 4; + return atol(s); +} + +const int kAllocSize = 1 << 28; // 256Mb +const int kTwoMb = 1 << 21; +const int kAsanShadowGranularity = 8; + +char *x; + +__attribute__((no_sanitize_address)) void TouchNoAsan(size_t i) { x[i] = 0; } + +int main() { + long rss[5]; + rss[0] = ReadShadowRss(); + // use mmap directly to avoid asan touching the shadow. + x = (char *)mmap(0, kAllocSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, 0, 0); + fprintf(stderr, "X: %p-%p\n", x, x + kAllocSize); + rss[1] = ReadShadowRss(); + + // Touch the allocated region, but not the shadow. + for (size_t i = 0; i < kAllocSize; i += kTwoMb * kAsanShadowGranularity) + TouchNoAsan(i); + rss[2] = ReadShadowRss(); + + // Touch the shadow just a bit, in 2Mb*Granularity steps. + for (size_t i = 0; i < kAllocSize; i += kTwoMb * kAsanShadowGranularity) + __asan_poison_memory_region(x + i, kAsanShadowGranularity); + rss[3] = ReadShadowRss(); + + // Touch all the shadow. + __asan_poison_memory_region(x, kAllocSize); + rss[4] = ReadShadowRss(); + + // Print the differences. + for (int i = 0; i < 4; i++) { + assert(rss[i] > 0); + assert(rss[i+1] >= rss[i]); + long diff = rss[i+1] / rss[i]; + fprintf(stderr, "RSS CHANGE IS %d => %d: %s (%ld vs %ld)\n", i, i + 1, + diff < 10 ? "SMALL" : "LARGE", rss[i], rss[i + 1]); + } +} +// CHECK: RSS CHANGE IS 2 => 3: SMALL +// CHECK: RSS CHANGE IS 3 => 4: LARGE -- 2.7.4