#pragma once #include #include #include #include #include #include #include #include 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> timePoints; boost::container::flat_map counts; struct PassInfo { PassInfo() = delete; PassInfo(bool nativeIn_, bool nativeOut_) : nativeIn(nativeIn_), nativeOut(nativeOut_) { } bool const nativeIn; bool const nativeOut; std::vector in; std::vector out; std::vector numActive; std::vector> liquiditySrcIn; std::vector> 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(0); // LCOV_EXCL_STOP } auto const& t = i->second; return std::chrono::duration_cast>(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 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 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 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> 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> 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, 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 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, STAmount>, XRPAmount>; inline BalanceDiffs balanceDiffs(PaymentSandbox const& sb, ReadView const& rv) { return {sb.balanceChanges(rv), sb.xrpDestroyed()}; } inline std::string balanceDiffsToString(std::optional 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