Index: compiler-rt/trunk/include/CMakeLists.txt =================================================================== --- compiler-rt/trunk/include/CMakeLists.txt +++ compiler-rt/trunk/include/CMakeLists.txt @@ -4,6 +4,7 @@ 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 Index: compiler-rt/trunk/include/sanitizer/esan_interface.h =================================================================== --- compiler-rt/trunk/include/sanitizer/esan_interface.h +++ compiler-rt/trunk/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 + +// 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 Index: compiler-rt/trunk/lib/esan/cache_frag.h =================================================================== --- compiler-rt/trunk/lib/esan/cache_frag.h +++ compiler-rt/trunk/lib/esan/cache_frag.h @@ -22,6 +22,7 @@ void initializeCacheFrag(); int finalizeCacheFrag(); +void reportCacheFrag(); } // namespace __esan Index: compiler-rt/trunk/lib/esan/cache_frag.cpp =================================================================== --- compiler-rt/trunk/lib/esan/cache_frag.cpp +++ compiler-rt/trunk/lib/esan/cache_frag.cpp @@ -199,4 +199,10 @@ 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 Index: compiler-rt/trunk/lib/esan/esan.h =================================================================== --- compiler-rt/trunk/lib/esan/esan.h +++ compiler-rt/trunk/lib/esan/esan.h @@ -37,6 +37,7 @@ 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); Index: compiler-rt/trunk/lib/esan/esan.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan.cpp +++ compiler-rt/trunk/lib/esan/esan.cpp @@ -228,6 +228,15 @@ 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) { Index: compiler-rt/trunk/lib/esan/esan_interface.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan_interface.cpp +++ compiler-rt/trunk/lib/esan/esan_interface.cpp @@ -109,3 +109,10 @@ 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" Index: compiler-rt/trunk/lib/esan/working_set.h =================================================================== --- compiler-rt/trunk/lib/esan/working_set.h +++ compiler-rt/trunk/lib/esan/working_set.h @@ -23,6 +23,7 @@ void initializeWorkingSet(); void initializeShadowWorkingSet(); int finalizeWorkingSet(); +void reportWorkingSet(); void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, bool IsWrite); Index: compiler-rt/trunk/lib/esan/working_set.cpp =================================================================== --- compiler-rt/trunk/lib/esan/working_set.cpp +++ compiler-rt/trunk/lib/esan/working_set.cpp @@ -118,6 +118,8 @@ } // 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 @@ // 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 @@ } } } - // 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 @@ // 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 @@ } } -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 @@ SizePerFreq[i][j]); } Freq = Freq << getFlags()->snapshot_step; - SizePerFreq[i].free(); } } @@ -252,6 +258,16 @@ 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; } Index: compiler-rt/trunk/test/esan/TestCases/workingset-midreport.cpp =================================================================== --- compiler-rt/trunk/test/esan/TestCases/workingset-midreport.cpp +++ compiler-rt/trunk/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 +#include +#include +#include +#include +#include + +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; +} Index: compiler-rt/trunk/test/esan/lit.cfg =================================================================== --- compiler-rt/trunk/test/esan/lit.cfg +++ compiler-rt/trunk/test/esan/lit.cfg @@ -23,6 +23,8 @@ 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 ",