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.
handleSidelineSignal(int SigNum,void * SigInfo,void * Ctx)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
registerSignal(int SigNum)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
runSideline(void * Arg)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
launchThread(SidelineFunc takeSample,void * Arg,u32 FreqMilliSec)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
joinThread()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.
adjustTimer(u32 FreqMilliSec)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