Home | History | Annotate | Line # | Download | only in utils
      1 // Copyright 2010 Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 // * Redistributions of source code must retain the above copyright
      9 //   notice, this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright
     11 //   notice, this list of conditions and the following disclaimer in the
     12 //   documentation and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors
     14 //   may be used to endorse or promote products derived from this software
     15 //   without specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 #include "utils/sanity.hpp"
     30 
     31 #if defined(HAVE_CONFIG_H)
     32 #include "config.h"
     33 #endif
     34 
     35 extern "C" {
     36 #include <signal.h>
     37 #include <unistd.h>
     38 }
     39 
     40 #include <cerrno>
     41 #include <cstdlib>
     42 #include <cstring>
     43 #include <iostream>
     44 
     45 #include "utils/format/macros.hpp"
     46 #include "utils/logging/macros.hpp"
     47 
     48 
     49 namespace {
     50 
     51 
     52 /// List of fatal signals to be intercepted by the sanity code.
     53 ///
     54 /// The tests hardcode this list; update them whenever the list gets updated.
     55 static int fatal_signals[] = { SIGABRT, SIGBUS, SIGSEGV, 0 };
     56 
     57 
     58 /// The path to the log file to report on crashes.  Be aware that this is empty
     59 /// until install_crash_handlers() is called.
     60 static std::string logfile;
     61 
     62 
     63 /// Prints a message to stderr.
     64 ///
     65 /// Note that this runs from a signal handler.  Calling write() is OK.
     66 ///
     67 /// \param message The message to print.
     68 static void
     69 err_write(const std::string& message)
     70 {
     71     if (::write(STDERR_FILENO, message.c_str(), message.length()) == -1) {
     72         // We are crashing.  If ::write fails, there is not much we could do,
     73         // specially considering that we are running within a signal handler.
     74         // Just ignore the error.
     75     }
     76 }
     77 
     78 
     79 /// The crash handler for fatal signals.
     80 ///
     81 /// The sole purpose of this is to print some informational data before
     82 /// reraising the original signal.
     83 ///
     84 /// \param signo The received signal.
     85 static void
     86 crash_handler(const int signo)
     87 {
     88     PRE(!logfile.empty());
     89 
     90     err_write(F("*** Fatal signal %s received\n") % signo);
     91     err_write(F("*** Log file is %s\n") % logfile);
     92     err_write(F("*** Please report this problem to %s detailing what you were "
     93                 "doing before the crash happened; if possible, include the log "
     94                 "file mentioned above\n") % PACKAGE_BUGREPORT);
     95 
     96     /// The handler is installed with SA_RESETHAND, so this is safe to do.  We
     97     /// really want to call the default handler to generate any possible core
     98     /// dumps.
     99     ::kill(::getpid(), signo);
    100 }
    101 
    102 
    103 /// Installs a handler for a fatal signal representing a crash.
    104 ///
    105 /// When the specified signal is captured, the crash_handler() will be called to
    106 /// print some informational details to the user and, later, the signal will be
    107 /// redelivered using the default handler to obtain a core dump.
    108 ///
    109 /// \param signo The fatal signal for which to install a handler.
    110 static void
    111 install_one_crash_handler(const int signo)
    112 {
    113     struct ::sigaction sa;
    114     sa.sa_handler = crash_handler;
    115     sigemptyset(&sa.sa_mask);
    116     sa.sa_flags = SA_RESETHAND;
    117 
    118     if (::sigaction(signo, &sa, NULL) == -1) {
    119         const int original_errno = errno;
    120         LW(F("Could not install crash handler for signal %s: %s") %
    121            signo % std::strerror(original_errno));
    122     } else
    123         LD(F("Installed crash handler for signal %s") % signo);
    124 }
    125 
    126 
    127 /// Returns a textual representation of an assertion type.
    128 ///
    129 /// The textual representation is user facing.
    130 ///
    131 /// \param type The type of the assertion.  If the type is unknown for whatever
    132 ///     reason, a special message is returned.  The code cannot abort in such a
    133 ///     case because this code is dealing for assertion errors.
    134 ///
    135 /// \return A textual description of the assertion type.
    136 static std::string
    137 format_type(const utils::assert_type type)
    138 {
    139     switch (type) {
    140     case utils::invariant: return "Invariant check failed";
    141     case utils::postcondition: return "Postcondition check failed";
    142     case utils::precondition: return "Precondition check failed";
    143     case utils::unreachable: return "Unreachable point reached";
    144     default: return "UNKNOWN ASSERTION TYPE";
    145     }
    146 }
    147 
    148 
    149 }  // anonymous namespace
    150 
    151 
    152 /// Raises an assertion error.
    153 ///
    154 /// This function prints information about the assertion failure and terminates
    155 /// execution immediately by calling std::abort().  This ensures a coredump so
    156 /// that the failure can be analyzed later.
    157 ///
    158 /// \param type The assertion type; this influences the printed message.
    159 /// \param file The file in which the assertion failed.
    160 /// \param line The line in which the assertion failed.
    161 /// \param message The failure message associated to the condition.
    162 void
    163 utils::sanity_failure(const assert_type type, const char* file,
    164                       const size_t line, const std::string& message)
    165 {
    166     std::cerr << "*** " << file << ":" << line << ": " << format_type(type);
    167     if (!message.empty())
    168         std::cerr << ": " << message << "\n";
    169     else
    170         std::cerr << "\n";
    171     std::abort();
    172 }
    173 
    174 
    175 /// Installs persistent handlers for crash signals.
    176 ///
    177 /// Should be called at the very beginning of the execution of the program to
    178 /// ensure that a signal handler for fatal crash signals is installed.
    179 ///
    180 /// \pre The function has not been called before.
    181 ///
    182 /// \param logfile_ The path to the log file to report during a crash.
    183 void
    184 utils::install_crash_handlers(const std::string& logfile_)
    185 {
    186     static bool installed = false;
    187     PRE(!installed);
    188     logfile = logfile_;
    189 
    190     for (const int* iter = &fatal_signals[0]; *iter != 0; iter++)
    191         install_one_crash_handler(*iter);
    192 
    193     installed = true;
    194 }
    195