Files
rippled/include/xrpl/tx/paths/detail/FlowDebugInfo.h
2026-02-20 12:15:30 -05:00

341 lines
9.9 KiB
C++

#pragma once
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <boost/container/flat_map.hpp>
#include <chrono>
#include <optional>
#include <sstream>
namespace xrpl {
namespace path {
namespace detail {
// Track performance information of a single payment
struct FlowDebugInfo
{
using clock = std::chrono::high_resolution_clock;
using time_point = clock::time_point;
boost::container::flat_map<std::string, std::pair<time_point, time_point>> timePoints;
boost::container::flat_map<std::string, std::size_t> counts;
struct PassInfo
{
PassInfo() = delete;
PassInfo(bool nativeIn_, bool nativeOut_) : nativeIn(nativeIn_), nativeOut(nativeOut_)
{
}
bool const nativeIn;
bool const nativeOut;
std::vector<EitherAmount> in;
std::vector<EitherAmount> out;
std::vector<size_t> numActive;
std::vector<std::vector<EitherAmount>> liquiditySrcIn;
std::vector<std::vector<EitherAmount>> liquiditySrcOut;
void
reserve(size_t s)
{
in.reserve(s);
out.reserve(s);
liquiditySrcIn.reserve(s);
liquiditySrcOut.reserve(s);
numActive.reserve(s);
}
size_t
size() const
{
return in.size();
}
void
push_back(EitherAmount const& in_amt, EitherAmount const& out_amt, std::size_t active)
{
in.push_back(in_amt);
out.push_back(out_amt);
numActive.push_back(active);
}
void
pushLiquiditySrc(EitherAmount const& eIn, EitherAmount const& eOut)
{
XRPL_ASSERT(
!liquiditySrcIn.empty(),
"xrpl::path::detail::FlowDebugInfo::pushLiquiditySrc : "
"non-empty liquidity source");
liquiditySrcIn.back().push_back(eIn);
liquiditySrcOut.back().push_back(eOut);
}
void
newLiquidityPass()
{
auto const s = liquiditySrcIn.size();
size_t const r = !numActive.empty() ? numActive.back() : 16;
liquiditySrcIn.resize(s + 1);
liquiditySrcIn.back().reserve(r);
liquiditySrcOut.resize(s + 1);
liquiditySrcOut.back().reserve(r);
}
};
PassInfo passInfo;
FlowDebugInfo() = delete;
FlowDebugInfo(bool nativeIn, bool nativeOut) : passInfo(nativeIn, nativeOut)
{
timePoints.reserve(16);
counts.reserve(16);
passInfo.reserve(64);
}
auto
duration(std::string const& tag) const
{
auto i = timePoints.find(tag);
if (i == timePoints.end())
{
// LCOV_EXCL_START
UNREACHABLE(
"xrpl::path::detail::FlowDebugInfo::duration : timepoint not "
"found");
return std::chrono::duration<double>(0);
// LCOV_EXCL_STOP
}
auto const& t = i->second;
return std::chrono::duration_cast<std::chrono::duration<double>>(t.second - t.first);
}
std::size_t
count(std::string const& tag) const
{
auto i = counts.find(tag);
if (i == counts.end())
return 0;
return i->second;
}
// Time the duration of the existence of the result
auto
timeBlock(std::string name)
{
struct Stopper
{
std::string tag;
FlowDebugInfo* info;
Stopper(std::string name, FlowDebugInfo& pi) : tag(std::move(name)), info(&pi)
{
auto const start = FlowDebugInfo::clock::now();
info->timePoints.emplace(tag, std::make_pair(start, start));
}
~Stopper()
{
auto const end = FlowDebugInfo::clock::now();
info->timePoints[tag].second = end;
}
Stopper(Stopper&&) = default;
};
return Stopper(std::move(name), *this);
}
void
inc(std::string const& tag)
{
auto i = counts.find(tag);
if (i == counts.end())
{
counts[tag] = 1;
}
++i->second;
}
void
setCount(std::string const& tag, std::size_t c)
{
counts[tag] = c;
}
std::size_t
passCount() const
{
return passInfo.size();
}
void
pushPass(EitherAmount const& in, EitherAmount const& out, std::size_t activeStrands)
{
passInfo.push_back(in, out, activeStrands);
}
void
pushLiquiditySrc(EitherAmount const& in, EitherAmount const& out)
{
passInfo.pushLiquiditySrc(in, out);
}
void
newLiquidityPass()
{
passInfo.newLiquidityPass();
}
std::string
to_string(bool writePassInfo) const
{
std::ostringstream ostr;
auto const d = duration("main");
ostr << "duration: " << d.count() << ", pass_count: " << passCount();
if (writePassInfo)
{
auto write_list = [&ostr](auto const& vals, auto&& fun, char delim = ';') {
ostr << '[';
if (!vals.empty())
{
ostr << fun(vals[0]);
for (size_t i = 1, e = vals.size(); i < e; ++i)
ostr << delim << fun(vals[i]);
}
ostr << ']';
};
auto writeXrpAmtList = [&write_list](
std::vector<EitherAmount> const& amts, char delim = ';') {
auto get_val = [](EitherAmount const& a) -> std::string {
return xrpl::to_string(a.xrp);
};
write_list(amts, get_val, delim);
};
auto writeIouAmtList = [&write_list](
std::vector<EitherAmount> const& amts, char delim = ';') {
auto get_val = [](EitherAmount const& a) -> std::string {
return xrpl::to_string(a.iou);
};
write_list(amts, get_val, delim);
};
auto writeIntList = [&write_list](std::vector<size_t> const& vals, char delim = ';') {
auto get_val = [](size_t const& v) -> size_t const& { return v; };
write_list(vals, get_val);
};
auto writeNestedIouAmtList =
[&ostr, &writeIouAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
ostr << '[';
if (!amts.empty())
{
writeIouAmtList(amts[0], '|');
for (size_t i = 1, e = amts.size(); i < e; ++i)
{
ostr << ';';
writeIouAmtList(amts[i], '|');
}
}
ostr << ']';
};
auto writeNestedXrpAmtList =
[&ostr, &writeXrpAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
ostr << '[';
if (!amts.empty())
{
writeXrpAmtList(amts[0], '|');
for (size_t i = 1, e = amts.size(); i < e; ++i)
{
ostr << ';';
writeXrpAmtList(amts[i], '|');
}
}
ostr << ']';
};
ostr << ", in_pass: ";
if (passInfo.nativeIn)
writeXrpAmtList(passInfo.in);
else
writeIouAmtList(passInfo.in);
ostr << ", out_pass: ";
if (passInfo.nativeOut)
writeXrpAmtList(passInfo.out);
else
writeIouAmtList(passInfo.out);
ostr << ", num_active: ";
writeIntList(passInfo.numActive);
if (!passInfo.liquiditySrcIn.empty() && !passInfo.liquiditySrcIn.back().empty())
{
ostr << ", l_src_in: ";
if (passInfo.nativeIn)
writeNestedXrpAmtList(passInfo.liquiditySrcIn);
else
writeNestedIouAmtList(passInfo.liquiditySrcIn);
ostr << ", l_src_out: ";
if (passInfo.nativeOut)
writeNestedXrpAmtList(passInfo.liquiditySrcOut);
else
writeNestedIouAmtList(passInfo.liquiditySrcOut);
}
}
return ostr.str();
}
};
inline void
writeDiffElement(
std::ostringstream& ostr,
std::pair<std::tuple<AccountID, AccountID, Currency>, STAmount> const& elem)
{
using namespace std;
auto const k = elem.first;
auto const v = elem.second;
ostr << '[' << get<0>(k) << '|' << get<1>(k) << '|' << get<2>(k) << '|' << v << ']';
};
template <class Iter>
void
writeDiffs(std::ostringstream& ostr, Iter begin, Iter end)
{
ostr << '[';
if (begin != end)
{
writeDiffElement(ostr, *begin);
++begin;
}
for (; begin != end; ++begin)
{
ostr << ';';
writeDiffElement(ostr, *begin);
}
ostr << ']';
};
using BalanceDiffs =
std::pair<std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>, XRPAmount>;
inline BalanceDiffs
balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
{
return {sb.balanceChanges(rv), sb.xrpDestroyed()};
}
inline std::string
balanceDiffsToString(std::optional<BalanceDiffs> const& bd)
{
if (!bd)
return std::string{};
auto const& diffs = bd->first;
auto const& xrpDestroyed = bd->second;
std::ostringstream ostr;
ostr << ", xrpDestroyed: " << to_string(xrpDestroyed);
ostr << ", balanceDiffs: ";
writeDiffs(ostr, diffs.begin(), diffs.end());
return ostr.str();
};
} // namespace detail
} // namespace path
} // namespace xrpl