Files
clang-p2996/compiler-rt/lib/esan/esan_sideline_linux.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

178 lines
6.4 KiB
C++

//===-- esan_sideline_linux.cpp ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// 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;
static const uptr SidelineIdUninitialized = 1;
// 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,
__sanitizer_siginfo *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.
InternalMmapVector<char> StackMap(SigAltStackSize);
stack_t 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"));
// We need to handle the return value from internal_clone() not having been
// assigned yet (for our CHECK in adjustTimer()) so we ensure this has a
// sentinel value.
SidelineId = SidelineIdUninitialized;
// 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) {
// The return value of internal_clone() may not have been assigned yet:
CHECK(internal_getpid() == SidelineId ||
SidelineId == SidelineIdUninitialized);
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