Chromium Code Reviews| 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 |