summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/CMakeLists.txt1
-rw-r--r--include/sanitizer/esan_interface.h46
-rw-r--r--lib/esan/cache_frag.cpp6
-rw-r--r--lib/esan/cache_frag.h1
-rw-r--r--lib/esan/esan.cpp9
-rw-r--r--lib/esan/esan.h1
-rw-r--r--lib/esan/esan_interface.cpp7
-rw-r--r--lib/esan/working_set.cpp26
-rw-r--r--lib/esan/working_set.h1
-rw-r--r--test/esan/TestCases/workingset-midreport.cpp71
-rw-r--r--test/esan/lit.cfg2
11 files changed, 166 insertions, 5 deletions
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index ad1437ed1..0139b0601 100644
--- a/include/CMakeLists.txt
+++ b/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/include/sanitizer/esan_interface.h b/include/sanitizer/esan_interface.h
new file mode 100644
index 000000000..31e9b9b4d
--- /dev/null
+++ b/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/lib/esan/cache_frag.cpp b/lib/esan/cache_frag.cpp
index 343abfcd5..a3e612dac 100644
--- a/lib/esan/cache_frag.cpp
+++ b/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/lib/esan/cache_frag.h b/lib/esan/cache_frag.h
index 9f8793220..646d3f85e 100644
--- a/lib/esan/cache_frag.h
+++ b/lib/esan/cache_frag.h
@@ -22,6 +22,7 @@ void processCacheFragCompilationUnitExit(void *Ptr);
void initializeCacheFrag();
int finalizeCacheFrag();
+void reportCacheFrag();
} // namespace __esan
diff --git a/lib/esan/esan.cpp b/lib/esan/esan.cpp
index 970f91a83..3c69b4e91 100644
--- a/lib/esan/esan.cpp
+++ b/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/lib/esan/esan.h b/lib/esan/esan.h
index 4171522db..371810d5e 100644
--- a/lib/esan/esan.h
+++ b/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/lib/esan/esan_interface.cpp b/lib/esan/esan_interface.cpp
index f6ad3caa8..8a64d1526 100644
--- a/lib/esan/esan_interface.cpp
+++ b/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/lib/esan/working_set.cpp b/lib/esan/working_set.cpp
index 622fd29e8..3fde5a8b5 100644
--- a/lib/esan/working_set.cpp
+++ b/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/lib/esan/working_set.h b/lib/esan/working_set.h
index 660b5d066..38ff0635d 100644
--- a/lib/esan/working_set.h
+++ b/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/test/esan/TestCases/workingset-midreport.cpp b/test/esan/TestCases/workingset-midreport.cpp
new file mode 100644
index 000000000..470e33ca3
--- /dev/null
+++ b/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/test/esan/lit.cfg b/test/esan/lit.cfg
index 5f7ba16dc..cf16a6b5d 100644
--- a/test/esan/lit.cfg
+++ b/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 ",