diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f80bada6..5d3e75c44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -456,6 +456,7 @@ set(TDLIB_SOURCE td/telegram/net/NetQueryCreator.cpp td/telegram/net/NetQueryDelayer.cpp td/telegram/net/NetQueryDispatcher.cpp + td/telegram/net/NetQueryStats.cpp td/telegram/net/NetStatsManager.cpp td/telegram/net/Proxy.cpp td/telegram/net/PublicRsaKeyShared.cpp @@ -628,6 +629,7 @@ set(TDLIB_SOURCE td/telegram/net/NetQueryCreator.h td/telegram/net/NetQueryDelayer.h td/telegram/net/NetQueryDispatcher.h + td/telegram/net/NetQueryStats.h td/telegram/net/NetStatsManager.h td/telegram/net/NetType.h td/telegram/net/Proxy.h diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 55dcb67b0..35009fe39 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -30,6 +30,8 @@ namespace td { class MultiTd : public Actor { public: + explicit MultiTd(Td::Options options) : options_(std::move(options)) { + } void create(int32 td_id, unique_ptr callback) { auto &td = tds_[td_id]; CHECK(td.empty()); @@ -47,7 +49,7 @@ class MultiTd : public Actor { auto context = std::make_shared(to_string(td_id)); auto old_context = set_context(context); auto old_tag = set_tag(context->tag_); - td = create_actor("Td", std::move(callback)); + td = create_actor("Td", std::move(callback), options_); set_context(old_context); set_tag(old_tag); } @@ -64,6 +66,7 @@ class MultiTd : public Actor { } private: + Td::Options options_; std::unordered_map> tds_; }; @@ -112,6 +115,7 @@ class TdReceiver { class MultiClient::Impl final { public: Impl() { + options_.net_query_stats = std::make_shared(); concurrent_scheduler_ = make_unique(); concurrent_scheduler_->init(0); receiver_ = make_unique(); @@ -120,7 +124,8 @@ class MultiClient::Impl final { ClientId create_client() { auto client_id = ++client_id_; - tds_[client_id] = concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_->create_callback(client_id)); + tds_[client_id] = + concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_->create_callback(client_id), options_); return client_id; } @@ -185,6 +190,7 @@ class MultiClient::Impl final { std::vector requests_; unique_ptr concurrent_scheduler_; ClientId client_id_{0}; + Td::Options options_; std::unordered_map> tds_; }; @@ -270,14 +276,16 @@ class TdReceiver { class MultiImpl { public: - MultiImpl() { + MultiImpl(std::shared_ptr net_query_stats) { concurrent_scheduler_ = std::make_shared(); concurrent_scheduler_->init(3); concurrent_scheduler_->start(); { auto guard = concurrent_scheduler_->get_main_guard(); - multi_td_ = create_actor("MultiTd"); + Td::Options options; + options.net_query_stats = std::move(net_query_stats); + multi_td_ = create_actor("MultiTd", std::move(options)); } scheduler_thread_ = thread([concurrent_scheduler = concurrent_scheduler_] { @@ -343,15 +351,20 @@ class MultiImplPool { [](auto &a, auto &b) { return a.lock().use_count() < b.lock().use_count(); }); auto res = impl.lock(); if (!res) { - res = std::make_shared(); + res = std::make_shared(net_query_stats_); impl = res; } return res; } + std::shared_ptr get_net_query_stats() const { + return net_query_stats_; + } + private: std::mutex mutex_; std::vector> impls_; + std::shared_ptr net_query_stats_ = std::make_shared(); }; class MultiClient::Impl final { diff --git a/td/telegram/ClientActor.cpp b/td/telegram/ClientActor.cpp index a53fd7e66..61932e486 100644 --- a/td/telegram/ClientActor.cpp +++ b/td/telegram/ClientActor.cpp @@ -9,12 +9,15 @@ #include "td/telegram/td_api.h" #include "td/telegram/net/NetQueryCounter.h" +#include "td/telegram/net/NetQueryStats.h" #include "td/telegram/Td.h" namespace td { -ClientActor::ClientActor(unique_ptr callback) { - td_ = create_actor("Td", std::move(callback)); +ClientActor::ClientActor(unique_ptr callback, Options options) { + Td::Options td_options; + td_options.net_query_stats = options.net_query_stats; + td_ = create_actor("Td", std::move(callback), std::move(td_options)); } void ClientActor::request(uint64 id, td_api::object_ptr request) { @@ -31,8 +34,16 @@ td_api::object_ptr ClientActor::execute(td_api::object_ptr create_net_query_stats() { + return std::make_shared(); +} + +void dump_pending_network_queries(NetQueryStats &stats) { + stats.dump_pending_network_queries(); +} + +uint64 get_pending_network_query_count(NetQueryStats &stats) { + return stats.get_count(); } } // namespace td diff --git a/td/telegram/ClientActor.h b/td/telegram/ClientActor.h index 54cb7250a..5255d7ba1 100644 --- a/td/telegram/ClientActor.h +++ b/td/telegram/ClientActor.h @@ -20,6 +20,7 @@ namespace td { class Td; +class NetQueryStats; /** * This is a low-level Actor interface for interaction with TDLib. The interface is a lot more flexible than @@ -27,11 +28,15 @@ class Td; */ class ClientActor : public Actor { public: + struct Options { + std::shared_ptr net_query_stats; + }; + /** * Creates a ClientActor using the specified callback. * \param[in] callback Callback for outgoing notifications from TDLib. */ - explicit ClientActor(unique_ptr callback); + explicit ClientActor(unique_ptr callback, Options options = {}); /** * Sends one request to TDLib. The answer will be received via callback. @@ -70,16 +75,18 @@ class ClientActor : public Actor { ActorOwn td_; }; +std::shared_ptr create_net_query_stats(); + /** * Dumps information about all pending network queries to the internal TDLib log. * This is useful for library debugging. */ -void dump_pending_network_queries(); +void dump_pending_network_queries(NetQueryStats &stats); /** * Returns the current number of pending network queries. Useful for library debugging. * \return Number of currently pending network queries. */ -uint64 get_pending_network_query_count(); +uint64 get_pending_network_query_count(NetQueryStats &stats); } // namespace td diff --git a/td/telegram/Global.cpp b/td/telegram/Global.cpp index 1901ae09d..eb4c0c69d 100644 --- a/td/telegram/Global.cpp +++ b/td/telegram/Global.cpp @@ -219,6 +219,10 @@ bool Global::ignore_backgrond_updates() const { shared_config_->get_option_boolean("ignore_background_updates"); } +void Global::set_net_query_stats(std::shared_ptr net_query_stats) { + net_query_creator_.set_create_func([=] { return td::make_unique(net_query_stats); }); +} + void Global::set_net_query_dispatcher(unique_ptr net_query_dispatcher) { net_query_dispatcher_ = std::move(net_query_dispatcher); } diff --git a/td/telegram/Global.h b/td/telegram/Global.h index 3b9cfc40a..a65084f88 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -102,9 +102,10 @@ class Global : public ActorContext { bool ignore_backgrond_updates() const; NetQueryCreator &net_query_creator() { - return net_query_creator_.get(); + return *net_query_creator_.get(); } + void set_net_query_stats(std::shared_ptr net_query_stats); void set_net_query_dispatcher(unique_ptr net_query_dispatcher); NetQueryDispatcher &net_query_dispatcher() { @@ -422,7 +423,7 @@ class Global : public ActorContext { ActorId state_manager_; - SchedulerLocalStorage net_query_creator_; + LazySchedulerLocalStorage> net_query_creator_; unique_ptr net_query_dispatcher_; unique_ptr shared_config_; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 6e7b479a6..f61c42b44 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3005,7 +3005,8 @@ class SetBackgroundRequest : public RequestActor<> { } }; -Td::Td(unique_ptr callback) : callback_(std::move(callback)) { +Td::Td(unique_ptr callback, Options options) + : callback_(std::move(callback)), td_options_(std::move(options)) { } Td::~Td() = default; @@ -3740,6 +3741,7 @@ void Td::start_up() { VLOG(td_init) << "Create Global"; set_context(std::make_shared()); + G()->set_net_query_stats(td_options_.net_query_stats); inc_request_actor_refcnt(); // guard inc_actor_refcnt(); // guard diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 46a174752..7ffb318af 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -14,6 +14,7 @@ #include "td/telegram/TdParameters.h" #include "td/telegram/TermsOfService.h" +#include "td/telegram/net/NetQueryStats.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -93,7 +94,11 @@ class Td final : public NetQueryCallback { Td &operator=(Td &&) = delete; ~Td() override; - explicit Td(unique_ptr callback); + struct Options { + std::shared_ptr net_query_stats; + }; + + Td(unique_ptr callback, Options options); void request(uint64 id, tl_object_ptr function); @@ -260,6 +265,7 @@ class Td final : public NetQueryCallback { TdParameters parameters_; unique_ptr callback_; + Options td_options_; StateManager::State connection_state_; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ae06288e6..f365f8403 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -846,7 +846,10 @@ class CliClient final : public Actor { uint64 generation_; }; - td_client_ = create_actor(name, make_unique(this, ++generation_)); + ClientActor::Options options; + options.net_query_stats = net_query_stats_; + + td_client_ = create_actor(name, make_unique(this, ++generation_), std::move(options)); } void init_td() { @@ -4164,7 +4167,7 @@ class CliClient final : public Actor { } else if (op == "q" || op == "Quit") { quit(); } else if (op == "dnq" || op == "DumpNetQueries") { - dump_pending_network_queries(); + dump_pending_network_queries(*net_query_stats_); } else if (op == "fatal") { LOG(FATAL) << "Fatal!"; } else if (op == "unreachable") { @@ -4289,6 +4292,7 @@ class CliClient final : public Actor { ConcurrentScheduler *scheduler_{nullptr}; bool use_test_dc_ = false; + std::shared_ptr net_query_stats_ = create_net_query_stats(); ActorOwn td_client_; std::queue cmd_queue_; bool close_flag_ = false; diff --git a/td/telegram/net/NetQuery.cpp b/td/telegram/net/NetQuery.cpp index 1a1f7be7b..e02565093 100644 --- a/td/telegram/net/NetQuery.cpp +++ b/td/telegram/net/NetQuery.cpp @@ -62,43 +62,4 @@ void NetQuery::set_error(Status status, string source) { set_error_impl(std::move(status), std::move(source)); } -TsList &NetQuery::get_net_query_list() { - static auto init_mutex = [] { - TsList::lock().unlock(); // initialize mutex before any NetQuery - return true; - }(); - CHECK(init_mutex); - static TsList net_query_list; - return net_query_list; -} - -void dump_pending_network_queries() { - auto n = NetQueryCounter::get_count(); - LOG(WARNING) << tag("pending net queries", n); - - decltype(n) i = 0; - bool was_gap = false; - auto &net_query_list = NetQuery::get_net_query_list(); - auto guard = net_query_list.lock(); - for (auto end = net_query_list.end(), cur = net_query_list.begin(); cur != end; cur = cur->get_next(), i++) { - if (i < 20 || i + 20 > n || i % (n / 20 + 1) == 0) { - if (was_gap) { - LOG(WARNING) << "..."; - was_gap = false; - } - const NetQueryDebug &debug = cur->get_data_unsafe(); - const NetQuery &nq = *static_cast(cur); - LOG(WARNING) << tag("user", debug.my_id_) << nq << tag("total flood", format::as_time(nq.total_timeout_)) - << tag("since start", format::as_time(Time::now_cached() - debug.start_timestamp_)) - << tag("state", debug.state_) - << tag("in this state", format::as_time(Time::now_cached() - debug.state_timestamp_)) - << tag("state changed", debug.state_change_count_) << tag("resend count", debug.resend_count_) - << tag("fail count", debug.send_failed_count_) << tag("ack state", debug.ack_state_) - << tag("unknown", debug.unknown_state_); - } else { - was_gap = true; - } - } -} - } // namespace td diff --git a/td/telegram/net/NetQuery.h b/td/telegram/net/NetQuery.h index aed0dd310..fe1f911ee 100644 --- a/td/telegram/net/NetQuery.h +++ b/td/telegram/net/NetQuery.h @@ -8,6 +8,7 @@ #include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryCounter.h" +#include "td/telegram/net/NetQueryStats.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" @@ -40,18 +41,6 @@ class NetQueryCallback : public Actor { virtual void on_result_resendable(NetQueryPtr query, Promise promise); }; -struct NetQueryDebug { - double start_timestamp_ = 0; - int32 my_id_ = 0; - int32 resend_count_ = 0; - string state_ = "empty"; - double state_timestamp_ = 0; - int32 state_change_count_ = 0; - int32 send_failed_count_ = 0; - int ack_state_ = 0; - bool unknown_state_ = false; -}; - class NetQuery : public TsListNode { public: NetQuery() = default; @@ -279,8 +268,6 @@ class NetQuery : public TsListNode { static int32 tl_magic(const BufferSlice &buffer_slice); - static TsList &get_net_query_list(); - private: State state_ = State::Empty; Type type_ = Type::Common; @@ -345,13 +332,12 @@ class NetQuery : public TsListNode { int32 file_type_ = -1; // to be set by caller NetQuery(State state, uint64 id, BufferSlice &&query, BufferSlice &&answer, DcId dc_id, Type type, AuthFlag auth_flag, - GzipFlag gzip_flag, int32 tl_constructor, double total_timeout_limit) + GzipFlag gzip_flag, int32 tl_constructor, double total_timeout_limit, NetQueryStats *stats) : state_(state) , type_(type) , auth_flag_(auth_flag) , gzip_flag_(gzip_flag) , dc_id_(dc_id) - , nq_counter_(true) , status_() , id_(id) , query_(std::move(query)) @@ -361,7 +347,9 @@ class NetQuery : public TsListNode { get_data_unsafe().my_id_ = get_my_id(); get_data_unsafe().start_timestamp_ = Time::now(); LOG(INFO) << *this; - get_net_query_list().put(this); + if (stats) { + nq_counter_ = stats->register_query(this); + } } }; diff --git a/td/telegram/net/NetQueryCreator.cpp b/td/telegram/net/NetQueryCreator.cpp index 85c088458..91d9ae6e6 100644 --- a/td/telegram/net/NetQueryCreator.cpp +++ b/td/telegram/net/NetQueryCreator.cpp @@ -68,7 +68,7 @@ NetQueryPtr NetQueryCreator::create(uint64 id, const telegram_api::Function &fun } } auto query = object_pool_.create(NetQuery::State::Query, id, std::move(slice), BufferSlice(), dc_id, type, auth_flag, - gzip_flag, tl_constructor, total_timeout_limit); + gzip_flag, tl_constructor, total_timeout_limit, net_query_stats_.get()); query->set_cancellation_token(query.generation()); return query; } diff --git a/td/telegram/net/NetQueryCreator.h b/td/telegram/net/NetQueryCreator.h index ba4e6b8fa..069adac5a 100644 --- a/td/telegram/net/NetQueryCreator.h +++ b/td/telegram/net/NetQueryCreator.h @@ -21,7 +21,8 @@ class Function; class NetQueryCreator { public: - NetQueryCreator() { + explicit NetQueryCreator(std::shared_ptr net_query_stats = {}) { + net_query_stats_ = std::move(net_query_stats); object_pool_.set_check_empty(true); } @@ -31,7 +32,8 @@ class NetQueryCreator { NetQueryPtr create_update(BufferSlice &&buffer) { return object_pool_.create(NetQuery::State::OK, 0, BufferSlice(), std::move(buffer), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::Off, 0, 0); + NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::Off, 0, 0, + net_query_stats_.get()); } NetQueryPtr create(const telegram_api::Function &function, DcId dc_id = DcId::main(), @@ -45,6 +47,7 @@ class NetQueryCreator { NetQuery::AuthFlag auth_flag); private: + std::shared_ptr net_query_stats_; ObjectPool object_pool_; }; diff --git a/td/telegram/net/NetQueryStats.cpp b/td/telegram/net/NetQueryStats.cpp new file mode 100644 index 000000000..cfe0260d0 --- /dev/null +++ b/td/telegram/net/NetQueryStats.cpp @@ -0,0 +1,50 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/net/NetQueryStats.h" + +#include "td/telegram/net/NetQuery.h" + +#include "td/utils/logging.h" +#include "td/utils/format.h" + +namespace td { +uint64 NetQueryStats::get_count() const { + return count_; +} + +void NetQueryStats::dump_pending_network_queries() { + auto n = get_count(); + LOG(WARNING) << tag("pending net queries", n); + + if (!use_list_) { + return; + } + decltype(n) i = 0; + bool was_gap = false; + auto &net_query_list = list_; + auto guard = net_query_list.lock(); + for (auto end = net_query_list.end(), cur = net_query_list.begin(); cur != end; cur = cur->get_next(), i++) { + if (i < 20 || i + 20 > n || i % (n / 20 + 1) == 0) { + if (was_gap) { + LOG(WARNING) << "..."; + was_gap = false; + } + const NetQueryDebug &debug = cur->get_data_unsafe(); + const NetQuery &nq = *static_cast(cur); + LOG(WARNING) << tag("user", debug.my_id_) << nq << tag("total flood", format::as_time(nq.total_timeout_)) + << tag("since start", format::as_time(Time::now_cached() - debug.start_timestamp_)) + << tag("state", debug.state_) + << tag("in this state", format::as_time(Time::now_cached() - debug.state_timestamp_)) + << tag("state changed", debug.state_change_count_) << tag("resend count", debug.resend_count_) + << tag("fail count", debug.send_failed_count_) << tag("ack state", debug.ack_state_) + << tag("unknown", debug.unknown_state_); + } else { + was_gap = true; + } + } +} +} // namespace td diff --git a/td/telegram/net/NetQueryStats.h b/td/telegram/net/NetQueryStats.h new file mode 100644 index 000000000..ccbc5edc6 --- /dev/null +++ b/td/telegram/net/NetQueryStats.h @@ -0,0 +1,50 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once +#include "td/telegram/net/NetQueryCounter.h" + +#include "td/utils/int_types.h" +#include "td/utils/TsList.h" + +#include + +namespace td { +struct NetQueryDebug { + double start_timestamp_ = 0; + int32 my_id_ = 0; + int32 resend_count_ = 0; + string state_ = "empty"; + double state_timestamp_ = 0; + int32 state_change_count_ = 0; + int32 send_failed_count_ = 0; + int ack_state_ = 0; + bool unknown_state_ = false; +}; + +class NetQueryStats { + public: + [[deprecated]] static NetQueryStats &get_default_stats() { + static NetQueryStats res; + return res; + } + + NetQueryCounter register_query(TsListNode *query) { + if (use_list_.load(std::memory_order_relaxed)) { + list_.put(query); + } + return NetQueryCounter(&count_); + } + + uint64 get_count() const; + void dump_pending_network_queries(); + + private: + NetQueryCounter::Counter count_; + std::atomic use_list_{true}; + TsList list_; +}; +} // namespace td diff --git a/tdactor/td/actor/SchedulerLocalStorage.h b/tdactor/td/actor/SchedulerLocalStorage.h index 16c0b6a99..61a570873 100644 --- a/tdactor/td/actor/SchedulerLocalStorage.h +++ b/tdactor/td/actor/SchedulerLocalStorage.h @@ -46,6 +46,10 @@ class LazySchedulerLocalStorage { LazySchedulerLocalStorage() = default; explicit LazySchedulerLocalStorage(std::function create_func) : create_func_(std::move(create_func)) { } + void set_create_func(std::function create_func) { + CHECK(!create_func_); + create_func_ = create_func; + } T &get() { auto &optional_value_ = sls_optional_value_.get(); diff --git a/tdutils/td/utils/TsList.h b/tdutils/td/utils/TsList.h index 064f1265b..0d650e609 100644 --- a/tdutils/td/utils/TsList.h +++ b/tdutils/td/utils/TsList.h @@ -202,6 +202,9 @@ class TsList : public TsListNode { template std::unique_lock TsListNode::lock() { + if (parent == nullptr) { + return {}; + } CHECK(parent != nullptr); return parent->lock(); }