1 | /*
|
2 | * Souffle - A Datalog Compiler
|
3 | * Copyright (c) 2017, The Souffle Developers. All rights reserved
|
4 | * Licensed under the Universal Permissive License v 1.0 as shown at:
|
5 | * - https://opensource.org/licenses/UPL
|
6 | * - <souffle root>/licenses/SOUFFLE-UPL.txt
|
7 | */
|
8 |
|
9 | /************************************************************************
|
10 | *
|
11 | * @file SignalHandler.h
|
12 | *
|
13 | * A signal handler for Souffle's interpreter and compiler.
|
14 | *
|
15 | ***********************************************************************/
|
16 |
|
17 | #pragma once
|
18 |
|
19 | #include <atomic>
|
20 | #include <csignal>
|
21 | #include <cstdio>
|
22 | #include <cstdlib>
|
23 | #include <cstring>
|
24 | #include <initializer_list>
|
25 | #include <iostream>
|
26 | #include <mutex>
|
27 | #include <string>
|
28 |
|
29 | #ifndef _MSC_VER
|
30 | #include <unistd.h>
|
31 | #else
|
32 | #include <io.h>
|
33 | #define STDERR_FILENO 2
|
34 | #endif
|
35 |
|
36 | namespace souffle {
|
37 |
|
38 | /**
|
39 | * Class SignalHandler captures signals
|
40 | * and reports the context where the signal occurs.
|
41 | * The signal handler is implemented as a singleton.
|
42 | */
|
43 | class SignalHandler {
|
44 | public:
|
45 | // get singleton
|
46 | static SignalHandler* instance() {
|
47 | static SignalHandler singleton;
|
48 | return &singleton;
|
49 | }
|
50 |
|
51 | // Enable logging
|
52 | void enableLogging() {
|
53 | logMessages = true;
|
54 | }
|
55 | // set signal message
|
56 | void setMsg(const char* m) {
|
57 | if (logMessages && m != nullptr) {
|
58 | static std::mutex outputMutex;
|
59 | static bool sameLine = false;
|
60 | std::lock_guard<std::mutex> guard(outputMutex);
|
61 | if (msg != nullptr && strcmp(m, msg) == 0) {
|
62 | std::cout << ".";
|
63 | sameLine = true;
|
64 | } else {
|
65 | if (sameLine) {
|
66 | sameLine = false;
|
67 | std::cout << std::endl;
|
68 | }
|
69 | std::string outputMessage(m);
|
70 | for (char& c : outputMessage) {
|
71 | if (c == '\n' || c == '\t') {
|
72 | c = ' ';
|
73 | }
|
74 | }
|
75 | std::cout << "Starting work on " << outputMessage << std::endl;
|
76 | }
|
77 | }
|
78 | msg = m;
|
79 | }
|
80 |
|
81 | /***
|
82 | * set signal handlers
|
83 | */
|
84 | void set() {
|
85 | if (!isSet && std::getenv("SOUFFLE_ALLOW_SIGNALS") == nullptr) {
|
86 | // register signals
|
87 | // floating point exception
|
88 | if ((prevFpeHandler = signal(SIGFPE, handler)) == SIG_ERR) {
|
89 | perror("Failed to set SIGFPE signal handler.");
|
90 | exit(1);
|
91 | }
|
92 | // user interrupts
|
93 | if ((prevIntHandler = signal(SIGINT, handler)) == SIG_ERR) {
|
94 | perror("Failed to set SIGINT signal handler.");
|
95 | exit(1);
|
96 | }
|
97 | // memory issues
|
98 | if ((prevSegVHandler = signal(SIGSEGV, handler)) == SIG_ERR) {
|
99 | perror("Failed to set SIGSEGV signal handler.");
|
100 | exit(1);
|
101 | }
|
102 | isSet = true;
|
103 | }
|
104 | }
|
105 |
|
106 | /***
|
107 | * reset signal handlers
|
108 | */
|
109 | void reset() {
|
110 | if (isSet) {
|
111 | // reset floating point exception
|
112 | if (signal(SIGFPE, prevFpeHandler) == SIG_ERR) {
|
113 | perror("Failed to reset SIGFPE signal handler.");
|
114 | exit(1);
|
115 | }
|
116 | // user interrupts
|
117 | if (signal(SIGINT, prevIntHandler) == SIG_ERR) {
|
118 | perror("Failed to reset SIGINT signal handler.");
|
119 | exit(1);
|
120 | }
|
121 | // memory issues
|
122 | if (signal(SIGSEGV, prevSegVHandler) == SIG_ERR) {
|
123 | perror("Failed to reset SIGSEGV signal handler.");
|
124 | exit(1);
|
125 | }
|
126 | isSet = false;
|
127 | }
|
128 | }
|
129 |
|
130 | /***
|
131 | * error handling routine that prints the rule context.
|
132 | */
|
133 |
|
134 | void error(const std::string& error) {
|
135 | if (msg != nullptr) {
|
136 | std::cerr << error << " in rule:\n" << msg << std::endl;
|
137 | } else {
|
138 | std::cerr << error << std::endl;
|
139 | }
|
140 | exit(1);
|
141 | }
|
142 |
|
143 | private:
|
144 | // signal context information
|
145 | std::atomic<const char*> msg;
|
146 | static_assert(decltype(msg)::is_always_lock_free, "cannot safely use in signal handler");
|
147 |
|
148 | // state of signal handler
|
149 | bool isSet = false;
|
150 |
|
151 | bool logMessages = false;
|
152 |
|
153 | // previous signal handler routines
|
154 | void (*prevFpeHandler)(int) = nullptr;
|
155 | void (*prevIntHandler)(int) = nullptr;
|
156 | void (*prevSegVHandler)(int) = nullptr;
|
157 |
|
158 | /**
|
159 | * Signal handler for various types of signals.
|
160 | */
|
161 | static void handler(int signal) {
|
162 | // Signal handlers have extreme restrictions on what stdlib/OS facilities are available.
|
163 | // This is b/c signals are async on most platforms.
|
164 | // See: https://en.cppreference.com/w/cpp/utility/program/signal
|
165 | // See: `man 7 signal`
|
166 |
|
167 | const char* error;
|
168 | switch (signal) {
|
169 | case SIGABRT: error = "Abort"; break;
|
170 | case SIGFPE: error = "Floating-point arithmetic exception"; break;
|
171 | case SIGILL: error = "Illegal instruction"; break;
|
172 | case SIGINT: error = "Interrupt"; break;
|
173 | case SIGSEGV: error = "Segmentation violation"; break;
|
174 | case SIGTERM: error = "Terminate"; break;
|
175 | default: error = "Unknown"; break;
|
176 | }
|
177 |
|
178 | auto write = [](std::initializer_list<char const*> const& msgs) {
|
179 | for (auto&& msg : msgs) {
|
180 | // assign to variable to suppress ignored-return-value error.
|
181 | // I don't think we care enough to handle this fringe failure mode.
|
182 | // Worse case we don't get an error message.
|
183 | #ifdef _MSC_VER
|
184 | [[maybe_unused]] auto _ =
|
185 | ::_write(STDERR_FILENO, msg, static_cast<unsigned int>(::strlen(msg)));
|
186 | #else
|
187 | [[maybe_unused]] auto _ =
|
188 | ::write(STDERR_FILENO, msg, static_cast<unsigned int>(::strlen(msg)));
|
189 | #endif
|
190 | }
|
191 | };
|
192 |
|
193 | // `instance()` is okay. Static `singleton` must already be constructed if we got here.
|
194 | if (const char* msg = instance()->msg)
|
195 | write({error, " signal in rule:\n", msg, "\n"});
|
196 | else
|
197 | write({error, " signal.\n"});
|
198 |
|
199 | std::_Exit(EXIT_FAILURE);
|
200 | }
|
201 |
|
202 | SignalHandler() : msg(nullptr) {}
|
203 | };
|
204 |
|
205 | } // namespace souffle
|