1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <cstdlib>
34 #include <string>
35 #include <typeinfo>
36 
37 #include "mongo/base/status.h"  // NOTE: This is safe as utils depend on base
38 #include "mongo/base/status_with.h"
39 #include "mongo/platform/compiler.h"
40 #include "mongo/util/concurrency/thread_name.h"
41 #include "mongo/util/debug_util.h"
42 
43 #define MONGO_INCLUDE_INVARIANT_H_WHITELISTED
44 #include "mongo/util/invariant.h"
45 #undef MONGO_INCLUDE_INVARIANT_H_WHITELISTED
46 
47 namespace mongo {
48 
49 class AssertionCount {
50 public:
51     AssertionCount();
52     void rollover();
53     void condrollover(int newValue);
54 
55     int regular;
56     int warning;
57     int msg;
58     int user;
59     int rollovers;
60 };
61 
62 extern AssertionCount assertionCount;
63 
64 
65 class DBException;
66 std::string causedBy(const DBException& e);
67 std::string causedBy(const std::string& e);
68 
69 /** Most mongo exceptions inherit from this; this is commonly caught in most threads */
70 class DBException : public std::exception {
71 public:
what()72     const char* what() const throw() final {
73         return reason().c_str();
74     }
75 
addContext(StringData context)76     virtual void addContext(StringData context) {
77         _status.addContext(context);
78     }
79 
toStatus(StringData context)80     Status toStatus(StringData context) const {
81         return _status.withContext(context);
82     }
toStatus()83     const Status& toStatus() const {
84         return _status;
85     }
86 
toString()87     virtual std::string toString() const {
88         return _status.toString();
89     }
90 
reason()91     const std::string& reason() const {
92         return _status.reason();
93     }
94 
code()95     ErrorCodes::Error code() const {
96         return _status.code();
97     }
98 
codeString()99     std::string codeString() const {
100         return _status.codeString();
101     }
102 
103     /**
104      * Returns true if this DBException's code is a member of the given category.
105      */
106     template <ErrorCategory category>
isA()107     bool isA() const {
108         return ErrorCodes::isA<category>(code());
109     }
110 
111     static AtomicBool traceExceptions;
112 
113 protected:
DBException(const Status & status)114     DBException(const Status& status) : _status(status) {
115         invariant(!status.isOK());
116         traceIfNeeded(*this);
117     }
118 
DBException(int code,StringData msg)119     DBException(int code, StringData msg)
120         : DBException(Status(code ? ErrorCodes::Error(code) : ErrorCodes::UnknownError, msg)) {}
121 
122 private:
123     static void traceIfNeeded(const DBException& e);
124 
125     /**
126      * This method exists only to make all non-final types in this hierarchy abstract to prevent
127      * accidental slicing.
128      */
129     virtual void defineOnlyInFinalSubclassToPreventSlicing() = 0;
130 
131     Status _status;
132 };
133 
134 class AssertionException : public DBException {
135 public:
AssertionException(const Status & status)136     AssertionException(const Status& status) : DBException(status) {}
AssertionException(int code,StringData msg)137     AssertionException(int code, StringData msg) : DBException(code, msg) {}
138 };
139 
140 /**
141  * The base class of all DBExceptions for codes of the given ErrorCategory to allow catching by
142  * category.
143  */
144 template <ErrorCategory kCategory>
145 class ExceptionForCat : public virtual AssertionException {
146 protected:
147     // This will only be called by subclasses, and they are required to instantiate
148     // AssertionException themselves since it is a virtual base. Therefore, the AssertionException
149     // construction here should never actually execute, but it is required to be present to allow
150     // subclasses to construct us.
ExceptionForCat()151     ExceptionForCat() : AssertionException((std::abort(), Status::OK())) {
152         invariant(isA<kCategory>());
153     }
154 };
155 
156 
157 /**
158  * This namespace contains implementation details for our error handling code and should not be used
159  * directly in general code.
160  */
161 namespace error_details {
162 
163 template <ErrorCodes::Error kCode, typename... Bases>
164 class ExceptionForImpl final : public Bases... {
165 public:
166     MONGO_STATIC_ASSERT(isNamedCode<kCode>);
167 
ExceptionForImpl(const Status & status)168     ExceptionForImpl(const Status& status) : AssertionException(status) {
169         invariant(status.code() == kCode);
170     }
171 
172 private:
defineOnlyInFinalSubclassToPreventSlicing()173     void defineOnlyInFinalSubclassToPreventSlicing() final {}
174 };
175 
176 template <ErrorCodes::Error code, typename categories = ErrorCategoriesFor<code>>
177 struct ExceptionForDispatcher;
178 
179 template <ErrorCodes::Error code, ErrorCategory... categories>
180 struct ExceptionForDispatcher<code, CategoryList<categories...>> {
181     using type = std::conditional_t<sizeof...(categories) == 0,
182                                     ExceptionForImpl<code, AssertionException>,
183                                     ExceptionForImpl<code, ExceptionForCat<categories>...>>;
184 };
185 
186 }  // namespace error_details
187 
188 
189 /**
190  * Resolves to the concrete exception type for the given error code.
191  *
192  * It will be a subclass of both AssertionException, along with ExceptionForCat<> of every category
193  * that the code belongs to.
194  *
195  * TODO in C++17 we can combine this with ExceptionForCat by doing something like:
196  * template <auto codeOrCategory> using ExceptionFor = typename
197  *      error_details::ExceptionForDispatcher<decltype(codeOrCategory)>::type;
198  */
199 template <ErrorCodes::Error code>
200 using ExceptionFor = typename error_details::ExceptionForDispatcher<code>::type;
201 
202 MONGO_COMPILER_NORETURN void verifyFailed(const char* expr, const char* file, unsigned line);
203 MONGO_COMPILER_NORETURN void invariantOKFailed(const char* expr,
204                                                const Status& status,
205                                                const char* file,
206                                                unsigned line) noexcept;
207 void wasserted(const char* expr, const char* file, unsigned line);
208 
209 #define fassertFailed MONGO_fassertFailed
210 #define MONGO_fassertFailed(...) ::mongo::fassertFailedWithLocation(__VA_ARGS__, __FILE__, __LINE__)
211 MONGO_COMPILER_NORETURN void fassertFailedWithLocation(int msgid,
212                                                        const char* file,
213                                                        unsigned line) noexcept;
214 
215 #define fassertFailedNoTrace MONGO_fassertFailedNoTrace
216 #define MONGO_fassertFailedNoTrace(...) \
217     ::mongo::fassertFailedNoTraceWithLocation(__VA_ARGS__, __FILE__, __LINE__)
218 MONGO_COMPILER_NORETURN void fassertFailedNoTraceWithLocation(int msgid,
219                                                               const char* file,
220                                                               unsigned line) noexcept;
221 
222 #define fassertFailedWithStatus MONGO_fassertFailedWithStatus
223 #define MONGO_fassertFailedWithStatus(...) \
224     ::mongo::fassertFailedWithStatusWithLocation(__VA_ARGS__, __FILE__, __LINE__)
225 MONGO_COMPILER_NORETURN void fassertFailedWithStatusWithLocation(int msgid,
226                                                                  const Status& status,
227                                                                  const char* file,
228                                                                  unsigned line) noexcept;
229 
230 #define fassertFailedWithStatusNoTrace MONGO_fassertFailedWithStatusNoTrace
231 #define MONGO_fassertFailedWithStatusNoTrace(...) \
232     ::mongo::fassertFailedWithStatusNoTraceWithLocation(__VA_ARGS__, __FILE__, __LINE__)
233 MONGO_COMPILER_NORETURN void fassertFailedWithStatusNoTraceWithLocation(int msgid,
234                                                                         const Status& status,
235                                                                         const char* file,
236                                                                         unsigned line) noexcept;
237 
238 /** a "user assertion".  throws UserAssertion.  logs.  typically used for errors that a user
239     could cause, such as duplicate key, disk full, etc.
240 */
241 MONGO_COMPILER_NORETURN void uassertedWithLocation(int msgid,
242                                                    StringData msg,
243                                                    const char* file,
244                                                    unsigned line);
245 
246 /** msgassert and massert are for errors that are internal but have a well defined error text
247     std::string.
248 */
249 
250 #define msgasserted MONGO_msgasserted
251 #define MONGO_msgasserted(...) ::mongo::msgassertedWithLocation(__VA_ARGS__, __FILE__, __LINE__)
252 MONGO_COMPILER_NORETURN void msgassertedWithLocation(int msgid,
253                                                      StringData msg,
254                                                      const char* file,
255                                                      unsigned line);
256 
257 /* convert various types of exceptions to strings */
258 std::string causedBy(StringData e);
259 std::string causedBy(const char* e);
260 std::string causedBy(const DBException& e);
261 std::string causedBy(const std::exception& e);
262 std::string causedBy(const std::string& e);
263 std::string causedBy(const std::string* e);
264 std::string causedBy(const Status& e);
265 
266 #define fassert MONGO_fassert
267 #define MONGO_fassert(...) ::mongo::fassertWithLocation(__VA_ARGS__, __FILE__, __LINE__)
268 
269 /** aborts on condition failure */
270 inline void fassertWithLocation(int msgid, bool testOK, const char* file, unsigned line) {
271     if (MONGO_unlikely(!testOK)) {
272         fassertFailedWithLocation(msgid, file, line);
273     }
274 }
275 
276 inline void fassertWithLocation(int msgid, const Status& status, const char* file, unsigned line) {
277     if (MONGO_unlikely(!status.isOK())) {
278         fassertFailedWithStatusWithLocation(msgid, status, file, line);
279     }
280 }
281 
282 #define fassertNoTrace MONGO_fassertNoTrace
283 #define MONGO_fassertNoTrace(...) \
284     ::mongo::fassertNoTraceWithLocation(__VA_ARGS__, __FILE__, __LINE__)
285 inline void fassertNoTraceWithLocation(int msgid, bool testOK, const char* file, unsigned line) {
286     if (MONGO_unlikely(!testOK)) {
287         fassertFailedNoTraceWithLocation(msgid, file, line);
288     }
289 }
290 
291 inline void fassertNoTraceWithLocation(int msgid,
292                                        const Status& status,
293                                        const char* file,
294                                        unsigned line) {
295     if (MONGO_unlikely(!status.isOK())) {
296         fassertFailedWithStatusNoTraceWithLocation(msgid, status, file, line);
297     }
298 }
299 
300 /**
301  * "user assert".  if asserts, user did something wrong, not our code.
302  *
303  * Using an immediately invoked lambda to give the compiler an easy way to inline the check (expr)
304  * and out-of-line the error path. This is most helpful when the error path involves building a
305  * complex error message in the expansion of msg. The call to the lambda is followed by
306  * MONGO_COMPILER_UNREACHABLE as it is impossible to mark a lambda noreturn.
307  */
308 #define uassert MONGO_uassert
309 #define MONGO_uassert(msgid, msg, expr)                                         \
310     do {                                                                        \
311         if (MONGO_unlikely(!(expr))) {                                          \
312             [&]() MONGO_COMPILER_COLD_FUNCTION {                                \
313                 ::mongo::uassertedWithLocation(msgid, msg, __FILE__, __LINE__); \
314             }();                                                                \
315             MONGO_COMPILER_UNREACHABLE;                                         \
316         }                                                                       \
317     } while (false)
318 
319 #define uasserted MONGO_uasserted
320 #define MONGO_uasserted(...) ::mongo::uassertedWithLocation(__VA_ARGS__, __FILE__, __LINE__)
321 
322 #define uassertStatusOK MONGO_uassertStatusOK
323 #define MONGO_uassertStatusOK(...) \
324     ::mongo::uassertStatusOKWithLocation(__VA_ARGS__, __FILE__, __LINE__)
325 inline void uassertStatusOKWithLocation(const Status& status, const char* file, unsigned line) {
326     if (MONGO_unlikely(!status.isOK())) {
327         uassertedWithLocation(status.code(), status.reason(), file, line);
328     }
329 }
330 
331 template <typename T>
332 inline T uassertStatusOKWithLocation(StatusWith<T> sw, const char* file, unsigned line) {
333     uassertStatusOKWithLocation(sw.getStatus(), file, line);
334     return std::move(sw.getValue());
335 }
336 
337 #define fassertStatusOK MONGO_fassertStatusOK
338 #define MONGO_fassertStatusOK(...) \
339     ::mongo::fassertStatusOKWithLocation(__VA_ARGS__, __FILE__, __LINE__)
340 template <typename T>
341 inline T fassertStatusOKWithLocation(int msgid, StatusWith<T> sw, const char* file, unsigned line) {
342     if (MONGO_unlikely(!sw.isOK())) {
343         fassertFailedWithStatusWithLocation(msgid, sw.getStatus(), file, line);
344     }
345     return std::move(sw.getValue());
346 }
347 
348 inline void fassertStatusOKWithLocation(int msgid,
349                                         const Status& s,
350                                         const char* file,
351                                         unsigned line) {
352     if (MONGO_unlikely(!s.isOK())) {
353         fassertFailedWithStatusWithLocation(msgid, s, file, line);
354     }
355 }
356 
357 /* warning only - keeps going */
358 #define wassert MONGO_wassert
359 #define MONGO_wassert(_Expression)                                \
360     do {                                                          \
361         if (MONGO_unlikely(!(_Expression))) {                     \
362             ::mongo::wasserted(#_Expression, __FILE__, __LINE__); \
363         }                                                         \
364     } while (false)
365 
366 /* display a message, no context, and throw assertionexception
367 
368    easy way to throw an exception and log something without our stack trace
369    display happening.
370 */
371 #define massert MONGO_massert
372 #define MONGO_massert(msgid, msg, expr)                                           \
373     do {                                                                          \
374         if (MONGO_unlikely(!(expr))) {                                            \
375             [&]() MONGO_COMPILER_COLD_FUNCTION {                                  \
376                 ::mongo::msgassertedWithLocation(msgid, msg, __FILE__, __LINE__); \
377             }();                                                                  \
378             MONGO_COMPILER_UNREACHABLE;                                           \
379         }                                                                         \
380     } while (false)
381 
382 
383 #define massertStatusOK MONGO_massertStatusOK
384 #define MONGO_massertStatusOK(...) \
385     ::mongo::massertStatusOKWithLocation(__VA_ARGS__, __FILE__, __LINE__)
386 inline void massertStatusOKWithLocation(const Status& status, const char* file, unsigned line) {
387     if (MONGO_unlikely(!status.isOK())) {
388         msgassertedWithLocation(status.code(), status.reason(), file, line);
389     }
390 }
391 
392 /* same as massert except no msgid */
393 #define verify(expression) MONGO_verify(expression)
394 #define MONGO_verify(_Expression)                                    \
395     do {                                                             \
396         if (MONGO_unlikely(!(_Expression))) {                        \
397             ::mongo::verifyFailed(#_Expression, __FILE__, __LINE__); \
398         }                                                            \
399     } while (false)
400 
401 #define invariantOK MONGO_invariantOK
402 #define MONGO_invariantOK(expression)                                                         \
403     do {                                                                                      \
404         const ::mongo::Status _invariantOK_status = expression;                               \
405         if (MONGO_unlikely(!_invariantOK_status.isOK())) {                                    \
406             ::mongo::invariantOKFailed(#expression, _invariantOK_status, __FILE__, __LINE__); \
407         }                                                                                     \
408     } while (false)
409 
410 #define dassertOK MONGO_dassertOK
411 #define MONGO_dassertOK(expression) \
412     if (kDebugBuild)                \
413     invariantOK(expression)
414 
415 // some special ids that we want to duplicate
416 
417 // > 10000 asserts
418 // < 10000 AssertionException
419 
420 enum { ASSERT_ID_DUPKEY = 11000 };
421 
422 std::string demangleName(const std::type_info& typeinfo);
423 
424 /**
425  * A utility function that converts an exception to a Status.
426  * Only call this function when there is an active exception
427  * (e.g. in a catch block).
428  *
429  * Note: this technique was created by Lisa Lippincott.
430  *
431  * Example usage:
432  *
433  *   Status myFunc() {
434  *       try {
435  *           funcThatThrows();
436  *           return Status::OK();
437  *       } catch (...) {
438  *           return exceptionToStatus();
439  *       }
440  *   }
441  */
442 Status exceptionToStatus() noexcept;
443 
444 }  // namespace mongo
445 
446 #define MONGO_ASSERT_ON_EXCEPTION(expression)                                         \
447     try {                                                                             \
448         expression;                                                                   \
449     } catch (const std::exception& e) {                                               \
450         std::stringstream ss;                                                         \
451         ss << "caught exception: " << e.what() << ' ' << __FILE__ << ' ' << __LINE__; \
452         msgasserted(13294, ss.str());                                                 \
453     } catch (...) {                                                                   \
454         massert(10437, "unknown exception", false);                                   \
455     }
456 
457 #define MONGO_ASSERT_ON_EXCEPTION_WITH_MSG(expression, msg)         \
458     try {                                                           \
459         expression;                                                 \
460     } catch (const std::exception& e) {                             \
461         std::stringstream ss;                                       \
462         ss << msg << " caught exception exception: " << e.what();   \
463         msgasserted(14043, ss.str());                               \
464     } catch (...) {                                                 \
465         msgasserted(14044, std::string("unknown exception") + msg); \
466     }
467 
468 /**
469  * The purpose of this macro is to instruct the compiler that a line of code will never be reached.
470  *
471  * Example:
472  *     // code above checks that expr can only be FOO or BAR
473  *     switch (expr) {
474  *     case FOO: { ... }
475  *     case BAR: { ... }
476  *     default:
477  *         MONGO_UNREACHABLE;
478  */
479 
480 #define MONGO_UNREACHABLE ::mongo::invariantFailed("Hit a MONGO_UNREACHABLE!", __FILE__, __LINE__);
481