blob: 2de25fba700ddcb61f1b309a353b0dba3910b358 [file] [log] [blame]
Derek Bruening07814762016-06-03 16:14:07 +00001//===-- 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
30namespace __esan {
31
32static const int SigAltStackSize = 4*1024;
33static const int SidelineStackSize = 4*1024;
Derek Bruening94197372016-07-19 05:03:38 +000034static const uptr SidelineIdUninitialized = 1;
Derek Bruening07814762016-06-03 16:14:07 +000035
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.
40static SidelineThread *TheThread;
41
42// We aren't passing SA_NODEFER so the same signal is blocked while here.
Vitaly Buka3e3f3cf2017-11-10 05:41:13 +000043void SidelineThread::handleSidelineSignal(int SigNum,
44 __sanitizer_siginfo *SigInfo,
Derek Bruening07814762016-06-03 16:14:07 +000045 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
55void 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
65int 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 Buka2a209552018-05-07 05:56:36 +000073 InternalMmapVector<char> StackMap(SigAltStackSize);
Hans Wennborg67ef6552017-08-22 21:54:37 +000074 stack_t SigAltStack;
Derek Bruening07814762016-06-03 16:14:07 +000075 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
103bool 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 Bruening94197372016-07-19 05:03:38 +0000118 // 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 Bruening07814762016-06-03 16:14:07 +0000122 // 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
139bool 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.
159bool SidelineThread::adjustTimer(u32 FreqMilliSec) {
Derek Bruening94197372016-07-19 05:03:38 +0000160 // The return value of internal_clone() may not have been assigned yet:
161 CHECK(internal_getpid() == SidelineId ||
162 SidelineId == SidelineIdUninitialized);
Derek Bruening07814762016-06-03 16:14:07 +0000163 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