993 lines
37 KiB
C++
993 lines
37 KiB
C++
/**
|
|
* Copyright (C) 2018-present MongoDB, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the Server Side Public License, version 1,
|
|
* as published by MongoDB, Inc.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* Server Side Public License for more details.
|
|
*
|
|
* You should have received a copy of the Server Side Public License
|
|
* along with this program. If not, see
|
|
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
|
*
|
|
* As a special exception, the copyright holders give permission to link the
|
|
* code of portions of this program with the OpenSSL library under certain
|
|
* conditions as described in each individual source file and distribute
|
|
* linked combinations including the program with the OpenSSL library. You
|
|
* must comply with the Server Side Public License in all respects for
|
|
* all of the code used other than as permitted herein. If you modify file(s)
|
|
* with this exception, you may extend this exception to your version of the
|
|
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
|
* delete this exception statement from your version. If you delete this
|
|
* exception statement from all source files in the program, then also delete
|
|
* it in the license file.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "mongo/base/error_codes.h"
|
|
#include "mongo/base/error_extra_info.h"
|
|
#include "mongo/base/static_assert.h"
|
|
#include "mongo/base/status.h"
|
|
#include "mongo/base/status_with.h"
|
|
#include "mongo/base/string_data.h"
|
|
#include "mongo/platform/atomic_word.h"
|
|
#include "mongo/platform/compiler.h"
|
|
#include "mongo/platform/source_location.h"
|
|
#include "mongo/util/assert_util_core.h" // IWYU pragma: export
|
|
#include "mongo/util/concurrency/thread_name.h"
|
|
#include "mongo/util/debug_util.h"
|
|
#include "mongo/util/exit_code.h"
|
|
#include "mongo/util/modules.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <exception>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <typeinfo>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace MONGO_MOD_PUB mongo {
|
|
|
|
/**
|
|
* Sets the appropriate state to enable/disable diagnostic logging based on `newVal`.
|
|
*/
|
|
MONGO_MOD_PRIVATE void setDiagnosticLoggingInAssertUtil(bool newVal);
|
|
|
|
class MONGO_MOD_NEEDS_REPLACEMENT AssertionCount {
|
|
public:
|
|
AssertionCount();
|
|
void rollover();
|
|
void condrollover(int newValue);
|
|
|
|
AtomicWord<int> regular;
|
|
AtomicWord<int> warning;
|
|
AtomicWord<int> msg;
|
|
AtomicWord<int> user;
|
|
AtomicWord<int> tripwire;
|
|
AtomicWord<int> rollovers;
|
|
};
|
|
|
|
MONGO_MOD_NEEDS_REPLACEMENT extern AssertionCount assertionCount;
|
|
class DBException;
|
|
|
|
/** Most mongo exceptions inherit from this; this is commonly caught in most threads */
|
|
class MONGO_MOD_UNFORTUNATELY_OPEN DBException : public std::exception {
|
|
public:
|
|
const char* what() const noexcept final {
|
|
return reason().c_str();
|
|
}
|
|
|
|
virtual void addContext(StringData context) {
|
|
_status.addContext(context);
|
|
}
|
|
|
|
Status toStatus(StringData context) const {
|
|
return _status.withContext(context);
|
|
}
|
|
const Status& toStatus() const {
|
|
return _status;
|
|
}
|
|
|
|
virtual std::string toString() const {
|
|
return _status.toString();
|
|
}
|
|
|
|
virtual void serialize(BSONObjBuilder* builder) const {
|
|
_status.serialize(builder);
|
|
}
|
|
|
|
const std::string& reason() const {
|
|
return _status.reason();
|
|
}
|
|
|
|
ErrorCodes::Error code() const {
|
|
return _status.code();
|
|
}
|
|
|
|
std::string codeString() const {
|
|
return _status.codeString();
|
|
}
|
|
|
|
/**
|
|
* Returns true if this DBException's code is a member of the given category.
|
|
*/
|
|
template <ErrorCategory category>
|
|
bool isA() const {
|
|
return ErrorCodes::isA<category>(*this);
|
|
}
|
|
|
|
/**
|
|
* Returns the generic ErrorExtraInfo if present.
|
|
*/
|
|
std::shared_ptr<const ErrorExtraInfo> extraInfo() const {
|
|
return _status.extraInfo();
|
|
}
|
|
|
|
/**
|
|
* Returns a specific subclass of ErrorExtraInfo if the error code matches that type.
|
|
*/
|
|
template <typename ErrorDetail>
|
|
std::shared_ptr<const ErrorDetail> extraInfo() const {
|
|
return _status.extraInfo<ErrorDetail>();
|
|
}
|
|
|
|
// TODO(modularity): We should provide a way for modules to own their options parsers.
|
|
MONGO_MOD_NEEDS_REPLACEMENT static inline AtomicWord<bool> traceExceptions{false};
|
|
|
|
/**
|
|
* Allows handling `ErrorCodes::WriteConflict` as a special case and if true, will call
|
|
* `printStackTrace` on every `WriteConflict` error. Can be set via the
|
|
* `traceWriteConflictExceptions` server parameter.
|
|
*/
|
|
MONGO_MOD_PRIVATE static inline AtomicWord<bool> traceWriteConflictExceptions{false};
|
|
|
|
protected:
|
|
DBException(const Status& status) : _status(status) {
|
|
invariant(!status.isOK());
|
|
traceIfNeeded(*this);
|
|
}
|
|
|
|
private:
|
|
static void traceIfNeeded(const DBException& e);
|
|
|
|
/**
|
|
* This method exists only to make all non-final types in this hierarchy abstract to prevent
|
|
* accidental slicing.
|
|
*/
|
|
virtual void defineOnlyInFinalSubclassToPreventSlicing() = 0;
|
|
|
|
Status _status;
|
|
};
|
|
|
|
class AssertionException : public DBException {
|
|
public:
|
|
AssertionException(const Status& status) : DBException(status) {}
|
|
};
|
|
|
|
/**
|
|
* Encompasses a class of exceptions due to lack of resources or conflicting resources. Can be used
|
|
* to conveniently catch all derived exceptions instead of enumerating each of them individually.
|
|
*/
|
|
class StorageUnavailableException : public DBException {
|
|
public:
|
|
using DBException::DBException;
|
|
};
|
|
|
|
/**
|
|
* Use `throwWriteConflictException()` instead of throwing `WriteConflictException` directly.
|
|
*/
|
|
class WriteConflictException final : public StorageUnavailableException {
|
|
public:
|
|
WriteConflictException(const Status& status) : StorageUnavailableException(status) {}
|
|
|
|
private:
|
|
void defineOnlyInFinalSubclassToPreventSlicing() final {}
|
|
};
|
|
|
|
/**
|
|
* Use `throwTemporarilyUnavailableException()` instead of throwing
|
|
* `TemporarilyUnavailableException` directly.
|
|
*/
|
|
class TemporarilyUnavailableException final : public StorageUnavailableException {
|
|
public:
|
|
TemporarilyUnavailableException(const Status& status) : StorageUnavailableException(status) {}
|
|
|
|
private:
|
|
void defineOnlyInFinalSubclassToPreventSlicing() final {}
|
|
};
|
|
|
|
/**
|
|
* Use `throwTransactionTooLargeForCache()` instead of throwing
|
|
* `TransactionTooLargeForCache` directly.
|
|
*/
|
|
class TransactionTooLargeForCacheException final : public StorageUnavailableException {
|
|
public:
|
|
TransactionTooLargeForCacheException(const Status& status)
|
|
: StorageUnavailableException(status) {}
|
|
|
|
private:
|
|
void defineOnlyInFinalSubclassToPreventSlicing() final {}
|
|
};
|
|
|
|
|
|
/**
|
|
* This namespace contains implementation details for our error handling code and should not be used
|
|
* directly in general code.
|
|
*/
|
|
namespace error_details {
|
|
|
|
/**
|
|
* The base class of all DBExceptions for codes of the given ErrorCategory to allow catching by
|
|
* category.
|
|
*/
|
|
template <ErrorCategory kCategory>
|
|
class ExceptionForCat : public virtual AssertionException {
|
|
protected:
|
|
// This will only be called by subclasses, and they are required to instantiate
|
|
// AssertionException themselves since it is a virtual base. Therefore, the AssertionException
|
|
// construction here should never actually execute, but it is required to be present to allow
|
|
// subclasses to construct us.
|
|
ExceptionForCat() : AssertionException((std::abort(), Status::OK())) {
|
|
invariant(isA<kCategory>());
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Only has public visibility to allow copying for throw ex; Use ExceptionFor<code> instead.
|
|
*/
|
|
template <ErrorCodes::Error kCode, typename... Bases>
|
|
class MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS ExceptionForCode final : public Bases... {
|
|
public:
|
|
MONGO_STATIC_ASSERT(isNamedCode<kCode>);
|
|
|
|
MONGO_MOD_PRIVATE ExceptionForCode(const Status& status) : AssertionException(status) {
|
|
invariant(status.code() == kCode);
|
|
}
|
|
|
|
// This is only a template to enable SFINAE. It will only be instantiated with the default
|
|
// value.
|
|
template <ErrorCodes::Error code_copy = kCode>
|
|
MONGO_MOD_PUBLIC std::shared_ptr<const ErrorExtraInfoFor<code_copy>> operator->() const {
|
|
MONGO_STATIC_ASSERT(code_copy == kCode);
|
|
return this->template extraInfo<ErrorExtraInfoFor<kCode>>();
|
|
}
|
|
|
|
private:
|
|
void defineOnlyInFinalSubclassToPreventSlicing() final {}
|
|
};
|
|
|
|
template <ErrorCodes::Error code, typename categories = ErrorCategoriesFor<code>>
|
|
struct ExceptionForCodeDispatcher;
|
|
|
|
template <ErrorCodes::Error code, ErrorCategory... categories>
|
|
struct ExceptionForCodeDispatcher<code, CategoryList<categories...>> {
|
|
using type = std::conditional_t<sizeof...(categories) == 0,
|
|
ExceptionForCode<code, AssertionException>,
|
|
ExceptionForCode<code, ExceptionForCat<categories>...>>;
|
|
};
|
|
|
|
// Note: second template parameter shouldn't be necessary but is on MSVC.
|
|
// It seems to be unable to disambiguate the partial specializations of an auto NTTP
|
|
// with concrete types and needs a dummy type template parameter to do so.
|
|
template <auto codeOrCat, typename = decltype(codeOrCat)>
|
|
struct ExceptionForDispatcher;
|
|
|
|
template <ErrorCodes::Error code>
|
|
struct ExceptionForDispatcher<code, ErrorCodes::Error> : ExceptionForCodeDispatcher<code> {};
|
|
|
|
template <>
|
|
struct ExceptionForDispatcher<ErrorCodes::WriteConflict, ErrorCodes::Error> {
|
|
using type = WriteConflictException;
|
|
};
|
|
|
|
template <>
|
|
struct ExceptionForDispatcher<ErrorCodes::TemporarilyUnavailable, ErrorCodes::Error> {
|
|
using type = TemporarilyUnavailableException;
|
|
};
|
|
|
|
template <>
|
|
struct ExceptionForDispatcher<ErrorCodes::TransactionTooLargeForCache, ErrorCodes::Error> {
|
|
using type = TransactionTooLargeForCacheException;
|
|
};
|
|
|
|
template <ErrorCategory category>
|
|
struct ExceptionForDispatcher<category, ErrorCategory> {
|
|
using type = ExceptionForCat<category>;
|
|
};
|
|
|
|
} // namespace error_details
|
|
|
|
|
|
/**
|
|
* Resolves to the concrete exception type for the given ErrorCode or ErrorCategory.
|
|
*
|
|
* It will be a subclass of AssertionException, and when passed an error code, it will also be a
|
|
* subclass of ExceptionFor<ErrorCategory> of every category that the code belongs to.
|
|
*/
|
|
template <auto codeOrCatagory>
|
|
requires std::is_same_v<decltype(codeOrCatagory), ErrorCodes::Error> ||
|
|
std::is_same_v<decltype(codeOrCatagory), ErrorCategory>
|
|
using ExceptionFor = typename error_details::ExceptionForDispatcher<codeOrCatagory>::type;
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void verifyFailed(
|
|
const char* expr, SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
}
|
|
|
|
namespace fassert_detail {
|
|
|
|
/** Convertible from exactly `int`, but not from bool or other types that convert to int. */
|
|
struct MsgId {
|
|
/** Allow exactly int */
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr explicit(false) MsgId(int id) : id{id} {}
|
|
|
|
/** Allow copy */
|
|
constexpr MsgId(const MsgId&) = default;
|
|
constexpr MsgId& operator=(const MsgId&) = default;
|
|
|
|
/** Reject everything else. */
|
|
template <typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, MsgId>, int> = 0>
|
|
explicit(false) MsgId(T&&) = delete;
|
|
|
|
int id;
|
|
};
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void failed(
|
|
MsgId msgid, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void failed(
|
|
MsgId msgid, const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void failedNoTrace(
|
|
MsgId msgid, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void failedNoTrace(
|
|
MsgId msgid, const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
/** Aborts if `cond` is false. */
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void check(
|
|
MsgId msgid, bool cond, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!cond)) {
|
|
failed(msgid, loc);
|
|
}
|
|
}
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void check(
|
|
MsgId msgid, const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
failed(msgid, status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T check(
|
|
MsgId msgid, StatusWith<T> sw, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!sw.isOK())) {
|
|
failed(msgid, sw.getStatus(), loc);
|
|
}
|
|
return std::move(sw.getValue());
|
|
}
|
|
|
|
/** Reject anything stringlike from being used as a bool cond by mistake. */
|
|
template <typename T, std::enable_if_t<std::is_convertible_v<T, StringData>, int> = 0>
|
|
void check(MsgId msgid, T&& cond, SourceLocation loc = MONGO_SOURCE_LOCATION()) = delete;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void checkNoTrace(
|
|
MsgId msgid, bool cond, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!cond)) {
|
|
failedNoTrace(msgid, loc);
|
|
}
|
|
}
|
|
|
|
/** Reject anything stringlike from being used as a bool cond by mistake. */
|
|
template <typename T, std::enable_if_t<std::is_convertible_v<T, StringData>, int> = 0>
|
|
void checkNoTrace(MsgId msgid, T&& cond, SourceLocation loc = MONGO_SOURCE_LOCATION()) = delete;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void checkNoTrace(
|
|
MsgId msgid, const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
failedNoTrace(msgid, status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T checkNoTrace(
|
|
MsgId msgid, StatusWith<T> sw, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!sw.isOK())) {
|
|
failedNoTrace(msgid, sw.getStatus(), loc);
|
|
}
|
|
return std::move(sw.getValue());
|
|
}
|
|
} // namespace fassert_detail
|
|
|
|
#define MONGO_fasserted(...) mongo::fassert_detail::failed(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
#define MONGO_fassertedNoTrace(...) \
|
|
mongo::fassert_detail::failedNoTrace(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
#define MONGO_fassert(...) mongo::fassert_detail::check(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
#define MONGO_fassertNoTrace(...) \
|
|
mongo::fassert_detail::checkNoTrace(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
|
|
/**
|
|
* `fassert` failures will terminate the entire process; this is used for
|
|
* low-level checks where continuing might lead to corrupt data or loss of data
|
|
* on disk. Additionally, `fassert` will log the assertion message with fatal
|
|
* severity and add a breakpoint before terminating.
|
|
*
|
|
* Each `fassert` call site must have a unique `msgid` as the first argument.
|
|
* These are chosen using the same convention as logv2 log IDs.
|
|
*
|
|
* To log a custom assert message with process-fatal semantics, use LOGV2_FATAL.
|
|
* Consider using this variant if there is only one way to reach the fatal point in code.
|
|
*
|
|
* The second argument is a condition. `fassert` invocations are forwarded to an
|
|
* overload set of handlers such that it can accept a `bool`, `const Status&`,
|
|
* or `StatusWith` as a condition.
|
|
*
|
|
* void fassert(int msgid, bool cond);
|
|
* void fassert(int msgid, const Status& status);
|
|
* T fassert(int msgid, StatusWith<T> statusWith);
|
|
*
|
|
* `fassert` also supports a StatusWith<T> argument.
|
|
* In that case, `fassert` returns the extracted `sw.getValue()`.
|
|
* Because the argument is passed by value, this is best used
|
|
* to wrap a function call directly to avoid copies:
|
|
*
|
|
* T value = fassert(9079702, someStatusWithFunc());
|
|
*
|
|
* See the `docs/exception_architecture.md` guide for more,
|
|
* including guidance on choosing a `msgid` number.
|
|
*/
|
|
#define fassert MONGO_fassert
|
|
|
|
/**
|
|
* Same usage and arguments as `fassert`, but performs
|
|
* a quickExit instead of a stacktrace-dumping abort.
|
|
* Use LOGV2_FATAL_NO_TRACE to supply a custom error message.
|
|
* Consider using this variant if there is only one way to reach the fatal point in code.
|
|
*/
|
|
#define fassertNoTrace MONGO_fassertNoTrace
|
|
|
|
/**
|
|
* Like `fassert`, without a condition argument.
|
|
* Use when failure was already determined, and needs to be reported.
|
|
*
|
|
* Though `fasserted` doesn't use a condition to determine whether
|
|
* to fire, it can still accept an optional diagnostic `Status`
|
|
* argument which will be emitted in the log.
|
|
*
|
|
* fasserted(9079703);
|
|
* fasserted(9079704, status);
|
|
* fasserted(9079705, {ErrorCodes::Overflow, "too much foo"});
|
|
*
|
|
* `fasserted` can also be spelled `fassertFailed`.
|
|
*/
|
|
#define fasserted MONGO_fasserted
|
|
|
|
/**
|
|
* Like `fasserted`, but performs a quickExit instead of a
|
|
* stacktrace-dumping abort.
|
|
*/
|
|
#define fassertedNoTrace MONGO_fassertedNoTrace
|
|
|
|
/**
|
|
* To match other assert macros, we'll prefer `fasserted` over the older
|
|
* `fassertFailed` spelling, but they're the same thing.
|
|
*/
|
|
#define fassertFailed fasserted
|
|
#define fassertFailedWithStatus fasserted
|
|
#define fassertFailedNoTrace fassertedNoTrace
|
|
#define fassertFailedWithStatusNoTrace fassertedNoTrace
|
|
|
|
namespace error_details {
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS inline const Status& makeStatus(const Status& s) {
|
|
return s;
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS const Status& makeStatus(const StatusWith<T>& sw) {
|
|
return sw.getStatus();
|
|
}
|
|
|
|
// This function exists so that uassert/massert can take plain int literals rather than requiring
|
|
// ErrorCodes::Error wrapping.
|
|
template <typename StringLike>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS Status makeStatus(int code, StringLike&& message) {
|
|
return Status(ErrorCodes::Error(code), std::forward<StringLike>(message));
|
|
}
|
|
|
|
template <typename ErrorDetail,
|
|
typename StringLike,
|
|
typename = stdx::enable_if_t<
|
|
std::is_base_of<ErrorExtraInfo, std::remove_reference_t<ErrorDetail>>::value>>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS Status makeStatus(ErrorDetail&& detail,
|
|
StringLike&& message) {
|
|
return Status(std::forward<ErrorDetail>(detail), std::forward<StringLike>(message));
|
|
}
|
|
|
|
} // namespace error_details
|
|
|
|
/**
|
|
* Common implementation for assert and assertFailed macros. Not for direct use.
|
|
*
|
|
* Using an immediately invoked lambda to give the compiler an easy way to inline the check (expr)
|
|
* and out-of-line the error path. This is most helpful when the error path involves building a
|
|
* complex error message in the expansion of msg. The call to the lambda is followed by
|
|
* MONGO_COMPILER_UNREACHABLE as it is impossible to mark a lambda noreturn.
|
|
* The source location is captured outside of the lambda, so that it represents
|
|
* the function name of the macro invocation site.
|
|
*/
|
|
#define MONGO_BASE_ASSERT_FAILED(fail_func, ...) \
|
|
do { \
|
|
auto loc = MONGO_SOURCE_LOCATION(); \
|
|
[&]() MONGO_COMPILER_COLD_FUNCTION { \
|
|
fail_func(::mongo::error_details::makeStatus(__VA_ARGS__), loc); \
|
|
}(); \
|
|
MONGO_COMPILER_UNREACHABLE; \
|
|
} while (false)
|
|
|
|
#define MONGO_BASE_ASSERT(fail_func, code, msg, cond) \
|
|
do { \
|
|
if (MONGO_unlikely(!(cond))) { \
|
|
MONGO_BASE_ASSERT_FAILED(fail_func, code, msg); \
|
|
} \
|
|
} while (false)
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void uassertedWithLocation(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void uassertStatusOKWithLocation(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
uassertedWithLocation(status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T uassertStatusOKWithLocation(
|
|
StatusWith<T> sw, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
uassertStatusOKWithLocation(sw.getStatus(), loc);
|
|
return std::move(sw.getValue());
|
|
}
|
|
|
|
template <typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void uassertStatusOKWithContextAndLocation(
|
|
const Status& status, ContextExpr&& contextExpr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
uassertedWithLocation(status.withContext(std::forward<ContextExpr>(contextExpr)()), loc);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T uassertStatusOKWithContextAndLocation(
|
|
StatusWith<T> sw, ContextExpr&& contextExpr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
uassertStatusOKWithContextAndLocation(
|
|
sw.getStatus(), std::forward<ContextExpr>(contextExpr), loc);
|
|
return std::move(sw.getValue());
|
|
}
|
|
} // namespace error_details
|
|
|
|
/**
|
|
* "user assert". if asserts, user did something wrong, not our code.
|
|
* On failure, throws an exception.
|
|
*/
|
|
#define uasserted(msgid, msg) \
|
|
MONGO_BASE_ASSERT_FAILED(::mongo::error_details::uassertedWithLocation, msgid, msg)
|
|
#define uassert(msgid, msg, expr) \
|
|
MONGO_BASE_ASSERT(::mongo::error_details::uassertedWithLocation, msgid, msg, expr)
|
|
|
|
#define uassertStatusOK(...) \
|
|
::mongo::error_details::uassertStatusOKWithLocation(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
|
|
/**
|
|
* Like uassertStatusOK(status), but also takes an expression that evaluates to something
|
|
* convertible to std::string to add more context to error messages. This contextExpr is only
|
|
* evaluated if the status is not OK.
|
|
*/
|
|
#define uassertStatusOKWithContext(status, contextExpr) \
|
|
::mongo::error_details::uassertStatusOKWithContextAndLocation( \
|
|
status, [&]() -> std::string { return (contextExpr); }, MONGO_SOURCE_LOCATION())
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void msgassertedWithLocation(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void massertStatusOKWithLocation(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
msgassertedWithLocation(status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T massertStatusOKWithLocation(
|
|
StatusWith<T> sw, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
massertStatusOKWithLocation(sw.getStatus(), loc);
|
|
return std::move(sw.getValue());
|
|
}
|
|
} // namespace error_details
|
|
|
|
/**
|
|
* massert is like uassert but it logs the message before throwing.
|
|
*/
|
|
#define massert(msgid, msg, expr) \
|
|
MONGO_BASE_ASSERT(::mongo::error_details::msgassertedWithLocation, msgid, msg, expr)
|
|
|
|
#define msgasserted(msgid, msg) \
|
|
MONGO_BASE_ASSERT_FAILED(::mongo::error_details::msgassertedWithLocation, msgid, msg)
|
|
|
|
#define massertStatusOK(...) \
|
|
::mongo::error_details::massertStatusOKWithLocation(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
|
|
#define MONGO_BASE_ASSERT_VA_4(fail_func, code, msg, cond) \
|
|
do { \
|
|
if (MONGO_unlikely(!(cond))) \
|
|
MONGO_BASE_ASSERT_FAILED(fail_func, (code), (msg)); \
|
|
} while (false)
|
|
|
|
#define MONGO_BASE_ASSERT_VA_2(fail_func, statusExpr) \
|
|
do { \
|
|
if (const auto& stLocal_ = (statusExpr); MONGO_unlikely(!stLocal_.isOK())) \
|
|
MONGO_BASE_ASSERT_FAILED(fail_func, stLocal_); \
|
|
} while (false)
|
|
|
|
#define MONGO_BASE_ASSERT_VA_EXPAND(x) x /**< MSVC workaround */
|
|
#define MONGO_BASE_ASSERT_VA_PICK(_1, _2, _3, _4, x, ...) x
|
|
#define MONGO_BASE_ASSERT_VA_DISPATCH(...) \
|
|
MONGO_BASE_ASSERT_VA_EXPAND(MONGO_BASE_ASSERT_VA_PICK(__VA_ARGS__, \
|
|
MONGO_BASE_ASSERT_VA_4, \
|
|
MONGO_BASE_ASSERT_VA_3, \
|
|
MONGO_BASE_ASSERT_VA_2, \
|
|
MONGO_BASE_ASSERT_VA_1)(__VA_ARGS__))
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void iassertFailed(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
}
|
|
|
|
/**
|
|
* `iassert` is provided as an alternative for `uassert` variants (e.g., `uassertStatusOK`)
|
|
* to support cases where we expect a failure, the failure is recoverable, or accounting for the
|
|
* failure, updating assertion counters, isn't desired. `iassert` logs at D3 instead of D1,
|
|
* which helps with reducing the noise of assertions in production. The goal is to keep one
|
|
* interface (i.e., `iassert(...)`) for all possible assertion variants, and use function
|
|
* overloading to expand type support as needed.
|
|
*/
|
|
#define iassert(...) \
|
|
MONGO_BASE_ASSERT_VA_DISPATCH(::mongo::error_details::iassertFailed, __VA_ARGS__)
|
|
#define iasserted(...) MONGO_BASE_ASSERT_FAILED(::mongo::error_details::iassertFailed, __VA_ARGS__)
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void tassertFailed(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
}
|
|
|
|
/**
|
|
* "tripwire/test assert". Like uassert, but with a deferred-fatality tripwire that gets
|
|
* checked prior to normal shutdown. Used to ensure that this assertion will both fail the
|
|
* operation and also cause a test suite failure.
|
|
*/
|
|
#define tassert(...) \
|
|
MONGO_BASE_ASSERT_VA_DISPATCH(::mongo::error_details::tassertFailed, __VA_ARGS__)
|
|
#define tasserted(...) MONGO_BASE_ASSERT_FAILED(::mongo::error_details::tassertFailed, __VA_ARGS__)
|
|
|
|
/**
|
|
* Return true if tripwire conditions have occurred.
|
|
*/
|
|
bool haveTripwireAssertionsOccurred();
|
|
|
|
/**
|
|
* If tripwire conditions have occurred, warn via the log.
|
|
*/
|
|
void warnIfTripwireAssertionsOccurred();
|
|
|
|
/**
|
|
* MONGO_verify is deprecated. It is like invariant() in debug builds and massert() in release
|
|
* builds.
|
|
*/
|
|
#define MONGO_verify(expression_) \
|
|
do { \
|
|
if (MONGO_unlikely(!(expression_))) { \
|
|
::mongo::error_details::verifyFailed(#expression_, MONGO_SOURCE_LOCATION()); \
|
|
} \
|
|
} while (false)
|
|
|
|
namespace error_details {
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void invariantOKFailed(
|
|
const char* expr, const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void invariantOKFailedWithMsg(
|
|
const char* expr,
|
|
const Status& status,
|
|
const std::string& msg,
|
|
SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void invariantWithLocation(
|
|
const Status& status, const char* expr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
::mongo::error_details::invariantOKFailed(expr, status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T invariantWithLocation(
|
|
StatusWith<T> sw, const char* expr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!sw.isOK())) {
|
|
::mongo::error_details::invariantOKFailed(expr, sw.getStatus(), loc);
|
|
}
|
|
return std::move(sw.getValue());
|
|
}
|
|
|
|
template <typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void invariantWithContextAndLocation(
|
|
const Status& status,
|
|
const char* expr,
|
|
ContextExpr&& contextExpr,
|
|
SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
::mongo::error_details::invariantOKFailedWithMsg(
|
|
expr, status, std::forward<ContextExpr>(contextExpr)(), loc);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T invariantWithContextAndLocation(
|
|
StatusWith<T> sw,
|
|
const char* expr,
|
|
ContextExpr&& contextExpr,
|
|
SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!sw.isOK())) {
|
|
::mongo::error_details::invariantOKFailedWithMsg(expr, sw.getStatus(), contextExpr(), loc);
|
|
}
|
|
return std::move(sw.getValue());
|
|
}
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS MONGO_COMPILER_NORETURN void invariantStatusOKFailed(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) noexcept;
|
|
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void invariantStatusOKWithLocation(
|
|
const Status& status, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
invariantStatusOKFailed(status, loc);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T invariantStatusOKWithLocation(
|
|
StatusWith<T> sw, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
invariantStatusOKWithLocation(sw.getStatus(), loc);
|
|
return std::move(sw.getValue());
|
|
}
|
|
|
|
template <typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr void invariantStatusOKWithContextAndLocation(
|
|
const Status& status, ContextExpr&& contextExpr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
if (MONGO_unlikely(!status.isOK())) {
|
|
invariantStatusOKFailed(status.withContext(std::forward<ContextExpr>(contextExpr)()), loc);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename ContextExpr>
|
|
MONGO_MOD_PUBLIC_FOR_TECHNICAL_REASONS constexpr T invariantStatusOKWithContextAndLocation(
|
|
StatusWith<T> sw, ContextExpr&& contextExpr, SourceLocation loc = MONGO_SOURCE_LOCATION()) {
|
|
invariantStatusOKWithContextAndLocation(
|
|
sw.getStatus(), std::forward<ContextExpr>(contextExpr), loc);
|
|
return std::move(sw.getValue());
|
|
}
|
|
} // namespace error_details
|
|
|
|
/**
|
|
* Like uassertStatusOK(status), but for checking if an invariant holds on a status.
|
|
*/
|
|
#define invariantStatusOK(...) \
|
|
::mongo::error_details::invariantStatusOKWithLocation(__VA_ARGS__, MONGO_SOURCE_LOCATION())
|
|
|
|
/**
|
|
* Similar to invariantStatusOK(status), but also takes an expression that evaluates to something
|
|
* convertible to std::string to add more context to error messages. This contextExpr is only
|
|
* evaluated if the status is not OK.
|
|
*/
|
|
#define invariantStatusOKWithContext(status, contextExpr) \
|
|
::mongo::error_details::invariantStatusOKWithContextAndLocation( \
|
|
status, [&]() -> std::string { return (contextExpr); }, MONGO_SOURCE_LOCATION())
|
|
|
|
std::string demangleName(const std::type_info& typeinfo);
|
|
|
|
/**
|
|
* A utility function that converts an exception to a Status.
|
|
* Only call this function when there is an active exception
|
|
* (e.g. in a catch block).
|
|
*
|
|
* Note: this technique was created by Lisa Lippincott.
|
|
*
|
|
* Example usage:
|
|
*
|
|
* Status myFunc() {
|
|
* try {
|
|
* funcThatThrows();
|
|
* return Status::OK();
|
|
* } catch (...) {
|
|
* return exceptionToStatus();
|
|
* }
|
|
* }
|
|
*/
|
|
Status exceptionToStatus();
|
|
|
|
/**
|
|
* Produces an invariant failure if executed. Use when reaching this statement indicates a
|
|
* programming error. Example:
|
|
* // code above checks that expr can only be FOO or BAR
|
|
* switch (expr) {
|
|
* case FOO: { ... }
|
|
* case BAR: { ... }
|
|
* default:
|
|
* MONGO_UNREACHABLE;
|
|
*/
|
|
#define MONGO_UNREACHABLE \
|
|
::mongo::error_details::invariantFailed("Hit a MONGO_UNREACHABLE!", MONGO_SOURCE_LOCATION());
|
|
|
|
/**
|
|
* Like `MONGO_UNREACHABLE`, but triggers a `tassert` instead of an `invariant`
|
|
*/
|
|
#define MONGO_UNREACHABLE_TASSERT(msgid) tasserted(msgid, "Hit a MONGO_UNREACHABLE_TASSERT!")
|
|
|
|
/**
|
|
* Produces an invariant failure if executed. Subset of MONGO_UNREACHABLE, but specifically
|
|
* to indicate that the program has reached a function that is unimplemented and should be
|
|
* unreachable from production.
|
|
* Example:
|
|
*
|
|
* void myFuncToDo() {
|
|
* MONGO_UNIMPLEMENTED;
|
|
* }
|
|
*/
|
|
#define MONGO_UNIMPLEMENTED \
|
|
::mongo::error_details::invariantFailed("Hit a MONGO_UNIMPLEMENTED!", MONGO_SOURCE_LOCATION());
|
|
|
|
/**
|
|
* Like `MONGO_UNIMPLEMENTED`, but triggers a `tassert` instead of an `invariant`
|
|
*/
|
|
#define MONGO_UNIMPLEMENTED_TASSERT(msgid) tasserted(msgid, "Hit a MONGO_UNIMPLEMENTED_TASSERT!")
|
|
|
|
namespace error_details {
|
|
/**
|
|
* A stack of auxiliary information to be dumped on invariant failure.
|
|
* These are intended to carry only very lightweight objects like short strings
|
|
* and numbers, to give some basic clue as to what was going on when a thread
|
|
* suffered an invariant failure.
|
|
*/
|
|
class ScopedDebugInfoStack {
|
|
public:
|
|
struct Rec {
|
|
virtual ~Rec() = default;
|
|
virtual std::string toString() const = 0;
|
|
virtual StringData label() const = 0;
|
|
};
|
|
|
|
void push(const Rec* rec) {
|
|
_stack.push_back(rec);
|
|
}
|
|
void pop() {
|
|
_stack.pop_back();
|
|
}
|
|
|
|
/**
|
|
* Returns the result of calling `toString` on every element in the stack in a way that prevents
|
|
* re-entry by keeping track of how many times we have recursively called this function. This
|
|
* prevents an infinite loop from occurring during a call to `getAll` via any exceptions thrown.
|
|
* Note that it is not correct to write a `ScopedDebugInfo` that throws an exception, and that
|
|
* the behavior described here is just a failsafe backstop against buggy formatters.
|
|
*/
|
|
std::vector<std::string> getAll();
|
|
|
|
private:
|
|
std::vector<const Rec*> _stack;
|
|
int _loggingDepth = 0;
|
|
};
|
|
|
|
/** Each thread has its own stack of scoped debug info. */
|
|
inline ScopedDebugInfoStack& scopedDebugInfoStack() {
|
|
thread_local ScopedDebugInfoStack tls;
|
|
return tls;
|
|
}
|
|
} // namespace error_details
|
|
|
|
/**
|
|
* An RAII type that attaches a datum to a ScopedDebugInfoStack, intended as a
|
|
* broad hint as to what the thread is doing. Pops that datum at scope
|
|
* exit. By default, attaches to the thread_local ScopedDebugInfoStack.
|
|
* If the thread encounters a fatal error, the thread's ScopedDebugInfoStack
|
|
* is logged. The formatting of the data is done lazily during failure handling.
|
|
*
|
|
* Example:
|
|
*
|
|
* void doSomethingAsUser(const User& currentUser) {
|
|
* ScopedDebugInfo userNameDbg("userName", currentUser.nameStringData());
|
|
* ScopedDebugInfo userIdDbg("userId", currentUser.id());
|
|
* somethingThatMightCrash(currentUser);
|
|
* }
|
|
*
|
|
* ScopedDebugInfo must only be used with trivially formattable values. Since it's diagnostic
|
|
* information, formatted during error handling, formatting must not itself fail. For example,
|
|
* formatting should not require synchronization or reading from disk. Since the result is logged,
|
|
* the formatting must follow the usual log redaction guidance.
|
|
*
|
|
* The lifetime of the ScopedDebugInfo must be carefully considered. By default, a ScopedDebugInfo
|
|
* is attached to a thread_local stack, so it must not outlive the thread that created it.
|
|
*/
|
|
template <typename T>
|
|
class ScopedDebugInfo {
|
|
public:
|
|
ScopedDebugInfo(
|
|
StringData label,
|
|
T v,
|
|
error_details::ScopedDebugInfoStack* stack = &error_details::scopedDebugInfoStack())
|
|
: label(label), v(std::move(v)), stack(stack) {
|
|
stack->push(&rec);
|
|
}
|
|
~ScopedDebugInfo() {
|
|
stack->pop();
|
|
}
|
|
ScopedDebugInfo(const ScopedDebugInfo&) noexcept = delete;
|
|
ScopedDebugInfo& operator=(const ScopedDebugInfo&) noexcept = delete;
|
|
|
|
private:
|
|
struct ThisRec : error_details::ScopedDebugInfoStack::Rec {
|
|
explicit ThisRec(const ScopedDebugInfo* owner) : owner(owner) {}
|
|
std::string toString() const override {
|
|
return fmt::format("{}: {}", owner->label, owner->v);
|
|
}
|
|
StringData label() const override {
|
|
return owner->label;
|
|
}
|
|
const ScopedDebugInfo* owner;
|
|
};
|
|
|
|
StringData label;
|
|
T v;
|
|
error_details::ScopedDebugInfoStack* stack;
|
|
ThisRec rec{this};
|
|
};
|
|
|
|
/** Convert string-likes, exceptions, and Status to formatted "caused by" strings. */
|
|
std::string causedBy(StringData e);
|
|
|
|
inline std::string causedBy(const std::exception& e) {
|
|
return causedBy(e.what());
|
|
}
|
|
|
|
inline std::string causedBy(const DBException& e) {
|
|
return causedBy(e.toString());
|
|
}
|
|
|
|
inline std::string causedBy(const Status& e) {
|
|
return causedBy(e.toString());
|
|
}
|
|
|
|
/**
|
|
* Catch and swallow all exceptions, leaving a uniform log message.
|
|
* Should be used only in destructors, to prevent termination.
|
|
*
|
|
* Usage:
|
|
* try {
|
|
* block
|
|
* } catch (...) {
|
|
* reportFailedDestructor(MONGO_SOURCE_LOCATION());
|
|
* }
|
|
*/
|
|
void reportFailedDestructor(SourceLocation loc = MONGO_SOURCE_LOCATION());
|
|
|
|
} // namespace MONGO_MOD_PUB mongo
|