]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/esan/esan_sideline_linux.cpp
MFC r355070:
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / esan / esan_sideline_linux.cpp
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   InternalMmapVector<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