blob: 976ca1f6de4861fed7f2d346045befb160dac55e [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
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"
20#include "mojo/edk/test/mojo_test_base.h"
21#include "mojo/edk/test/multiprocess_test_helper.h"
22#include "mojo/public/cpp/bindings/binding.h"
23#include "mojo/public/cpp/system/message_pipe.h"
24
25namespace IPC {
26namespace {
27
28struct TestParams {
29 TestParams() {}
30 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;
84 int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
85 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,
228 GetIOThreadTaskRunner(), false, shutdown_event.get());
229 } else {
230 channel_proxy = IPC::ChannelProxy::Create(
231 TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
232 GetIOThreadTaskRunner());
233 }
234 listener.Init(channel_proxy.get());
235
236 LockThreadAffinity thread_locker(kSharedCore);
237 std::vector<TestParams> params_list = GetDefaultTestParams();
238 for (const auto& params : params_list) {
239 base::RunLoop run_loop;
240
241 listener.SetTestParams(params, label, sync,
242 run_loop.QuitWhenIdleClosure());
243
244 // This initial message will kick-start the ping-pong of messages.
245 channel_proxy->Send(new TestMsg_Hello);
246
247 run_loop.Run();
248 }
249
250 // Send quit message.
251 channel_proxy->Send(new TestMsg_Quit);
252
253 EXPECT_TRUE(WaitForClientShutdown());
254 channel_proxy.reset();
255 }
256};
257
258TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
259 RunPingPongServer("IPC_CPU_Async", false);
260}
261
262TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
263 RunPingPongServer("IPC_CPU_Sync", true);
264}
265
266class MojoSteadyPingPongTest : public mojo::edk::test::MojoTestBase {
267 public:
268 MojoSteadyPingPongTest() = default;
269
270 protected:
271 void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
272 label_ = label;
273 sync_ = sync;
274
275 mojo::MessagePipeHandle mp_handle(mp);
276 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
277 ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
278
279 LockThreadAffinity thread_locker(kSharedCore);
280 std::vector<TestParams> params_list = GetDefaultTestParams();
281 for (const auto& params : params_list) {
282 params_ = params;
283 payload_ = std::string(params.message_size, 'a');
284
285 ping_receiver_->Ping("hello", base::Bind(&MojoSteadyPingPongTest::OnHello,
286 base::Unretained(this)));
287 base::RunLoop run_loop;
288 quit_closure_ = run_loop.QuitWhenIdleClosure();
289 run_loop.Run();
290 }
291
292 ping_receiver_->Quit();
293
294 ignore_result(ping_receiver_.PassInterface().PassHandle().release());
295 }
296
297 void OnHello(const std::string& value) {
298 cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
299
300 frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
301
302 timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
303 &MojoSteadyPingPongTest::StartPingPong);
304 }
305
306 void StartPingPong() {
307 if (sync_) {
308 base::TimeTicks before = base::TimeTicks::Now();
309 for (count_down_ = params_.messages_per_frame; count_down_ > 0;
310 --count_down_) {
311 std::string response;
312 ping_receiver_->SyncPing(payload_, &response);
313 DCHECK_EQ(response, payload_);
314 }
315
316 if (base::TimeTicks::Now() - before >
317 GetFrameTime(params_.frames_per_second)) {
318 LOG(ERROR) << "Frame " << frame_count_down_
319 << " wasn't able to complete on time!";
320 }
321
322 CHECK_GT(frame_count_down_, 0);
323 frame_count_down_--;
324 if (frame_count_down_ == 0)
325 StopPingPong();
326 } else {
327 if (count_down_ != 0) {
328 LOG(ERROR) << "Frame " << frame_count_down_
329 << " wasn't able to complete on time!";
330 } else {
331 SendPing();
332 }
333 count_down_ = params_.messages_per_frame;
334 }
335 }
336
337 void StopPingPong() {
338 cpu_logger_.reset();
339 timer_.AbandonAndStop();
340 quit_closure_.Run();
341 }
342
343 void OnPong(const std::string& value) {
344 // Include message deserialization in latency.
345 DCHECK_EQ(payload_.size(), value.size());
346
347 CHECK_GT(count_down_, 0);
348 count_down_--;
349 if (count_down_ > 0) {
350 SendPing();
351 } else {
352 CHECK_GT(frame_count_down_, 0);
353 frame_count_down_--;
354 if (frame_count_down_ == 0)
355 StopPingPong();
356 }
357 }
358
359 void SendPing() {
360 ping_receiver_->Ping(payload_, base::Bind(&MojoSteadyPingPongTest::OnPong,
361 base::Unretained(this)));
362 }
363
364 static int RunPingPongClient(MojoHandle mp) {
365 mojo::MessagePipeHandle mp_handle(mp);
366 mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
367
368 LockThreadAffinity thread_locker(kSharedCore);
369 base::RunLoop run_loop;
370 ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
371 run_loop.Run();
372 return 0;
373 }
374
375 private:
376 TestParams params_;
377 std::string payload_;
378 std::string label_;
379 bool sync_ = false;
380
381 IPC::mojom::ReflectorPtr ping_receiver_;
382
383 int count_down_ = 0;
384 int frame_count_down_ = 0;
385
386 base::RepeatingTimer timer_;
387 std::unique_ptr<PerfCpuLogger> cpu_logger_;
388
389 base::Closure quit_closure_;
390
391 DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
392};
393
394DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
395 base::MessageLoop main_message_loop;
396 return RunPingPongClient(h);
397}
398
399// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
400// instead of raw IPC::Messages.
401TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
402 RunTestClient("PingPongClient", [&](MojoHandle h) {
403 base::MessageLoop main_message_loop;
404 RunPingPongServer(h, "Mojo_CPU_Async", false);
405 });
406}
407
408TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
409 RunTestClient("PingPongClient", [&](MojoHandle h) {
410 base::MessageLoop main_message_loop;
411 RunPingPongServer(h, "Mojo_CPU_Sync", true);
412 });
413}
414
415} // namespace
416} // namespace IPC