Antithesis instrumentation improvements (#5213)

* Rename ASSERT to XRPL_ASSERT
* Upgrade to Anthithesis SDK 0.4.4, and use new 0.4.4 features
  * automatic cast to bool, like assert
* Add instrumentation workflow to verify build with instrumentation enabled
This commit is contained in:
Bronek Kozicki
2024-12-16 22:48:33 +00:00
committed by GitHub
parent ea1fffeebf
commit eabca8439f
223 changed files with 1452 additions and 1344 deletions

View File

@@ -112,7 +112,7 @@ public:
operator=(Slice s)
{
// Ensure the slice isn't a subset of the buffer.
ASSERT(
XRPL_ASSERT(
s.size() == 0 || size_ == 0 || s.data() < p_.get() ||
s.data() >= p_.get() + size_,
"ripple::Buffer::operator=(Slice) : input not a subset");

View File

@@ -43,7 +43,7 @@ namespace ripple {
constexpr std::size_t
calculatePercent(std::size_t count, std::size_t total)
{
assert(total != 0); // NOTE No ASSERT here, because constexpr
assert(total != 0); // NOTE No XRPL_ASSERT here, because constexpr
return ((std::min(count, total) * 100) + total - 1) / total;
}

View File

@@ -143,7 +143,7 @@ class SlabAllocator
void
deallocate(std::uint8_t* ptr) noexcept
{
ASSERT(
XRPL_ASSERT(
own(ptr),
"ripple::SlabAllocator::SlabBlock::deallocate : own input");
@@ -188,7 +188,7 @@ public:
boost::alignment::align_up(sizeof(Type) + extra, itemAlignment_))
, slabSize_(alloc)
{
ASSERT(
XRPL_ASSERT(
(itemAlignment_ & (itemAlignment_ - 1)) == 0,
"ripple::SlabAllocator::SlabAllocator : valid alignment");
}
@@ -300,8 +300,8 @@ public:
bool
deallocate(std::uint8_t* ptr) noexcept
{
ASSERT(
ptr != nullptr,
XRPL_ASSERT(
ptr,
"ripple::SlabAllocator::SlabAllocator::deallocate : non-null "
"input");

View File

@@ -103,7 +103,7 @@ public:
std::uint8_t
operator[](std::size_t i) const noexcept
{
ASSERT(
XRPL_ASSERT(
i < size_,
"ripple::Slice::operator[](std::size_t) const : valid input");
return data_[i];

View File

@@ -291,7 +291,7 @@ public:
std::is_trivially_copyable<typename Container::value_type>::value>>
explicit base_uint(Container const& c)
{
ASSERT(
XRPL_ASSERT(
c.size() * sizeof(typename Container::value_type) == size(),
"ripple::base_uint::base_uint(Container auto) : input size match");
std::memcpy(data_.data(), c.data(), size());
@@ -304,7 +304,7 @@ public:
base_uint&>
operator=(Container const& c)
{
ASSERT(
XRPL_ASSERT(
c.size() * sizeof(typename Container::value_type) == size(),
"ripple::base_uint::operator=(Container auto) : input size match");
std::memcpy(data_.data(), c.data(), size());

View File

@@ -258,8 +258,8 @@ public:
? *partitions
: std::thread::hardware_concurrency();
map_.resize(partitions_);
ASSERT(
partitions_ != 0,
XRPL_ASSERT(
partitions_,
"ripple::partitioned_unordered_map::partitioned_unordered_map : "
"nonzero partitions");
}

View File

@@ -114,7 +114,7 @@ std::enable_if_t<
Integral>
rand_int(Engine& engine, Integral min, Integral max)
{
ASSERT(max > min, "ripple::rand_int : max over min inputs");
XRPL_ASSERT(max > min, "ripple::rand_int : max over min inputs");
// This should have no state and constructing it should
// be very cheap. If that turns out not to be the case

View File

@@ -235,7 +235,7 @@ public:
explicit scope_unlock(std::unique_lock<Mutex>& lock) noexcept(true)
: plock(&lock)
{
ASSERT(
XRPL_ASSERT(
plock->owns_lock(),
"ripple::scope_unlock::scope_unlock : mutex must be locked");
plock->unlock();

View File

@@ -117,7 +117,7 @@ public:
packed_spinlock(std::atomic<T>& lock, int index)
: bits_(lock), mask_(static_cast<T>(1) << index)
{
ASSERT(
XRPL_ASSERT(
index >= 0 && (mask_ != 0),
"ripple::packed_spinlock::packed_spinlock : valid index and mask");
}

View File

@@ -174,8 +174,8 @@ private:
, m_repeat(repeat)
, m_probe(probe)
{
ASSERT(
m_probe != nullptr,
XRPL_ASSERT(
m_probe,
"beast::io_latency_probe::sample_op::sample_op : non-null "
"probe input");
m_probe->addref();
@@ -187,8 +187,8 @@ private:
, m_repeat(from.m_repeat)
, m_probe(from.m_probe)
{
ASSERT(
m_probe != nullptr,
XRPL_ASSERT(
m_probe,
"beast::io_latency_probe::sample_op::sample_op(sample_op&&) : "
"non-null probe input");
from.m_probe = nullptr;

View File

@@ -61,7 +61,7 @@ public:
void
set(time_point const& when)
{
ASSERT(
XRPL_ASSERT(
!Clock::is_steady || when >= now_,
"beast::manual_clock::set(time_point) : forward input");
now_ = when;
@@ -80,7 +80,7 @@ public:
void
advance(std::chrono::duration<Rep, Period> const& elapsed)
{
ASSERT(
XRPL_ASSERT(
!Clock::is_steady || (now_ + elapsed) >= now_,
"beast::manual_clock::advance(duration) : forward input");
now_ += elapsed;

View File

@@ -1330,7 +1330,7 @@ public:
size_type
bucket(Key const& k) const
{
ASSERT(
XRPL_ASSERT(
bucket_count() != 0,
"beast::detail::aged_unordered_container::bucket : nonzero bucket "
"count");
@@ -1474,7 +1474,7 @@ private:
{
if (would_exceed(additional))
m_buck.resize(size() + additional, m_cont);
ASSERT(
XRPL_ASSERT(
load_factor() <= max_load_factor(),
"beast::detail::aged_unordered_container::maybe_rehash : maximum "
"load factor");

View File

@@ -160,9 +160,8 @@ struct LexicalCast<Out, char const*>
bool
operator()(Out& out, char const* in) const
{
ASSERT(
in != nullptr,
"beast::detail::LexicalCast(char const*) : non-null input");
XRPL_ASSERT(
in, "beast::detail::LexicalCast(char const*) : non-null input");
return LexicalCast<Out, std::string_view>()(out, in);
}
};
@@ -177,9 +176,7 @@ struct LexicalCast<Out, char*>
bool
operator()(Out& out, char* in) const
{
ASSERT(
in != nullptr,
"beast::detail::LexicalCast(char*) : non-null input");
XRPL_ASSERT(in, "beast::detail::LexicalCast(char*) : non-null input");
return LexicalCast<Out, std::string_view>()(out, in);
}
};

View File

@@ -205,7 +205,7 @@ public:
*/
Stream(Sink& sink, Severity level) : m_sink(sink), m_level(level)
{
ASSERT(
XRPL_ASSERT(
m_level < severities::kDisabled,
"beast::Journal::Stream::Stream : maximum level");
}

View File

@@ -28,37 +28,43 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#endif
#include <antithesis_sdk.h>
#else
#define ALWAYS(cond, name, ...) assert((name) && (cond))
#define ALWAYS_OR_UNREACHABLE(cond, name, ...) assert((name) && (cond))
#define SOMETIMES(cond, name, ...)
#define REACHABLE(name, ...)
#define UNREACHABLE(name, ...) assert((name) && false)
// Macros below are copied from antithesis_sdk.h and slightly simplified
// The duplication is because Visual Studio 2019 cannot compile that header
// even with the option -Zc:__cplusplus added.
#define ALWAYS(cond, message, ...) assert((message) && (cond))
#define ALWAYS_OR_UNREACHABLE(cond, message, ...) assert((message) && (cond))
#define SOMETIMES(cond, message, ...)
#define REACHABLE(message, ...)
#define UNREACHABLE(message, ...) assert((message) && false)
#endif
#define ASSERT ALWAYS_OR_UNREACHABLE
#define XRPL_ASSERT ALWAYS_OR_UNREACHABLE
// How to use the instrumentation macros:
//
// ALWAYS if cond must be true and the line must be reached during fuzzing
// ASSERT if cond must be true but the line might not be reached during fuzzing
// REACHABLE if the line must be reached during fuzzing
// SOMETIMES a hint for the fuzzer to try to make the cond true
// UNREACHABLE if the line must not be reached (in fuzzing or in normal use)
// * XRPL_ASSERT if cond must be true but the line might not be reached during
// fuzzing. Same like `assert` in normal use.
// * ALWAYS if cond must be true _and_ the line must be reached during fuzzing.
// Same like `assert` in normal use.
// * REACHABLE if the line must be reached during fuzzing
// * SOMETIMES a hint for the fuzzer to try to make the cond true
// * UNREACHABLE if the line must not be reached (in fuzzing or in normal use).
// Same like `assert(false)` in normal use.
//
// NOTE: ASSERT has similar semantics as C assert macro, with minor differences:
// * ASSERT must have an unique name (naming convention in CONTRIBUTING.md)
// * the condition (which comes first) must be *implicitly* convertible to bool
// * during fuzzing, the program will continue execution past a failed ASSERT
// NOTE: XRPL_ASSERT has similar semantics as C `assert` macro, with only minor
// differences:
// * XRPL_ASSERT must have an unique name (naming convention in CONTRIBUTING.md)
// * during fuzzing, the program will continue execution past failed XRPL_ASSERT
//
// We continue to use regular C assert inside unit tests and inside constexpr
// We continue to use regular C `assert` inside unit tests and inside constexpr
// functions.
//
// NOTE: UNREACHABLE does *not* have the same semantics as std::unreachable.
// The program will continue execution past an UNREACHABLE in a Release build
// and during fuzzing (similar to ASSERT).
// and during fuzzing (similar to failed XRPL_ASSERT).
// Also, the naming convention in UNREACHABLE is subtly different from other
// instrumentation macros - its name describes the condition which was *not*
// meant to happen, while name in other macros describe the condition that is
// instrumentation macros - its name describes the condition which was _not_
// meant to happen, while name in other macros describes the condition that is
// meant to happen (e.g. as in "assert that this happens").
#endif

View File

@@ -42,7 +42,7 @@ rngfill(void* buffer, std::size_t bytes, Generator& g)
bytes -= sizeof(v);
}
ASSERT(
XRPL_ASSERT(
bytes < sizeof(result_type), "beast::rngfill(void*) : maximum bytes");
#ifdef __GNUC__

View File

@@ -53,7 +53,7 @@ toSTAmount(XRPAmount const& xrp)
inline STAmount
toSTAmount(XRPAmount const& xrp, Issue const& iss)
{
ASSERT(
XRPL_ASSERT(
isXRP(iss.account) && isXRP(iss.currency),
"ripple::toSTAmount : is XRP");
return toSTAmount(xrp);
@@ -74,14 +74,14 @@ template <>
inline IOUAmount
toAmount<IOUAmount>(STAmount const& amt)
{
ASSERT(
XRPL_ASSERT(
amt.mantissa() < std::numeric_limits<std::int64_t>::max(),
"ripple::toAmount<IOUAmount> : maximum mantissa");
bool const isNeg = amt.negative();
std::int64_t const sMant =
isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
ASSERT(!isXRP(amt), "ripple::toAmount<IOUAmount> : is not XRP");
XRPL_ASSERT(!isXRP(amt), "ripple::toAmount<IOUAmount> : is not XRP");
return IOUAmount(sMant, amt.exponent());
}
@@ -89,14 +89,14 @@ template <>
inline XRPAmount
toAmount<XRPAmount>(STAmount const& amt)
{
ASSERT(
XRPL_ASSERT(
amt.mantissa() < std::numeric_limits<std::int64_t>::max(),
"ripple::toAmount<XRPAmount> : maximum mantissa");
bool const isNeg = amt.negative();
std::int64_t const sMant =
isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
ASSERT(isXRP(amt), "ripple::toAmount<XRPAmount> : is XRP");
XRPL_ASSERT(isXRP(amt), "ripple::toAmount<XRPAmount> : is XRP");
return XRPAmount(sMant);
}

View File

@@ -151,7 +151,7 @@ public:
explicit FeatureBitset(base const& b) : base(b)
{
ASSERT(
XRPL_ASSERT(
b.count() == count(),
"ripple::FeatureBitset::FeatureBitset(base) : count match");
}
@@ -160,7 +160,7 @@ public:
explicit FeatureBitset(uint256 const& f, Fs&&... fs)
{
initFromFeatures(f, std::forward<Fs>(fs)...);
ASSERT(
XRPL_ASSERT(
count() == (sizeof...(fs) + 1),
"ripple::FeatureBitset::FeatureBitset(uint256) : count and "
"sizeof... do match");
@@ -171,7 +171,7 @@ public:
{
for (auto const& f : fs)
set(featureToBitsetIndex(f));
ASSERT(
XRPL_ASSERT(
fs.size() == count(),
"ripple::FeatureBitset::FeatureBitset(Container auto) : count and "
"size do match");

View File

@@ -426,12 +426,12 @@ mulDivU(Source1 value, Dest mul, Source2 div)
{
// split the asserts so if one hits, the user can tell which
// without a debugger.
ASSERT(
XRPL_ASSERT(
value.value() >= 0,
"ripple::feeunit::mulDivU : minimum value input");
ASSERT(
XRPL_ASSERT(
mul.value() >= 0, "ripple::feeunit::mulDivU : minimum mul input");
ASSERT(
XRPL_ASSERT(
div.value() >= 0, "ripple::feeunit::mulDivU : minimum div input");
return std::nullopt;
}

View File

@@ -220,7 +220,8 @@ page(uint256 const& root, std::uint64_t index = 0) noexcept;
inline Keylet
page(Keylet const& root, std::uint64_t index = 0) noexcept
{
ASSERT(root.type == ltDIR_NODE, "ripple::keylet::page : valid root type");
XRPL_ASSERT(
root.type == ltDIR_NODE, "ripple::keylet::page : valid root type");
return page(root.key, index);
}
/** @} */

View File

@@ -159,7 +159,7 @@ struct MultiApiJson
-> std::
invoke_result_t<Fn, decltype(json.val[0]), Version, Args&&...>
{
ASSERT(
XRPL_ASSERT(
valid(version) && index(version) >= 0 && index(version) < size,
"ripple::detail::MultiApiJson::operator<Args...>() : valid "
"version");
@@ -179,7 +179,7 @@ struct MultiApiJson
operator()(Json& json, Version version, Fn fn) const
-> std::invoke_result_t<Fn, decltype(json.val[0])>
{
ASSERT(
XRPL_ASSERT(
valid(version) && index(version) >= 0 && index(version) < size,
"ripple::detail::MultiApiJson::operator() : valid version");
return std::invoke(fn, json.val[index(version)]);

View File

@@ -298,7 +298,7 @@ public:
friend double
relativeDistance(Quality const& q1, Quality const& q2)
{
ASSERT(
XRPL_ASSERT(
q1.m_value > 0 && q2.m_value > 0,
"ripple::Quality::relativeDistance : minimum inputs");

View File

@@ -355,7 +355,7 @@ STAmount::STAmount(
, mIsNegative(negative)
{
// mValue is uint64, but needs to fit in the range of int64
ASSERT(
XRPL_ASSERT(
mValue <= std::numeric_limits<std::int64_t>::max(),
"ripple::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
"maximum mantissa input");

View File

@@ -170,8 +170,9 @@ template <int Bits>
void
STBitString<Bits>::add(Serializer& s) const
{
ASSERT(getFName().isBinary(), "ripple::STBitString::add : field is binary");
ASSERT(
XRPL_ASSERT(
getFName().isBinary(), "ripple::STBitString::add : field is binary");
XRPL_ASSERT(
getFName().fieldType == getSType(),
"ripple::STBitString::add : field type match");
s.addBitString<Bits>(value_);

View File

@@ -110,8 +110,9 @@ template <typename Integer>
inline void
STInteger<Integer>::add(Serializer& s) const
{
ASSERT(getFName().isBinary(), "ripple::STInteger::add : field is binary");
ASSERT(
XRPL_ASSERT(
getFName().isBinary(), "ripple::STInteger::add : field is binary");
XRPL_ASSERT(
getFName().fieldType == getSType(),
"ripple::STInteger::add : field type match");
s.addInteger(value_);

View File

@@ -737,8 +737,7 @@ STObject::Proxy<T>::assign(U&& u)
t = dynamic_cast<T*>(st_->getPField(*f_, true));
else
t = dynamic_cast<T*>(st_->makeFieldPresent(*f_));
ASSERT(
t != nullptr, "ripple::STObject::Proxy::assign : type cast succeeded");
XRPL_ASSERT(t, "ripple::STObject::Proxy::assign : type cast succeeded");
*t = std::forward<U>(u);
}
@@ -1034,17 +1033,17 @@ STObject::at(TypedField<T> const& f) const
if (auto const u = dynamic_cast<T const*>(b))
return u->value();
ASSERT(
mType != nullptr,
XRPL_ASSERT(
mType,
"ripple::STObject::at(TypedField auto) : field template non-null");
ASSERT(
XRPL_ASSERT(
b->getSType() == STI_NOTPRESENT,
"ripple::STObject::at(TypedField auto) : type not present");
if (mType->style(f) == soeOPTIONAL)
Throw<STObject::FieldErr>("Missing optional field: " + f.getName());
ASSERT(
XRPL_ASSERT(
mType->style(f) == soeDEFAULT,
"ripple::STObject::at(TypedField auto) : template style is default");
@@ -1064,16 +1063,16 @@ STObject::at(OptionaledField<T> const& of) const
auto const u = dynamic_cast<T const*>(b);
if (!u)
{
ASSERT(
mType != nullptr,
XRPL_ASSERT(
mType,
"ripple::STObject::at(OptionaledField auto) : field template "
"non-null");
ASSERT(
XRPL_ASSERT(
b->getSType() == STI_NOTPRESENT,
"ripple::STObject::at(OptionaledField auto) : type not present");
if (mType->style(*of.f) == soeOPTIONAL)
return std::nullopt;
ASSERT(
XRPL_ASSERT(
mType->style(*of.f) == soeDEFAULT,
"ripple::STObject::at(OptionaledField auto) : template style is "
"default");

View File

@@ -257,7 +257,7 @@ inline STPathElement::STPathElement(
is_offer_ = false;
mAccountID = *account;
mType |= typeAccount;
ASSERT(
XRPL_ASSERT(
mAccountID != noAccount(),
"ripple::STPathElement::STPathElement : account is set");
}
@@ -272,7 +272,7 @@ inline STPathElement::STPathElement(
{
mIssuerID = *issuer;
mType |= typeIssuer;
ASSERT(
XRPL_ASSERT(
mIssuerID != noAccount(),
"ripple::STPathElement::STPathElement : issuer is set");
}

View File

@@ -176,7 +176,7 @@ STValidation::STValidation(
Throw<std::runtime_error>("Invalid signature in validation");
}
ASSERT(
XRPL_ASSERT(
nodeID_.isNonZero(),
"ripple::STValidation::STValidation(SerialIter) : nonzero node");
}
@@ -201,7 +201,7 @@ STValidation::STValidation(
, nodeID_(nodeID)
, seenTime_(signTime)
{
ASSERT(
XRPL_ASSERT(
nodeID_.isNonZero(),
"ripple::STValidation::STValidation(PublicKey, SecretKey) : nonzero "
"node");

View File

@@ -55,8 +55,8 @@ public:
if (size)
{
ASSERT(
data != nullptr,
XRPL_ASSERT(
data,
"ripple::Serializer::Serializer(void const*) : non-null input");
std::memcpy(mData.data(), data, size);
}
@@ -333,7 +333,8 @@ Serializer::addVL(Iter begin, Iter end, int len)
len -= begin->size();
#endif
}
ASSERT(len == 0, "ripple::Serializer::addVL : length matches distance");
XRPL_ASSERT(
len == 0, "ripple::Serializer::addVL : length matches distance");
return ret;
}

View File

@@ -116,7 +116,7 @@ public:
STAmount
getDeliveredAmount() const
{
ASSERT(
XRPL_ASSERT(
hasDeliveredAmount(),
"ripple::TxMeta::getDeliveredAmount : non-null delivered amount");
return *mDelivered;

View File

@@ -148,11 +148,11 @@ inplace_bigint_div_rem(std::span<uint64_t> numerator, std::uint64_t divisor)
unsigned __int128 const denom128 = denom;
unsigned __int128 const d = num / denom128;
unsigned __int128 const r = num - (denom128 * d);
ASSERT(
XRPL_ASSERT(
d >> 64 == 0,
"ripple::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
"valid division result");
ASSERT(
XRPL_ASSERT(
r >> 64 == 0,
"ripple::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
"valid remainder");
@@ -179,7 +179,7 @@ b58_10_to_b58_be(std::uint64_t input)
{
[[maybe_unused]] static constexpr std::uint64_t B_58_10 =
430804206899405824; // 58^10;
ASSERT(
XRPL_ASSERT(
input < B_58_10,
"ripple::b58_fast::detail::b58_10_to_b58_be : valid input");
constexpr std::size_t resultSize = 10;

View File

@@ -401,7 +401,7 @@ public:
{
std::lock_guard _(lock_);
Entry& entry(iter->second);
ASSERT(
XRPL_ASSERT(
entry.refcount == 0,
"ripple::Resource::Logic::erase : entry not used");
inactive_.erase(inactive_.iterator_to(entry));

View File

@@ -512,7 +512,7 @@ template <class String>
void
BaseWSPeer<Handler, Impl>::fail(error_code ec, String const& what)
{
ASSERT(
XRPL_ASSERT(
strand_.running_in_this_thread(),
"ripple::BaseWSPeer::fail : strand in this thread");