Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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; |
Derek Bruening | 9419737 | 2016-07-19 05:03:38 +0000 | [diff] [blame] | 34 | static const uptr SidelineIdUninitialized = 1; |
Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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. |
Vitaly Buka | 3e3f3cf | 2017-11-10 05:41:13 +0000 | [diff] [blame] | 43 | void SidelineThread::handleSidelineSignal(int SigNum, |
| 44 | __sanitizer_siginfo *SigInfo, |
Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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. |
Vitaly Buka | 2a20955 | 2018-05-07 05:56:36 +0000 | [diff] [blame] | 73 | InternalMmapVector<char> StackMap(SigAltStackSize); |
Hans Wennborg | 67ef655 | 2017-08-22 21:54:37 +0000 | [diff] [blame] | 74 | stack_t SigAltStack; |
Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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")); |
Derek Bruening | 9419737 | 2016-07-19 05:03:38 +0000 | [diff] [blame] | 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; |
Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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) { |
Derek Bruening | 9419737 | 2016-07-19 05:03:38 +0000 | [diff] [blame] | 160 | // The return value of internal_clone() may not have been assigned yet: |
| 161 | CHECK(internal_getpid() == SidelineId || |
| 162 | SidelineId == SidelineIdUninitialized); |
Derek Bruening | 0781476 | 2016-06-03 16:14:07 +0000 | [diff] [blame] | 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 |