[compiler-rt] r271277 - [esan|wset] Iterate all memory to compute the total working set

Derek Bruening via llvm-commits llvm-commits at lists.llvm.org
Tue May 31 06:41:18 PDT 2016


Author: bruening
Date: Tue May 31 08:41:07 2016
New Revision: 271277

URL: http://llvm.org/viewvc/llvm-project?rev=271277&view=rev
Log:
[esan|wset] Iterate all memory to compute the total working set

Summary:
Adds iteration of all application memory in an efficient manner using
shadow faults.  Shadow memory starts out inaccessible and we mark it
writable one page at a time on each fault when the instrumentation touches
it.  This allows iteration over just the mapped shadow memory, saving
significant time.

Adds a process-end iteration and pretty-printing of the final result.

Adds a new test and updates the existing tests.

Reviewers: aizatsky, filcab

Subscribers: vitalybuka, zhaoqin, kcc, eugenis, llvm-commits, kubabrecka

Differential Revision: http://reviews.llvm.org/D20578

Added:
    compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp   (with props)
Modified:
    compiler-rt/trunk/lib/esan/esan.cpp
    compiler-rt/trunk/lib/esan/working_set.cpp
    compiler-rt/trunk/lib/esan/working_set_posix.cpp
    compiler-rt/trunk/test/esan/TestCases/workingset-memset.cpp
    compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp

Modified: compiler-rt/trunk/lib/esan/esan.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/esan.cpp?rev=271277&r1=271276&r2=271277&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/esan.cpp (original)
+++ compiler-rt/trunk/lib/esan/esan.cpp Tue May 31 08:41:07 2016
@@ -149,8 +149,16 @@ static void initializeShadow() {
     VPrintf(1, "Shadow #%d: [%zx-%zx) (%zuGB)\n", i, ShadowStart, ShadowEnd,
             (ShadowEnd - ShadowStart) >> 30);
 
-    uptr Map = (uptr)MmapFixedNoReserve(ShadowStart, ShadowEnd - ShadowStart,
-                                        "shadow");
+    uptr Map;
+    if (WhichTool == ESAN_WorkingSet) {
+      // We want to identify all shadow pages that are touched so we start
+      // out inaccessible.
+      Map = (uptr)MmapFixedNoAccess(ShadowStart, ShadowEnd- ShadowStart,
+                                    "shadow");
+    } else {
+      Map = (uptr)MmapFixedNoReserve(ShadowStart, ShadowEnd - ShadowStart,
+                                     "shadow");
+    }
     if (Map != ShadowStart) {
       Printf("FATAL: EfficiencySanitizer failed to map its shadow memory.\n");
       Die();

Modified: compiler-rt/trunk/lib/esan/working_set.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/working_set.cpp?rev=271277&r1=271276&r2=271277&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/working_set.cpp (original)
+++ compiler-rt/trunk/lib/esan/working_set.cpp Tue May 31 08:41:07 2016
@@ -16,6 +16,7 @@
 #include "esan.h"
 #include "esan_flags.h"
 #include "esan_shadow.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
 
 // We shadow every cache line of app memory with one shadow byte.
 // - The highest bit of each shadow byte indicates whether the corresponding
@@ -30,6 +31,9 @@ typedef unsigned char byte;
 
 namespace __esan {
 
+// Our shadow memory assumes that the line size is 64.
+static const u32 CacheLineSize = 64;
+
 // See the shadow byte layout description above.
 static const u32 TotalWorkingSetBitIdx = 7;
 static const u32 CurWorkingSetBitIdx = 0;
@@ -75,16 +79,80 @@ void processRangeAccessWorkingSet(uptr P
   }
 }
 
+// This routine will word-align ShadowStart and ShadowEnd prior to scanning.
+static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart,
+                                     uptr ShadowEnd) {
+  u32 WorkingSetSize = 0;
+  u32 ByteValue = 0x1 << BitIdx;
+  u32 WordValue = ByteValue | ByteValue << 8 | ByteValue << 16 |
+    ByteValue << 24;
+  // Get word aligned start.
+  ShadowStart = RoundDownTo(ShadowStart, sizeof(u32));
+  for (u32 *Ptr = (u32 *)ShadowStart; Ptr < (u32 *)ShadowEnd; ++Ptr) {
+    if ((*Ptr & WordValue) != 0) {
+      byte *BytePtr = (byte *)Ptr;
+      for (u32 j = 0; j < sizeof(u32); ++j) {
+        if (BytePtr[j] & ByteValue) {
+          ++WorkingSetSize;
+          // TODO: Accumulate to the lower-frequency bit to the left.
+        }
+      }
+      // Clear this bit from every shadow byte.
+      *Ptr &= ~WordValue;
+    }
+  }
+  return WorkingSetSize;
+}
+
+// Scan shadow memory to calculate the number of cache lines being accessed,
+// i.e., the number of non-zero bits indexed by BitIdx in each shadow byte.
+// We also clear the lowest bits (most recent working set snapshot).
+static u32 computeWorkingSizeAndReset(u32 BitIdx) {
+  u32 WorkingSetSize = 0;
+  MemoryMappingLayout MemIter(true/*cache*/);
+  uptr Start, End, Prot;
+  while (MemIter.Next(&Start, &End, nullptr/*offs*/, nullptr/*file*/,
+                      0/*file size*/, &Prot)) {
+    VPrintf(4, "%s: considering %p-%p app=%d shadow=%d prot=%u\n",
+            __FUNCTION__, Start, End, Prot, isAppMem(Start),
+            isShadowMem(Start));
+    if (isShadowMem(Start) && (Prot & MemoryMappingLayout::kProtectionWrite)) {
+      VPrintf(3, "%s: walking %p-%p\n", __FUNCTION__, Start, End);
+      WorkingSetSize += countAndClearShadowValues(BitIdx, Start, End);
+    }
+  }
+  return WorkingSetSize;
+}
+
 void initializeWorkingSet() {
-  // The shadow mapping assumes 64 so this cannot be changed.
-  CHECK(getFlags()->cache_line_size == 64);
+  CHECK(getFlags()->cache_line_size == CacheLineSize);
   registerMemoryFaultHandler();
 }
 
+static u32 getSizeForPrinting(u32 NumOfCachelines, const char *&Unit) {
+  // We need a constant to avoid software divide support:
+  static const u32 KilobyteCachelines = (0x1 << 10) / CacheLineSize;
+  static const u32 MegabyteCachelines = KilobyteCachelines << 10;
+
+  if (NumOfCachelines > 10 * MegabyteCachelines) {
+    Unit = "MB";
+    return NumOfCachelines / MegabyteCachelines;
+  } else if (NumOfCachelines > 10 * KilobyteCachelines) {
+    Unit = "KB";
+    return NumOfCachelines / KilobyteCachelines;
+  } else {
+    Unit = "Bytes";
+    return NumOfCachelines * CacheLineSize;
+  }
+}
+
 int finalizeWorkingSet() {
-  // FIXME NYI: we need to add memory scanning to report the total lines
-  // touched, and later add sampling to get intermediate values.
-  Report("%s is not finished: nothing yet to report\n", SanitizerToolName);
+  // Get the working set size for the entire execution.
+  u32 NumOfCachelines = computeWorkingSizeAndReset(TotalWorkingSetBitIdx);
+  const char *Unit;
+  u32 Size = getSizeForPrinting(NumOfCachelines, Unit);
+  Report(" %s: the total working set size: %u %s (%u cache lines)\n",
+         SanitizerToolName, Size, Unit, NumOfCachelines);
   return 0;
 }
 

Modified: compiler-rt/trunk/lib/esan/working_set_posix.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/working_set_posix.cpp?rev=271277&r1=271276&r2=271277&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/working_set_posix.cpp (original)
+++ compiler-rt/trunk/lib/esan/working_set_posix.cpp Tue May 31 08:41:07 2016
@@ -68,10 +68,16 @@ static void reinstateDefaultHandler(int
 // app to handle it just as the app would do without our tool in place.
 static void handleMemoryFault(int SigNum, void *Info, void *Ctx) {
   if (SigNum == SIGSEGV) {
-
-    // TODO: Add shadow memory fault detection and handling.
-
-    if (AppSigAct.sigaction) {
+    // We rely on si_addr being filled in (thus we do not support old kernels).
+    siginfo_t *SigInfo = (siginfo_t *)Info;
+    uptr Addr = (uptr)SigInfo->si_addr;
+    if (isShadowMem(Addr)) {
+      VPrintf(3, "Shadow fault @%p\n", Addr);
+      uptr PageSize = GetPageSizeCached();
+      int Res = internal_mprotect((void *)RoundDownTo(Addr, PageSize),
+                                  PageSize, PROT_READ|PROT_WRITE);
+      CHECK(Res == 0);
+    } else if (AppSigAct.sigaction) {
       // FIXME: For simplicity we ignore app options including its signal stack
       // (we just use ours) and all the delivery flags.
       AppSigAct.sigaction(SigNum, Info, Ctx);

Modified: compiler-rt/trunk/test/esan/TestCases/workingset-memset.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/esan/TestCases/workingset-memset.cpp?rev=271277&r1=271276&r2=271277&view=diff
==============================================================================
--- compiler-rt/trunk/test/esan/TestCases/workingset-memset.cpp (original)
+++ compiler-rt/trunk/test/esan/TestCases/workingset-memset.cpp Tue May 31 08:41:07 2016
@@ -16,6 +16,5 @@ int main(int argc, char **argv) {
     memset((char *)p + 63*i, i, 63*i);
   munmap(p, size);
   return 0;
-  // FIXME: once the memory scan and size report is in place add it here.
-  // CHECK: {{.*}}EfficiencySanitizer is not finished: nothing yet to report
+  // CHECK: {{.*}} EfficiencySanitizer: the total working set size: 77 KB (12{{[0-9]+}} cache lines)
 }

Modified: compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp?rev=271277&r1=271276&r2=271277&view=diff
==============================================================================
--- compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp (original)
+++ compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp Tue May 31 08:41:07 2016
@@ -57,3 +57,4 @@ int main(int argc, char **argv) {
 // CHECK-NEXT: Past longjmp for signal
 // CHECK-NEXT: Handling SIGSEGV for sigaction
 // CHECK-NEXT: Past longjmp for sigaction
+// CHECK:      {{.*}} EfficiencySanitizer: the total working set size: {{[0-9][0-9][0-9]}} Bytes ({{[0-9][0-9]}} cache lines)

Added: compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp?rev=271277&view=auto
==============================================================================
--- compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp (added)
+++ compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp Tue May 31 08:41:07 2016
@@ -0,0 +1,30 @@
+// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <assert.h>
+
+const int size = 0x1 << 25; // 523288 cache lines
+const int line_size = 64;
+
+int main(int argc, char **argv) {
+  char *bufA = (char *)malloc(sizeof(char) * line_size);
+  char bufB[64];
+  char *bufC = (char *)mmap(0, size, PROT_READ | PROT_WRITE,
+                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  bufA[0] = 0;
+  // This additional access to the same line should not increase the line
+  // count: but it's difficult to make a non-flaky test that measures the
+  // lines down to the ones digit so right now we're not really testing that.
+  // If we add a heap-only mode we may be able to be more precise.
+  bufA[1] = 0;
+  bufB[33] = 1;
+  for (int i = 0; i < size; i += line_size)
+    bufC[i] = 0;
+  free(bufA);
+  munmap(bufC, 0x4000);
+  // CHECK: {{.*}} EfficiencySanitizer: the total working set size: 32 MB (524{{[0-9][0-9][0-9]}} cache lines)
+  return 0;
+}

Propchange: compiler-rt/trunk/test/esan/TestCases/workingset-simple.cpp
------------------------------------------------------------------------------
    svn:eol-style = LF




More information about the llvm-commits mailing list