OILS / vendor / souffle / SignalHandler.h View on Github | oilshell.org

205 lines, 107 significant
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
36namespace 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 */
43class SignalHandler {
44public:
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
143private:
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