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