• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <sys/syscall.h>
2 #include <sys/time.h>
3 #include <time.h>
4 #include <unistd.h>
5 
6 #include <atomic>
7 #include <csignal>
8 #include <string>
9 
10 #include "absl/base/log_severity.h"
11 #include "absl/flags/flag.h"
12 #include "absl/flags/parse.h"
13 #include "absl/log/check.h"
14 #include "absl/log/globals.h"
15 #include "absl/log/initialize.h"
16 #include "absl/log/log.h"
17 #include "absl/strings/string_view.h"
18 #include "absl/time/clock.h"
19 #include "absl/time/time.h"
20 
21 ABSL_FLAG(std::string, sigev_notify_kind, "",
22           "The C name for the kind of POSIX timer to create (sigev_notify), or "
23           "\"syscall(SIGEV_THREAD)\" for a manual syscall approach which "
24           "checks that no threads were created.");
25 
main(int argc,char * argv[])26 int main(int argc, char* argv[]) {
27   absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
28   absl::ParseCommandLine(argc, argv);
29   absl::InitializeLog();
30 
31   static std::atomic<bool> timer_expired(false);
32   static std::atomic<pid_t> tid(0);
33   static_assert(std::atomic<pid_t>::is_always_lock_free);
34   // Handle SIGPROF by recording that it arrived.
35   signal(
36       SIGPROF, +[](int) {
37         timer_expired.store(true);
38         tid.store(syscall(__NR_gettid));
39       });
40 
41   const std::string sigev_notify_kind = absl::GetFlag(FLAGS_sigev_notify_kind);
42   struct sigevent sev {};
43   sev.sigev_signo = SIGPROF;
44 
45   if (sigev_notify_kind == "SIGEV_THREAD" ||
46       sigev_notify_kind == "syscall(SIGEV_THREAD)") {
47     sev.sigev_notify = SIGEV_THREAD;
48     sev.sigev_notify_function = +[](sigval_t) {
49       timer_expired.store(true);
50       tid.store(syscall(__NR_gettid));
51     };
52   } else if (sigev_notify_kind == "SIGEV_SIGNAL") {
53     sev.sigev_notify = SIGEV_SIGNAL;
54     sev.sigev_signo = SIGPROF;
55   } else if (sigev_notify_kind == "SIGEV_NONE") {
56     sev.sigev_notify = SIGEV_NONE;
57   } else if (sigev_notify_kind == "SIGEV_THREAD_ID") {
58     sev.sigev_notify = SIGEV_THREAD_ID;
59     sev.sigev_signo = SIGPROF;
60 #ifndef sigev_notify_thread_id
61     sev._sigev_un._tid = syscall(__NR_gettid);
62 #else
63     sev.sigev_notify_thread_id = syscall(__NR_gettid);
64 #endif
65   } else {
66     LOG(QFATAL) << "Invalid --sigev_notify_kind: " << sigev_notify_kind;
67   }
68 
69   struct itimerspec timerspec {};
70   timerspec.it_interval.tv_sec = 0;
71   timerspec.it_interval.tv_nsec = 1'000'000;
72   timerspec.it_value.tv_sec = 0;
73   timerspec.it_value.tv_nsec = 1'000'000;
74 
75   if (sigev_notify_kind == "syscall(SIGEV_THREAD)") {
76     // Use raw syscalls.
77     int timer;
78     PCHECK(syscall(__NR_timer_create, CLOCK_REALTIME, &sev, &timer) == 0);
79     PCHECK(syscall(__NR_timer_settime, timer, 0, &timerspec, nullptr) == 0);
80 
81     // Long enough to effectively guarantee that we see the notification.
82     absl::SleepFor(absl::Milliseconds(30));
83 
84     PCHECK(syscall(__NR_timer_gettime, timer, &timerspec) == 0);
85     PCHECK(syscall(__NR_timer_getoverrun, timer) != -1);
86 
87     PCHECK(syscall(__NR_timer_delete, timer) == 0);
88 
89     // The syscall with SIGEV_THREAD doesn't spawn a thread, which we can verify
90     // by checking that the thread ID is the main thread.
91     CHECK_EQ(tid.load(), syscall(__NR_gettid));
92   } else {
93     timer_t timer;
94     PCHECK(timer_create(CLOCK_REALTIME, &sev, &timer) == 0);
95     PCHECK(timer_settime(timer, 0, &timerspec, nullptr) == 0);
96 
97     // Long enough to effectively guarantee that we see the notification.
98     absl::SleepFor(absl::Milliseconds(30));
99 
100     PCHECK(timer_gettime(timer, &timerspec) == 0);
101     PCHECK(timer_getoverrun(timer) != -1);
102 
103     PCHECK(timer_delete(timer) == 0);
104   }
105 
106   if (sigev_notify_kind == "SIGEV_THREAD" ||
107       sigev_notify_kind == "syscall(SIGEV_THREAD)" ||
108       sigev_notify_kind == "SIGEV_THREAD_ID" ||
109       sigev_notify_kind == "SIGEV_SIGNAL") {
110     CHECK(timer_expired.load());
111   } else {
112     CHECK_EQ(sigev_notify_kind, "SIGEV_NONE");
113     CHECK(!timer_expired.load());
114   }
115 
116   return 0;
117 }
118