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