Index: include/CMakeLists.txt =================================================================== --- include/CMakeLists.txt +++ 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: include/sanitizer/esan_interface.h =================================================================== --- /dev/null +++ include/sanitizer/esan_interface.h @@ -0,0 +1,32 @@ +//===-- 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 + +#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 __esan_report(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_ESAN_INTERFACE_H Index: lib/esan/cache_frag.h =================================================================== --- lib/esan/cache_frag.h +++ lib/esan/cache_frag.h @@ -22,6 +22,7 @@ void initializeCacheFrag(); int finalizeCacheFrag(); +void reportCacheFrag(); } // namespace __esan Index: lib/esan/cache_frag.cpp =================================================================== --- lib/esan/cache_frag.cpp +++ lib/esan/cache_frag.cpp @@ -191,4 +191,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: lib/esan/esan.h =================================================================== --- lib/esan/esan.h +++ 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: lib/esan/esan.cpp =================================================================== --- lib/esan/esan.cpp +++ 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: lib/esan/esan_interface.cpp =================================================================== --- lib/esan/esan_interface.cpp +++ 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: lib/esan/working_set.h =================================================================== --- lib/esan/working_set.h +++ 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: lib/esan/working_set.cpp =================================================================== --- lib/esan/working_set.cpp +++ lib/esan/working_set.cpp @@ -118,6 +118,7 @@ } // This routine will word-align ShadowStart and ShadowEnd prior to scanning. +// It does *not* clear for BitIdx==TotalWorkingSetBitIdx. static u32 countAndClearShadowValues(u32 BitIdx, uptr ShadowStart, uptr ShadowEnd) { u32 WorkingSetSize = 0; @@ -127,6 +128,7 @@ // Get word aligned start. ShadowStart = RoundDownTo(ShadowStart, sizeof(u32)); bool Accum = getFlags()->record_snapshots && BitIdx < MaxAccumBitIdx; + bool Clear = BitIdx < TotalWorkingSetBitIdx; for (u32 *Ptr = (u32 *)ShadowStart; Ptr < (u32 *)ShadowEnd; ++Ptr) { if ((*Ptr & WordValue) != 0) { byte *BytePtr = (byte *)Ptr; @@ -139,8 +141,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 +153,7 @@ // 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. static u32 computeWorkingSizeAndReset(u32 BitIdx) { u32 WorkingSetSize = 0; MemoryMappingLayout MemIter(true/*cache*/); @@ -226,10 +231,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 +247,6 @@ SizePerFreq[i][j]); } Freq = Freq << getFlags()->snapshot_step; - SizePerFreq[i].free(); } } @@ -252,6 +255,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: test/esan/TestCases/workingset-midreport.cpp =================================================================== --- /dev/null +++ test/esan/TestCases/workingset-midreport.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_esan_wset -O0 %s -o %t 2>&1 +// RUN: %run %t 2>&1 | FileCheck %s + +#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(); + } + // We should get 2 roughly identical reports: + __esan_report(); + munmap(buf, size); + // We only check for a few samples here to reduce the chance of flakiness. + // CHECK: =={{[0-9]+}}== Total number of samples: {{[0-9]+}} + // CHECK-NEXT: =={{[0-9]+}}== Samples array #0 at period 20 ms + // CHECK-NEXT: =={{[0-9]+}}==# 0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 1: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 2: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 3: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK: =={{[0-9]+}}== Samples array #1 at period 80 ms + // CHECK-NEXT: =={{[0-9]+}}==# 0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK: =={{[0-9]+}}== Samples array #2 at period 320 ms + // CHECK: =={{[0-9]+}}== Samples array #3 at period 1280 ms + // CHECK: =={{[0-9]+}}== Samples array #4 at period 5120 ms + // CHECK: =={{[0-9]+}}== Samples array #5 at period 20 sec + // CHECK: =={{[0-9]+}}== Samples array #6 at period 81 sec + // CHECK: =={{[0-9]+}}== Samples array #7 at period 327 sec + // CHECK: {{.*}} EfficiencySanitizer: the total working set size: 32 MB (5242{{[0-9][0-9]}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}== Total number of samples: {{[0-9]+}} + // CHECK-NEXT: =={{[0-9]+}}== Samples array #0 at period 20 ms + // CHECK-NEXT: =={{[0-9]+}}==# 0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 1: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 2: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK-NEXT: =={{[0-9]+}}==# 3: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK: =={{[0-9]+}}== Samples array #1 at period 80 ms + // CHECK-NEXT: =={{[0-9]+}}==# 0: {{[ 0-9]+}} {{KB|MB|Bytes}} ({{[ 0-9]+}} cache lines) + // CHECK: =={{[0-9]+}}== Samples array #2 at period 320 ms + // CHECK: =={{[0-9]+}}== Samples array #3 at period 1280 ms + // CHECK: =={{[0-9]+}}== Samples array #4 at period 5120 ms + // CHECK: =={{[0-9]+}}== Samples array #5 at period 20 sec + // CHECK: =={{[0-9]+}}== Samples array #6 at period 81 sec + // CHECK: =={{[0-9]+}}== Samples array #7 at period 327 sec + // CHECK: {{.*}} EfficiencySanitizer: the total working set size: 32 MB (5242{{[0-9][0-9]}} cache lines) + return 0; +}