xref: /freebsd/contrib/kyua/utils/sanity.cpp (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 // Copyright 2010 The Kyua Authors.
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