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