1 // 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.
5 #include "build/build_config.h"
9 #elif defined(OS_POSIX)
10 #include <sys/types.h>
17 #include "base/basictypes.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/perftimer.h"
21 #include "base/pickle.h"
22 #include "base/stringprintf.h"
23 #include "base/threading/thread.h"
24 #include "base/time.h"
25 #include "ipc/ipc_descriptors.h"
26 #include "ipc/ipc_channel.h"
27 #include "ipc/ipc_channel_proxy.h"
28 #include "ipc/ipc_message_utils.h"
29 #include "ipc/ipc_multiprocess_test.h"
30 #include "ipc/ipc_sender.h"
31 #include "ipc/ipc_test_base.h"
35 // This test times the roundtrip IPC message cycle.
37 // TODO(brettw): Make this test run by default.
39 class IPCChannelPerfTest
: public IPCTestBase
{
42 // This class simply collects stats about abstract "events" (each of which has a
43 // start time and an end time).
44 class EventTimeTracker
{
46 explicit EventTimeTracker(const char* name
)
51 void AddEvent(const base::TimeTicks
& start
, const base::TimeTicks
& end
) {
54 base::TimeDelta duration
= end
- start
;
55 total_duration_
+= duration
;
56 max_duration_
= std::max(max_duration_
, duration
);
59 void ShowResults() const {
60 VLOG(1) << name_
<< " count: " << count_
;
61 VLOG(1) << name_
<< " total duration: "
62 << total_duration_
.InMillisecondsF() << " ms";
63 VLOG(1) << name_
<< " average duration: "
64 << (total_duration_
.InMillisecondsF() / static_cast<double>(count_
))
66 VLOG(1) << name_
<< " maximum duration: "
67 << max_duration_
.InMillisecondsF() << " ms";
72 total_duration_
= base::TimeDelta();
73 max_duration_
= base::TimeDelta();
77 const std::string name_
;
80 base::TimeDelta total_duration_
;
81 base::TimeDelta max_duration_
;
83 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker
);
86 // This channel listener just replies to all messages with the exact same
87 // message. It assumes each message has one string parameter. When the string
88 // "quit" is sent, it will exit.
89 class ChannelReflectorListener
: public IPC::Listener
{
91 ChannelReflectorListener()
93 latency_tracker_("Client messages") {
94 VLOG(1) << "Client listener up";
97 ~ChannelReflectorListener() {
98 VLOG(1) << "Client listener down";
99 latency_tracker_
.ShowResults();
102 void Init(IPC::Channel
* channel
) {
107 virtual bool OnMessageReceived(const IPC::Message
& message
) {
110 PickleIterator
iter(message
);
112 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
114 EXPECT_TRUE(iter
.ReadInt(&msgid
));
116 EXPECT_TRUE(iter
.ReadString(&payload
));
118 // Include message deserialization in latency.
119 base::TimeTicks now
= base::TimeTicks::Now();
121 if (payload
== "hello") {
122 latency_tracker_
.Reset();
123 } else if (payload
== "quit") {
124 latency_tracker_
.ShowResults();
125 MessageLoop::current()->QuitWhenIdle();
128 // Don't track hello and quit messages.
129 latency_tracker_
.AddEvent(
130 base::TimeTicks::FromInternalValue(time_internal
), now
);
133 IPC::Message
* msg
= new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL
);
134 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
135 msg
->WriteInt(msgid
);
136 msg
->WriteString(payload
);
142 IPC::Channel
* channel_
;
143 EventTimeTracker latency_tracker_
;
146 class ChannelPerfListener
: public IPC::Listener
{
148 ChannelPerfListener()
153 latency_tracker_("Server messages") {
154 VLOG(1) << "Server listener up";
157 ~ChannelPerfListener() {
158 VLOG(1) << "Server listener down";
161 void Init(IPC::Channel
* channel
) {
166 // Call this before running the message loop.
167 void SetTestParams(int msg_count
, size_t msg_size
) {
168 DCHECK_EQ(0, count_down_
);
169 msg_count_
= msg_count
;
170 msg_size_
= msg_size
;
171 count_down_
= msg_count_
;
172 payload_
= std::string(msg_size_
, 'a');
175 virtual bool OnMessageReceived(const IPC::Message
& message
) {
178 PickleIterator
iter(message
);
180 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
182 EXPECT_TRUE(iter
.ReadInt(&msgid
));
183 std::string reflected_payload
;
184 EXPECT_TRUE(iter
.ReadString(&reflected_payload
));
186 // Include message deserialization in latency.
187 base::TimeTicks now
= base::TimeTicks::Now();
189 if (reflected_payload
== "hello") {
190 // Start timing on hello.
191 latency_tracker_
.Reset();
192 DCHECK(!perf_logger_
.get());
193 std::string test_name
= base::StringPrintf(
194 "IPC_Perf_%dx_%u", msg_count_
, static_cast<unsigned>(msg_size_
));
195 perf_logger_
.reset(new PerfTimeLogger(test_name
.c_str()));
197 DCHECK_EQ(payload_
.size(), reflected_payload
.size());
199 latency_tracker_
.AddEvent(
200 base::TimeTicks::FromInternalValue(time_internal
), now
);
202 CHECK(count_down_
> 0);
204 if (count_down_
== 0) {
205 perf_logger_
.reset(); // Stop the perf timer now.
206 latency_tracker_
.ShowResults();
207 MessageLoop::current()->QuitWhenIdle();
212 IPC::Message
* msg
= new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL
);
213 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
214 msg
->WriteInt(count_down_
);
215 msg
->WriteString(payload_
);
221 IPC::Channel
* channel_
;
226 std::string payload_
;
227 EventTimeTracker latency_tracker_
;
228 scoped_ptr
<PerfTimeLogger
> perf_logger_
;
231 TEST_F(IPCChannelPerfTest
, Performance
) {
232 // Setup IPC channel.
233 ChannelPerfListener perf_listener
;
234 IPC::Channel
chan(kReflectorChannel
, IPC::Channel::MODE_SERVER
,
236 perf_listener
.Init(&chan
);
237 ASSERT_TRUE(chan
.Connect());
239 base::ProcessHandle process_handle
= SpawnChild(TEST_REFLECTOR
, &chan
);
240 ASSERT_TRUE(process_handle
);
242 const size_t kMsgSizeBase
= 12;
243 const int kMsgSizeMaxExp
= 5;
244 int msg_count
= 100000;
245 size_t msg_size
= kMsgSizeBase
;
246 for (int i
= 1; i
<= kMsgSizeMaxExp
; i
++) {
247 perf_listener
.SetTestParams(msg_count
, msg_size
);
249 // This initial message will kick-start the ping-pong of messages.
250 IPC::Message
* message
=
251 new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL
);
252 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
253 message
->WriteInt(-1);
254 message
->WriteString("hello");
258 MessageLoop::current()->Run();
260 msg_size
*= kMsgSizeBase
;
263 // Send quit message.
264 IPC::Message
* message
= new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL
);
265 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
266 message
->WriteInt(-1);
267 message
->WriteString("quit");
270 // Clean up child process.
271 EXPECT_TRUE(base::WaitForSingleProcess(process_handle
,
272 base::TimeDelta::FromSeconds(5)));
273 base::CloseProcessHandle(process_handle
);
276 // This message loop bounces all messages back to the sender.
277 MULTIPROCESS_IPC_TEST_MAIN(RunReflector
) {
278 MessageLoopForIO main_message_loop
;
279 ChannelReflectorListener channel_reflector_listener
;
280 IPC::Channel
chan(kReflectorChannel
, IPC::Channel::MODE_CLIENT
,
281 &channel_reflector_listener
);
282 channel_reflector_listener
.Init(&chan
);
283 CHECK(chan
.Connect());
285 MessageLoop::current()->Run();