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