[compiler-rt] r226636 - [asan] use MADV_NOHUGEPAGE for shadow to reduce the actual memory usage

Kostya Serebryany kcc at google.com
Tue Jan 20 18:05:31 PST 2015


Author: kcc
Date: Tue Jan 20 20:05:31 2015
New Revision: 226636

URL: http://llvm.org/viewvc/llvm-project?rev=226636&view=rev
Log:
[asan] use MADV_NOHUGEPAGE for shadow to reduce the actual memory usage

Added:
    compiler-rt/trunk/test/asan/TestCases/Linux/nohugepage_test.cc
Modified:
    compiler-rt/trunk/lib/asan/asan_internal.h
    compiler-rt/trunk/lib/asan/asan_poisoning.h
    compiler-rt/trunk/lib/asan/asan_rtl.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc

Modified: compiler-rt/trunk/lib/asan/asan_internal.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_internal.h?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_internal.h (original)
+++ compiler-rt/trunk/lib/asan/asan_internal.h Tue Jan 20 20:05:31 2015
@@ -108,6 +108,8 @@ void AppendToErrorMessageBuffer(const ch
 
 void *AsanDlSymNext(const char *sym);
 
+void ReserveShadowMemoryRange(uptr beg, uptr end);
+
 // Platform-specific options.
 #if SANITIZER_MAC
 bool PlatformHasDifferentMemcpyAndMemmove();

Modified: compiler-rt/trunk/lib/asan/asan_poisoning.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_poisoning.h?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_poisoning.h (original)
+++ compiler-rt/trunk/lib/asan/asan_poisoning.h Tue Jan 20 20:05:31 2015
@@ -64,8 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr
       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);
     }
   }
 }

Modified: compiler-rt/trunk/lib/asan/asan_rtl.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_rtl.cc?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_rtl.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_rtl.cc Tue Jan 20 20:05:31 2015
@@ -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(upt
            "Perhaps you're using ulimit -v\n", size);
     Abort();
   }
+  if (common_flags()->no_huge_pages_for_shadow)
+    NoHugePagesInRegion(beg, size);
 }
 
 // --------------- LowLevelAllocateCallbac ---------- {{{1

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h Tue Jan 20 20:05:31 2015
@@ -76,6 +76,7 @@ void FlushUnneededShadowMemory(uptr addr
 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.

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc Tue Jan 20 20:05:31 2015
@@ -139,3 +139,5 @@ COMMON_FLAG(const char *, stack_trace_fo
             "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. ")

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc Tue Jan 20 20:05:31 2015
@@ -44,6 +44,12 @@ void FlushUnneededShadowMemory(uptr addr
   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));

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc Tue Jan 20 20:05:31 2015
@@ -132,6 +132,10 @@ void FlushUnneededShadowMemory(uptr addr
   // 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)));

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc?rev=226636&r1=226635&r2=226636&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc Tue Jan 20 20:05:31 2015
@@ -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);

Added: compiler-rt/trunk/test/asan/TestCases/Linux/nohugepage_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/nohugepage_test.cc?rev=226636&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Linux/nohugepage_test.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Linux/nohugepage_test.cc Tue Jan 20 20:05:31 2015
@@ -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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sanitizer/asan_interface.h>
+
+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





More information about the llvm-commits mailing list