Home | History | Annotate | Download | only in esan
      1 //===-- esan_sideline_linux.cpp ---------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of EfficiencySanitizer, a family of performance tuners.
     11 //
     12 // Support for a separate or "sideline" tool thread on Linux.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "sanitizer_common/sanitizer_platform.h"
     16 #if SANITIZER_LINUX
     17 
     18 #include "esan_sideline.h"
     19 #include "sanitizer_common/sanitizer_atomic.h"
     20 #include "sanitizer_common/sanitizer_common.h"
     21 #include "sanitizer_common/sanitizer_linux.h"
     22 #include <errno.h>
     23 #include <sched.h>
     24 #include <sys/prctl.h>
     25 #include <sys/signal.h>
     26 #include <sys/time.h>
     27 #include <sys/types.h>
     28 #include <sys/wait.h>
     29 
     30 namespace __esan {
     31 
     32 static const int SigAltStackSize = 4*1024;
     33 static const int SidelineStackSize = 4*1024;
     34 
     35 // FIXME: we'll need some kind of TLS (can we trust that a pthread key will
     36 // work in our non-POSIX thread?) to access our data in our signal handler
     37 // with multiple sideline threads.  For now we assume there is only one
     38 // sideline thread and we use a dirty solution of a global var.
     39 static SidelineThread *TheThread;
     40 
     41 // We aren't passing SA_NODEFER so the same signal is blocked while here.
     42 void SidelineThread::handleSidelineSignal(int SigNum, void *SigInfo,
     43                                           void *Ctx) {
     44   VPrintf(3, "Sideline signal %d\n", SigNum);
     45   CHECK_EQ(SigNum, SIGALRM);
     46   // See above about needing TLS to avoid this global var.
     47   SidelineThread *Thread = TheThread;
     48   if (atomic_load(&Thread->SidelineExit, memory_order_relaxed) != 0)
     49     return;
     50   Thread->sampleFunc(Thread->FuncArg);
     51 }
     52 
     53 void SidelineThread::registerSignal(int SigNum) {
     54   __sanitizer_sigaction SigAct;
     55   internal_memset(&SigAct, 0, sizeof(SigAct));
     56   SigAct.sigaction = handleSidelineSignal;
     57   // We do not pass SA_NODEFER as we want to block the same signal.
     58   SigAct.sa_flags = SA_ONSTACK | SA_SIGINFO;
     59   int Res = internal_sigaction(SigNum, &SigAct, nullptr);
     60   CHECK_EQ(Res, 0);
     61 }
     62 
     63 int SidelineThread::runSideline(void *Arg) {
     64   VPrintf(1, "Sideline thread starting\n");
     65   SidelineThread *Thread = static_cast<SidelineThread*>(Arg);
     66 
     67   // If the parent dies, we want to exit also.
     68   internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
     69 
     70   // Set up a signal handler on an alternate stack for safety.
     71   InternalScopedBuffer<char> StackMap(SigAltStackSize);
     72   struct sigaltstack SigAltStack;
     73   SigAltStack.ss_sp = StackMap.data();
     74   SigAltStack.ss_size = SigAltStackSize;
     75   SigAltStack.ss_flags = 0;
     76   internal_sigaltstack(&SigAltStack, nullptr);
     77 
     78   // We inherit the signal mask from the app thread.  In case
     79   // we weren't created at init time, we ensure the mask is empty.
     80   __sanitizer_sigset_t SigSet;
     81   internal_sigfillset(&SigSet);
     82   int Res = internal_sigprocmask(SIG_UNBLOCK, &SigSet, nullptr);
     83   CHECK_EQ(Res, 0);
     84 
     85   registerSignal(SIGALRM);
     86 
     87   bool TimerSuccess = Thread->adjustTimer(Thread->Freq);
     88   CHECK(TimerSuccess);
     89 
     90   // We loop, doing nothing but handling itimer signals.
     91   while (atomic_load(&TheThread->SidelineExit, memory_order_relaxed) == 0)
     92     sched_yield();
     93 
     94   if (!Thread->adjustTimer(0))
     95     VPrintf(1, "Failed to disable timer\n");
     96 
     97   VPrintf(1, "Sideline thread exiting\n");
     98   return 0;
     99 }
    100 
    101 bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg,
    102                                   u32 FreqMilliSec) {
    103   // This can only be called once.  However, we can't clear a field in
    104   // the constructor and check for that here as the constructor for
    105   // a static instance is called *after* our module_ctor and thus after
    106   // this routine!  Thus we rely on the TheThread check below.
    107   CHECK(TheThread == nullptr); // Only one sideline thread is supported.
    108   TheThread = this;
    109   sampleFunc = takeSample;
    110   FuncArg = Arg;
    111   Freq = FreqMilliSec;
    112   atomic_store(&SidelineExit, 0, memory_order_relaxed);
    113 
    114   // We do without a guard page.
    115   Stack = static_cast<char*>(MmapOrDie(SidelineStackSize, "SidelineStack"));
    116   // By omitting CLONE_THREAD, the child is in its own thread group and will not
    117   // receive any of the application's signals.
    118   SidelineId = internal_clone(
    119       runSideline, Stack + SidelineStackSize,
    120       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
    121       this, nullptr /* parent_tidptr */,
    122       nullptr /* newtls */, nullptr /* child_tidptr */);
    123   int ErrCode;
    124   if (internal_iserror(SidelineId, &ErrCode)) {
    125     Printf("FATAL: EfficiencySanitizer failed to spawn a thread (code %d).\n",
    126            ErrCode);
    127     Die();
    128     return false; // Not reached.
    129   }
    130   return true;
    131 }
    132 
    133 bool SidelineThread::joinThread() {
    134   VPrintf(1, "Joining sideline thread\n");
    135   bool Res = true;
    136   atomic_store(&SidelineExit, 1, memory_order_relaxed);
    137   while (true) {
    138     uptr Status = internal_waitpid(SidelineId, nullptr, __WALL);
    139     int ErrCode;
    140     if (!internal_iserror(Status, &ErrCode))
    141       break;
    142     if (ErrCode == EINTR)
    143       continue;
    144     VPrintf(1, "Failed to join sideline thread (errno %d)\n", ErrCode);
    145     Res = false;
    146     break;
    147   }
    148   UnmapOrDie(Stack, SidelineStackSize);
    149   return Res;
    150 }
    151 
    152 // Must be called from the sideline thread itself.
    153 bool SidelineThread::adjustTimer(u32 FreqMilliSec) {
    154   CHECK(internal_getpid() == SidelineId);
    155   Freq = FreqMilliSec;
    156   struct itimerval TimerVal;
    157   TimerVal.it_interval.tv_sec = (time_t) Freq / 1000;
    158   TimerVal.it_interval.tv_usec = (time_t) (Freq % 1000) * 1000;
    159   TimerVal.it_value.tv_sec = (time_t) Freq / 1000;
    160   TimerVal.it_value.tv_usec = (time_t) (Freq % 1000) * 1000;
    161   // As we're in a different thread group, we cannot use either
    162   // ITIMER_PROF or ITIMER_VIRTUAL without taking up scheduled
    163   // time ourselves: thus we must use real time.
    164   int Res = setitimer(ITIMER_REAL, &TimerVal, nullptr);
    165   return (Res == 0);
    166 }
    167 
    168 } // namespace __esan
    169 
    170 #endif // SANITIZER_LINUX
    171