diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc index 6b19fabb823cc4..b9875db0314bca 100644 --- a/content/common/child_process_host_impl.cc +++ b/content/common/child_process_host_impl.cc @@ -34,7 +34,6 @@ #include "ipc/ipc_channel_mojo.h" #include "ipc/ipc_logging.h" #include "ipc/message_filter.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h" #include "services/service_manager/public/cpp/interface_provider.h" @@ -126,8 +125,7 @@ ChildProcessHostImpl::ChildProcessHostImpl(ChildProcessHostDelegate* delegate, kChildProcessReceiverAttachmentName), IPC::Channel::MODE_SERVER, this, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), - mojo::internal::MessageQuotaChecker::MaybeCreate()); + base::SingleThreadTaskRunner::GetCurrentDefault()); } else if (ipc_mode_ == IpcMode::kNormal) { child_process_.Bind(mojo::PendingRemote( mojo_invitation_->AttachMessagePipe( @@ -223,8 +221,7 @@ void ChildProcessHostImpl::CreateChannelMojo() { channel_ = IPC::ChannelMojo::Create( std::move(bootstrap), IPC::Channel::MODE_SERVER, this, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), - mojo::internal::MessageQuotaChecker::MaybeCreate()); + base::SingleThreadTaskRunner::GetCurrentDefault()); } DCHECK(channel_); diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc index 11e23861d6a757..a364f0ea41f5f3 100644 --- a/gpu/ipc/client/gpu_channel_host.cc +++ b/gpu/ipc/client/gpu_channel_host.cc @@ -17,7 +17,6 @@ #include "gpu/ipc/common/command_buffer_id.h" #include "gpu/ipc/common/gpu_watchdog_timeout.h" #include "ipc/ipc_channel_mojo.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/sync_call_restrictions.h" #include "url/gurl.h" @@ -215,9 +214,9 @@ void GpuChannelHost::Listener::Initialize( mojo::PendingAssociatedReceiver receiver, scoped_refptr io_task_runner) { base::AutoLock lock(lock_); - channel_ = IPC::ChannelMojo::Create( - std::move(handle), IPC::Channel::MODE_CLIENT, this, io_task_runner, - io_task_runner, mojo::internal::MessageQuotaChecker::MaybeCreate()); + channel_ = + IPC::ChannelMojo::Create(std::move(handle), IPC::Channel::MODE_CLIENT, + this, io_task_runner, io_task_runner); DCHECK(channel_); bool result = channel_->Connect(); DCHECK(result); diff --git a/ipc/ipc_channel_common.cc b/ipc/ipc_channel_common.cc index f3597d3b8202d6..9eb94f4bcd4979 100644 --- a/ipc/ipc_channel_common.cc +++ b/ipc/ipc_channel_common.cc @@ -6,7 +6,6 @@ #include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_mojo.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/system/message_pipe.h" namespace IPC { @@ -41,8 +40,7 @@ std::unique_ptr Channel::CreateClient( return ChannelMojo::Create( mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle), Channel::MODE_CLIENT, listener, ipc_task_runner, - base::SingleThreadTaskRunner::GetCurrentDefault(), - mojo::internal::MessageQuotaChecker::MaybeCreate()); + base::SingleThreadTaskRunner::GetCurrentDefault()); #endif } @@ -58,8 +56,7 @@ std::unique_ptr Channel::CreateServer( return ChannelMojo::Create( mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle), Channel::MODE_SERVER, listener, ipc_task_runner, - base::SingleThreadTaskRunner::GetCurrentDefault(), - mojo::internal::MessageQuotaChecker::MaybeCreate()); + base::SingleThreadTaskRunner::GetCurrentDefault()); #endif } diff --git a/ipc/ipc_channel_factory.cc b/ipc/ipc_channel_factory.cc index 7f683632e053de..a24bd676937332 100644 --- a/ipc/ipc_channel_factory.cc +++ b/ipc/ipc_channel_factory.cc @@ -8,7 +8,6 @@ #include "base/task/single_thread_task_runner.h" #include "build/build_config.h" #include "ipc/ipc_channel_mojo.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" namespace IPC { @@ -20,10 +19,7 @@ class PlatformChannelFactory : public ChannelFactory { ChannelHandle handle, Channel::Mode mode, const scoped_refptr& ipc_task_runner) - : handle_(handle), - mode_(mode), - ipc_task_runner_(ipc_task_runner), - quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {} + : handle_(handle), mode_(mode), ipc_task_runner_(ipc_task_runner) {} PlatformChannelFactory(const PlatformChannelFactory&) = delete; PlatformChannelFactory& operator=(const PlatformChannelFactory&) = delete; @@ -35,8 +31,7 @@ class PlatformChannelFactory : public ChannelFactory { DCHECK(handle_.is_mojo_channel_handle()); return ChannelMojo::Create( mojo::ScopedMessagePipeHandle(handle_.mojo_handle), mode_, listener, - ipc_task_runner_, base::SingleThreadTaskRunner::GetCurrentDefault(), - quota_checker_); + ipc_task_runner_, base::SingleThreadTaskRunner::GetCurrentDefault()); #endif } @@ -44,16 +39,10 @@ class PlatformChannelFactory : public ChannelFactory { return ipc_task_runner_; } - scoped_refptr GetQuotaChecker() - override { - return quota_checker_; - } - private: ChannelHandle handle_; Channel::Mode mode_; scoped_refptr ipc_task_runner_; - scoped_refptr quota_checker_; }; } // namespace diff --git a/ipc/ipc_channel_factory.h b/ipc/ipc_channel_factory.h index 5da6131956716d..0ed4606b81380e 100644 --- a/ipc/ipc_channel_factory.h +++ b/ipc/ipc_channel_factory.h @@ -14,12 +14,6 @@ #include "base/task/single_thread_task_runner.h" #include "ipc/ipc_channel.h" -namespace mojo { -namespace internal { -class MessageQuotaChecker; -} // namespace internal -} // namespace mojo - namespace IPC { // Encapsulates how a Channel is created. A ChannelFactory can be @@ -37,8 +31,6 @@ class COMPONENT_EXPORT(IPC) ChannelFactory { virtual ~ChannelFactory() { } virtual std::unique_ptr BuildChannel(Listener* listener) = 0; virtual scoped_refptr GetIPCTaskRunner() = 0; - virtual scoped_refptr - GetQuotaChecker() = 0; }; } // namespace IPC diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc index d6c78468aa5937..974b96fcf33954 100644 --- a/ipc/ipc_channel_mojo.cc +++ b/ipc/ipc_channel_mojo.cc @@ -30,7 +30,6 @@ #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/thread_safe_proxy.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -49,33 +48,25 @@ class MojoChannelFactory : public ChannelFactory { : handle_(std::move(handle)), mode_(mode), ipc_task_runner_(ipc_task_runner), - proxy_task_runner_(proxy_task_runner), - quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {} + proxy_task_runner_(proxy_task_runner) {} MojoChannelFactory(const MojoChannelFactory&) = delete; MojoChannelFactory& operator=(const MojoChannelFactory&) = delete; std::unique_ptr BuildChannel(Listener* listener) override { return ChannelMojo::Create(std::move(handle_), mode_, listener, - ipc_task_runner_, proxy_task_runner_, - quota_checker_); + ipc_task_runner_, proxy_task_runner_); } scoped_refptr GetIPCTaskRunner() override { return ipc_task_runner_; } - scoped_refptr GetQuotaChecker() - override { - return quota_checker_; - } - private: mojo::ScopedMessagePipeHandle handle_; const Channel::Mode mode_; scoped_refptr ipc_task_runner_; scoped_refptr proxy_task_runner_; - scoped_refptr quota_checker_; }; class ThreadSafeChannelProxy : public mojo::ThreadSafeProxy { @@ -135,11 +126,9 @@ std::unique_ptr ChannelMojo::Create( Mode mode, Listener* listener, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker) { + const scoped_refptr& proxy_task_runner) { return base::WrapUnique(new ChannelMojo(std::move(handle), mode, listener, - ipc_task_runner, proxy_task_runner, - quota_checker)); + ipc_task_runner, proxy_task_runner)); } // static @@ -167,12 +156,11 @@ ChannelMojo::ChannelMojo( Mode mode, Listener* listener, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker) + const scoped_refptr& proxy_task_runner) : task_runner_(ipc_task_runner), pipe_(handle.get()), listener_(listener) { weak_ptr_ = weak_factory_.GetWeakPtr(); bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner, - proxy_task_runner, quota_checker); + proxy_task_runner); } void ChannelMojo::ForwardMessage(mojo::Message message) { diff --git a/ipc/ipc_channel_mojo.h b/ipc/ipc_channel_mojo.h index 66f2e5e5923393..4eb44e014b746b 100644 --- a/ipc/ipc_channel_mojo.h +++ b/ipc/ipc_channel_mojo.h @@ -49,8 +49,7 @@ class COMPONENT_EXPORT(IPC) ChannelMojo Mode mode, Listener* listener, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker); + const scoped_refptr& proxy_task_runner); // Create a factory object for ChannelMojo. // The factory is used to create Mojo-based ChannelProxy family. @@ -102,8 +101,7 @@ class COMPONENT_EXPORT(IPC) ChannelMojo Mode mode, Listener* listener, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker); + const scoped_refptr& proxy_task_runner); void ForwardMessage(mojo::Message message); diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc index fe3dd502cdc4b0..6073ff6b917e5e 100644 --- a/ipc/ipc_channel_proxy.cc +++ b/ipc/ipc_channel_proxy.cc @@ -224,9 +224,6 @@ void ChannelProxy::Context::Clear() { // Called on the IPC::Channel thread void ChannelProxy::Context::OnSendMessage(std::unique_ptr message) { - if (quota_checker_) - quota_checker_->AfterMessagesDequeued(1); - if (!channel_) { OnChannelClosed(); return; @@ -422,9 +419,6 @@ void ChannelProxy::Context::AddGenericAssociatedInterfaceForIOThread( } void ChannelProxy::Context::Send(Message* message) { - if (quota_checker_) - quota_checker_->BeforeMessagesEnqueued(1); - ipc_task_runner()->PostTask( FROM_HERE, base::BindOnce(&ChannelProxy::Context::OnSendMessage, this, base::WrapUnique(message))); @@ -503,9 +497,6 @@ void ChannelProxy::Init(std::unique_ptr factory, DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!did_init_); - DCHECK(!context_->quota_checker_); - context_->quota_checker_ = factory->GetQuotaChecker(); - if (create_pipe_now) { // Create the channel immediately. This effectively sets up the // low-level pipe so that the client can connect. Without creating diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h index 58a6937b0de301..c9be29e9519656 100644 --- a/ipc/ipc_channel_proxy.h +++ b/ipc/ipc_channel_proxy.h @@ -27,7 +27,6 @@ #include "ipc/ipc_sender.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" @@ -368,9 +367,6 @@ class COMPONENT_EXPORT(IPC) ChannelProxy : public Sender { std::unique_ptr channel_; bool channel_connected_called_; - // The quota checker associated with this channel, if any. - scoped_refptr quota_checker_; - // Lock for |channel_| value. This is only relevant in the context of // thread-safe send. base::Lock channel_lifetime_lock_; diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc index d68f1a3c02fd53..6ecb81b6e6a02c 100644 --- a/ipc/ipc_mojo_bootstrap.cc +++ b/ipc/ipc_mojo_bootstrap.cc @@ -140,11 +140,9 @@ class ChannelAssociatedGroupController ChannelAssociatedGroupController( bool set_interface_id_namespace_bit, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker) + const scoped_refptr& proxy_task_runner) : task_runner_(ipc_task_runner), proxy_task_runner_(proxy_task_runner), - quota_checker_(quota_checker), set_interface_id_namespace_bit_(set_interface_id_namespace_bit), dispatcher_(this), control_message_handler_(this), @@ -202,8 +200,6 @@ class ChannelAssociatedGroupController base::AutoLock lock(outgoing_messages_lock_); std::swap(outgoing_messages, outgoing_messages_); } - if (quota_checker_ && outgoing_messages.size()) - quota_checker_->AfterMessagesDequeued(outgoing_messages.size()); for (auto& message : outgoing_messages) SendMessage(&message); @@ -220,8 +216,6 @@ class ChannelAssociatedGroupController base::BindOnce(&ChannelAssociatedGroupController::OnPipeError, base::Unretained(this))); connector_->set_enforce_errors_from_incoming_receiver(false); - if (quota_checker_) - connector_->SetMessageQuotaChecker(quota_checker_); // Don't let the Connector do any sort of queuing on our behalf. Individual // messages bound for the IPC::ChannelProxy thread (i.e. that vast majority @@ -272,9 +266,6 @@ class ChannelAssociatedGroupController connector_.reset(); base::AutoLock lock(outgoing_messages_lock_); - if (quota_checker_ && outgoing_messages_.size()) - quota_checker_->AfterMessagesDequeued(outgoing_messages_.size()); - outgoing_messages_.clear(); } @@ -821,8 +812,6 @@ class ChannelAssociatedGroupController if (!connector_ || paused_) { if (!shut_down_) { base::AutoLock lock(outgoing_messages_lock_); - if (quota_checker_) - quota_checker_->BeforeMessagesEnqueued(1); outgoing_messages_.emplace_back(std::move(*message)); } return true; @@ -1163,7 +1152,6 @@ class ChannelAssociatedGroupController scoped_refptr task_runner_; const scoped_refptr proxy_task_runner_; - const scoped_refptr quota_checker_; const bool set_interface_id_namespace_bit_; bool paused_ = false; std::unique_ptr connector_; @@ -1292,12 +1280,11 @@ std::unique_ptr MojoBootstrap::Create( mojo::ScopedMessagePipeHandle handle, Channel::Mode mode, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker) { + const scoped_refptr& proxy_task_runner) { return std::make_unique( - std::move(handle), base::MakeRefCounted( - mode == Channel::MODE_SERVER, ipc_task_runner, - proxy_task_runner, quota_checker)); + std::move(handle), + base::MakeRefCounted( + mode == Channel::MODE_SERVER, ipc_task_runner, proxy_task_runner)); } } // namespace IPC diff --git a/ipc/ipc_mojo_bootstrap.h b/ipc/ipc_mojo_bootstrap.h index 43156e541d5ba4..b432e448303637 100644 --- a/ipc/ipc_mojo_bootstrap.h +++ b/ipc/ipc_mojo_bootstrap.h @@ -18,7 +18,6 @@ #include "ipc/ipc_listener.h" #include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/associated_remote.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -102,8 +101,7 @@ class COMPONENT_EXPORT(IPC) MojoBootstrap { mojo::ScopedMessagePipeHandle handle, Channel::Mode mode, const scoped_refptr& ipc_task_runner, - const scoped_refptr& proxy_task_runner, - const scoped_refptr& quota_checker); + const scoped_refptr& proxy_task_runner); // Initialize the Channel pipe and interface endpoints. This performs all // setup except actually starting to read messages off the pipe. diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc index 723dfc28ad9ba0..8771bcdd69041d 100644 --- a/ipc/ipc_mojo_bootstrap_unittest.cc +++ b/ipc/ipc_mojo_bootstrap_unittest.cc @@ -118,13 +118,12 @@ class IPCMojoBootstrapTest : public testing::Test { TEST_F(IPCMojoBootstrapTest, Connect) { base::test::SingleThreadTaskEnvironment task_environment; - Connection connection( - IPC::MojoBootstrap::Create( - helper_.StartChild("IPCMojoBootstrapTestClient"), - IPC::Channel::MODE_SERVER, - base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr), - kTestServerPid); + Connection connection(IPC::MojoBootstrap::Create( + helper_.StartChild("IPCMojoBootstrapTestClient"), + IPC::Channel::MODE_SERVER, + base::SingleThreadTaskRunner::GetCurrentDefault(), + base::SingleThreadTaskRunner::GetCurrentDefault()), + kTestServerPid); mojo::PendingAssociatedReceiver receiver; connection.TakeReceiver(&receiver); @@ -149,7 +148,7 @@ MULTIPROCESS_TEST_MAIN_WITH_SETUP( std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe), IPC::Channel::MODE_CLIENT, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr), + base::SingleThreadTaskRunner::GetCurrentDefault()), kTestClientPid); mojo::PendingAssociatedReceiver receiver; @@ -171,7 +170,7 @@ TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) { helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"), IPC::Channel::MODE_SERVER, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr), + base::SingleThreadTaskRunner::GetCurrentDefault()), kTestServerPid); mojo::PendingAssociatedReceiver receiver; @@ -199,7 +198,7 @@ MULTIPROCESS_TEST_MAIN_WITH_SETUP( std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe), IPC::Channel::MODE_CLIENT, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr), + base::SingleThreadTaskRunner::GetCurrentDefault()), kTestClientPid); mojo::PendingAssociatedReceiver receiver; diff --git a/ipc/ipc_test_base.cc b/ipc/ipc_test_base.cc index 865d6cd54f1249..49dc73842de2ec 100644 --- a/ipc/ipc_test_base.cc +++ b/ipc/ipc_test_base.cc @@ -33,7 +33,7 @@ void IPCChannelMojoTestBase::CreateChannel(IPC::Listener* listener) { channel_ = IPC::ChannelMojo::Create( TakeHandle(), IPC::Channel::MODE_SERVER, listener, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr); + base::SingleThreadTaskRunner::GetCurrentDefault()); } bool IPCChannelMojoTestBase::ConnectChannel() { @@ -60,7 +60,7 @@ void IpcChannelMojoTestClient::Connect(IPC::Listener* listener) { channel_ = IPC::ChannelMojo::Create( std::move(handle_), IPC::Channel::MODE_CLIENT, listener, base::SingleThreadTaskRunner::GetCurrentDefault(), - base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr); + base::SingleThreadTaskRunner::GetCurrentDefault()); CHECK(channel_->Connect()); } diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn index c080f1afc48576..aa7a4e45580b4a 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn @@ -169,8 +169,6 @@ component("bindings") { "lib/interface_ptr_state.h", "lib/interface_serialization.h", "lib/message_dispatcher.cc", - "lib/message_quota_checker.cc", - "lib/message_quota_checker.h", "lib/multiplex_router.cc", "lib/multiplex_router.h", "lib/native_enum_data.h", diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h index 4bc9f721046a13..a35ba18dc3f6f8 100644 --- a/mojo/public/cpp/bindings/connector.h +++ b/mojo/public/cpp/bindings/connector.h @@ -30,9 +30,6 @@ class Lock; } namespace mojo { -namespace internal { -class MessageQuotaChecker; -} class SyncHandleWatcher; @@ -224,10 +221,6 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { base::SequencedTaskRunner* task_runner() const { return task_runner_.get(); } - // Sets the quota checker. - void SetMessageQuotaChecker( - scoped_refptr checker); - // Allows testing environments to override the default serialization behavior // of newly constructed Connector instances. Must be called before any // Connector instances are constructed. @@ -334,9 +327,6 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { SEQUENCE_CHECKER(sequence_checker_); - // The quota checker associate with this connector, if any. - scoped_refptr quota_checker_; - // Indicates whether the Connector is configured to actively read from its // message pipe. As long as this is true, the Connector is only safe to // destroy in sequence with `task_runner_` tasks. diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc index b69c53d5f6fc18..5835675ff84592 100644 --- a/mojo/public/cpp/bindings/lib/connector.cc +++ b/mojo/public/cpp/bindings/lib/connector.cc @@ -28,7 +28,6 @@ #include "mojo/public/c/system/quota.h" #include "mojo/public/cpp/bindings/features.h" #include "mojo/public/cpp/bindings/lib/may_auto_lock.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/mojo_buildflags.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" #include "mojo/public/cpp/bindings/tracing_helpers.h" @@ -182,13 +181,6 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, } Connector::~Connector() { - if (quota_checker_) { - // Clear the message pipe handle in the checker. - quota_checker_->SetMessagePipe(MessagePipeHandle()); - UMA_HISTOGRAM_COUNTS_1M("Mojo.Connector.MaxUnreadMessageQuotaUsed", - quota_checker_->GetMaxQuotaUsage()); - } - if (is_receiving_) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CancelWait(); @@ -351,9 +343,6 @@ bool Connector::Accept(Message* message) { } } - if (quota_checker_) - quota_checker_->BeforeWrite(); - MojoResult rv = WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(), MOJO_WRITE_MESSAGE_FLAG_NONE); @@ -398,14 +387,6 @@ void Connector::AllowWokenUpBySyncWatchOnSameThread() { sync_watcher_->AllowWokenUpBySyncWatchOnSameThread(); } -void Connector::SetMessageQuotaChecker( - scoped_refptr checker) { - DCHECK(checker && !quota_checker_); - - quota_checker_ = std::move(checker); - quota_checker_->SetMessagePipe(message_pipe_.get()); -} - // static void Connector::OverrideDefaultSerializationBehaviorForTesting( OutgoingSerializationMode outgoing_mode, diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.cc b/mojo/public/cpp/bindings/lib/message_quota_checker.cc deleted file mode 100644 index 8921b41146873e..00000000000000 --- a/mojo/public/cpp/bindings/lib/message_quota_checker.cc +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" - -#include - -#include "base/check_op.h" -#include "base/debug/activity_tracker.h" -#include "base/debug/alias.h" -#include "base/debug/dump_without_crashing.h" -#include "base/memory/scoped_refptr.h" -#include "base/metrics/field_trial_params.h" -#include "base/rand_util.h" -#include "base/synchronization/lock.h" -#include "base/types/pass_key.h" -#include "mojo/public/c/system/quota.h" -#include "mojo/public/cpp/bindings/features.h" -#include "mojo/public/cpp/bindings/mojo_buildflags.h" - -namespace mojo { -namespace internal { -namespace { - -const base::FeatureParam kMojoRecordUnreadMessageCountSampleRate = { - &features::kMojoRecordUnreadMessageCount, "SampleRate", - 100 // Sample 1% of Connectors by default. */ -}; - -const base::FeatureParam kMojoRecordUnreadMessageCountQuotaValue = { - &features::kMojoRecordUnreadMessageCount, "QuotaValue", - 100 // Use a 100 message quote by default. -}; - -const base::FeatureParam kMojoRecordUnreadMessageCountCrashThreshold = { - &features::kMojoRecordUnreadMessageCount, "CrashThreshold", - 0 // Set to zero to disable crash dumps by default. -}; - -NOINLINE void MaybeDumpWithoutCrashing( - size_t total_quota_used, - absl::optional message_pipe_quota_used, - int64_t seconds_since_construction, - double average_write_rate, - uint64_t messages_enqueued, - uint64_t messages_dequeued, - uint64_t messages_written) { - static bool have_crashed = false; - if (have_crashed) - return; - - // Only crash once per process/per run. Note that this is slightly racy - // against concurrent quota overruns on multiple threads, but that's fine. - have_crashed = true; - - size_t local_quota_used = total_quota_used; - bool had_message_pipe = false; - if (message_pipe_quota_used.has_value()) { - had_message_pipe = true; - local_quota_used -= message_pipe_quota_used.value(); - } - - // Normalize the write rate to writes/second. - double average_write_rate_per_second = - average_write_rate / - MessageQuotaChecker::DecayingRateAverage::kSamplingInterval.InSecondsF(); - base::debug::Alias(&total_quota_used); - base::debug::Alias(&local_quota_used); - base::debug::Alias(&had_message_pipe); - base::debug::Alias(&seconds_since_construction); - base::debug::Alias(&average_write_rate_per_second); - - // Note that these values are acquired non-atomically with respect to the - // variables above, and so may have increased since the quota overflow - // occurred. They will still give a good indication of the traffic and the - // traffic mix on this MessageQuotaChecker. - base::debug::Alias(&messages_enqueued); - base::debug::Alias(&messages_dequeued); - base::debug::Alias(&messages_written); - - // Also record the data for extended crash reporting. - base::debug::ScopedActivity scoped_activity; - auto& user_data = scoped_activity.user_data(); - user_data.SetUint("total_quota_used", total_quota_used); - user_data.SetUint("local_quota_used", local_quota_used); - user_data.SetBool("had_message_pipe", had_message_pipe); - user_data.SetUint("seconds_since_construction", seconds_since_construction); - user_data.SetUint("average_write_rate_per_second", - static_cast(average_write_rate_per_second)); - user_data.SetUint("messages_enqueued", messages_enqueued); - user_data.SetUint("messages_dequeued", messages_dequeued); - user_data.SetUint("messages_enqueued", messages_enqueued); - user_data.SetUint("messages_written", messages_written); - - // This is happening because the user of the interface implicated on the crash - // stack has queued up an unreasonable number of messages, namely - // |total_quota_used|. - base::debug::DumpWithoutCrashing(); -} - -int64_t ToSamplingInterval(base::TimeTicks when) { - return (when - base::TimeTicks::UnixEpoch()) - .IntDiv(MessageQuotaChecker::DecayingRateAverage::kSamplingInterval); -} - -base::TimeTicks FromSamplingInterval(int64_t sampling_interval) { - return MessageQuotaChecker::DecayingRateAverage::kSamplingInterval * - sampling_interval + - base::TimeTicks::UnixEpoch(); -} - -} // namespace - -constexpr base::TimeDelta - MessageQuotaChecker::DecayingRateAverage::kSamplingInterval; -constexpr double MessageQuotaChecker::DecayingRateAverage::kSampleWeight; - -// static -scoped_refptr MessageQuotaChecker::MaybeCreate() { - static const Configuration config = GetConfiguration(); - return MaybeCreateImpl(config); -} - -void MessageQuotaChecker::BeforeWrite() { - ++messages_written_; - QuotaCheckImpl(0u); -} - -void MessageQuotaChecker::BeforeMessagesEnqueued(size_t num) { - DCHECK_NE(num, 0u); - messages_enqueued_ += num; - QuotaCheckImpl(num); -} - -void MessageQuotaChecker::AfterMessagesDequeued(size_t num) { - base::AutoLock hold(lock_); - DCHECK_LE(num, consumed_quota_); - DCHECK_NE(num, 0u); - messages_dequeued_ += num; - consumed_quota_ -= num; -} - -size_t MessageQuotaChecker::GetMaxQuotaUsage() { - base::AutoLock hold(lock_); - return max_consumed_quota_; -} - -void MessageQuotaChecker::SetMessagePipe(MessagePipeHandle message_pipe) { - base::AutoLock hold(lock_); - message_pipe_ = message_pipe; - if (!message_pipe_) - return; - - MojoResult rv = - MojoSetQuota(message_pipe.value(), MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, - config_->unread_message_count_quota, nullptr); - DCHECK_EQ(MOJO_RESULT_OK, rv); -} - -size_t MessageQuotaChecker::GetCurrentQuotaStatusForTesting() { - base::AutoLock hold(lock_); - size_t quota_used = consumed_quota_; - absl::optional message_pipe_quota_used = GetCurrentMessagePipeQuota(); - if (message_pipe_quota_used.has_value()) - quota_used += message_pipe_quota_used.value(); - - return quota_used; -} - -// static -MessageQuotaChecker::Configuration -MessageQuotaChecker::GetConfigurationForTesting() { - return GetConfiguration(); -} - -// static -scoped_refptr MessageQuotaChecker::MaybeCreateForTesting( - const Configuration& config) { - return MaybeCreateImpl(config); -} - -MessageQuotaChecker::MessageQuotaChecker(const Configuration* config, - base::PassKey) - : config_(config), creation_time_(base::TimeTicks::Now()) {} -MessageQuotaChecker::~MessageQuotaChecker() = default; - -// static -MessageQuotaChecker::Configuration MessageQuotaChecker::GetConfiguration() { - // Const since this may be called from any thread. Initialization is - // thread-safe. This is a workaround since some consumers of Mojo (e.g. many - // browser tests) use base::FeatureList incorrectly and thus cause data races - // when features are queried from arbitrary threads. - static const bool is_enabled = - base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount); - - Configuration ret; - ret.is_enabled = is_enabled; - ret.sample_rate = kMojoRecordUnreadMessageCountSampleRate.Get(); - - // Lower-bound the quota value to 100, which implies roughly 2% message - // overhead for sampled pipes. - constexpr int kMinQuotaValue = 100; - ret.unread_message_count_quota = - std::max(kMinQuotaValue, kMojoRecordUnreadMessageCountQuotaValue.Get()); - ret.crash_threshold = kMojoRecordUnreadMessageCountCrashThreshold.Get(); - ret.maybe_crash_function = &MaybeDumpWithoutCrashing; - return ret; -} - -// static -scoped_refptr MessageQuotaChecker::MaybeCreateImpl( - const Configuration& config) { - if (!config.is_enabled) - return nullptr; - - if (base::RandInt(0, config.sample_rate - 1) != 0) - return nullptr; - - return base::MakeRefCounted( - &config, base::PassKey()); -} - -absl::optional MessageQuotaChecker::GetCurrentMessagePipeQuota() { - lock_.AssertAcquired(); - - if (!message_pipe_) - return absl::nullopt; - - uint64_t limit = 0; - uint64_t usage = 0; - MojoResult rv = MojoQueryQuota(message_pipe_.value(), - MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, - &limit, &usage); - DCHECK_NE(MOJO_QUOTA_LIMIT_NONE, limit); - return rv == MOJO_RESULT_OK ? usage : 0u; -} - -void MessageQuotaChecker::QuotaCheckImpl(size_t num_enqueued) { - bool new_max = false; - - // By the time a crash is reported, another thread might have consumed some of - // the locally queued messages, and/or the message pipe might have been unset. - // To make the crash reports as useful as possible, grab the state of the - // local and the message pipe queues into individual variables, then pass them - // into the crashing function. - size_t total_quota_used = 0u; - base::TimeTicks now; - double average_write_rate = 0.0; - absl::optional message_pipe_quota_used; - { - base::AutoLock hold(lock_); - - message_pipe_quota_used = GetCurrentMessagePipeQuota(); - now = base::TimeTicks::Now(); - if (num_enqueued) { - consumed_quota_ += num_enqueued; - } else { - // BeforeWrite passes num_enqueued zero, as the message won't be locally - // enqueued. The assumption is that there's already a message pipe in - // play, and that the caller is keeping it alive somehow. - DCHECK(message_pipe_); - DCHECK(message_pipe_quota_used.has_value()); - - // Accrue this write event to the write rate average. - write_rate_average_.AccrueEvent(now); - - // Account for the message about to be written to the message pipe in the - // the full tally. - ++message_pipe_quota_used.value(); - } - - total_quota_used += consumed_quota_; - if (message_pipe_quota_used.has_value()) - total_quota_used += message_pipe_quota_used.value(); - - if (total_quota_used > max_consumed_quota_) { - max_consumed_quota_ = total_quota_used; - new_max = true; - // Retrieve the average rate, in the case that a crash is imminent. - average_write_rate = write_rate_average_.GetDecayedRateAverage(now); - } - } - - if (new_max && config_->crash_threshold != 0 && - total_quota_used >= config_->crash_threshold) { - DCHECK(!now.is_null()); - int64_t seconds_since_construction = (now - creation_time_).InSeconds(); - config_->maybe_crash_function( - total_quota_used, message_pipe_quota_used, seconds_since_construction, - average_write_rate, messages_enqueued_.load(), - messages_dequeued_.load(), messages_written_.load()); - } -} - -MessageQuotaChecker::DecayingRateAverage::DecayingRateAverage() { - events_sampling_interval_ = ToSamplingInterval(base::TimeTicks::Now()); -} - -void MessageQuotaChecker::DecayingRateAverage::AccrueEvent( - base::TimeTicks when) { - const int64_t sampling_interval = ToSamplingInterval(when); - DCHECK_GE(sampling_interval, events_sampling_interval_); - if (sampling_interval == events_sampling_interval_) { - // The time is still in the sampling interval, just add the event. - ++events_; - return; - } - DCHECK_GT(sampling_interval, events_sampling_interval_); - - // Add the new sample and decay it to the previous event sampling interval. - decayed_average_ = events_ * kSampleWeight + decayed_average_ * kDecayFactor; - - // Decay the average to the current sampling interval - 1. - const int64_t decayed_average_age = - sampling_interval - events_sampling_interval_ - 1; - if (decayed_average_age) - decayed_average_ *= pow(kDecayFactor, decayed_average_age); - - // Start a new event sampling interval. - events_ = 1; - events_sampling_interval_ = sampling_interval; -} - -double MessageQuotaChecker::DecayingRateAverage::GetDecayedRateAverage( - base::TimeTicks when) const { - // Three cases: - // - |when| is exactly at the start of a sampling interval. - // - |when| is within the current sampling interval. - // - |when| is beyond the end of the current sampling interval. - const int64_t sampling_interval = ToSamplingInterval(when); - double age_in_sampling_intervals = - (when - FromSamplingInterval(events_sampling_interval_)) / - kSamplingInterval; - DCHECK_LE(0.0, age_in_sampling_intervals); - if (when == FromSamplingInterval(events_sampling_interval_)) { - DCHECK_EQ(0.0, age_in_sampling_intervals); - // It is possible that an event has been accrued to the very start of a - // sampling interval. Technically this converges like so: - // - // lim when t -> 0 = - events_ * log(kDecayFactor) / kSamplingInterval - // - // For simplicity's sake, this is treated as synonymous with the decayed - // average at the end of the previous sampling interval here. - return decayed_average_; - } else if (sampling_interval == events_sampling_interval_) { - DCHECK_GT(1.0, age_in_sampling_intervals); - - // Use a decay factor that's exponential in the age |when|, relative to - // the sampling interval. This yields a stabler estimate than straight up - // extrapolating the rate to the end of the sampling interval, as that - // method is very sensitive to noise in sample timing near zero age. - double decay_factor = pow(kDecayFactor, age_in_sampling_intervals); - // Scale up the events collected so far to the rate. - double event_rate = events_ / age_in_sampling_intervals; - - return event_rate * (1.0 - decay_factor) + decayed_average_ * decay_factor; - } else { - DCHECK_LE(1.0, age_in_sampling_intervals); - // Compute the decayed average to the start of - // events_sampling_interval_ + 1. - double average = events_ * kSampleWeight + decayed_average_ * kDecayFactor; - - // And age it to |when|. - return average * pow(kDecayFactor, age_in_sampling_intervals - 1.0); - } -} - -// static -base::TimeTicks -MessageQuotaChecker::DecayingRateAverage::GetNextSamplingIntervalForTesting( - base::TimeTicks when) { - return FromSamplingInterval(ToSamplingInterval(when) + 1); -} - -} // namespace internal -} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.h b/mojo/public/cpp/bindings/lib/message_quota_checker.h deleted file mode 100644 index 3e1752c824dc15..00000000000000 --- a/mojo/public/cpp/bindings/lib/message_quota_checker.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_ - -#include -#include - -#include "base/component_export.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/thread_annotations.h" -#include "base/time/time.h" -#include "base/types/pass_key.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "third_party/abseil-cpp/absl/types/optional.h" - -namespace mojo { -namespace internal { - -// This class keeps track of how many messages are in-flight for a message pipe, -// including messages that are posted or locally queued. -// -// Message pipe owners may have reason to implement their own mechanism for -// queuing outgoing messages before writing them to a pipe. This class helps -// with unread message quota monitoring in such cases, since Mojo's own -// quota monitoring on the pipe cannot account for such external queues. -// Callers are responsible for invoking |BeforeMessagesEnqueued()| and -// |AfterMessagesDequeued()| when making respective changes to their local -// outgoing queue. Additionally, |BeforeWrite()| should be called immediately -// before writing each message to the corresponding message pipe. -// -// Also note that messages posted to a different sequence with base::ThreadPool -// and the like, need to be treated as locally queued. Task queues can grow -// arbitrarily long, and it's ideal to perform unread quota checks before -// posting. -// -// Either |BeforeMessagesEnqueued()| or |BeforeWrite()| may cause the quota -// to be exceeded, thus invoking the |maybe_crash_function| set in this -// object's Configuration. -class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) MessageQuotaChecker - : public base::RefCountedThreadSafe { - public: - // A helper class to maintain a decaying average for the rate of events per - // sampling interval over time. - class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) DecayingRateAverage { - public: - DecayingRateAverage(); - - // Accrues one event at time |when|. Note that |when| must increase - // monotonically from one event to the next. - void AccrueEvent(base::TimeTicks when); - // Retrieves the current rate average, decayed to |when|. - double GetDecayedRateAverage(base::TimeTicks when) const; - - // The length of a sampling interval in seconds. - static constexpr base::TimeDelta kSamplingInterval = base::Seconds(5); - - // Returns the start of the sampling interval after the interval that - // |when| falls into. - static base::TimeTicks GetNextSamplingIntervalForTesting( - base::TimeTicks when); - - private: - // A new sample is weighed at this rate into the average, whereas the old - // average is weighed at kDecayFactor^age; Note that - // (kSampleWeight + kDecayFactor) == 1.0. - static constexpr double kSampleWeight = 0.5; - static constexpr double kDecayFactor = (1 - kSampleWeight); - - // The event count for the current or most recent sampling interval and - // the ordinal sampling interval they correspond to. - size_t events_ = 0; - int64_t events_sampling_interval_; - - // The so-far accrued average to |events_sampling_interval_|. - double decayed_average_ = 0.0; - }; - - // Exposed for use in tests. - struct Configuration; - - // Returns a new instance if this invocation has been sampled for quota - // checking. - static scoped_refptr MaybeCreate(); - - // Public for base::MakeRefCounted(). Use MaybeCreate(). - MessageQuotaChecker(const Configuration* config, - base::PassKey); - - // Call before writing a message to |message_pipe_|. - void BeforeWrite(); - - // Call before queueing |num| messages. - void BeforeMessagesEnqueued(size_t num); - // Call after de-queueing |num| messages. - void AfterMessagesDequeued(size_t num); - - // Returns the high watermark of quota usage observed by this instance. - size_t GetMaxQuotaUsage(); - - // Set or unset the message pipe associated with this quota checker. - void SetMessagePipe(MessagePipeHandle message_pipe); - - // Test support. - size_t GetCurrentQuotaStatusForTesting(); - static Configuration GetConfigurationForTesting(); - static scoped_refptr MaybeCreateForTesting( - const Configuration& config); - - private: - friend class base::RefCountedThreadSafe; - ~MessageQuotaChecker(); - static Configuration GetConfiguration(); - static scoped_refptr MaybeCreateImpl( - const Configuration& config); - - // Returns the amount of unread message quota currently used if there is - // an associated message pipe. - absl::optional GetCurrentMessagePipeQuota(); - void QuotaCheckImpl(size_t num_enqueued); - - raw_ptr config_; - - // The time ticks when this instance was created. - const base::TimeTicks creation_time_; - - - // Cumulative counts for the number of messages enqueued with - // |BeforeMessagesEnqueued()| and dequeued with |BeforeMessagesDequeued()|. - std::atomic messages_enqueued_{0}; - std::atomic messages_dequeued_{0}; - std::atomic messages_written_{0}; - - // Guards all state below here. - base::Lock lock_; - - // A decaying average of the rate of call to BeforeWrite per second. - DecayingRateAverage write_rate_average_ GUARDED_BY(lock_); - - // The locally consumed quota, e.g. the difference between the counts passed - // to |BeforeMessagesEnqueued()| and |BeforeMessagesDequeued()|. - size_t consumed_quota_ GUARDED_BY(lock_) = 0u; - // The high watermark consumed quota observed. - size_t max_consumed_quota_ GUARDED_BY(lock_) = 0u; - // The message pipe this instance observes, if any. - MessagePipeHandle message_pipe_ GUARDED_BY(lock_); -}; - -struct MessageQuotaChecker::Configuration { - bool is_enabled = false; - size_t sample_rate = 0u; - size_t unread_message_count_quota = 0u; - size_t crash_threshold = 0u; - void (*maybe_crash_function)(size_t quota_used, - absl::optional message_pipe_quota_used, - int64_t seconds_since_construction, - double average_write_rate, - uint64_t messages_enqueued, - uint64_t messages_dequeued, - uint64_t messages_written); -}; - -} // namespace internal -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_ diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc index d8dd91b88100ea..1ca2bbd0b6dc4e 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc @@ -22,7 +22,6 @@ #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/interface_endpoint_controller.h" #include "mojo/public/cpp/bindings/lib/may_auto_lock.h" -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h" namespace mojo { @@ -388,11 +387,6 @@ MultiplexRouter::MultiplexRouter( connector_.set_incoming_receiver(&dispatcher_); - scoped_refptr quota_checker = - internal::MessageQuotaChecker::MaybeCreate(); - if (quota_checker) - connector_.SetMessageQuotaChecker(std::move(quota_checker)); - if (primary_interface_name) { control_message_handler_.SetDescription(base::JoinString( {primary_interface_name, "[primary] PipeControlMessageHandler"}, " ")); diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn index e7f1bfa9c22c31..160fb6077a38e9 100644 --- a/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/mojo/public/cpp/bindings/tests/BUILD.gn @@ -34,7 +34,6 @@ source_set("tests") { "map_unittest.cc", "message_queue.cc", "message_queue.h", - "message_quota_checker_unittest.cc", "message_unittest.cc", "multiplex_router_unittest.cc", "native_struct_unittest.cc", diff --git a/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc b/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc deleted file mode 100644 index 4b22984055b6e2..00000000000000 --- a/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/lib/message_quota_checker.h" - -#include "base/test/scoped_feature_list.h" -#include "base/test/task_environment.h" -#include "base/time/time.h" -#include "mojo/core/embedder/embedder.h" -#include "mojo/public/c/system/quota.h" -#include "mojo/public/cpp/bindings/features.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/abseil-cpp/absl/types/optional.h" - -namespace mojo { -namespace test { -namespace { - -class MessageQuotaCheckerTest : public testing::Test { - public: - MessageQuotaCheckerTest() { - EXPECT_EQ(nullptr, instance_); - instance_ = this; - } - ~MessageQuotaCheckerTest() override { - EXPECT_EQ(this, instance_); - instance_ = nullptr; - } - - protected: - using MessageQuotaChecker = internal::MessageQuotaChecker; - using Configuration = MessageQuotaChecker::Configuration; - - static constexpr base::TimeDelta kOneSamplingInterval = - MessageQuotaChecker::DecayingRateAverage::kSamplingInterval; - - void Advance(base::TimeDelta delta) { - task_environment_.FastForwardBy(delta); - } - - static void RecordDumpAttempt(size_t total_quota_used, - absl::optional message_pipe_quota_used, - int64_t seconds_since_construction, - double average_write_rate, - uint64_t messages_enqueued, - uint64_t messages_dequeued, - uint64_t messages_written) { - ++instance_->num_dumps_; - instance_->last_dump_total_quota_used_ = total_quota_used; - instance_->last_dump_message_pipe_quota_used_ = message_pipe_quota_used; - instance_->last_seconds_since_construction_ = seconds_since_construction; - instance_->last_average_write_rate_ = average_write_rate; - instance_->last_messages_enqueued_ = messages_enqueued; - instance_->last_messages_dequeued_ = messages_dequeued; - instance_->last_messages_written_ = messages_written; - } - - // Mock time to allow testing - base::test::TaskEnvironment task_environment_{ - base::test::TaskEnvironment::TimeSource::MOCK_TIME}; - - size_t num_dumps_ = false; - size_t last_dump_total_quota_used_ = 0u; - absl::optional last_dump_message_pipe_quota_used_; - int64_t last_seconds_since_construction_ = 0; - double last_average_write_rate_ = 0.0; - uint64_t last_messages_enqueued_ = 0u; - uint64_t last_messages_dequeued_ = 0u; - uint64_t last_messages_written_ = 0u; - - static const Configuration enabled_config_; - - static MessageQuotaCheckerTest* instance_; -}; - -constexpr base::TimeDelta MessageQuotaCheckerTest::kOneSamplingInterval; -const MessageQuotaCheckerTest::Configuration - MessageQuotaCheckerTest::enabled_config_ = {true, 1, 100, 200, - &RecordDumpAttempt}; -MessageQuotaCheckerTest* MessageQuotaCheckerTest::instance_ = nullptr; - -TEST_F(MessageQuotaCheckerTest, ReadsConfigurationFromFeatures) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - base::FieldTrialParams params; - params["SampleRate"] = "19"; - // Quota value parameter below the minimum the checker will allow. - params["QuotaValue"] = "57"; - params["CrashThreshold"] = "225"; - - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - features::kMojoRecordUnreadMessageCount, params); - - // Validate that the configuration reads from the feature configuration. - const MessageQuotaChecker::Configuration config = - MessageQuotaChecker::GetConfigurationForTesting(); - - EXPECT_TRUE(config.is_enabled); - EXPECT_EQ(19u, config.sample_rate); - EXPECT_EQ(100u, config.unread_message_count_quota); - EXPECT_EQ(225u, config.crash_threshold); - EXPECT_NE(nullptr, config.maybe_crash_function); -} - -TEST_F(MessageQuotaCheckerTest, DisabledByDefault) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - const MessageQuotaChecker::Configuration config = - MessageQuotaChecker::GetConfigurationForTesting(); - EXPECT_FALSE(config.is_enabled); - - // Validate that no MessageQuoteCheckers are created in the default feature - // configuration. Run a bunch of iterations, as this function returns an - // instance randomly. - for (size_t i = 0; i < 1000; ++i) - ASSERT_EQ(nullptr, MessageQuotaChecker::MaybeCreate()); -} - -TEST_F(MessageQuotaCheckerTest, CreatesWhenEnabled) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - // Run a bunch of iterations, as this function returns an instance randomly. - for (size_t i = 0; i < 1000; ++i) - EXPECT_NE(nullptr, - MessageQuotaChecker::MaybeCreateForTesting(enabled_config_)); -} - -TEST_F(MessageQuotaCheckerTest, CountsRight) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - scoped_refptr checker = - MessageQuotaChecker::MaybeCreateForTesting(enabled_config_); - - ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting()); - ASSERT_EQ(0u, checker->GetMaxQuotaUsage()); - - checker->BeforeMessagesEnqueued(10); - ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting()); - ASSERT_EQ(10u, checker->GetMaxQuotaUsage()); - - checker->AfterMessagesDequeued(5); - ASSERT_EQ(5u, checker->GetCurrentQuotaStatusForTesting()); - ASSERT_EQ(10u, checker->GetMaxQuotaUsage()); - - ASSERT_EQ(0u, num_dumps_); -} - -TEST_F(MessageQuotaCheckerTest, CountsMessagePipeAlso) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - MessagePipe pipe; - scoped_refptr checker = - MessageQuotaChecker::MaybeCreateForTesting(enabled_config_); - - uint64_t limit = 0; - uint64_t usage = 0; - MojoResult rv = MojoQueryQuota(pipe.handle0.get().value(), - MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, - &limit, &usage); - ASSERT_EQ(MOJO_RESULT_OK, rv); - ASSERT_EQ(MOJO_QUOTA_LIMIT_NONE, limit); - - checker->SetMessagePipe(pipe.handle0.get()); - - // Validate that the checker sets an unread message quota on the pipe, and - // that it clamps to the minimum of 100. - rv = MojoQueryQuota(pipe.handle0.get().value(), - MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, &limit, - &usage); - ASSERT_EQ(MOJO_RESULT_OK, rv); - ASSERT_EQ(100u, limit); - - ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting()); - - const char kMessage[] = "hello"; - for (size_t i = 0; i < 10; ++i) { - checker->BeforeWrite(); - ASSERT_EQ(MOJO_RESULT_OK, - WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - ASSERT_EQ(10u, checker->GetMaxQuotaUsage()); - ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting()); - - checker->BeforeMessagesEnqueued(10); - ASSERT_EQ(20u, checker->GetMaxQuotaUsage()); - ASSERT_EQ(20u, checker->GetCurrentQuotaStatusForTesting()); - - ASSERT_EQ(0u, num_dumps_); -} - -TEST_F(MessageQuotaCheckerTest, DumpsCoreOnOverrun) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - // Make sure to start the test on an even sampling interval to get consistent - // average computations below. - base::TimeTicks t0 = MessageQuotaChecker::DecayingRateAverage:: - GetNextSamplingIntervalForTesting(base::TimeTicks::Now()); - task_environment_.AdvanceClock(t0 - base::TimeTicks::Now()); - - MessagePipe pipe; - scoped_refptr checker = - MessageQuotaChecker::MaybeCreateForTesting(enabled_config_); - - // Fast forward time by a few sampling intervals. - Advance(10 * kOneSamplingInterval); - - // Queue up 100 messages. - checker->SetMessagePipe(pipe.handle0.get()); - const char kMessage[] = "hello"; - for (size_t i = 0; i < 100; ++i) { - checker->BeforeWrite(); - ASSERT_EQ(MOJO_RESULT_OK, - WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - // The crash threshold is at 200 per the config, so shouldn't have attempted - // a core dump yet. - ASSERT_EQ(0u, num_dumps_); - - checker->BeforeMessagesEnqueued(99); - ASSERT_EQ(0u, num_dumps_); - - Advance(kOneSamplingInterval); - checker->BeforeMessagesEnqueued(1); - EXPECT_EQ(1u, num_dumps_); - EXPECT_EQ(200u, last_dump_total_quota_used_); - EXPECT_EQ(100u, last_dump_message_pipe_quota_used_); - EXPECT_EQ((11 * kOneSamplingInterval).InSeconds(), - last_seconds_since_construction_); - EXPECT_EQ(50, last_average_write_rate_); - - checker->BeforeWrite(); - ASSERT_EQ(MOJO_RESULT_OK, - WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(2u, num_dumps_); - EXPECT_EQ(201u, last_dump_total_quota_used_); - EXPECT_EQ(101u, last_dump_message_pipe_quota_used_); - EXPECT_EQ((11 * kOneSamplingInterval).InSeconds(), - last_seconds_since_construction_); - EXPECT_EQ(50, last_average_write_rate_); - - Advance(kOneSamplingInterval); - checker->SetMessagePipe(mojo::MessagePipeHandle()); - checker->AfterMessagesDequeued(50); - checker->BeforeMessagesEnqueued(300); - EXPECT_EQ(3u, num_dumps_); - EXPECT_EQ(350u, last_dump_total_quota_used_); - EXPECT_FALSE(last_dump_message_pipe_quota_used_.has_value()); - EXPECT_EQ((12 * kOneSamplingInterval).InSeconds(), - last_seconds_since_construction_); - EXPECT_EQ(25.5, last_average_write_rate_); - EXPECT_EQ(400u, last_messages_enqueued_); - EXPECT_EQ(50u, last_messages_dequeued_); - EXPECT_EQ(101u, last_messages_written_); -} - -TEST_F(MessageQuotaCheckerTest, DecayingRateAverage) { - if (mojo::core::IsMojoIpczEnabled()) { - GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz."; - } - - // Make sure to start the test on an even sampling interval to get consistent - // average computations below. - base::TimeTicks t0 = MessageQuotaChecker::DecayingRateAverage:: - GetNextSamplingIntervalForTesting(base::TimeTicks::Now()); - task_environment_.AdvanceClock(t0 - base::TimeTicks::Now()); - - // Walk time forward a bit from the epoch. - t0 += 101 * kOneSamplingInterval; - - MessageQuotaChecker::DecayingRateAverage avg; - EXPECT_EQ(0.0, avg.GetDecayedRateAverage(t0)); - - // Tally two events in the same sampling interval. - avg.AccrueEvent(t0); - avg.AccrueEvent(t0); - - // The decayed average rate is half of the rate this sampling interval. - t0 += kOneSamplingInterval; - EXPECT_EQ(1.0, avg.GetDecayedRateAverage(t0)); - - // Tally another two events in a subsequent sampling interval. - avg.AccrueEvent(t0); - avg.AccrueEvent(t0); - - t0 += kOneSamplingInterval; - EXPECT_EQ(1.5, avg.GetDecayedRateAverage(t0)); - - // Tally another two events in a subsequent sampling interval. - avg.AccrueEvent(t0); - avg.AccrueEvent(t0); - EXPECT_EQ(1.75, avg.GetDecayedRateAverage(t0 + kOneSamplingInterval)); - - // Make sure the average is decayed with time, including within a sampling - // interval. - EXPECT_EQ(0.875, avg.GetDecayedRateAverage(t0 + 2 * kOneSamplingInterval)); - EXPECT_NEAR(0.619, avg.GetDecayedRateAverage(t0 + 2.5 * kOneSamplingInterval), - 0.001); - EXPECT_EQ(0.4375, avg.GetDecayedRateAverage(t0 + 3 * kOneSamplingInterval)); - - t0 += 10 * kOneSamplingInterval; - avg.AccrueEvent(t0); - avg.AccrueEvent(t0); - // The previous average should have decayed by 1/1024. - EXPECT_EQ(1.0 + 1.75 / 1024.0, - avg.GetDecayedRateAverage(t0 + kOneSamplingInterval)); - - // Explicitly test simple interpolation in a sampling interval by setting - // up an average that's conveniently converged to 4.0. - MessageQuotaChecker::DecayingRateAverage avg2; - for (size_t i = 0; i < 16; ++i) - avg2.AccrueEvent(t0); - t0 += 2 * kOneSamplingInterval; - EXPECT_EQ(4.0, avg2.GetDecayedRateAverage(t0)); - - // Now test 0/8, 1/8, 1/4, 1/2 and 3/4-way interpolation. - avg2.AccrueEvent(t0); - // The special case of adding an event and requesting the average at the - // start of sampling interval explicitly excludes that event. - EXPECT_EQ(4.0, avg2.GetDecayedRateAverage(t0)); - EXPECT_NEAR(4.332, - avg2.GetDecayedRateAverage(t0 + 1.0 / 8.0 * kOneSamplingInterval), - 0.001); - EXPECT_EQ(4.0, - avg2.GetDecayedRateAverage(t0 + 1.0 / 4.0 * kOneSamplingInterval)); - avg2.AccrueEvent(t0); - EXPECT_EQ(4.0, - avg2.GetDecayedRateAverage(t0 + 2.0 / 4.0 * kOneSamplingInterval)); - avg2.AccrueEvent(t0); - EXPECT_EQ(4.0, - avg2.GetDecayedRateAverage(t0 + 3.0 / 4.0 * kOneSamplingInterval)); -} - -} // namespace -} // namespace test -} // namespace mojo