OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/mach/mach_message_server.h" | |
16 | |
17 #include <mach/mach.h> | |
18 #include <string.h> | |
19 | |
20 #include "base/basictypes.h" | |
21 #include "gtest/gtest.h" | |
22 #include "util/file/fd_io.h" | |
23 #include "util/test/errors.h" | |
24 #include "util/test/mac/mach_errors.h" | |
25 #include "util/test/mac/mach_multiprocess.h" | |
26 | |
27 namespace { | |
28 | |
29 using namespace crashpad; | |
30 using namespace crashpad::test; | |
31 | |
32 class TestMachMessageServer : public MachMessageServerInterface, | |
33 public MachMultiprocess { | |
34 public: | |
35 // Variations on the MachMessageServer test. | |
36 enum TestType { | |
Robert Sesek
2014/09/08 16:28:47
No tests for the error reply scenario? What about
| |
37 // The client sends one message to the server, which will wait indefinitely | |
38 // in blocking mode for it. | |
39 kBasic = 0, | |
40 | |
41 // The server waits in nonblocking mode and the client sends nothing, so | |
42 // the server should return immediately without processing any message. | |
43 kNonblockingNoMessage, | |
44 | |
45 // The server waits in blocking mode for one message, but with a timeout. | |
46 // The client sends no message, so the server returns after the timeout. | |
47 kTimeoutNoMessage, | |
48 | |
49 // The client sends one message to the server and then signals the server | |
50 // that it’s safe to start waiting for it in nonblocking mode. The message | |
51 // is in the server’s queue, so it’s able to receive it when it begins | |
52 // listening in nonblocking mode. | |
53 kNonblocking, | |
54 | |
55 // The client sends one message to the server, which will wait in blocking | |
56 // mode for it up to a specific timeout. | |
57 kTimeout, | |
58 | |
59 // The server waits for as many messages as it can receive in blocking mode | |
60 // with a timeout. The client sends several messages, and the server | |
61 // processes them all. | |
62 kPersistentTenMessages, | |
63 | |
64 // The client sends several messages to the server and then signals the | |
65 // server that it’s safe to start waiting for them in nonblocking mode. The | |
66 // server then listens for them in nonblocking persistent mode, and receives | |
67 // all of them because they’ve been queued up. The client doesn’t wait for | |
68 // the replies until after it’s put all of its requests into the server’s | |
69 // queue. | |
70 // | |
71 // This test is sensitive to the length of the IPC queue limit. Mach ports | |
72 // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which | |
73 // is MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this | |
74 // test must be below this, because the server does not begin dequeueing | |
75 // request messages until the client has finished sending them. | |
76 kPersistentNonblockingFourMessages, | |
77 }; | |
78 | |
79 explicit TestMachMessageServer(TestType test_type) | |
80 : MachMessageServerInterface(), | |
81 MachMultiprocess(), | |
82 test_type_(test_type) { | |
83 } | |
84 | |
85 // Runs the test, verifying that the number or requests and replies begins | |
86 // and remains equal. Returns the number of round-trip messages processed. | |
87 uint32_t Test() { | |
88 EXPECT_EQ(requests_, replies_); | |
89 uint32_t start = requests_; | |
90 | |
91 Run(); | |
92 | |
93 EXPECT_EQ(requests_, replies_); | |
94 return requests_ - start; | |
95 } | |
96 | |
97 // MachMessageServerInterface: | |
98 | |
99 virtual bool MachMessageServerFunction( | |
100 mach_msg_header_t* in, | |
101 mach_msg_header_t* out, | |
102 bool* destroy_complex_request) override { | |
103 *destroy_complex_request = true; | |
104 | |
105 switch (test_type_) { | |
106 case kNonblockingNoMessage: | |
107 case kTimeoutNoMessage: | |
108 // These test types should not result in any messages being processed. | |
109 ADD_FAILURE(); | |
110 return false; | |
111 | |
112 default: | |
113 break; | |
114 } | |
115 | |
116 const ReceiveRequestMessage* request = | |
117 reinterpret_cast<ReceiveRequestMessage*>(in); | |
118 EXPECT_EQ(RemotePort(), request->header.msgh_remote_port); | |
119 EXPECT_EQ(sizeof(SendRequestMessage), request->header.msgh_size); | |
120 EXPECT_EQ(requests_, request->number); | |
121 ++requests_; | |
122 | |
123 SendReplyMessage* reply = | |
124 reinterpret_cast<SendReplyMessage*>(out); | |
125 reply->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); | |
126 reply->header.msgh_remote_port = request->header.msgh_remote_port; | |
127 reply->header.msgh_size = sizeof(SendReplyMessage); | |
128 reply->header.msgh_local_port = MACH_PORT_NULL; | |
129 reply->number = replies_++; | |
130 | |
131 return true; | |
132 } | |
133 | |
134 virtual mach_msg_size_t MachMessageServerRequestSize() override { | |
135 return sizeof(SendRequestMessage); | |
136 } | |
137 | |
138 virtual mach_msg_size_t MachMessageServerReplySize() override { | |
139 return sizeof(SendReplyMessage); | |
140 } | |
141 | |
142 private: | |
143 struct SendRequestMessage { | |
144 mach_msg_header_t header; | |
145 uint32_t number; | |
146 }; | |
147 | |
148 struct ReceiveRequestMessage : public SendRequestMessage { | |
149 mach_msg_trailer_t trailer; | |
150 }; | |
151 | |
152 struct SendReplyMessage { | |
153 mach_msg_header_t header; | |
154 uint32_t number; | |
155 }; | |
156 | |
157 struct ReceiveReplyMessage : public SendReplyMessage { | |
158 mach_msg_trailer_t trailer; | |
159 }; | |
160 | |
161 // MachMultiprocess: | |
162 | |
163 virtual void MachMultiprocessParent() override { | |
164 switch (test_type_) { | |
165 case kNonblocking: | |
166 case kPersistentNonblockingFourMessages: { | |
167 // Wait until the child is done sending what it’s going to send. | |
168 char c; | |
169 ssize_t rv = ReadFD(ReadPipeFD(), &c, 1); | |
170 EXPECT_EQ(1, rv) << ErrnoMessage("read"); | |
171 EXPECT_EQ('\0', c); | |
172 break; | |
173 } | |
174 | |
175 default: | |
176 break; | |
177 } | |
178 | |
179 bool persistent = false; | |
180 bool nonblocking = false; | |
181 mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE; | |
182 kern_return_t expect = KERN_SUCCESS; | |
183 | |
184 switch (test_type_) { | |
185 case kNonblockingNoMessage: | |
186 nonblocking = true; | |
187 expect = MACH_RCV_TIMED_OUT; | |
188 break; | |
189 case kTimeoutNoMessage: | |
190 timeout = 10; // milliseconds | |
191 expect = MACH_RCV_TIMED_OUT; | |
192 break; | |
193 case kNonblocking: | |
194 nonblocking = true; | |
195 break; | |
196 case kTimeout: | |
197 timeout = 10; // milliseconds | |
198 break; | |
199 case kPersistentTenMessages: | |
200 persistent = true; | |
201 timeout = 10; // milliseconds | |
202 expect = MACH_RCV_TIMED_OUT; | |
203 break; | |
204 case kPersistentNonblockingFourMessages: | |
205 persistent = true; | |
206 nonblocking = true; | |
207 expect = MACH_RCV_TIMED_OUT; | |
208 break; | |
209 default: | |
210 break; | |
211 } | |
212 | |
213 kern_return_t kr; | |
214 ASSERT_EQ(expect, (kr = MachMessageServer(this, | |
215 LocalPort(), | |
216 MACH_MSG_OPTION_NONE, | |
217 persistent, | |
218 nonblocking, | |
219 timeout))) | |
220 << MachErrorMessage(kr, "MachMessageServer"); | |
221 } | |
222 | |
223 virtual void MachMultiprocessChild() override { | |
Robert Sesek
2014/09/08 16:28:47
The test is pretty comprehensive, which is good, b
| |
224 size_t count = 1; | |
225 switch (test_type_) { | |
226 case kNonblockingNoMessage: | |
227 case kTimeoutNoMessage: | |
228 count = 0; | |
229 break; | |
230 case kPersistentTenMessages: | |
231 count = 10; | |
232 break; | |
233 case kPersistentNonblockingFourMessages: | |
234 count = 4; | |
235 break; | |
236 default: | |
237 break; | |
238 } | |
239 | |
240 for (size_t index = 0; index < count; ++index) { | |
241 if (test_type_ == kPersistentNonblockingFourMessages) { | |
242 // For this test, all of the messages need to go into the queue before | |
243 // the parent is allowed to start processing them, so the replies won’t | |
244 // be processed until all of the requests are sent. | |
245 ChildSendRequest(); | |
246 } else { | |
247 ChildSendRequestAndWaitForReply(); | |
248 } | |
249 if (testing::Test::HasFatalFailure()) { | |
250 return; | |
251 } | |
252 } | |
253 | |
254 if (test_type_ == kPersistentNonblockingFourMessages) { | |
255 // Now that all of the requests have been sent, let the parent know that | |
256 // it’s safe to begin processing them, and then wait for the replies. | |
257 ChildNotifyParentViaPipe(); | |
258 if (testing::Test::HasFatalFailure()) { | |
259 return; | |
260 } | |
261 | |
262 for (size_t index = 0; index < count; ++index) { | |
263 ChildWaitForReply(); | |
264 if (testing::Test::HasFatalFailure()) { | |
265 return; | |
266 } | |
267 } | |
268 } | |
269 } | |
270 | |
271 // In the child process, sends a request message to the server. | |
272 void ChildSendRequest() { | |
273 SendRequestMessage request = {}; | |
274 request.header.msgh_bits = | |
275 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); | |
276 request.header.msgh_size = sizeof(request); | |
277 request.header.msgh_remote_port = RemotePort(); | |
278 request.header.msgh_local_port = LocalPort(); | |
279 request.number = requests_++; | |
280 | |
281 kern_return_t kr = mach_msg(&request.header, | |
282 MACH_SEND_MSG | MACH_SEND_TIMEOUT, | |
283 request.header.msgh_size, | |
284 0, | |
285 MACH_PORT_NULL, | |
286 MACH_MSG_TIMEOUT_NONE, | |
287 MACH_PORT_NULL); | |
288 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); | |
289 } | |
290 | |
291 // In the child process, waits for a reply message from the server. | |
292 void ChildWaitForReply() { | |
293 ReceiveReplyMessage reply = {}; | |
294 kern_return_t kr = mach_msg(&reply.header, | |
295 MACH_RCV_MSG, | |
296 0, | |
297 sizeof(reply), | |
298 LocalPort(), | |
299 MACH_MSG_TIMEOUT_NONE, | |
300 MACH_PORT_NULL); | |
301 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); | |
302 | |
303 ASSERT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), | |
304 reply.header.msgh_remote_port); | |
305 ASSERT_EQ(sizeof(SendReplyMessage), reply.header.msgh_size); | |
306 ASSERT_EQ(replies_, reply.number); | |
307 ++replies_; | |
308 } | |
309 | |
310 // For test types where the child needs to notify the server in the parent | |
311 // that the child is ready, this method will send a byte via the POSIX pipe. | |
312 // The parent will be waiting in a read() on this pipe, and will proceed to | |
313 // running MachMessageServer() once it’s received. | |
314 void ChildNotifyParentViaPipe() { | |
315 char c = '\0'; | |
316 ssize_t rv = WriteFD(WritePipeFD(), &c, 1); | |
317 ASSERT_EQ(1, rv) << ErrnoMessage("write"); | |
318 } | |
319 | |
320 // In the child process, sends a request message to the server and then | |
321 // receives a reply message. | |
322 void ChildSendRequestAndWaitForReply() { | |
323 ChildSendRequest(); | |
324 if (testing::Test::HasFatalFailure()) { | |
325 return; | |
326 } | |
327 | |
328 if (test_type_ == kNonblocking) { | |
329 // The parent is waiting to read a byte to indicate that the message has | |
330 // been placed in the queue. | |
331 ChildNotifyParentViaPipe(); | |
332 if (testing::Test::HasFatalFailure()) { | |
333 return; | |
334 } | |
335 } | |
336 | |
337 ChildWaitForReply(); | |
338 } | |
339 | |
340 TestType test_type_; | |
341 | |
342 static uint32_t requests_; | |
343 static uint32_t replies_; | |
344 | |
345 DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer); | |
346 }; | |
347 | |
348 uint32_t TestMachMessageServer::requests_; | |
349 uint32_t TestMachMessageServer::replies_; | |
350 | |
351 TEST(MachMessageServer, Basic) { | |
352 TestMachMessageServer test_mach_message_server(TestMachMessageServer::kBasic); | |
353 EXPECT_EQ(1u, test_mach_message_server.Test()); | |
354 } | |
355 | |
356 TEST(MachMessageServer, NonblockingNoMessage) { | |
357 TestMachMessageServer test_mach_message_server( | |
358 TestMachMessageServer::kNonblockingNoMessage); | |
359 EXPECT_EQ(0u, test_mach_message_server.Test()); | |
360 } | |
361 | |
362 TEST(MachMessageServer, TimeoutNoMessage) { | |
363 TestMachMessageServer test_mach_message_server( | |
364 TestMachMessageServer::kTimeoutNoMessage); | |
365 EXPECT_EQ(0u, test_mach_message_server.Test()); | |
366 } | |
367 | |
368 TEST(MachMessageServer, Nonblocking) { | |
369 TestMachMessageServer test_mach_message_server( | |
370 TestMachMessageServer::kNonblocking); | |
371 EXPECT_EQ(1u, test_mach_message_server.Test()); | |
372 } | |
373 | |
374 TEST(MachMessageServer, PersistentTenMessages) { | |
375 TestMachMessageServer test_mach_message_server( | |
376 TestMachMessageServer::kPersistentTenMessages); | |
377 EXPECT_EQ(10u, test_mach_message_server.Test()); | |
378 } | |
379 | |
380 TEST(MachMessageServer, PersistentNonblockingFourMessages) { | |
381 TestMachMessageServer test_mach_message_server( | |
382 TestMachMessageServer::kPersistentNonblockingFourMessages); | |
383 EXPECT_EQ(4u, test_mach_message_server.Test()); | |
384 } | |
385 | |
386 } // namespace | |
OLD | NEW |