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