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