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