Adding instrumentation to locate the source of jankiness.
[chromium-blink-merge.git] / ipc / ipc_perftest_support.cc
blobe60de91ec60782d1584104a6ecf718e21c18409e
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 "ipc/ipc_perftest_support.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/pickle.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/perf_time_logger.h"
16 #include "base/test/test_io_thread.h"
17 #include "base/threading/thread.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "ipc/ipc_channel.h"
21 #include "ipc/ipc_channel_proxy.h"
22 #include "ipc/ipc_descriptors.h"
23 #include "ipc/ipc_message_utils.h"
24 #include "ipc/ipc_sender.h"
26 namespace IPC {
27 namespace test {
29 // This class simply collects stats about abstract "events" (each of which has a
30 // start time and an end time).
31 class EventTimeTracker {
32 public:
33 explicit EventTimeTracker(const char* name)
34 : name_(name),
35 count_(0) {
38 void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
39 DCHECK(end >= start);
40 count_++;
41 base::TimeDelta duration = end - start;
42 total_duration_ += duration;
43 max_duration_ = std::max(max_duration_, duration);
46 void ShowResults() const {
47 VLOG(1) << name_ << " count: " << count_;
48 VLOG(1) << name_ << " total duration: "
49 << total_duration_.InMillisecondsF() << " ms";
50 VLOG(1) << name_ << " average duration: "
51 << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
52 << " ms";
53 VLOG(1) << name_ << " maximum duration: "
54 << max_duration_.InMillisecondsF() << " ms";
57 void Reset() {
58 count_ = 0;
59 total_duration_ = base::TimeDelta();
60 max_duration_ = base::TimeDelta();
63 private:
64 const std::string name_;
66 uint64 count_;
67 base::TimeDelta total_duration_;
68 base::TimeDelta max_duration_;
70 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
73 // This channel listener just replies to all messages with the exact same
74 // message. It assumes each message has one string parameter. When the string
75 // "quit" is sent, it will exit.
76 class ChannelReflectorListener : public Listener {
77 public:
78 ChannelReflectorListener()
79 : channel_(NULL),
80 latency_tracker_("Client messages") {
81 VLOG(1) << "Client listener up";
84 virtual ~ChannelReflectorListener() {
85 VLOG(1) << "Client listener down";
86 latency_tracker_.ShowResults();
89 void Init(Channel* channel) {
90 DCHECK(!channel_);
91 channel_ = channel;
94 virtual bool OnMessageReceived(const Message& message) override {
95 CHECK(channel_);
97 PickleIterator iter(message);
98 int64 time_internal;
99 EXPECT_TRUE(iter.ReadInt64(&time_internal));
100 int msgid;
101 EXPECT_TRUE(iter.ReadInt(&msgid));
102 std::string payload;
103 EXPECT_TRUE(iter.ReadString(&payload));
105 // Include message deserialization in latency.
106 base::TimeTicks now = base::TimeTicks::Now();
108 if (payload == "hello") {
109 latency_tracker_.Reset();
110 } else if (payload == "quit") {
111 latency_tracker_.ShowResults();
112 base::MessageLoop::current()->QuitWhenIdle();
113 return true;
114 } else {
115 // Don't track hello and quit messages.
116 latency_tracker_.AddEvent(
117 base::TimeTicks::FromInternalValue(time_internal), now);
120 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
121 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
122 msg->WriteInt(msgid);
123 msg->WriteString(payload);
124 channel_->Send(msg);
125 return true;
128 private:
129 Channel* channel_;
130 EventTimeTracker latency_tracker_;
133 class PerformanceChannelListener : public Listener {
134 public:
135 explicit PerformanceChannelListener(const std::string& label)
136 : label_(label),
137 sender_(NULL),
138 msg_count_(0),
139 msg_size_(0),
140 count_down_(0),
141 latency_tracker_("Server messages") {
142 VLOG(1) << "Server listener up";
145 virtual ~PerformanceChannelListener() {
146 VLOG(1) << "Server listener down";
149 void Init(Sender* sender) {
150 DCHECK(!sender_);
151 sender_ = sender;
154 // Call this before running the message loop.
155 void SetTestParams(int msg_count, size_t msg_size) {
156 DCHECK_EQ(0, count_down_);
157 msg_count_ = msg_count;
158 msg_size_ = msg_size;
159 count_down_ = msg_count_;
160 payload_ = std::string(msg_size_, 'a');
163 virtual bool OnMessageReceived(const Message& message) override {
164 CHECK(sender_);
166 PickleIterator iter(message);
167 int64 time_internal;
168 EXPECT_TRUE(iter.ReadInt64(&time_internal));
169 int msgid;
170 EXPECT_TRUE(iter.ReadInt(&msgid));
171 std::string reflected_payload;
172 EXPECT_TRUE(iter.ReadString(&reflected_payload));
174 // Include message deserialization in latency.
175 base::TimeTicks now = base::TimeTicks::Now();
177 if (reflected_payload == "hello") {
178 // Start timing on hello.
179 latency_tracker_.Reset();
180 DCHECK(!perf_logger_.get());
181 std::string test_name =
182 base::StringPrintf("IPC_%s_Perf_%dx_%u",
183 label_.c_str(),
184 msg_count_,
185 static_cast<unsigned>(msg_size_));
186 perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
187 } else {
188 DCHECK_EQ(payload_.size(), reflected_payload.size());
190 latency_tracker_.AddEvent(
191 base::TimeTicks::FromInternalValue(time_internal), now);
193 CHECK(count_down_ > 0);
194 count_down_--;
195 if (count_down_ == 0) {
196 perf_logger_.reset(); // Stop the perf timer now.
197 latency_tracker_.ShowResults();
198 base::MessageLoop::current()->QuitWhenIdle();
199 return true;
203 Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
204 msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
205 msg->WriteInt(count_down_);
206 msg->WriteString(payload_);
207 sender_->Send(msg);
208 return true;
211 private:
212 std::string label_;
213 Sender* sender_;
214 int msg_count_;
215 size_t msg_size_;
217 int count_down_;
218 std::string payload_;
219 EventTimeTracker latency_tracker_;
220 scoped_ptr<base::PerfTimeLogger> perf_logger_;
223 std::vector<PingPongTestParams>
224 IPCChannelPerfTestBase::GetDefaultTestParams() {
225 // Test several sizes. We use 12^N for message size, and limit the message
226 // count to keep the test duration reasonable.
227 std::vector<PingPongTestParams> list;
228 list.push_back(PingPongTestParams(12, 50000));
229 list.push_back(PingPongTestParams(144, 50000));
230 list.push_back(PingPongTestParams(1728, 50000));
231 list.push_back(PingPongTestParams(20736, 12000));
232 list.push_back(PingPongTestParams(248832, 100));
233 return list;
236 void IPCChannelPerfTestBase::RunTestChannelPingPong(
237 const std::vector<PingPongTestParams>& params) {
238 Init("PerformanceClient");
240 // Set up IPC channel and start client.
241 PerformanceChannelListener listener("Channel");
242 CreateChannel(&listener);
243 listener.Init(channel());
244 ASSERT_TRUE(ConnectChannel());
245 ASSERT_TRUE(StartClient());
247 for (size_t i = 0; i < params.size(); i++) {
248 listener.SetTestParams(params[i].message_count(),
249 params[i].message_size());
251 // This initial message will kick-start the ping-pong of messages.
252 Message* message =
253 new Message(0, 2, Message::PRIORITY_NORMAL);
254 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
255 message->WriteInt(-1);
256 message->WriteString("hello");
257 sender()->Send(message);
259 // Run message loop.
260 base::MessageLoop::current()->Run();
263 // Send quit message.
264 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
265 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
266 message->WriteInt(-1);
267 message->WriteString("quit");
268 sender()->Send(message);
270 EXPECT_TRUE(WaitForClientShutdown());
271 DestroyChannel();
274 void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
275 const std::vector<PingPongTestParams>& params) {
276 InitWithCustomMessageLoop("PerformanceClient",
277 make_scoped_ptr(new base::MessageLoop()));
279 base::TestIOThread io_thread(base::TestIOThread::kAutoStart);
281 // Set up IPC channel and start client.
282 PerformanceChannelListener listener("ChannelProxy");
283 CreateChannelProxy(&listener, io_thread.task_runner());
284 listener.Init(channel_proxy());
285 ASSERT_TRUE(StartClient());
287 for (size_t i = 0; i < params.size(); i++) {
288 listener.SetTestParams(params[i].message_count(),
289 params[i].message_size());
291 // This initial message will kick-start the ping-pong of messages.
292 Message* message =
293 new Message(0, 2, Message::PRIORITY_NORMAL);
294 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
295 message->WriteInt(-1);
296 message->WriteString("hello");
297 sender()->Send(message);
299 // Run message loop.
300 base::MessageLoop::current()->Run();
303 // Send quit message.
304 Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
305 message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
306 message->WriteInt(-1);
307 message->WriteString("quit");
308 sender()->Send(message);
310 EXPECT_TRUE(WaitForClientShutdown());
311 DestroyChannelProxy();
315 PingPongTestClient::PingPongTestClient()
316 : listener_(new ChannelReflectorListener()) {
319 PingPongTestClient::~PingPongTestClient() {
322 scoped_ptr<Channel> PingPongTestClient::CreateChannel(
323 Listener* listener) {
324 return Channel::CreateClient(
325 IPCTestBase::GetChannelName("PerformanceClient"), listener);
328 int PingPongTestClient::RunMain() {
329 scoped_ptr<Channel> channel = CreateChannel(listener_.get());
330 listener_->Init(channel.get());
331 CHECK(channel->Connect());
333 base::MessageLoop::current()->Run();
334 return 0;
337 scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() {
338 return main_message_loop_.message_loop_proxy();
341 } // namespace test
342 } // namespace IPC