[esan] Add __esan_report for mid-run data

Summary:
Adds a new public interface routine __esan_report() which can be used to
request profiling results prior to abnormal termination (e.g., for a server
process killed by its parent where the normal exit does not allow for
normal result reporting).

Implements this for the working-set tool.  The cache frag tool is left
unimplemented as it requires missing iteration capabilities.

Adds a new test.

Reviewers: aizatsky

Subscribers: vitalybuka, zhaoqin, kcc, eugenis, llvm-commits, kubabrecka

Differential Revision: http://reviews.llvm.org/D22098

llvm-svn: 274964
This commit is contained in:
Derek Bruening 2016-07-09 04:13:25 +00:00
parent 081fd139b3
commit dec4bd0838
11 changed files with 166 additions and 5 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -22,6 +22,7 @@ void processCacheFragCompilationUnitExit(void *Ptr);
void initializeCacheFrag();
int finalizeCacheFrag();
void reportCacheFrag();
} // namespace __esan

View File

@ -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) {

View File

@ -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);

View File

@ -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"

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 ",