blob: 2932ce838f307e2f635b4bbc4f29f0d13ed1b603 [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"
23#include "base/time/time.h"
24#include "build/build_config.h"
25#include "ipc/ipc_channel.h"
26#include "ipc/ipc_channel_proxy.h"
27#include "ipc/ipc_descriptors.h"
28#include "ipc/ipc_message_utils.h"
29#include "ipc/ipc_sender.h"
30
31namespace IPC {
32namespace test {
33
brucedawsonaa3d0c52015-02-12 22:33:2234// Avoid core 0 due to conflicts with Intel's Power Gadget.
35// Setting thread affinity will fail harmlessly on single/dual core machines.
36const int kSharedCore = 2;
37
morrita373af03b2014-09-09 19:35:2438// This class simply collects stats about abstract "events" (each of which has a
39// start time and an end time).
40class EventTimeTracker {
41 public:
42 explicit EventTimeTracker(const char* name)
43 : name_(name),
44 count_(0) {
45 }
46
47 void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
48 DCHECK(end >= start);
49 count_++;
50 base::TimeDelta duration = end - start;
51 total_duration_ += duration;
52 max_duration_ = std::max(max_duration_, duration);
53 }
54
55 void ShowResults() const {
56 VLOG(1) << name_ << " count: " << count_;
57 VLOG(1) << name_ << " total duration: "
58 << total_duration_.InMillisecondsF() << " ms";
59 VLOG(1) << name_ << " average duration: "
60 << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
61 << " ms";
62 VLOG(1) << name_ << " maximum duration: "
63 << max_duration_.InMillisecondsF() << " ms";
64 }
65
66 void Reset() {
67 count_ = 0;
68 total_duration_ = base::TimeDelta();
69 max_duration_ = base::TimeDelta();
70 }
71
72 private:
73 const std::string name_;
74
tfarina10a5c062015-09-04 18:47:5775 uint64_t count_;
morrita373af03b2014-09-09 19:35:2476 base::TimeDelta total_duration_;
77 base::TimeDelta max_duration_;
78
79 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
80};
81
82// This channel listener just replies to all messages with the exact same
83// message. It assumes each message has one string parameter. When the string
84// "quit" is sent, it will exit.
85class ChannelReflectorListener : public Listener {
86 public:
87 ChannelReflectorListener()
88 : channel_(NULL),
89 latency_tracker_("Client messages") {
90 VLOG(1) << "Client listener up";
91 }
92
dchengfe61fca2014-10-22 02:29:5293 ~ChannelReflectorListener() override {
morrita373af03b2014-09-09 19:35:2494 VLOG(1) << "Client listener down";
95 latency_tracker_.ShowResults();
96 }
97
98 void Init(Channel* channel) {
99 DCHECK(!channel_);
100 channel_ = channel;
101 }
102
dchengfe61fca2014-10-22 02:29:52103 bool OnMessageReceived(const Message& message) override {
morrita373af03b2014-09-09 19:35:24104 CHECK(channel_);
105
brettwbd4d7112015-06-03 04:29:25106 base::PickleIterator iter(message);
tfarina10a5c062015-09-04 18:47:57107 int64_t time_internal;
morrita373af03b2014-09-09 19:35:24108 EXPECT_TRUE(iter.ReadInt64(&time_internal));
109 int msgid;
110 EXPECT_TRUE(iter.ReadInt(&msgid));
brucedawsoneaa38962015-03-10 01:46:50111 base::StringPiece payload;
112 EXPECT_TRUE(iter.ReadStringPiece(&payload));
morrita373af03b2014-09-09 19:35:24113
114 // Include message deserialization in latency.
115 base::TimeTicks now = base::TimeTicks::Now();
116
117 if (payload == "hello") {
118 latency_tracker_.Reset();
119 } else if (payload == "quit") {
120 latency_tracker_.ShowResults();
121 base::MessageLoop::current()->QuitWhenIdle();
122 return true;
123 } else {
124 // Don't track hello and quit messages.
125 latency_tracker_.AddEvent(
126 base::TimeTicks::FromInternalValue(time_internal), now);
127 }
128
129 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
130 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
131 msg->WriteInt(msgid);
132 msg->WriteString(payload);
133 channel_->Send(msg);
134 return true;
135 }
136
137 private:
138 Channel* channel_;
139 EventTimeTracker latency_tracker_;
140};
141
142class PerformanceChannelListener : public Listener {
143 public:
144 explicit PerformanceChannelListener(const std::string& label)
145 : label_(label),
146 sender_(NULL),
147 msg_count_(0),
148 msg_size_(0),
149 count_down_(0),
150 latency_tracker_("Server messages") {
151 VLOG(1) << "Server listener up";
152 }
153
dchengfe61fca2014-10-22 02:29:52154 ~PerformanceChannelListener() override {
dchengf3076af2014-10-21 18:02:42155 VLOG(1) << "Server listener down";
156 }
morrita373af03b2014-09-09 19:35:24157
158 void Init(Sender* sender) {
159 DCHECK(!sender_);
160 sender_ = sender;
161 }
162
163 // Call this before running the message loop.
164 void SetTestParams(int msg_count, size_t msg_size) {
165 DCHECK_EQ(0, count_down_);
166 msg_count_ = msg_count;
167 msg_size_ = msg_size;
168 count_down_ = msg_count_;
169 payload_ = std::string(msg_size_, 'a');
170 }
171
dchengfe61fca2014-10-22 02:29:52172 bool OnMessageReceived(const Message& message) override {
morrita373af03b2014-09-09 19:35:24173 CHECK(sender_);
174
brettwbd4d7112015-06-03 04:29:25175 base::PickleIterator iter(message);
tfarina10a5c062015-09-04 18:47:57176 int64_t time_internal;
morrita373af03b2014-09-09 19:35:24177 EXPECT_TRUE(iter.ReadInt64(&time_internal));
178 int msgid;
179 EXPECT_TRUE(iter.ReadInt(&msgid));
180 std::string reflected_payload;
181 EXPECT_TRUE(iter.ReadString(&reflected_payload));
182
183 // Include message deserialization in latency.
184 base::TimeTicks now = base::TimeTicks::Now();
185
186 if (reflected_payload == "hello") {
187 // Start timing on hello.
188 latency_tracker_.Reset();
189 DCHECK(!perf_logger_.get());
190 std::string test_name =
191 base::StringPrintf("IPC_%s_Perf_%dx_%u",
192 label_.c_str(),
193 msg_count_,
194 static_cast<unsigned>(msg_size_));
195 perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
196 } else {
197 DCHECK_EQ(payload_.size(), reflected_payload.size());
198
199 latency_tracker_.AddEvent(
200 base::TimeTicks::FromInternalValue(time_internal), now);
201
202 CHECK(count_down_ > 0);
203 count_down_--;
204 if (count_down_ == 0) {
205 perf_logger_.reset(); // Stop the perf timer now.
206 latency_tracker_.ShowResults();
207 base::MessageLoop::current()->QuitWhenIdle();
208 return true;
209 }
210 }
211
212 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
213 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
214 msg->WriteInt(count_down_);
215 msg->WriteString(payload_);
216 sender_->Send(msg);
217 return true;
218 }
219
220 private:
221 std::string label_;
222 Sender* sender_;
223 int msg_count_;
224 size_t msg_size_;
225
226 int count_down_;
227 std::string payload_;
228 EventTimeTracker latency_tracker_;
danakj03de39b22016-04-23 04:21:09229 std::unique_ptr<base::PerfTimeLogger> perf_logger_;
morrita373af03b2014-09-09 19:35:24230};
231
sammce4d0abd2016-03-07 22:38:04232IPCChannelPerfTestBase::IPCChannelPerfTestBase() = default;
233IPCChannelPerfTestBase::~IPCChannelPerfTestBase() = default;
234
morrita373af03b2014-09-09 19:35:24235std::vector<PingPongTestParams>
236IPCChannelPerfTestBase::GetDefaultTestParams() {
237 // Test several sizes. We use 12^N for message size, and limit the message
238 // count to keep the test duration reasonable.
239 std::vector<PingPongTestParams> list;
240 list.push_back(PingPongTestParams(12, 50000));
241 list.push_back(PingPongTestParams(144, 50000));
242 list.push_back(PingPongTestParams(1728, 50000));
243 list.push_back(PingPongTestParams(20736, 12000));
brucedawson903fdfd2015-02-05 00:57:52244 list.push_back(PingPongTestParams(248832, 1000));
morrita373af03b2014-09-09 19:35:24245 return list;
246}
247
248void IPCChannelPerfTestBase::RunTestChannelPingPong(
249 const std::vector<PingPongTestParams>& params) {
250 Init("PerformanceClient");
251
252 // Set up IPC channel and start client.
253 PerformanceChannelListener listener("Channel");
254 CreateChannel(&listener);
255 listener.Init(channel());
256 ASSERT_TRUE(ConnectChannel());
257 ASSERT_TRUE(StartClient());
258
brucedawsonaa3d0c52015-02-12 22:33:22259 LockThreadAffinity thread_locker(kSharedCore);
morrita373af03b2014-09-09 19:35:24260 for (size_t i = 0; i < params.size(); i++) {
261 listener.SetTestParams(params[i].message_count(),
262 params[i].message_size());
263
264 // This initial message will kick-start the ping-pong of messages.
265 Message* message =
266 new Message(0, 2, Message::PRIORITY_NORMAL);
267 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
268 message->WriteInt(-1);
269 message->WriteString("hello");
270 sender()->Send(message);
271
272 // Run message loop.
fdoray8e32586852016-06-22 19:56:16273 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24274 }
275
276 // Send quit message.
277 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
278 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
279 message->WriteInt(-1);
280 message->WriteString("quit");
281 sender()->Send(message);
282
283 EXPECT_TRUE(WaitForClientShutdown());
284 DestroyChannel();
285}
286
287void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
288 const std::vector<PingPongTestParams>& params) {
sammce4d0abd2016-03-07 22:38:04289 io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
morrita373af03b2014-09-09 19:35:24290 InitWithCustomMessageLoop("PerformanceClient",
danakj03de39b22016-04-23 04:21:09291 base::WrapUnique(new base::MessageLoop()));
morrita373af03b2014-09-09 19:35:24292
293 // Set up IPC channel and start client.
294 PerformanceChannelListener listener("ChannelProxy");
sammce4d0abd2016-03-07 22:38:04295 CreateChannelProxy(&listener, io_thread_->task_runner());
morrita373af03b2014-09-09 19:35:24296 listener.Init(channel_proxy());
297 ASSERT_TRUE(StartClient());
298
brucedawsonaa3d0c52015-02-12 22:33:22299 LockThreadAffinity thread_locker(kSharedCore);
morrita373af03b2014-09-09 19:35:24300 for (size_t i = 0; i < params.size(); i++) {
301 listener.SetTestParams(params[i].message_count(),
302 params[i].message_size());
303
304 // This initial message will kick-start the ping-pong of messages.
305 Message* message =
306 new Message(0, 2, Message::PRIORITY_NORMAL);
307 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
308 message->WriteInt(-1);
309 message->WriteString("hello");
310 sender()->Send(message);
311
312 // Run message loop.
fdoray8e32586852016-06-22 19:56:16313 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24314 }
315
316 // Send quit message.
317 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
318 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
319 message->WriteInt(-1);
320 message->WriteString("quit");
321 sender()->Send(message);
322
323 EXPECT_TRUE(WaitForClientShutdown());
324 DestroyChannelProxy();
sammce4d0abd2016-03-07 22:38:04325
326 io_thread_.reset();
morrita373af03b2014-09-09 19:35:24327}
328
329
330PingPongTestClient::PingPongTestClient()
331 : listener_(new ChannelReflectorListener()) {
332}
333
334PingPongTestClient::~PingPongTestClient() {
335}
336
danakj03de39b22016-04-23 04:21:09337std::unique_ptr<Channel> PingPongTestClient::CreateChannel(Listener* listener) {
erikchen27aa7d82015-06-16 21:21:04338 return Channel::CreateClient(IPCTestBase::GetChannelName("PerformanceClient"),
erikchen30dc2812015-09-24 03:26:38339 listener);
morrita373af03b2014-09-09 19:35:24340}
341
342int PingPongTestClient::RunMain() {
brucedawsonaa3d0c52015-02-12 22:33:22343 LockThreadAffinity thread_locker(kSharedCore);
danakj03de39b22016-04-23 04:21:09344 std::unique_ptr<Channel> channel = CreateChannel(listener_.get());
morrita373af03b2014-09-09 19:35:24345 listener_->Init(channel.get());
346 CHECK(channel->Connect());
347
fdoray8e32586852016-06-22 19:56:16348 base::RunLoop().Run();
morrita373af03b2014-09-09 19:35:24349 return 0;
350}
351
352scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() {
skyostile687bdff2015-05-12 11:29:21353 return main_message_loop_.task_runner();
morrita373af03b2014-09-09 19:35:24354}
355
brucedawsonaa3d0c52015-02-12 22:33:22356LockThreadAffinity::LockThreadAffinity(int cpu_number)
357 : affinity_set_ok_(false) {
358#if defined(OS_WIN)
brucedawsond3beca72015-12-10 02:22:44359 const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
brucedawsonaa3d0c52015-02-12 22:33:22360 old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
361 affinity_set_ok_ = old_affinity_ != 0;
362#elif defined(OS_LINUX)
363 cpu_set_t cpuset;
364 CPU_ZERO(&cpuset);
365 CPU_SET(cpu_number, &cpuset);
366 auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
367 DCHECK_EQ(0, get_result);
368 auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
369 // Check for get_result failure, even though it should always succeed.
370 affinity_set_ok_ = (set_result == 0) && (get_result == 0);
371#endif
372 if (!affinity_set_ok_)
373 LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
374}
375
376LockThreadAffinity::~LockThreadAffinity() {
377 if (!affinity_set_ok_)
378 return;
379#if defined(OS_WIN)
380 auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
381 DCHECK_NE(0u, set_result);
382#elif defined(OS_LINUX)
383 auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
384 DCHECK_EQ(0, set_result);
385#endif
386}
387
morrita373af03b2014-09-09 19:35:24388} // namespace test
389} // namespace IPC