diff options
| -rw-r--r-- | compiler-rt/include/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | compiler-rt/include/sanitizer/esan_interface.h | 46 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/cache_frag.cpp | 6 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/cache_frag.h | 1 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/esan.cpp | 9 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/esan.h | 1 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/esan_interface.cpp | 7 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/working_set.cpp | 26 | ||||
| -rw-r--r-- | compiler-rt/lib/esan/working_set.h | 1 | ||||
| -rw-r--r-- | compiler-rt/test/esan/TestCases/workingset-midreport.cpp | 71 | ||||
| -rw-r--r-- | compiler-rt/test/esan/lit.cfg | 2 | 
11 files changed, 166 insertions, 5 deletions
| diff --git a/compiler-rt/include/CMakeLists.txt b/compiler-rt/include/CMakeLists.txt index ad1437ed15e..0139b0601ea 100644 --- a/compiler-rt/include/CMakeLists.txt +++ b/compiler-rt/include/CMakeLists.txt @@ -4,6 +4,7 @@ set(SANITIZER_HEADERS    sanitizer/common_interface_defs.h    sanitizer/coverage_interface.h    sanitizer/dfsan_interface.h +  sanitizer/esan_interface.h    sanitizer/linux_syscall_hooks.h    sanitizer/lsan_interface.h    sanitizer/msan_interface.h diff --git a/compiler-rt/include/sanitizer/esan_interface.h b/compiler-rt/include/sanitizer/esan_interface.h new file mode 100644 index 00000000000..31e9b9b4df3 --- /dev/null +++ b/compiler-rt/include/sanitizer/esan_interface.h @@ -0,0 +1,46 @@ +//===-- sanitizer/esan_interface.h ------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ESAN_INTERFACE_H +#define SANITIZER_ESAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> + +// We declare our interface routines as weak to allow the user to avoid +// ifdefs and instead use this pattern to allow building the same sources +// with and without our runtime library: +//     if (__esan_report) +//       __esan_report(); +#ifdef _MSC_VER +/* selectany is as close to weak as we'll get. */ +#define COMPILER_RT_WEAK __declspec(selectany) +#elif __GNUC__ +#define COMPILER_RT_WEAK __attribute__((weak)) +#else +#define COMPILER_RT_WEAK +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// This function can be called mid-run (or at the end of a run for +// a server process that doesn't shut down normally) to request that +// data for that point in the run be reported from the tool. +void COMPILER_RT_WEAK __esan_report(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_ESAN_INTERFACE_H diff --git a/compiler-rt/lib/esan/cache_frag.cpp b/compiler-rt/lib/esan/cache_frag.cpp index 343abfcd584..a3e612daceb 100644 --- a/compiler-rt/lib/esan/cache_frag.cpp +++ b/compiler-rt/lib/esan/cache_frag.cpp @@ -199,4 +199,10 @@ int finalizeCacheFrag() {    return 0;  } +void reportCacheFrag() { +  VPrintf(2, "in esan::%s\n", __FUNCTION__); +  // FIXME: Not yet implemented.  We need to iterate over all of the +  // compilation unit data. +} +  } // namespace __esan diff --git a/compiler-rt/lib/esan/cache_frag.h b/compiler-rt/lib/esan/cache_frag.h index 9f879322074..646d3f85ed9 100644 --- a/compiler-rt/lib/esan/cache_frag.h +++ b/compiler-rt/lib/esan/cache_frag.h @@ -22,6 +22,7 @@ void processCacheFragCompilationUnitExit(void *Ptr);  void initializeCacheFrag();  int finalizeCacheFrag(); +void reportCacheFrag();  } // namespace __esan diff --git a/compiler-rt/lib/esan/esan.cpp b/compiler-rt/lib/esan/esan.cpp index 970f91a837d..3c69b4e91b5 100644 --- a/compiler-rt/lib/esan/esan.cpp +++ b/compiler-rt/lib/esan/esan.cpp @@ -228,6 +228,15 @@ int finalizeLibrary() {    return 0;  } +void reportResults() { +  VPrintf(1, "in esan::%s\n", __FUNCTION__); +  if (__esan_which_tool == ESAN_CacheFrag) { +    return reportCacheFrag(); +  } else if (__esan_which_tool == ESAN_WorkingSet) { +    return reportWorkingSet(); +  } +} +  void processCompilationUnitInit(void *Ptr) {    VPrintf(2, "in esan::%s\n", __FUNCTION__);    if (__esan_which_tool == ESAN_CacheFrag) { diff --git a/compiler-rt/lib/esan/esan.h b/compiler-rt/lib/esan/esan.h index 4171522dbbf..371810d5eba 100644 --- a/compiler-rt/lib/esan/esan.h +++ b/compiler-rt/lib/esan/esan.h @@ -37,6 +37,7 @@ extern bool EsanDuringInit;  void initializeLibrary(ToolType Tool);  int finalizeLibrary(); +void reportResults();  // Esan creates the variable per tool per compilation unit at compile time  // and passes its pointer Ptr to the runtime library.  void processCompilationUnitInit(void *Ptr); diff --git a/compiler-rt/lib/esan/esan_interface.cpp b/compiler-rt/lib/esan/esan_interface.cpp index f6ad3caa841..8a64d152611 100644 --- a/compiler-rt/lib/esan/esan_interface.cpp +++ b/compiler-rt/lib/esan/esan_interface.cpp @@ -109,3 +109,10 @@ void __esan_unaligned_loadN(void *Addr, uptr Size) {  void __esan_unaligned_storeN(void *Addr, uptr Size) {    processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, true);  } + +// Public interface: +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __esan_report() { +  reportResults(); +} +} // extern "C" diff --git a/compiler-rt/lib/esan/working_set.cpp b/compiler-rt/lib/esan/working_set.cpp index 622fd29e846..3fde5a8b5fc 100644 --- a/compiler-rt/lib/esan/working_set.cpp +++ b/compiler-rt/lib/esan/working_set.cpp @@ -118,6 +118,8 @@ void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size,  }  // This routine will word-align ShadowStart and ShadowEnd prior to scanning. +// It does *not* clear for BitIdx==TotalWorkingSetBitIdx, as that top bit +// measures the access during the entire execution and should never be cleared.  static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart,                                       uptr ShadowEnd) {    u32 WorkingSetSize = 0; @@ -127,6 +129,8 @@ static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart,    // Get word aligned start.    ShadowStart = RoundDownTo(ShadowStart, sizeof(u32));    bool Accum = getFlags()->record_snapshots && BitIdx < MaxAccumBitIdx; +  // Do not clear the bit that measures access during the entire execution. +  bool Clear = BitIdx < TotalWorkingSetBitIdx;    for (u32 *Ptr = (u32 *)ShadowStart; Ptr < (u32 *)ShadowEnd; ++Ptr) {      if ((*Ptr & WordValue) != 0) {        byte *BytePtr = (byte *)Ptr; @@ -139,8 +143,10 @@ static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart,            }          }        } -      // Clear this bit from every shadow byte. -      *Ptr &= ~WordValue; +      if (Clear) { +        // Clear this bit from every shadow byte. +        *Ptr &= ~WordValue; +      }      }    }    return WorkingSetSize; @@ -149,6 +155,8 @@ static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart,  // 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). +// We do *not* clear for BitIdx==TotalWorkingSetBitIdx, as that top bit +// measures the access during the entire execution and should never be cleared.  static u32 computeWorkingSizeAndReset(u32 BitIdx) {    u32 WorkingSetSize = 0;    MemoryMappingLayout MemIter(true/*cache*/); @@ -226,10 +234,9 @@ static u32 getSizeForPrinting(u32 NumOfCachelines, const char *&Unit) {    }  } -int finalizeWorkingSet() { +void reportWorkingSet() {    const char *Unit;    if (getFlags()->record_snapshots) { -    Thread.joinThread();      u32 Freq = 1;      Report(" Total number of samples: %u\n", SnapshotNum);      for (u32 i = 0; i < NumFreq; ++i) { @@ -243,7 +250,6 @@ int finalizeWorkingSet() {                 SizePerFreq[i][j]);        }        Freq = Freq << getFlags()->snapshot_step; -      SizePerFreq[i].free();      }    } @@ -252,6 +258,16 @@ int finalizeWorkingSet() {    u32 Size = getSizeForPrinting(NumOfCachelines, Unit);    Report(" %s: the total working set size: %u %s (%u cache lines)\n",           SanitizerToolName, Size, Unit, NumOfCachelines); +} + +int finalizeWorkingSet() { +  if (getFlags()->record_snapshots) +    Thread.joinThread(); +  reportWorkingSet(); +  if (getFlags()->record_snapshots) { +    for (u32 i = 0; i < NumFreq; ++i) +      SizePerFreq[i].free(); +  }    return 0;  } diff --git a/compiler-rt/lib/esan/working_set.h b/compiler-rt/lib/esan/working_set.h index 660b5d066a7..38ff0635d0a 100644 --- a/compiler-rt/lib/esan/working_set.h +++ b/compiler-rt/lib/esan/working_set.h @@ -23,6 +23,7 @@ namespace __esan {  void initializeWorkingSet();  void initializeShadowWorkingSet();  int finalizeWorkingSet(); +void reportWorkingSet();  void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size,                                    bool IsWrite); diff --git a/compiler-rt/test/esan/TestCases/workingset-midreport.cpp b/compiler-rt/test/esan/TestCases/workingset-midreport.cpp new file mode 100644 index 00000000000..470e33ca36e --- /dev/null +++ b/compiler-rt/test/esan/TestCases/workingset-midreport.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_esan_wset -O0 %s -o %t 2>&1 +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ESAN + +// RUN: %clang -O0 %s -o %t 2>&1 +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NO-ESAN + +#include <sanitizer/esan_interface.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +const int size = 0x1 << 25; // 523288 cache lines +const int iters = 6; + +int main(int argc, char **argv) { +  char *buf = (char *)mmap(0, size, PROT_READ | PROT_WRITE, +                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +  // Try to increase the probability that the sideline thread is +  // scheduled.  Unfortunately we can't do proper synchronization +  // without some form of annotation or something. +  sched_yield(); +  // Do enough work to get at least 4 samples. +  for (int j = 0; j < iters; ++j) { +    for (int i = 0; i < size; ++i) +      buf[i] = i; +    sched_yield(); +  } +  // Ensure a non-esan build works without ifdefs: +  if (__esan_report) { +    // We should get 2 roughly identical reports: +    __esan_report(); +  } +  munmap(buf, size); +  fprintf(stderr, "all done\n"); +  // CHECK-NO-ESAN:   all done +  // We only check for a few samples here to reduce the chance of flakiness: +  // CHECK-ESAN:      =={{[0-9]+}}== Total number of samples: {{[0-9]+}} +  // CHECK-ESAN-NEXT: =={{[0-9]+}}== Samples array #0 at period 20 ms +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   1: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   2: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   3: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #1 at period 80 ms +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #2 at period 320 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #3 at period 1280 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #4 at period 5120 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #5 at period 20 sec +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #6 at period 81 sec +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #7 at period 327 sec +  // CHECK-ESAN: {{.*}} EfficiencySanitizer: the total working set size: 32 MB (5242{{[0-9][0-9]}} cache lines) +  // CHECK-ESAN-NEXT: all done +  // CHECK-ESAN-NEXT: =={{[0-9]+}}== Total number of samples: {{[0-9]+}} +  // CHECK-ESAN-NEXT: =={{[0-9]+}}== Samples array #0 at period 20 ms +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   1: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   2: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   3: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #1 at period 80 ms +  // CHECK-ESAN-NEXT: =={{[0-9]+}}==#   0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #2 at period 320 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #3 at period 1280 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #4 at period 5120 ms +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #5 at period 20 sec +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #6 at period 81 sec +  // CHECK-ESAN:      =={{[0-9]+}}== Samples array #7 at period 327 sec +  // CHECK-ESAN: {{.*}} EfficiencySanitizer: the total working set size: 32 MB (5242{{[0-9][0-9]}} cache lines) +  return 0; +} diff --git a/compiler-rt/test/esan/lit.cfg b/compiler-rt/test/esan/lit.cfg index 5f7ba16dce7..cf16a6b5df4 100644 --- a/compiler-rt/test/esan/lit.cfg +++ b/compiler-rt/test/esan/lit.cfg @@ -23,6 +23,8 @@ unit_cxxflags = (["-I%s" % esan_incdir, "-std=c++11",  def build_invocation(compile_flags):    return " " + " ".join([config.clang] + compile_flags) + " " +config.substitutions.append( ("%clang ", +                              build_invocation(base_cflags)) )  config.substitutions.append( ("%clang_esan_frag ",                                build_invocation(frag_cflags)) )  config.substitutions.append( ("%clang_esan_wset ", | 

