blob: ec6f9d1776e2cbe6f93834538cea1922a75883d1 [file] [log] [blame]
morrita373af03b2014-09-09 19:35:241// Copyright (c) 2012 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 "ipc/ipc_perftest_support.h"
6
avi246998d82015-12-22 02:39:047#include <stddef.h>
8#include <stdint.h>
9
morrita373af03b2014-09-09 19:35:2410#include <algorithm>
danakj03de39b22016-04-23 04:21:0911#include <memory>
morrita373af03b2014-09-09 19:35:2412#include <string>
13
morrita373af03b2014-09-09 19:35:2414#include "base/logging.h"
tfarina4da82752015-09-16 09:56:2115#include "base/macros.h"
danakj03de39b22016-04-23 04:21:0916#include "base/memory/ptr_util.h"
morrita373af03b2014-09-09 19:35:2417#include "base/pickle.h"
fdoray8e32586852016-06-22 19:56:1618#include "base/run_loop.h"
morrita373af03b2014-09-09 19:35:2419#include "base/strings/stringprintf.h"
20#include "base/test/perf_time_logger.h"
21#include "base/test/test_io_thread.h"
22#include "base/threading/thread.h"
rockota34707ca2016-07-20 04:28:3223#include "base/threading/thread_task_runner_handle.h"
morrita373af03b2014-09-09 19:35:2424#include "base/time/time.h"
25#include "build/build_config.h"
26#include "ipc/ipc_channel.h"
27#include "ipc/ipc_channel_proxy.h"
28#include "ipc/ipc_descriptors.h"
29#include "ipc/ipc_message_utils.h"
30#include "ipc/ipc_sender.h"
31
32namespace IPC {
33namespace test {
34
brucedawsonaa3d0c52015-02-12 22:33:2235// Avoid core 0 due to conflicts with Intel's Power Gadget.
36// Setting thread affinity will fail harmlessly on single/dual core machines.
37const int kSharedCore = 2;
38
morrita373af03b2014-09-09 19:35:2439// This class simply collects stats about abstract "events" (each of which has a
40// start time and an end time).
41class EventTimeTracker {
42 public:
43 explicit EventTimeTracker(const char* name)
44 : name_(name),
45 count_(0) {
46 }
47
48 void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
49 DCHECK(end >= start);
50 count_++;
51 base::TimeDelta duration = end - start;
52 total_duration_ += duration;
53 max_duration_ = std::max(max_duration_, duration);
54 }
55
56 void ShowResults() const {
57 VLOG(1) << name_ << " count: " << count_;
58 VLOG(1) << name_ << " total duration: "
59 << total_duration_.InMillisecondsF() << " ms";
60 VLOG(1) << name_ << " average duration: "
61 << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
62 << " ms";
63 VLOG(1) << name_ << " maximum duration: "
64 << max_duration_.InMillisecondsF() << " ms";
65 }
66
67 void Reset() {
68 count_ = 0;
69 total_duration_ = base::TimeDelta();
70 max_duration_ = base::TimeDelta();
71 }
72
73 private:
74 const std::string name_;
75
tfarina10a5c062015-09-04 18:47:5776 uint64_t count_;
morrita373af03b2014-09-09 19:35:2477 base::TimeDelta total_duration_;
78 base::TimeDelta max_duration_;
79
80 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
81};
82
83// This channel listener just replies to all messages with the exact same
84// message. It assumes each message has one string parameter. When the string
85// "quit" is sent, it will exit.
86class ChannelReflectorListener : public Listener {
87 public:
88 ChannelReflectorListener()
89 : channel_(NULL),
90 latency_tracker_("Client messages") {
91 VLOG(1) << "Client listener up";
92 }
93
dchengfe61fca2014-10-22 02:29:5294 ~ChannelReflectorListener() override {
morrita373af03b2014-09-09 19:35:2495 VLOG(1) << "Client listener down";
96 latency_tracker_.ShowResults();
97 }
98
99 void Init(Channel* channel) {
100 DCHECK(!channel_);
101 channel_ = channel;
102 }
103
dchengfe61fca2014-10-22 02:29:52104 bool OnMessageReceived(const Message& message) override {
morrita373af03b2014-09-09 19:35:24105 CHECK(channel_);
106
brettwbd4d7112015-06-03 04:29:25107 base::PickleIterator iter(message);
tfarina10a5c062015-09-04 18:47:57108 int64_t time_internal;
morrita373af03b2014-09-09 19:35:24109 EXPECT_TRUE(iter.ReadInt64(&time_internal));
110 int msgid;
111 EXPECT_TRUE(iter.ReadInt(&msgid));
brucedawsoneaa38962015-03-10 01:46:50112 base::StringPiece payload;
113 EXPECT_TRUE(iter.ReadStringPiece(&payload));
morrita373af03b2014-09-09 19:35:24114
115 // Include message deserialization in latency.
116 base::TimeTicks now = base::TimeTicks::Now();
117
118 if (payload == "hello") {
119 latency_tracker_.Reset();
120 } else if (payload == "quit") {
121 latency_tracker_.ShowResults();
122 base::MessageLoop::current()->QuitWhenIdle();
123 return true;
124 } else {
125 // Don't track hello and quit messages.
126 latency_tracker_.AddEvent(
127 base::TimeTicks::FromInternalValue(time_internal), now);
128 }
129
130 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
131 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
132 msg->WriteInt(msgid);
133 msg->WriteString(payload);
134 channel_->Send(msg);
135 return true;
136 }
137
138 private:
139 Channel* channel_;
140 EventTimeTracker latency_tracker_;
141};
142
143class PerformanceChannelListener : public Listener {
144 public:
145 explicit PerformanceChannelListener(const std::string& label)
146 : label_(label),
147 sender_(NULL),
148 msg_count_(0),
149 msg_size_(0),
150 count_down_(0),
151 latency_tracker_("Server messages") {
152 VLOG(1) << "Server listener up";
153 }
154
dchengfe61fca2014-10-22 02:29:52155 ~PerformanceChannelListener() override {
dchengf3076af2014-10-21 18:02:42156 VLOG(1) << "Server listener down";
157 }
morrita373af03b2014-09-09 19:35:24158
159 void Init(Sender* sender) {
160 DCHECK(!sender_);
161 sender_ = sender;
162 }
163
164 // Call this before running the message loop.
165 void SetTestParams(int msg_count, size_t msg_size) {
166 DCHECK_EQ(0, count_down_);
167 msg_count_ = msg_count;
168 msg_size_ = msg_size;
169 count_down_ = msg_count_;
170 payload_ = std::string(msg_size_, 'a');
171 }
172
dchengfe61fca2014-10-22 02:29:52173 bool OnMessageReceived(const Message& message) override {
morrita373af03b2014-09-09 19:35:24174 CHECK(sender_);
175
brettwbd4d7112015-06-03 04:29:25176 base::PickleIterator iter(message);
tfarina10a5c062015-09-04 18:47:57177 int64_t time_internal;
morrita373af03b2014-09-09 19:35:24178 EXPECT_TRUE(iter.ReadInt64(&time_internal));
179 int msgid;
180 EXPECT_TRUE(iter.ReadInt(&msgid));
181 std::string reflected_payload;
182 EXPECT_TRUE(iter.ReadString(&reflected_payload));
183
184 // Include message deserialization in latency.
185 base::TimeTicks now = base::TimeTicks::Now();
186
187 if (reflected_payload == "hello") {
188 // Start timing on hello.
189 latency_tracker_.Reset();
190 DCHECK(!perf_logger_.get());
191 std::string test_name =
192 base::StringPrintf("IPC_%s_Perf_%dx_%u",
193 label_.c_str(),
194 msg_count_,
195 static_cast<unsigned>(msg_size_));
196 perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
197 } else {
198 DCHECK_EQ(payload_.size(), reflected_payload.size());
199
200 latency_tracker_.AddEvent(
201 base::TimeTicks::FromInternalValue(time_internal), now);
202
203 CHECK(count_down_ > 0);
204 count_down_--;
205 if (count_down_ == 0) {
206 perf_logger_.reset(); // Stop the perf timer now.
207 latency_tracker_.ShowResults();
208 base::MessageLoop::current()->QuitWhenIdle();
209 return true;
210 }
211 }
212
213 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
214 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
215 msg->WriteInt(count_down_);
216 msg->WriteString(payload_);
217 sender_->Send(msg);
218 return true;
219 }
220
221 private:
222 std::string label_;
223 Sender* sender_;
224 int msg_count_;
225 size_t msg_size_;
226
227 int count_down_;
228 std::string payload_;
229 EventTimeTracker latency_tracker_;
danakj03de39b22016-04-23 04:21:09230 std::unique_ptr<base::PerfTimeLogger> perf_logger_;
morrita373af03b2014-09-09 19:35:24231};
232
sammce4d0abd2016-03-07 22:38:04233IPCChannelPerfTestBase::IPCChannelPerfTestBase() = default;
234IPCChannelPerfTestBase::~IPCChannelPerfTestBase() = default;
235
morrita373af03b2014-09-09 19:35:24236std::vector<PingPongTestParams>
237IPCChannelPerfTestBase::GetDefaultTestParams() {
238 // Test several sizes. We use 12^N for message size, and limit the message
239 // count to keep the test duration reasonable.
240 std::vector<PingPongTestParams> list;
241 list.push_back(PingPongTestParams(12, 50000));
242 list.push_back(PingPongTestParams(144, 50000));
243 list.push_back(PingPongTestParams(1728, 50000));
244 list.push_back(PingPongTestParams(20736, 12000));
brucedawson903fdfd2015-02-05 00:57:52245 list.push_back(PingPongTestParams(248832, 1000));
morrita373af03b2014-09-09 19:35:24246 return list;
247}
248
249void IPCChannelPerfTestBase::RunTestChannelPingPong(
250 const std::vector<PingPongTestParams>& params) {
251 Init("PerformanceClient");
252
253 // Set up IPC channel and start client.
254 PerformanceChannelListener listener("Channel");
255 CreateChannel(&listener);
256 listener.Init(channel());
257 ASSERT_TRUE(ConnectChannel());
258 ASSERT_TRUE(StartClient());
259
brucedawsonaa3d0c52015-02-12 22:33:22260 LockThreadAffinity thread_locker(kSharedCore);
morrita373af03b2014-09-09 19:35:24261 for (size_t i = 0; i < params.size(); i++) {
262 listener.SetTestParams(params[i].message_count(),
263 params[i].message_size());
264
265 // This initial message will kick-start the ping-pong of messages.
266 Message* message =
267 new Message(0, 2, Message::PRIORITY_NORMAL);
268 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
269 message->WriteInt(-1);
270 message->WriteString("hello");
271 sender()->Send(message);
272
273 // Run message loop.
fdoray8e32586852016-06-22 19:56:16274 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24275 }
276
277 // Send quit message.
278 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
279 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
280 message->WriteInt(-1);
281 message->WriteString("quit");
282 sender()->Send(message);
283
284 EXPECT_TRUE(WaitForClientShutdown());
285 DestroyChannel();
286}
287
288void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
289 const std::vector<PingPongTestParams>& params) {
sammce4d0abd2016-03-07 22:38:04290 io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
morrita373af03b2014-09-09 19:35:24291 InitWithCustomMessageLoop("PerformanceClient",
danakj03de39b22016-04-23 04:21:09292 base::WrapUnique(new base::MessageLoop()));
morrita373af03b2014-09-09 19:35:24293
294 // Set up IPC channel and start client.
295 PerformanceChannelListener listener("ChannelProxy");
sammce4d0abd2016-03-07 22:38:04296 CreateChannelProxy(&listener, io_thread_->task_runner());
morrita373af03b2014-09-09 19:35:24297 listener.Init(channel_proxy());
298 ASSERT_TRUE(StartClient());
299
brucedawsonaa3d0c52015-02-12 22:33:22300 LockThreadAffinity thread_locker(kSharedCore);
morrita373af03b2014-09-09 19:35:24301 for (size_t i = 0; i < params.size(); i++) {
302 listener.SetTestParams(params[i].message_count(),
303 params[i].message_size());
304
305 // This initial message will kick-start the ping-pong of messages.
306 Message* message =
307 new Message(0, 2, Message::PRIORITY_NORMAL);
308 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
309 message->WriteInt(-1);
310 message->WriteString("hello");
311 sender()->Send(message);
312
313 // Run message loop.
fdoray8e32586852016-06-22 19:56:16314 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24315 }
316
317 // Send quit message.
318 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
319 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
320 message->WriteInt(-1);
321 message->WriteString("quit");
322 sender()->Send(message);
323
324 EXPECT_TRUE(WaitForClientShutdown());
325 DestroyChannelProxy();
sammce4d0abd2016-03-07 22:38:04326
327 io_thread_.reset();
morrita373af03b2014-09-09 19:35:24328}
329
330
331PingPongTestClient::PingPongTestClient()
332 : listener_(new ChannelReflectorListener()) {
333}
334
335PingPongTestClient::~PingPongTestClient() {
336}
337
danakj03de39b22016-04-23 04:21:09338std::unique_ptr<Channel> PingPongTestClient::CreateChannel(Listener* listener) {
rockota34707ca2016-07-20 04:28:32339 return Channel::CreateClient(
340 IPCTestBase::GetChannelName("PerformanceClient"), listener,
341 base::ThreadTaskRunnerHandle::Get());
morrita373af03b2014-09-09 19:35:24342}
343
344int PingPongTestClient::RunMain() {
brucedawsonaa3d0c52015-02-12 22:33:22345 LockThreadAffinity thread_locker(kSharedCore);
danakj03de39b22016-04-23 04:21:09346 std::unique_ptr<Channel> channel = CreateChannel(listener_.get());
morrita373af03b2014-09-09 19:35:24347 listener_->Init(channel.get());
348 CHECK(channel->Connect());
349
fdoray8e32586852016-06-22 19:56:16350 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24351 return 0;
352}
353
354scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() {
skyostile687bdff2015-05-12 11:29:21355 return main_message_loop_.task_runner();
morrita373af03b2014-09-09 19:35:24356}
357
brucedawsonaa3d0c52015-02-12 22:33:22358LockThreadAffinity::LockThreadAffinity(int cpu_number)
359 : affinity_set_ok_(false) {
360#if defined(OS_WIN)
brucedawsond3beca72015-12-10 02:22:44361 const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
brucedawsonaa3d0c52015-02-12 22:33:22362 old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
363 affinity_set_ok_ = old_affinity_ != 0;
364#elif defined(OS_LINUX)
365 cpu_set_t cpuset;
366 CPU_ZERO(&cpuset);
367 CPU_SET(cpu_number, &cpuset);
368 auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
369 DCHECK_EQ(0, get_result);
370 auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
371 // Check for get_result failure, even though it should always succeed.
372 affinity_set_ok_ = (set_result == 0) && (get_result == 0);
373#endif
374 if (!affinity_set_ok_)
375 LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
376}
377
378LockThreadAffinity::~LockThreadAffinity() {
379 if (!affinity_set_ok_)
380 return;
381#if defined(OS_WIN)
382 auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
383 DCHECK_NE(0u, set_result);
384#elif defined(OS_LINUX)
385 auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
386 DCHECK_EQ(0, set_result);
387#endif
388}
389
morrita373af03b2014-09-09 19:35:24390} // namespace test
391} // namespace IPC