1 // Copyright 2016 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 <string>
6 #include <utility>
7
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/test/perf_time_logger.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "mojo/edk/test/mojo_test_base.h"
16 #include "mojo/edk/test/scoped_ipc_support.h"
17 #include "mojo/public/cpp/bindings/strong_binding.h"
18 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace mojo {
22 namespace {
23
24 class EchoServiceImpl : public test::EchoService {
25 public:
26 EchoServiceImpl(InterfaceRequest<EchoService> request,
27 const base::Closure& quit_closure);
28 ~EchoServiceImpl() override;
29
30 // |EchoService| methods:
31 void Echo(const std::string& test_data,
32 const EchoCallback& callback) override;
33
34 private:
35 const StrongBinding<EchoService> binding_;
36 const base::Closure quit_closure_;
37 };
38
EchoServiceImpl(InterfaceRequest<EchoService> request,const base::Closure & quit_closure)39 EchoServiceImpl::EchoServiceImpl(InterfaceRequest<EchoService> request,
40 const base::Closure& quit_closure)
41 : binding_(this, std::move(request)), quit_closure_(quit_closure) {}
42
~EchoServiceImpl()43 EchoServiceImpl::~EchoServiceImpl() {
44 quit_closure_.Run();
45 }
46
Echo(const std::string & test_data,const EchoCallback & callback)47 void EchoServiceImpl::Echo(const std::string& test_data,
48 const EchoCallback& callback) {
49 callback.Run(test_data);
50 }
51
52 class PingPongTest {
53 public:
54 explicit PingPongTest(test::EchoServicePtr service);
55
56 void RunTest(int iterations, int batch_size, int message_size);
57
58 private:
59 void DoPing();
60 void OnPingDone(const std::string& reply);
61
62 test::EchoServicePtr service_;
63 const base::Callback<void(const std::string&)> ping_done_callback_;
64
65 int iterations_;
66 int batch_size_;
67 std::string message_;
68
69 int current_iterations_;
70 int calls_outstanding_;
71
72 base::Closure quit_closure_;
73 };
74
PingPongTest(test::EchoServicePtr service)75 PingPongTest::PingPongTest(test::EchoServicePtr service)
76 : service_(std::move(service)),
77 ping_done_callback_(
78 base::Bind(&PingPongTest::OnPingDone, base::Unretained(this))) {}
79
RunTest(int iterations,int batch_size,int message_size)80 void PingPongTest::RunTest(int iterations, int batch_size, int message_size) {
81 iterations_ = iterations;
82 batch_size_ = batch_size;
83 message_ = std::string(message_size, 'a');
84 current_iterations_ = 0;
85 calls_outstanding_ = 0;
86
87 base::MessageLoop::current()->SetNestableTasksAllowed(true);
88 base::RunLoop run_loop;
89 quit_closure_ = run_loop.QuitClosure();
90 base::ThreadTaskRunnerHandle::Get()->PostTask(
91 FROM_HERE, base::Bind(&PingPongTest::DoPing, base::Unretained(this)));
92 run_loop.Run();
93 }
94
DoPing()95 void PingPongTest::DoPing() {
96 DCHECK_EQ(0, calls_outstanding_);
97 current_iterations_++;
98 if (current_iterations_ > iterations_) {
99 quit_closure_.Run();
100 return;
101 }
102
103 calls_outstanding_ = batch_size_;
104 for (int i = 0; i < batch_size_; i++) {
105 service_->Echo(message_, ping_done_callback_);
106 }
107 }
108
OnPingDone(const std::string & reply)109 void PingPongTest::OnPingDone(const std::string& reply) {
110 DCHECK_GT(calls_outstanding_, 0);
111 calls_outstanding_--;
112
113 if (!calls_outstanding_)
114 DoPing();
115 }
116
117 class MojoE2EPerftest : public edk::test::MojoTestBase {
118 public:
RunTestOnTaskRunner(base::TaskRunner * runner,MojoHandle client_mp,const std::string & test_name)119 void RunTestOnTaskRunner(base::TaskRunner* runner,
120 MojoHandle client_mp,
121 const std::string& test_name) {
122 if (runner == base::ThreadTaskRunnerHandle::Get().get()) {
123 RunTests(client_mp, test_name);
124 } else {
125 base::RunLoop run_loop;
126 runner->PostTaskAndReply(
127 FROM_HERE, base::Bind(&MojoE2EPerftest::RunTests,
128 base::Unretained(this), client_mp, test_name),
129 run_loop.QuitClosure());
130 run_loop.Run();
131 }
132 }
133
134 protected:
135 base::MessageLoop message_loop_;
136
137 private:
RunTests(MojoHandle client_mp,const std::string & test_name)138 void RunTests(MojoHandle client_mp, const std::string& test_name) {
139 const int kMessages = 10000;
140 const int kBatchSizes[] = {1, 10, 100};
141 const int kMessageSizes[] = {8, 64, 512, 4096, 65536};
142
143 test::EchoServicePtr service;
144 service.Bind(InterfacePtrInfo<test::EchoService>(
145 ScopedMessagePipeHandle(MessagePipeHandle(client_mp)),
146 service.version()));
147 PingPongTest test(std::move(service));
148
149 for (int batch_size : kBatchSizes) {
150 for (int message_size : kMessageSizes) {
151 int num_messages = kMessages;
152 if (message_size == 65536)
153 num_messages /= 10;
154 std::string sub_test_name = base::StringPrintf(
155 "%s/%dx%d/%dbytes", test_name.c_str(), num_messages / batch_size,
156 batch_size, message_size);
157 base::PerfTimeLogger timer(sub_test_name.c_str());
158 test.RunTest(num_messages / batch_size, batch_size, message_size);
159 }
160 }
161 }
162 };
163
CreateAndRunService(InterfaceRequest<test::EchoService> request,const base::Closure & cb)164 void CreateAndRunService(InterfaceRequest<test::EchoService> request,
165 const base::Closure& cb) {
166 new EchoServiceImpl(std::move(request), cb);
167 }
168
DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService,MojoE2EPerftest,mp)169 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService, MojoE2EPerftest, mp) {
170 MojoHandle service_mp;
171 EXPECT_EQ("hello", ReadMessageWithHandles(mp, &service_mp, 1));
172
173 InterfaceRequest<test::EchoService> request;
174 request.Bind(ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
175 base::RunLoop run_loop;
176 edk::test::GetIoTaskRunner()->PostTask(
177 FROM_HERE,
178 base::Bind(&CreateAndRunService, base::Passed(&request),
179 base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
180 message_loop_.task_runner(), FROM_HERE,
181 run_loop.QuitClosure())));
182 run_loop.Run();
183 }
184
TEST_F(MojoE2EPerftest,MultiProcessEchoMainThread)185 TEST_F(MojoE2EPerftest, MultiProcessEchoMainThread) {
186 RUN_CHILD_ON_PIPE(PingService, mp)
187 MojoHandle client_mp, service_mp;
188 CreateMessagePipe(&client_mp, &service_mp);
189 WriteMessageWithHandles(mp, "hello", &service_mp, 1);
190 RunTestOnTaskRunner(message_loop_.task_runner().get(), client_mp,
191 "MultiProcessEchoMainThread");
192 END_CHILD()
193 }
194
TEST_F(MojoE2EPerftest,MultiProcessEchoIoThread)195 TEST_F(MojoE2EPerftest, MultiProcessEchoIoThread) {
196 RUN_CHILD_ON_PIPE(PingService, mp)
197 MojoHandle client_mp, service_mp;
198 CreateMessagePipe(&client_mp, &service_mp);
199 WriteMessageWithHandles(mp, "hello", &service_mp, 1);
200 RunTestOnTaskRunner(edk::test::GetIoTaskRunner(), client_mp,
201 "MultiProcessEchoIoThread");
202 END_CHILD()
203 }
204
205 } // namespace
206 } // namespace mojo
207