blob: 4c5d22c38e195524f6d0e3868b1105f5782e1800 [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"
Carlos Caballero05cd1c32019-06-07 16:42:1413#include "base/test/scoped_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"
Yuzhu Shen4d50dc42017-09-06 20:39:0923#include "mojo/public/cpp/bindings/binding.h"
24#include "mojo/public/cpp/system/message_pipe.h"
25
26namespace IPC {
27namespace {
28
29struct TestParams {
Chris Watkins2d879af2017-11-30 02:11:5930 TestParams() = default;
Yuzhu Shen4d50dc42017-09-06 20:39:0931 TestParams(size_t in_message_size,
32 size_t in_frames_per_second,
33 size_t in_messages_per_frame,
34 size_t in_duration_in_seconds)
35 : message_size(in_message_size),
36 frames_per_second(in_frames_per_second),
37 messages_per_frame(in_messages_per_frame),
38 duration_in_seconds(in_duration_in_seconds) {}
39
40 size_t message_size;
41 size_t frames_per_second;
42 size_t messages_per_frame;
43 size_t duration_in_seconds;
44};
45
46std::vector<TestParams> GetDefaultTestParams() {
47 std::vector<TestParams> list;
48 list.push_back({144, 20, 10, 10});
49 list.push_back({144, 60, 10, 10});
50 return list;
51}
52
53std::string GetLogTitle(const std::string& label, const TestParams& params) {
54 return base::StringPrintf(
55 "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
56 params.message_size, params.frames_per_second, params.messages_per_frame);
57}
58
59base::TimeDelta GetFrameTime(size_t frames_per_second) {
60 return base::TimeDelta::FromSecondsD(1.0 / frames_per_second);
61}
62
63class PerfCpuLogger {
64 public:
65 explicit PerfCpuLogger(base::StringPiece test_name)
66 : test_name_(test_name),
67 process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
68 process_metrics_->GetPlatformIndependentCPUUsage();
69 }
70
71 ~PerfCpuLogger() {
72 double result = process_metrics_->GetPlatformIndependentCPUUsage();
73 base::LogPerfResult(test_name_.c_str(), result, "%");
74 }
75
76 private:
77 std::string test_name_;
78 std::unique_ptr<base::ProcessMetrics> process_metrics_;
79
80 DISALLOW_COPY_AND_ASSIGN(PerfCpuLogger);
81};
82
83MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
84 MojoPerfTestClient client;
Ken Rockot8a7f35f2018-07-04 19:40:5685 int rv = mojo::core::test::MultiprocessTestHelper::RunClientMain(
Yuzhu Shen4d50dc42017-09-06 20:39:0986 base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
87 true /* pass_pipe_ownership_to_main */);
88
89 base::RunLoop run_loop;
90 run_loop.RunUntilIdle();
91
92 return rv;
93}
94
95class ChannelSteadyPingPongListener : public Listener {
96 public:
97 ChannelSteadyPingPongListener() = default;
98
99 ~ChannelSteadyPingPongListener() override = default;
100
101 void Init(Sender* sender) {
102 DCHECK(!sender_);
103 sender_ = sender;
104 }
105
106 void SetTestParams(const TestParams& params,
107 const std::string& label,
108 bool sync,
109 const base::Closure& quit_closure) {
110 params_ = params;
111 label_ = label;
112 sync_ = sync;
113 quit_closure_ = quit_closure;
114 payload_ = std::string(params.message_size, 'a');
115 }
116
117 bool OnMessageReceived(const Message& message) override {
118 CHECK(sender_);
119
120 bool handled = true;
121 IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
122 IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
123 IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
124 IPC_MESSAGE_UNHANDLED(handled = false)
125 IPC_END_MESSAGE_MAP()
126 return handled;
127 }
128
129 void OnHello() {
130 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
131
132 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
133
134 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
135 &ChannelSteadyPingPongListener::StartPingPong);
136 }
137
138 void StartPingPong() {
139 if (sync_) {
140 base::TimeTicks before = base::TimeTicks::Now();
141 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
142 --count_down_) {
143 std::string response;
144 sender_->Send(new TestMsg_SyncPing(payload_, &response));
145 DCHECK_EQ(response, payload_);
146 }
147
148 if (base::TimeTicks::Now() - before >
149 GetFrameTime(params_.frames_per_second)) {
150 LOG(ERROR) << "Frame " << frame_count_down_
151 << " wasn't able to complete on time!";
152 }
153
154 CHECK_GT(frame_count_down_, 0);
155 frame_count_down_--;
156 if (frame_count_down_ == 0)
157 StopPingPong();
158 } else {
159 if (count_down_ != 0) {
160 LOG(ERROR) << "Frame " << frame_count_down_
161 << " wasn't able to complete on time!";
162 } else {
163 SendPong();
164 }
165 count_down_ = params_.messages_per_frame;
166 }
167 }
168
169 void StopPingPong() {
170 cpu_logger_.reset();
171 timer_.AbandonAndStop();
172 quit_closure_.Run();
173 }
174
175 void OnPing(const std::string& payload) {
176 // Include message deserialization in latency.
177 DCHECK_EQ(payload_.size(), payload.size());
178
179 CHECK_GT(count_down_, 0);
180 count_down_--;
181 if (count_down_ > 0) {
182 SendPong();
183 } else {
184 CHECK_GT(frame_count_down_, 0);
185 frame_count_down_--;
186 if (frame_count_down_ == 0)
187 StopPingPong();
188 }
189 }
190
191 void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
192
193 private:
194 Sender* sender_ = nullptr;
195 TestParams params_;
196 std::string payload_;
197 std::string label_;
198 bool sync_ = false;
199
200 int count_down_ = 0;
201 int frame_count_down_ = 0;
202
203 base::RepeatingTimer timer_;
204 std::unique_ptr<PerfCpuLogger> cpu_logger_;
205
206 base::Closure quit_closure_;
207};
208
209class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
210 public:
211 ChannelSteadyPingPongTest() = default;
212 ~ChannelSteadyPingPongTest() override = default;
213
214 void RunPingPongServer(const std::string& label, bool sync) {
215 Init("MojoPerfTestClient");
216
217 // Set up IPC channel and start client.
218 ChannelSteadyPingPongListener listener;
219
220 std::unique_ptr<ChannelProxy> channel_proxy;
221 std::unique_ptr<base::WaitableEvent> shutdown_event;
222
223 if (sync) {
224 shutdown_event = std::make_unique<base::WaitableEvent>(
225 base::WaitableEvent::ResetPolicy::MANUAL,
226 base::WaitableEvent::InitialState::NOT_SIGNALED);
227 channel_proxy = IPC::SyncChannel::Create(
228 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
Hajime Hoshiff15e972017-11-09 06:37:09229 GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
230 shutdown_event.get());
Yuzhu Shen4d50dc42017-09-06 20:39:09231 } else {
232 channel_proxy = IPC::ChannelProxy::Create(
233 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
Hajime Hoshiff15e972017-11-09 06:37:09234 GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
Yuzhu Shen4d50dc42017-09-06 20:39:09235 }
236 listener.Init(channel_proxy.get());
237
238 LockThreadAffinity thread_locker(kSharedCore);
239 std::vector<TestParams> params_list = GetDefaultTestParams();
240 for (const auto& params : params_list) {
241 base::RunLoop run_loop;
242
243 listener.SetTestParams(params, label, sync,
244 run_loop.QuitWhenIdleClosure());
245
246 // This initial message will kick-start the ping-pong of messages.
247 channel_proxy->Send(new TestMsg_Hello);
248
249 run_loop.Run();
250 }
251
252 // Send quit message.
253 channel_proxy->Send(new TestMsg_Quit);
254
255 EXPECT_TRUE(WaitForClientShutdown());
256 channel_proxy.reset();
257 }
258};
259
260TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
261 RunPingPongServer("IPC_CPU_Async", false);
262}
263
264TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
265 RunPingPongServer("IPC_CPU_Sync", true);
266}
267
Ken Rockot8a7f35f2018-07-04 19:40:56268class MojoSteadyPingPongTest : public mojo::core::test::MojoTestBase {
Yuzhu Shen4d50dc42017-09-06 20:39:09269 public:
270 MojoSteadyPingPongTest() = default;
271
272 protected:
273 void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
274 label_ = label;
275 sync_ = sync;
276
277 mojo::MessagePipeHandle mp_handle(mp);
278 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
279 ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
280
281 LockThreadAffinity thread_locker(kSharedCore);
282 std::vector<TestParams> params_list = GetDefaultTestParams();
283 for (const auto& params : params_list) {
284 params_ = params;
285 payload_ = std::string(params.message_size, 'a');
286
287 ping_receiver_->Ping("hello", base::Bind(&MojoSteadyPingPongTest::OnHello,
288 base::Unretained(this)));
289 base::RunLoop run_loop;
290 quit_closure_ = run_loop.QuitWhenIdleClosure();
291 run_loop.Run();
292 }
293
294 ping_receiver_->Quit();
295
296 ignore_result(ping_receiver_.PassInterface().PassHandle().release());
297 }
298
299 void OnHello(const std::string& value) {
300 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
301
302 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
303
304 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
305 &MojoSteadyPingPongTest::StartPingPong);
306 }
307
308 void StartPingPong() {
309 if (sync_) {
310 base::TimeTicks before = base::TimeTicks::Now();
311 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
312 --count_down_) {
313 std::string response;
314 ping_receiver_->SyncPing(payload_, &response);
315 DCHECK_EQ(response, payload_);
316 }
317
318 if (base::TimeTicks::Now() - before >
319 GetFrameTime(params_.frames_per_second)) {
320 LOG(ERROR) << "Frame " << frame_count_down_
321 << " wasn't able to complete on time!";
322 }
323
324 CHECK_GT(frame_count_down_, 0);
325 frame_count_down_--;
326 if (frame_count_down_ == 0)
327 StopPingPong();
328 } else {
329 if (count_down_ != 0) {
330 LOG(ERROR) << "Frame " << frame_count_down_
331 << " wasn't able to complete on time!";
332 } else {
333 SendPing();
334 }
335 count_down_ = params_.messages_per_frame;
336 }
337 }
338
339 void StopPingPong() {
340 cpu_logger_.reset();
341 timer_.AbandonAndStop();
342 quit_closure_.Run();
343 }
344
345 void OnPong(const std::string& value) {
346 // Include message deserialization in latency.
347 DCHECK_EQ(payload_.size(), value.size());
348
349 CHECK_GT(count_down_, 0);
350 count_down_--;
351 if (count_down_ > 0) {
352 SendPing();
353 } else {
354 CHECK_GT(frame_count_down_, 0);
355 frame_count_down_--;
356 if (frame_count_down_ == 0)
357 StopPingPong();
358 }
359 }
360
361 void SendPing() {
362 ping_receiver_->Ping(payload_, base::Bind(&MojoSteadyPingPongTest::OnPong,
363 base::Unretained(this)));
364 }
365
366 static int RunPingPongClient(MojoHandle mp) {
367 mojo::MessagePipeHandle mp_handle(mp);
368 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
369
370 LockThreadAffinity thread_locker(kSharedCore);
371 base::RunLoop run_loop;
372 ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
373 run_loop.Run();
374 return 0;
375 }
376
377 private:
378 TestParams params_;
379 std::string payload_;
380 std::string label_;
381 bool sync_ = false;
382
383 IPC::mojom::ReflectorPtr ping_receiver_;
384
385 int count_down_ = 0;
386 int frame_count_down_ = 0;
387
388 base::RepeatingTimer timer_;
389 std::unique_ptr<PerfCpuLogger> cpu_logger_;
390
391 base::Closure quit_closure_;
392
393 DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
394};
395
396DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
Carlos Caballero05cd1c32019-06-07 16:42:14397 base::test::ScopedTaskEnvironment scoped_task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09398 return RunPingPongClient(h);
399}
400
401// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
402// instead of raw IPC::Messages.
403TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
404 RunTestClient("PingPongClient", [&](MojoHandle h) {
Carlos Caballero05cd1c32019-06-07 16:42:14405 base::test::ScopedTaskEnvironment scoped_task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09406 RunPingPongServer(h, "Mojo_CPU_Async", false);
407 });
408}
409
410TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
411 RunTestClient("PingPongClient", [&](MojoHandle h) {
Carlos Caballero05cd1c32019-06-07 16:42:14412 base::test::ScopedTaskEnvironment scoped_task_environment;
Yuzhu Shen4d50dc42017-09-06 20:39:09413 RunPingPongServer(h, "Mojo_CPU_Sync", true);
414 });
415}
416
417} // namespace
418} // namespace IPC