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/memory/ptr_util.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_current.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/perf_time_logger.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "mojo/core/embedder/embedder.h"
18 #include "mojo/core/test/mojo_test_base.h"
19 #include "mojo/public/cpp/bindings/strong_binding.h"
20 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace mojo {
24 namespace {
25
26 class EchoServiceImpl : public test::EchoService {
27 public:
28 explicit EchoServiceImpl(const base::Closure& quit_closure);
29 ~EchoServiceImpl() override;
30
31 // |EchoService| methods:
32 void Echo(const std::string& test_data,
33 const EchoCallback& callback) override;
34
35 private:
36 const base::Closure quit_closure_;
37 };
38
EchoServiceImpl(const base::Closure & quit_closure)39 EchoServiceImpl::EchoServiceImpl(const base::Closure& quit_closure)
40 : quit_closure_(quit_closure) {}
41
~EchoServiceImpl()42 EchoServiceImpl::~EchoServiceImpl() {
43 quit_closure_.Run();
44 }
45
Echo(const std::string & test_data,const EchoCallback & callback)46 void EchoServiceImpl::Echo(const std::string& test_data,
47 const EchoCallback& callback) {
48 callback.Run(test_data);
49 }
50
51 class PingPongTest {
52 public:
53 explicit PingPongTest(test::EchoServicePtr service);
54
55 void RunTest(int iterations, int batch_size, int message_size);
56
57 private:
58 void DoPing();
59 void OnPingDone(const std::string& reply);
60
61 test::EchoServicePtr service_;
62 const base::Callback<void(const std::string&)> ping_done_callback_;
63
64 int iterations_;
65 int batch_size_;
66 std::string message_;
67
68 int current_iterations_;
69 int calls_outstanding_;
70
71 base::Closure quit_closure_;
72 };
73
PingPongTest(test::EchoServicePtr service)74 PingPongTest::PingPongTest(test::EchoServicePtr service)
75 : service_(std::move(service)),
76 ping_done_callback_(
77 base::Bind(&PingPongTest::OnPingDone, base::Unretained(this))) {}
78
RunTest(int iterations,int batch_size,int message_size)79 void PingPongTest::RunTest(int iterations, int batch_size, int message_size) {
80 iterations_ = iterations;
81 batch_size_ = batch_size;
82 message_ = std::string(message_size, 'a');
83 current_iterations_ = 0;
84 calls_outstanding_ = 0;
85
86 base::MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
87 base::RunLoop run_loop;
88 quit_closure_ = run_loop.QuitClosure();
89 base::ThreadTaskRunnerHandle::Get()->PostTask(
90 FROM_HERE, base::Bind(&PingPongTest::DoPing, base::Unretained(this)));
91 run_loop.Run();
92 }
93
DoPing()94 void PingPongTest::DoPing() {
95 DCHECK_EQ(0, calls_outstanding_);
96 current_iterations_++;
97 if (current_iterations_ > iterations_) {
98 quit_closure_.Run();
99 return;
100 }
101
102 calls_outstanding_ = batch_size_;
103 for (int i = 0; i < batch_size_; i++) {
104 service_->Echo(message_, ping_done_callback_);
105 }
106 }
107
OnPingDone(const std::string & reply)108 void PingPongTest::OnPingDone(const std::string& reply) {
109 DCHECK_GT(calls_outstanding_, 0);
110 calls_outstanding_--;
111
112 if (!calls_outstanding_)
113 DoPing();
114 }
115
116 class MojoE2EPerftest : public core::test::MojoTestBase {
117 public:
RunTestOnTaskRunner(base::TaskRunner * runner,MojoHandle client_mp,const std::string & test_name)118 void RunTestOnTaskRunner(base::TaskRunner* runner,
119 MojoHandle client_mp,
120 const std::string& test_name) {
121 if (runner == base::ThreadTaskRunnerHandle::Get().get()) {
122 RunTests(client_mp, test_name);
123 } else {
124 base::RunLoop run_loop;
125 runner->PostTaskAndReply(
126 FROM_HERE,
127 base::Bind(&MojoE2EPerftest::RunTests, base::Unretained(this),
128 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 MakeStrongBinding(std::make_unique<EchoServiceImpl>(cb), std::move(request));
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 auto request = InterfaceRequest<test::EchoService>(
174 ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
175 base::RunLoop run_loop;
176 core::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 RunTestClient("PingService", [&](MojoHandle 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 });
193 }
194
TEST_F(MojoE2EPerftest,MultiProcessEchoIoThread)195 TEST_F(MojoE2EPerftest, MultiProcessEchoIoThread) {
196 RunTestClient("PingService", [&](MojoHandle mp) {
197 MojoHandle client_mp, service_mp;
198 CreateMessagePipe(&client_mp, &service_mp);
199 WriteMessageWithHandles(mp, "hello", &service_mp, 1);
200 RunTestOnTaskRunner(core::GetIOTaskRunner().get(), client_mp,
201 "MultiProcessEchoIoThread");
202 });
203 }
204
205 } // namespace
206 } // namespace mojo
207