1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/check_op.h"
9 #include "base/functional/bind.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/process/process_metrics.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/task/single_thread_task_runner.h"
16 #include "base/test/perf_log.h"
17 #include "base/test/task_environment.h"
18 #include "base/timer/timer.h"
19 #include "ipc/ipc_channel_proxy.h"
20 #include "ipc/ipc_perftest_messages.h"
21 #include "ipc/ipc_perftest_util.h"
22 #include "ipc/ipc_sync_channel.h"
23 #include "ipc/ipc_test.mojom.h"
24 #include "ipc/ipc_test_base.h"
25 #include "mojo/core/test/mojo_test_base.h"
26 #include "mojo/core/test/multiprocess_test_helper.h"
27 #include "mojo/public/cpp/bindings/pending_remote.h"
28 #include "mojo/public/cpp/bindings/remote.h"
29 #include "mojo/public/cpp/system/message_pipe.h"
35 TestParams() = default;
36 TestParams(size_t in_message_size,
37 size_t in_frames_per_second,
38 size_t in_messages_per_frame,
39 size_t in_duration_in_seconds)
40 : message_size(in_message_size),
41 frames_per_second(in_frames_per_second),
42 messages_per_frame(in_messages_per_frame),
43 duration_in_seconds(in_duration_in_seconds) {}
46 size_t frames_per_second;
47 size_t messages_per_frame;
48 size_t duration_in_seconds;
51 std::vector<TestParams> GetDefaultTestParams() {
52 std::vector<TestParams> list;
53 list.push_back({144, 20, 10, 10});
54 list.push_back({144, 60, 10, 10});
58 std::string GetLogTitle(const std::string& label, const TestParams& params) {
59 return base::StringPrintf(
60 "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
61 params.message_size, params.frames_per_second, params.messages_per_frame);
64 base::TimeDelta GetFrameTime(size_t frames_per_second) {
65 return base::Seconds(1.0 / frames_per_second);
70 explicit PerfCpuLogger(base::StringPiece test_name)
71 : test_name_(test_name),
72 process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
73 // Query the CPU usage once to start the recording interval.
74 const double inital_cpu_usage =
75 process_metrics_->GetPlatformIndependentCPUUsage();
76 // This should have been the first call so the reported cpu usage should be
78 DCHECK_EQ(inital_cpu_usage, 0.0);
81 PerfCpuLogger(const PerfCpuLogger&) = delete;
82 PerfCpuLogger& operator=(const PerfCpuLogger&) = delete;
85 double result = process_metrics_->GetPlatformIndependentCPUUsage();
86 base::LogPerfResult(test_name_.c_str(), result, "%");
90 std::string test_name_;
91 std::unique_ptr<base::ProcessMetrics> process_metrics_;
94 MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
95 MojoPerfTestClient client;
96 int rv = mojo::core::test::MultiprocessTestHelper::RunClientMain(
97 base::BindOnce(&MojoPerfTestClient::Run, base::Unretained(&client)),
98 true /* pass_pipe_ownership_to_main */);
100 base::RunLoop run_loop;
101 run_loop.RunUntilIdle();
106 class ChannelSteadyPingPongListener : public Listener {
108 ChannelSteadyPingPongListener() = default;
110 ~ChannelSteadyPingPongListener() override = default;
112 void Init(Sender* sender) {
117 void SetTestParams(const TestParams& params,
118 const std::string& label,
120 base::OnceClosure quit_closure) {
124 quit_closure_ = std::move(quit_closure);
125 payload_ = std::string(params.message_size, 'a');
128 bool OnMessageReceived(const Message& message) override {
132 IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
133 IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
134 IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
135 IPC_MESSAGE_UNHANDLED(handled = false)
136 IPC_END_MESSAGE_MAP()
141 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
143 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
145 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
146 &ChannelSteadyPingPongListener::StartPingPong);
149 void StartPingPong() {
151 base::TimeTicks before = base::TimeTicks::Now();
152 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
154 std::string response;
155 sender_->Send(new TestMsg_SyncPing(payload_, &response));
156 DCHECK_EQ(response, payload_);
159 if (base::TimeTicks::Now() - before >
160 GetFrameTime(params_.frames_per_second)) {
161 LOG(ERROR) << "Frame " << frame_count_down_
162 << " wasn't able to complete on time!";
165 CHECK_GT(frame_count_down_, 0);
167 if (frame_count_down_ == 0)
170 if (count_down_ != 0) {
171 LOG(ERROR) << "Frame " << frame_count_down_
172 << " wasn't able to complete on time!";
176 count_down_ = params_.messages_per_frame;
180 void StopPingPong() {
182 timer_.AbandonAndStop();
183 std::move(quit_closure_).Run();
186 void OnPing(const std::string& payload) {
187 // Include message deserialization in latency.
188 DCHECK_EQ(payload_.size(), payload.size());
190 CHECK_GT(count_down_, 0);
192 if (count_down_ > 0) {
195 CHECK_GT(frame_count_down_, 0);
197 if (frame_count_down_ == 0)
202 void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
205 raw_ptr<Sender> sender_ = nullptr;
207 std::string payload_;
212 int frame_count_down_ = 0;
214 base::RepeatingTimer timer_;
215 std::unique_ptr<PerfCpuLogger> cpu_logger_;
217 base::OnceClosure quit_closure_;
220 class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
222 ChannelSteadyPingPongTest() = default;
223 ~ChannelSteadyPingPongTest() override = default;
225 void RunPingPongServer(const std::string& label, bool sync) {
226 Init("MojoPerfTestClient");
228 // Set up IPC channel and start client.
229 ChannelSteadyPingPongListener listener;
231 std::unique_ptr<ChannelProxy> channel_proxy;
232 std::unique_ptr<base::WaitableEvent> shutdown_event;
235 shutdown_event = std::make_unique<base::WaitableEvent>(
236 base::WaitableEvent::ResetPolicy::MANUAL,
237 base::WaitableEvent::InitialState::NOT_SIGNALED);
238 channel_proxy = IPC::SyncChannel::Create(
239 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
240 GetIOThreadTaskRunner(),
241 base::SingleThreadTaskRunner::GetCurrentDefault(), false,
242 shutdown_event.get());
244 channel_proxy = IPC::ChannelProxy::Create(
245 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
246 GetIOThreadTaskRunner(),
247 base::SingleThreadTaskRunner::GetCurrentDefault());
249 listener.Init(channel_proxy.get());
251 LockThreadAffinity thread_locker(kSharedCore);
252 std::vector<TestParams> params_list = GetDefaultTestParams();
253 for (const auto& params : params_list) {
254 base::RunLoop run_loop;
256 listener.SetTestParams(params, label, sync,
257 run_loop.QuitWhenIdleClosure());
259 // This initial message will kick-start the ping-pong of messages.
260 channel_proxy->Send(new TestMsg_Hello);
265 // Send quit message.
266 channel_proxy->Send(new TestMsg_Quit);
268 EXPECT_TRUE(WaitForClientShutdown());
269 channel_proxy.reset();
273 TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
274 RunPingPongServer("IPC_CPU_Async", false);
277 TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
278 RunPingPongServer("IPC_CPU_Sync", true);
281 class MojoSteadyPingPongTest : public mojo::core::test::MojoTestBase {
283 MojoSteadyPingPongTest() = default;
285 MojoSteadyPingPongTest(const MojoSteadyPingPongTest&) = delete;
286 MojoSteadyPingPongTest& operator=(const MojoSteadyPingPongTest&) = delete;
289 void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
293 mojo::MessagePipeHandle mp_handle(mp);
294 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
296 mojo::PendingRemote<IPC::mojom::Reflector>(std::move(scoped_mp), 0u));
298 LockThreadAffinity thread_locker(kSharedCore);
299 std::vector<TestParams> params_list = GetDefaultTestParams();
300 for (const auto& params : params_list) {
302 payload_ = std::string(params.message_size, 'a');
304 ping_receiver_->Ping("hello",
305 base::BindOnce(&MojoSteadyPingPongTest::OnHello,
306 base::Unretained(this)));
307 base::RunLoop run_loop;
308 quit_closure_ = run_loop.QuitWhenIdleClosure();
312 ping_receiver_->Quit();
314 std::ignore = ping_receiver_.Unbind().PassPipe().release();
317 void OnHello(const std::string& value) {
318 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
320 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
322 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
323 &MojoSteadyPingPongTest::StartPingPong);
326 void StartPingPong() {
328 base::TimeTicks before = base::TimeTicks::Now();
329 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
331 std::string response;
332 ping_receiver_->SyncPing(payload_, &response);
333 DCHECK_EQ(response, payload_);
336 if (base::TimeTicks::Now() - before >
337 GetFrameTime(params_.frames_per_second)) {
338 LOG(ERROR) << "Frame " << frame_count_down_
339 << " wasn't able to complete on time!";
342 CHECK_GT(frame_count_down_, 0);
344 if (frame_count_down_ == 0)
347 if (count_down_ != 0) {
348 LOG(ERROR) << "Frame " << frame_count_down_
349 << " wasn't able to complete on time!";
353 count_down_ = params_.messages_per_frame;
357 void StopPingPong() {
359 timer_.AbandonAndStop();
360 std::move(quit_closure_).Run();
363 void OnPong(const std::string& value) {
364 // Include message deserialization in latency.
365 DCHECK_EQ(payload_.size(), value.size());
367 CHECK_GT(count_down_, 0);
369 if (count_down_ > 0) {
372 CHECK_GT(frame_count_down_, 0);
374 if (frame_count_down_ == 0)
380 ping_receiver_->Ping(payload_,
381 base::BindOnce(&MojoSteadyPingPongTest::OnPong,
382 base::Unretained(this)));
385 static int RunPingPongClient(MojoHandle mp) {
386 mojo::MessagePipeHandle mp_handle(mp);
387 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
389 LockThreadAffinity thread_locker(kSharedCore);
390 base::RunLoop run_loop;
391 ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
398 std::string payload_;
402 mojo::Remote<IPC::mojom::Reflector> ping_receiver_;
405 int frame_count_down_ = 0;
407 base::RepeatingTimer timer_;
408 std::unique_ptr<PerfCpuLogger> cpu_logger_;
410 base::OnceClosure quit_closure_;
413 DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
414 base::test::SingleThreadTaskEnvironment task_environment;
415 return RunPingPongClient(h);
418 // Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
419 // instead of raw IPC::Messages.
420 TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
421 RunTestClient("PingPongClient", [&](MojoHandle h) {
422 base::test::SingleThreadTaskEnvironment task_environment;
423 RunPingPongServer(h, "Mojo_CPU_Async", false);
427 TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
428 RunTestClient("PingPongClient", [&](MojoHandle h) {
429 base::test::SingleThreadTaskEnvironment task_environment;
430 RunPingPongServer(h, "Mojo_CPU_Sync", true);