| 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
|