1 //===-- esan_sideline_linux.cpp ---------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of EfficiencySanitizer, a family of performance tuners.
12 // Support for a separate or "sideline" tool thread on Linux.
13 //===----------------------------------------------------------------------===//
15 #include "sanitizer_common/sanitizer_platform.h"
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"
24 #include <sys/prctl.h>
25 #include <sys/signal.h>
27 #include <sys/types.h>
32 static const int SigAltStackSize
= 4*1024;
33 static const int SidelineStackSize
= 4*1024;
34 static const uptr SidelineIdUninitialized
= 1;
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
;
42 // We aren't passing SA_NODEFER so the same signal is blocked while here.
43 void SidelineThread::handleSidelineSignal(int SigNum
, void *SigInfo
,
45 VPrintf(3, "Sideline signal %d\n", SigNum
);
46 CHECK_EQ(SigNum
, SIGALRM
);
47 // See above about needing TLS to avoid this global var.
48 SidelineThread
*Thread
= TheThread
;
49 if (atomic_load(&Thread
->SidelineExit
, memory_order_relaxed
) != 0)
51 Thread
->sampleFunc(Thread
->FuncArg
);
54 void SidelineThread::registerSignal(int SigNum
) {
55 __sanitizer_sigaction SigAct
;
56 internal_memset(&SigAct
, 0, sizeof(SigAct
));
57 SigAct
.sigaction
= handleSidelineSignal
;
58 // We do not pass SA_NODEFER as we want to block the same signal.
59 SigAct
.sa_flags
= SA_ONSTACK
| SA_SIGINFO
;
60 int Res
= internal_sigaction(SigNum
, &SigAct
, nullptr);
64 int SidelineThread::runSideline(void *Arg
) {
65 VPrintf(1, "Sideline thread starting\n");
66 SidelineThread
*Thread
= static_cast<SidelineThread
*>(Arg
);
68 // If the parent dies, we want to exit also.
69 internal_prctl(PR_SET_PDEATHSIG
, SIGKILL
, 0, 0, 0);
71 // Set up a signal handler on an alternate stack for safety.
72 InternalScopedBuffer
<char> StackMap(SigAltStackSize
);
73 struct sigaltstack SigAltStack
;
74 SigAltStack
.ss_sp
= StackMap
.data();
75 SigAltStack
.ss_size
= SigAltStackSize
;
76 SigAltStack
.ss_flags
= 0;
77 internal_sigaltstack(&SigAltStack
, nullptr);
79 // We inherit the signal mask from the app thread. In case
80 // we weren't created at init time, we ensure the mask is empty.
81 __sanitizer_sigset_t SigSet
;
82 internal_sigfillset(&SigSet
);
83 int Res
= internal_sigprocmask(SIG_UNBLOCK
, &SigSet
, nullptr);
86 registerSignal(SIGALRM
);
88 bool TimerSuccess
= Thread
->adjustTimer(Thread
->Freq
);
91 // We loop, doing nothing but handling itimer signals.
92 while (atomic_load(&TheThread
->SidelineExit
, memory_order_relaxed
) == 0)
95 if (!Thread
->adjustTimer(0))
96 VPrintf(1, "Failed to disable timer\n");
98 VPrintf(1, "Sideline thread exiting\n");
102 bool SidelineThread::launchThread(SidelineFunc takeSample
, void *Arg
,
104 // This can only be called once. However, we can't clear a field in
105 // the constructor and check for that here as the constructor for
106 // a static instance is called *after* our module_ctor and thus after
107 // this routine! Thus we rely on the TheThread check below.
108 CHECK(TheThread
== nullptr); // Only one sideline thread is supported.
110 sampleFunc
= takeSample
;
113 atomic_store(&SidelineExit
, 0, memory_order_relaxed
);
115 // We do without a guard page.
116 Stack
= static_cast<char*>(MmapOrDie(SidelineStackSize
, "SidelineStack"));
117 // We need to handle the return value from internal_clone() not having been
118 // assigned yet (for our CHECK in adjustTimer()) so we ensure this has a
120 SidelineId
= SidelineIdUninitialized
;
121 // By omitting CLONE_THREAD, the child is in its own thread group and will not
122 // receive any of the application's signals.
123 SidelineId
= internal_clone(
124 runSideline
, Stack
+ SidelineStackSize
,
125 CLONE_VM
| CLONE_FS
| CLONE_FILES
| CLONE_UNTRACED
,
126 this, nullptr /* parent_tidptr */,
127 nullptr /* newtls */, nullptr /* child_tidptr */);
129 if (internal_iserror(SidelineId
, &ErrCode
)) {
130 Printf("FATAL: EfficiencySanitizer failed to spawn a thread (code %d).\n",
133 return false; // Not reached.
138 bool SidelineThread::joinThread() {
139 VPrintf(1, "Joining sideline thread\n");
141 atomic_store(&SidelineExit
, 1, memory_order_relaxed
);
143 uptr Status
= internal_waitpid(SidelineId
, nullptr, __WALL
);
145 if (!internal_iserror(Status
, &ErrCode
))
147 if (ErrCode
== EINTR
)
149 VPrintf(1, "Failed to join sideline thread (errno %d)\n", ErrCode
);
153 UnmapOrDie(Stack
, SidelineStackSize
);
157 // Must be called from the sideline thread itself.
158 bool SidelineThread::adjustTimer(u32 FreqMilliSec
) {
159 // The return value of internal_clone() may not have been assigned yet:
160 CHECK(internal_getpid() == SidelineId
||
161 SidelineId
== SidelineIdUninitialized
);
163 struct itimerval TimerVal
;
164 TimerVal
.it_interval
.tv_sec
= (time_t) Freq
/ 1000;
165 TimerVal
.it_interval
.tv_usec
= (time_t) (Freq
% 1000) * 1000;
166 TimerVal
.it_value
.tv_sec
= (time_t) Freq
/ 1000;
167 TimerVal
.it_value
.tv_usec
= (time_t) (Freq
% 1000) * 1000;
168 // As we're in a different thread group, we cannot use either
169 // ITIMER_PROF or ITIMER_VIRTUAL without taking up scheduled
170 // time ourselves: thus we must use real time.
171 int Res
= setitimer(ITIMER_REAL
, &TimerVal
, nullptr);
175 } // namespace __esan
177 #endif // SANITIZER_LINUX