[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:
parent
081fd139b3
commit
dec4bd0838
@ -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
|
||||
|
||||
46
compiler-rt/include/sanitizer/esan_interface.h
Normal file
46
compiler-rt/include/sanitizer/esan_interface.h
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -22,6 +22,7 @@ void processCacheFragCompilationUnitExit(void *Ptr);
|
||||
|
||||
void initializeCacheFrag();
|
||||
int finalizeCacheFrag();
|
||||
void reportCacheFrag();
|
||||
|
||||
} // namespace __esan
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
71
compiler-rt/test/esan/TestCases/workingset-midreport.cpp
Normal file
71
compiler-rt/test/esan/TestCases/workingset-midreport.cpp
Normal 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;
|
||||
}
|
||||
@ -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 ",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user