mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
341 lines
9.9 KiB
C++
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
|