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