Files
clang-p2996/compiler-rt/lib/esan/esan_sideline_linux.cpp
Derek Bruening 07814769a8 [esan] Add sideline itimer support
Summary:
Adds support for creating a separate thread for performing "sideline"
actions on a periodic basis via an itimer.  A new class SidelineThread
implements this feature, exposing a sampling callback to the caller.

Adds initial usage of sideline sampling to the working set tool.  For now
it simply prints the usage at each snapshot at verbosity level 1.  Adds a
test of this behavior.  Adds a new option -record_snapshots to control
whether we sample and a new option -sample_freq to control the periodicity
of the sampling.

Reviewers: aizatsky

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

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

llvm-svn: 271682
2016-06-03 16:14:07 +00:00

171 lines
5.9 KiB
C++

//===-- esan_sideline_linux.cpp ---------------------------------*- 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.
//
// Support for a separate or "sideline" tool thread on Linux.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_LINUX
#include "esan_sideline.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_linux.h"
#include <errno.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
namespace __esan {
static const int SigAltStackSize = 4*1024;
static const int SidelineStackSize = 4*1024;
// FIXME: we'll need some kind of TLS (can we trust that a pthread key will
// work in our non-POSIX thread?) to access our data in our signal handler
// with multiple sideline threads. For now we assume there is only one
// sideline thread and we use a dirty solution of a global var.
static SidelineThread *TheThread;
// We aren't passing SA_NODEFER so the same signal is blocked while here.
void SidelineThread::handleSidelineSignal(int SigNum, void *SigInfo,
void *Ctx) {
VPrintf(3, "Sideline signal %d\n", SigNum);
CHECK_EQ(SigNum, SIGALRM);
// See above about needing TLS to avoid this global var.
SidelineThread *Thread = TheThread;
if (atomic_load(&Thread->SidelineExit, memory_order_relaxed) != 0)
return;
Thread->sampleFunc(Thread->FuncArg);
}
void SidelineThread::registerSignal(int SigNum) {
__sanitizer_sigaction SigAct;
internal_memset(&SigAct, 0, sizeof(SigAct));
SigAct.sigaction = handleSidelineSignal;
// We do not pass SA_NODEFER as we want to block the same signal.
SigAct.sa_flags = SA_ONSTACK | SA_SIGINFO;
int Res = internal_sigaction(SigNum, &SigAct, nullptr);
CHECK_EQ(Res, 0);
}
int SidelineThread::runSideline(void *Arg) {
VPrintf(1, "Sideline thread starting\n");
SidelineThread *Thread = static_cast<SidelineThread*>(Arg);
// If the parent dies, we want to exit also.
internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
// Set up a signal handler on an alternate stack for safety.
InternalScopedBuffer<char> StackMap(SigAltStackSize);
struct sigaltstack SigAltStack;
SigAltStack.ss_sp = StackMap.data();
SigAltStack.ss_size = SigAltStackSize;
SigAltStack.ss_flags = 0;
internal_sigaltstack(&SigAltStack, nullptr);
// We inherit the signal mask from the app thread. In case
// we weren't created at init time, we ensure the mask is empty.
__sanitizer_sigset_t SigSet;
internal_sigfillset(&SigSet);
int Res = internal_sigprocmask(SIG_UNBLOCK, &SigSet, nullptr);
CHECK_EQ(Res, 0);
registerSignal(SIGALRM);
bool TimerSuccess = Thread->adjustTimer(Thread->Freq);
CHECK(TimerSuccess);
// We loop, doing nothing but handling itimer signals.
while (atomic_load(&TheThread->SidelineExit, memory_order_relaxed) == 0)
sched_yield();
if (!Thread->adjustTimer(0))
VPrintf(1, "Failed to disable timer\n");
VPrintf(1, "Sideline thread exiting\n");
return 0;
}
bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg,
u32 FreqMilliSec) {
// This can only be called once. However, we can't clear a field in
// the constructor and check for that here as the constructor for
// a static instance is called *after* our module_ctor and thus after
// this routine! Thus we rely on the TheThread check below.
CHECK(TheThread == nullptr); // Only one sideline thread is supported.
TheThread = this;
sampleFunc = takeSample;
FuncArg = Arg;
Freq = FreqMilliSec;
atomic_store(&SidelineExit, 0, memory_order_relaxed);
// We do without a guard page.
Stack = static_cast<char*>(MmapOrDie(SidelineStackSize, "SidelineStack"));
// By omitting CLONE_THREAD, the child is in its own thread group and will not
// receive any of the application's signals.
SidelineId = internal_clone(
runSideline, Stack + SidelineStackSize,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
this, nullptr /* parent_tidptr */,
nullptr /* newtls */, nullptr /* child_tidptr */);
int ErrCode;
if (internal_iserror(SidelineId, &ErrCode)) {
Printf("FATAL: EfficiencySanitizer failed to spawn a thread (code %d).\n",
ErrCode);
Die();
return false; // Not reached.
}
return true;
}
bool SidelineThread::joinThread() {
VPrintf(1, "Joining sideline thread\n");
bool Res = true;
atomic_store(&SidelineExit, 1, memory_order_relaxed);
while (true) {
uptr Status = internal_waitpid(SidelineId, nullptr, __WALL);
int ErrCode;
if (!internal_iserror(Status, &ErrCode))
break;
if (ErrCode == EINTR)
continue;
VPrintf(1, "Failed to join sideline thread (errno %d)\n", ErrCode);
Res = false;
break;
}
UnmapOrDie(Stack, SidelineStackSize);
return Res;
}
// Must be called from the sideline thread itself.
bool SidelineThread::adjustTimer(u32 FreqMilliSec) {
CHECK(internal_getpid() == SidelineId);
Freq = FreqMilliSec;
struct itimerval TimerVal;
TimerVal.it_interval.tv_sec = (time_t) Freq / 1000;
TimerVal.it_interval.tv_usec = (time_t) (Freq % 1000) * 1000;
TimerVal.it_value.tv_sec = (time_t) Freq / 1000;
TimerVal.it_value.tv_usec = (time_t) (Freq % 1000) * 1000;
// As we're in a different thread group, we cannot use either
// ITIMER_PROF or ITIMER_VIRTUAL without taking up scheduled
// time ourselves: thus we must use real time.
int Res = setitimer(ITIMER_REAL, &TimerVal, nullptr);
return (Res == 0);
}
} // namespace __esan
#endif // SANITIZER_LINUX