diff options
Diffstat (limited to 'compiler-rt')
| -rw-r--r-- | compiler-rt/lib/esan/esan.cpp | 12 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/working_set.cpp | 78 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/working_set_posix.cpp | 14 | ||||
| -rw-r--r-- | compiler-rt/test/esan/TestCases/workingset-memset.cpp | 3 | ||||
| -rw-r--r-- | compiler-rt/test/esan/TestCases/workingset-signal-posix.cpp | 1 | ||||
| -rw-r--r-- | compiler-rt/test/esan/TestCases/workingset-simple.cpp | 30 | 
6 files changed, 125 insertions, 13 deletions
| diff --git a/compiler-rt/lib/esan/esan.cpp b/compiler-rt/lib/esan/esan.cpp index c9629d65cd8..f0a4965184c 100644 --- a/compiler-rt/lib/esan/esan.cpp +++ b/compiler-rt/lib/esan/esan.cpp @@ -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(); diff --git a/compiler-rt/lib/esan/working_set.cpp b/compiler-rt/lib/esan/working_set.cpp index 68ea9e74a24..dcd68373706 100644 --- a/compiler-rt/lib/esan/working_set.cpp +++ b/compiler-rt/lib/esan/working_set.cpp @@ -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 PC, uptr Addr, SIZE_T Size,    }  } +// 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;  } diff --git a/compiler-rt/lib/esan/working_set_posix.cpp b/compiler-rt/lib/esan/working_set_posix.cpp index f0191766bb9..0f36c86fe9a 100644 --- a/compiler-rt/lib/esan/working_set_posix.cpp +++ b/compiler-rt/lib/esan/working_set_posix.cpp @@ -68,10 +68,16 @@ static void reinstateDefaultHandler(int SigNum) {  // 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); diff --git a/compiler-rt/test/esan/TestCases/workingset-memset.cpp b/compiler-rt/test/esan/TestCases/workingset-memset.cpp index a0c36e3aebc..9c972ec7a73 100644 --- a/compiler-rt/test/esan/TestCases/workingset-memset.cpp +++ b/compiler-rt/test/esan/TestCases/workingset-memset.cpp @@ -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)  } diff --git a/compiler-rt/test/esan/TestCases/workingset-signal-posix.cpp b/compiler-rt/test/esan/TestCases/workingset-signal-posix.cpp index c0245760c4e..fe7ced0cc8e 100644 --- a/compiler-rt/test/esan/TestCases/workingset-signal-posix.cpp +++ b/compiler-rt/test/esan/TestCases/workingset-signal-posix.cpp @@ -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) diff --git a/compiler-rt/test/esan/TestCases/workingset-simple.cpp b/compiler-rt/test/esan/TestCases/workingset-simple.cpp new file mode 100644 index 00000000000..c8a2d52e7b5 --- /dev/null +++ b/compiler-rt/test/esan/TestCases/workingset-simple.cpp @@ -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; +} | 

