blob: d8a5242972bf176d4afbd78e0f6261795c1d1bcd [file] [log] [blame]
Yuzhu Shen4d50dc42017-09-06 20:39:091// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <memory>
6
Sebastien Marchand6d0558fd2019-01-25 16:49:377#include "base/bind.h"
Yuzhu Shen4d50dc42017-09-06 20:39:098#include "base/process/process_metrics.h"
9#include "base/run_loop.h"
10#include "base/strings/stringprintf.h"
11#include "base/synchronization/waitable_event.h"
12#include "base/test/perf_log.h"
Gabriel Charettec7108742019-08-23 03:31:4013#include "base/test/task_environment.h"
Yuzhu Shen4d50dc42017-09-06 20:39:0914#include "base/timer/timer.h"
15#include "ipc/ipc_channel_proxy.h"
16#include "ipc/ipc_perftest_messages.h"
17#include "ipc/ipc_perftest_util.h"
18#include "ipc/ipc_sync_channel.h"
19#include "ipc/ipc_test.mojom.h"
20#include "ipc/ipc_test_base.h"
Ken Rockot8a7f35f2018-07-04 19:40:5621#include "mojo/core/test/mojo_test_base.h"
22#include "mojo/core/test/multiprocess_test_helper.h"
Julie Jeongeun Kim6eb409ed2019-09-28 01:27:5423#include "mojo/public/cpp/bindings/pending_remote.h"
24#include "mojo/public/cpp/bindings/remote.h"
Yuzhu Shen4d50dc42017-09-06 20:39:0925#include "mojo/public/cpp/system/message_pipe.h"
26
27namespace IPC {
28namespace {
29
30struct TestParams {
Chris Watkins2d879af2017-11-30 02:11:5931 TestParams() = default;
Yuzhu Shen4d50dc42017-09-06 20:39:0932 TestParams(size_t in_message_size,
33 size_t in_frames_per_second,
34 size_t in_messages_per_frame,
35 size_t in_duration_in_seconds)
36 : message_size(in_message_size),
37 frames_per_second(in_frames_per_second),
38 messages_per_frame(in_messages_per_frame),
39 duration_in_seconds(in_duration_in_seconds) {}
40
41 size_t message_size;
42 size_t frames_per_second;
43 size_t messages_per_frame;
44 size_t duration_in_seconds;
45};
46
47std::vector<TestParams> GetDefaultTestParams() {
48 std::vector<TestParams> list;
49 list.push_back({144, 20, 10, 10});
50 list.push_back({144, 60, 10, 10});
51 return list;
52}
53
54std::string GetLogTitle(const std::string& label, const TestParams& params) {
55 return base::StringPrintf(
56 "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
57 params.message_size, params.frames_per_second, params.messages_per_frame);
58}
59
60base::TimeDelta GetFrameTime(size_t frames_per_second) {
61 return base::TimeDelta::FromSecondsD(1.0 / frames_per_second);
62}
63
64class PerfCpuLogger {
65 public:
66 explicit PerfCpuLogger(base::StringPiece test_name)
67 : test_name_(test_name),
68 process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
69 process_metrics_->GetPlatformIndependentCPUUsage();
70 }
71
72 ~PerfCpuLogger() {
73 double result = process_metrics_->GetPlatformIndependentCPUUsage();
74 base::LogPerfResult(test_name_.c_str(), result, "%");
75 }
76
77 private:
78 std::string test_name_;
79 std::unique_ptr<base::ProcessMetrics> process_metrics_;
80
81 DISALLOW_COPY_AND_ASSIGN(PerfCpuLogger);
82};
83
84MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
85 MojoPerfTestClient client;
Ken Rockot8a7f35f2018-07-04 19:40:5686 int rv = mojo::core::test::MultiprocessTestHelper::RunClientMain(
Matt Falkenhagenfb888f02019-11-21 00:30:0287 base::BindOnce(&MojoPerfTestClient::Run, base::Unretained(&client)),
Yuzhu Shen4d50dc42017-09-06 20:39:0988 true /* pass_pipe_ownership_to_main */);
89
90 base::RunLoop run_loop;
91 run_loop.RunUntilIdle();
92
93 return rv;
94}
95
96class ChannelSteadyPingPongListener : public Listener {
97 public:
98 ChannelSteadyPingPongListener() = default;
99
100 ~ChannelSteadyPingPongListener() override = default;
101
102 void Init(Sender* sender) {
103 DCHECK(!sender_);
104 sender_ = sender;
105 }
106
107 void SetTestParams(const TestParams& params,
108 const std::string& label,
109 bool sync,
110 const base::Closure& quit_closure) {
111 params_ = params;
112 label_ = label;
113 sync_ = sync;
114 quit_closure_ = quit_closure;
115 payload_ = std::string(params.message_size, 'a');
116 }
117
118 bool OnMessageReceived(const Message& message) override {
119 CHECK(sender_);
120
121 bool handled = true;
122 IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
123 IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
124 IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
125 IPC_MESSAGE_UNHANDLED(handled = false)
126 IPC_END_MESSAGE_MAP()
127 return handled;
128 }
129
130 void OnHello() {
131 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
132
133 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
134
135 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
136 &ChannelSteadyPingPongListener::StartPingPong);
137 }
138
139 void StartPingPong() {
140 if (sync_) {
141 base::TimeTicks before = base::TimeTicks::Now();
142 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
143 --count_down_) {
144 std::string response;
145 sender_->Send(new TestMsg_SyncPing(payload_, &response));
146 DCHECK_EQ(response, payload_);
147 }
148
149 if (base::TimeTicks::Now() - before >
150 GetFrameTime(params_.frames_per_second)) {
151 LOG(ERROR) << "Frame " << frame_count_down_
152 << " wasn't able to complete on time!";
153 }
154
155 CHECK_GT(frame_count_down_, 0);
156 frame_count_down_--;
157 if (frame_count_down_ == 0)
158 StopPingPong();
159 } else {
160 if (count_down_ != 0) {
161 LOG(ERROR) << "Frame " << frame_count_down_
162 << " wasn't able to complete on time!";
163 } else {
164 SendPong();
165 }
166 count_down_ = params_.messages_per_frame;
167 }
168 }
169
170 void StopPingPong() {
171 cpu_logger_.reset();
172 timer_.AbandonAndStop();
173 quit_closure_.Run();
174 }
175
176 void OnPing(const std::string& payload) {
177 // Include message deserialization in latency.
178 DCHECK_EQ(payload_.size(), payload.size());
179
180 CHECK_GT(count_down_, 0);
181 count_down_--;
182 if (count_down_ > 0) {
183 SendPong();
184 } else {
185 CHECK_GT(frame_count_down_, 0);
186 frame_count_down_--;
187 if (frame_count_down_ == 0)
188 StopPingPong();
189 }
190 }
191
192 void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
193
194 private:
195 Sender* sender_ = nullptr;
196 TestParams params_;
197 std::string payload_;
198 std::string label_;
199 bool sync_ = false;
200
201 int count_down_ = 0;
202 int frame_count_down_ = 0;
203
204 base::RepeatingTimer timer_;
205 std::unique_ptr<PerfCpuLogger> cpu_logger_;
206
207 base::Closure quit_closure_;
208};
209
210class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
211 public:
212 ChannelSteadyPingPongTest() = default;
213 ~ChannelSteadyPingPongTest() override = default;
214
215 void RunPingPongServer(const std::string& label, bool sync) {
216 Init("MojoPerfTestClient");
217
218 // Set up IPC channel and start client.
219 ChannelSteadyPingPongListener listener;
220
221 std::unique_ptr<ChannelProxy> channel_proxy;
222 std::unique_ptr<base::WaitableEvent> shutdown_event;
223
224 if (sync) {
225 shutdown_event = std::make_unique<base::WaitableEvent>(
226 base::WaitableEvent::ResetPolicy::MANUAL,
227 base::WaitableEvent::InitialState::NOT_SIGNALED);
228 channel_proxy = IPC::SyncChannel::Create(
229 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
Hajime Hoshiff15e972017-11-09 06:37:09230 GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
231 shutdown_event.get());
Yuzhu Shen4d50dc42017-09-06 20:39:09232 } else {
233 channel_proxy = IPC::ChannelProxy::Create(
234 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
Hajime Hoshiff15e972017-11-09 06:37:09235 GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
Yuzhu Shen4d50dc42017-09-06 20:39:09236 }
237 listener.Init(channel_proxy.get());
238
239 LockThreadAffinity thread_locker(kSharedCore);
240 std::vector<TestParams> params_list = GetDefaultTestParams();
241 for (const auto& params : params_list) {
242 base::RunLoop run_loop;
243
244 listener.SetTestParams(params, label, sync,
245 run_loop.QuitWhenIdleClosure());
246
247 // This initial message will kick-start the ping-pong of messages.
248 channel_proxy->Send(new TestMsg_Hello);
249
250 run_loop.Run();
251 }
252
253 // Send quit message.
254 channel_proxy->Send(new TestMsg_Quit);
255
256 EXPECT_TRUE(WaitForClientShutdown());
257 channel_proxy.reset();
258 }
259};
260
261TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
262 RunPingPongServer("IPC_CPU_Async", false);
263}
264
265TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
266 RunPingPongServer("IPC_CPU_Sync", true);
267}
268
Ken Rockot8a7f35f2018-07-04 19:40:56269class MojoSteadyPingPongTest : public mojo::core::test::MojoTestBase {
Yuzhu Shen4d50dc42017-09-06 20:39:09270 public:
271 MojoSteadyPingPongTest() = default;
272
273 protected:
274 void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
275 label_ = label;
276 sync_ = sync;
277
278 mojo::MessagePipeHandle mp_handle(mp);
279 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
Julie Jeongeun Kim6eb409ed2019-09-28 01:27:54280 ping_receiver_.Bind(
281 mojo::PendingRemote<IPC::mojom::Reflector>(std::move(scoped_mp), 0u));
Yuzhu Shen4d50dc42017-09-06 20:39:09282
283 LockThreadAffinity thread_locker(kSharedCore);
284 std::vector<TestParams> params_list = GetDefaultTestParams();
285 for (const auto& params : params_list) {
286 params_ = params;
287 payload_ = std::string(params.message_size, 'a');
288
Matt Falkenhagenfb888f02019-11-21 00:30:02289 ping_receiver_->Ping("hello",
290 base::BindOnce(&MojoSteadyPingPongTest::OnHello,
291 base::Unretained(this)));
Yuzhu Shen4d50dc42017-09-06 20:39:09292 base::RunLoop run_loop;
293 quit_closure_ = run_loop.QuitWhenIdleClosure();
294 run_loop.Run();
295 }
296
297 ping_receiver_->Quit();
298
Julie Jeongeun Kim6eb409ed2019-09-28 01:27:54299 ignore_result(ping_receiver_.Unbind().PassPipe().release());
Yuzhu Shen4d50dc42017-09-06 20:39:09300 }
301
302 void OnHello(const std::string& value) {
303 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
304
305 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
306
307 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
308 &MojoSteadyPingPongTest::StartPingPong);
309 }
310
311 void StartPingPong() {
312 if (sync_) {
313 base::TimeTicks before = base::TimeTicks::Now();
314 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
315 --count_down_) {
316 std::string response;
317 ping_receiver_->SyncPing(payload_, &response);
318 DCHECK_EQ(response, payload_);
319 }
320
321 if (base::TimeTicks::Now() - before >
322 GetFrameTime(params_.frames_per_second)) {
323 LOG(ERROR) << "Frame " << frame_count_down_
324 << " wasn't able to complete on time!";
325 }
326
327 CHECK_GT(frame_count_down_, 0);
328 frame_count_down_--;
329 if (frame_count_down_ == 0)
330 StopPingPong();
331 } else {
332 if (count_down_ != 0) {
333 LOG(ERROR) << "Frame " << frame_count_down_
334 << " wasn't able to complete on time!";
335 } else {
336 SendPing();
337 }
338 count_down_ = params_.messages_per_frame;
339 }
340 }
341
342 void StopPingPong() {
343 cpu_logger_.reset();
344 timer_.AbandonAndStop();
345 quit_closure_.Run();
346 }
347
348 void OnPong(const std::string& value) {
349 // Include message deserialization in latency.
350 DCHECK_EQ(payload_.size(), value.size());
351
352 CHECK_GT(count_down_, 0);
353 count_down_--;
354 if (count_down_ > 0) {
355 SendPing();
356 } else {
357 CHECK_GT(frame_count_down_, 0);
358 frame_count_down_--;
359 if (frame_count_down_ == 0)
360 StopPingPong();
361 }
362 }
363
364 void SendPing() {
Matt Falkenhagenfb888f02019-11-21 00:30:02365 ping_receiver_->Ping(payload_,
366 base::BindOnce(&MojoSteadyPingPongTest::OnPong,
367 base::Unretained(this)));
Yuzhu Shen4d50dc42017-09-06 20:39:09368 }
369
370 static int RunPingPongClient(MojoHandle mp) {
371 mojo::MessagePipeHandle mp_handle(mp);
372 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
373
374 LockThreadAffinity thread_locker(kSharedCore);
375 base::RunLoop run_loop;
376 ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
377 run_loop.Run();
378 return 0;
379 }
380
381 private:
382 TestParams params_;
383 std::string payload_;
384 std::string label_;
385 bool sync_ = false;
386
Julie Jeongeun Kim6eb409ed2019-09-28 01:27:54387 mojo::Remote<IPC::mojom::Reflector> ping_receiver_;
Yuzhu Shen4d50dc42017-09-06 20:39:09388
389 int count_down_ = 0;
390 int frame_count_down_ = 0;
391
392 base::RepeatingTimer timer_;
393 std::unique_ptr<PerfCpuLogger> cpu_logger_;
394
395 base::Closure quit_closure_;
396
397 DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
398};
399
400DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
Gabriel Charettec523fa62019-09-09 23:03:32401 base::test::SingleThreadTaskEnvironment task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09402 return RunPingPongClient(h);
403}
404
405// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
406// instead of raw IPC::Messages.
407TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
408 RunTestClient("PingPongClient", [&](MojoHandle h) {
Gabriel Charettec523fa62019-09-09 23:03:32409 base::test::SingleThreadTaskEnvironment task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09410 RunPingPongServer(h, "Mojo_CPU_Async", false);
411 });
412}
413
414TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
415 RunTestClient("PingPongClient", [&](MojoHandle h) {
Gabriel Charettec523fa62019-09-09 23:03:32416 base::test::SingleThreadTaskEnvironment task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09417 RunPingPongServer(h, "Mojo_CPU_Sync", true);
418 });
419}
420
421} // namespace
422} // namespace IPC