Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(631)

Side by Side Diff: util/test/mac/mach_multiprocess.cc

Issue 506143002: Refactor MachMultiprocess (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « util/test/mac/mach_multiprocess.h ('k') | util/test/mac/mach_multiprocess_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Crashpad Authors. All rights reserved. 1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with 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 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "util/test/mac/mach_multiprocess.h" 15 #include "util/test/mac/mach_multiprocess.h"
16 16
17 #include <AvailabilityMacros.h> 17 #include <AvailabilityMacros.h>
18 #include <bsm/libbsm.h> 18 #include <bsm/libbsm.h>
19 #include <servers/bootstrap.h> 19 #include <servers/bootstrap.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <sys/wait.h>
23 20
24 #include <string> 21 #include <string>
25 22
26 #include "base/auto_reset.h" 23 #include "base/auto_reset.h"
27 #include "base/files/scoped_file.h"
28 #include "base/logging.h" 24 #include "base/logging.h"
29 #include "base/mac/scoped_mach_port.h" 25 #include "base/mac/scoped_mach_port.h"
30 #include "base/memory/scoped_ptr.h" 26 #include "base/memory/scoped_ptr.h"
31 #include "base/rand_util.h" 27 #include "base/rand_util.h"
32 #include "base/strings/stringprintf.h"
33 #include "gtest/gtest.h" 28 #include "gtest/gtest.h"
34 #include "util/mach/bootstrap.h" 29 #include "util/mach/bootstrap.h"
30 #include "util/misc/scoped_forbid_return.h"
35 #include "util/test/errors.h" 31 #include "util/test/errors.h"
36 #include "util/test/mac/mach_errors.h" 32 #include "util/test/mac/mach_errors.h"
37 33
38 namespace { 34 namespace {
39 35
40 class ScopedNotReached {
41 public:
42 ScopedNotReached() {}
43 ~ScopedNotReached() { abort(); }
44
45 private:
46 DISALLOW_COPY_AND_ASSIGN(ScopedNotReached);
47 };
48
49 // The “hello” message contains a send right to the child process’ task port. 36 // The “hello” message contains a send right to the child process’ task port.
50 struct SendHelloMessage : public mach_msg_base_t { 37 struct SendHelloMessage : public mach_msg_base_t {
51 mach_msg_port_descriptor_t port_descriptor; 38 mach_msg_port_descriptor_t port_descriptor;
52 }; 39 };
53 40
54 struct ReceiveHelloMessage : public SendHelloMessage { 41 struct ReceiveHelloMessage : public SendHelloMessage {
55 mach_msg_audit_trailer_t audit_trailer; 42 mach_msg_audit_trailer_t audit_trailer;
56 }; 43 };
57 44
58 } // namespace 45 } // namespace
59 46
60 namespace crashpad { 47 namespace crashpad {
61 namespace test { 48 namespace test {
62 49
63 using namespace testing; 50 using namespace testing;
64 51
65 namespace internal { 52 namespace internal {
66 53
67 struct MachMultiprocessInfo { 54 struct MachMultiprocessInfo {
68 MachMultiprocessInfo() 55 MachMultiprocessInfo()
69 : service_name(), 56 : service_name(),
70 pipe_c2p_read(-1),
71 pipe_c2p_write(-1),
72 pipe_p2c_read(-1),
73 pipe_p2c_write(-1),
74 child_pid(0),
75 read_pipe_fd(-1),
76 write_pipe_fd(-1),
77 local_port(MACH_PORT_NULL), 57 local_port(MACH_PORT_NULL),
78 remote_port(MACH_PORT_NULL), 58 remote_port(MACH_PORT_NULL),
79 child_task(MACH_PORT_NULL) {} 59 child_task(MACH_PORT_NULL) {}
80 60
81 std::string service_name; 61 std::string service_name;
82 base::ScopedFD pipe_c2p_read; // child to parent
83 base::ScopedFD pipe_c2p_write; // child to parent
84 base::ScopedFD pipe_p2c_read; // parent to child
85 base::ScopedFD pipe_p2c_write; // parent to child
86 pid_t child_pid; // valid only in parent
87 int read_pipe_fd; // pipe_c2p_read in parent, pipe_p2c_read in child
88 int write_pipe_fd; // pipe_p2c_write in parent, pipe_c2p_write in child
89 base::mac::ScopedMachReceiveRight local_port; 62 base::mac::ScopedMachReceiveRight local_port;
90 base::mac::ScopedMachSendRight remote_port; 63 base::mac::ScopedMachSendRight remote_port;
91 base::mac::ScopedMachSendRight child_task; // valid only in parent 64 base::mac::ScopedMachSendRight child_task; // valid only in parent
92 }; 65 };
93 66
94 } // namespace internal 67 } // namespace internal
95 68
96 MachMultiprocess::MachMultiprocess() : info_(NULL) { 69 MachMultiprocess::MachMultiprocess() : Multiprocess(), info_(NULL) {
97 } 70 }
98 71
99 void MachMultiprocess::Run() { 72 void MachMultiprocess::Run() {
100 ASSERT_EQ(NULL, info_); 73 ASSERT_EQ(NULL, info_);
101 scoped_ptr<internal::MachMultiprocessInfo> info( 74 scoped_ptr<internal::MachMultiprocessInfo> info(
102 new internal::MachMultiprocessInfo); 75 new internal::MachMultiprocessInfo);
103 base::AutoReset<internal::MachMultiprocessInfo*> reset_info(&info_, 76 base::AutoReset<internal::MachMultiprocessInfo*> reset_info(&info_,
104 info.get()); 77 info.get());
105 78
106 int pipe_fds_c2p[2]; 79 return Multiprocess::Run();
107 int rv = pipe(pipe_fds_c2p); 80 }
108 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
109 81
110 info_->pipe_c2p_read.reset(pipe_fds_c2p[0]); 82 MachMultiprocess::~MachMultiprocess() {
111 info_->pipe_c2p_write.reset(pipe_fds_c2p[1]); 83 }
112 84
113 int pipe_fds_p2c[2]; 85 void MachMultiprocess::PreFork() {
114 rv = pipe(pipe_fds_p2c); 86 Multiprocess::PreFork();
115 ASSERT_EQ(0, rv) << ErrnoMessage("pipe"); 87 if (testing::Test::HasFatalFailure()) {
116 88 return;
117 info_->pipe_p2c_read.reset(pipe_fds_p2c[0]); 89 }
118 info_->pipe_p2c_write.reset(pipe_fds_p2c[1]);
119 90
120 // Set up the parent port and register it with the bootstrap server before 91 // Set up the parent port and register it with the bootstrap server before
121 // forking, so that it’s guaranteed to be there when the child attempts to 92 // forking, so that it’s guaranteed to be there when the child attempts to
122 // look it up. 93 // look it up.
123 info_->service_name = "com.googlecode.crashpad.test.mach_multiprocess."; 94 info_->service_name = "com.googlecode.crashpad.test.mach_multiprocess.";
124 for (int index = 0; index < 16; ++index) { 95 for (int index = 0; index < 16; ++index) {
125 info_->service_name.append(1, base::RandInt('A', 'Z')); 96 info_->service_name.append(1, base::RandInt('A', 'Z'));
126 } 97 }
127 98
128 mach_port_t local_port; 99 mach_port_t local_port;
129 kern_return_t kr = 100 kern_return_t kr =
130 BootstrapCheckIn(bootstrap_port, info_->service_name, &local_port); 101 BootstrapCheckIn(bootstrap_port, info_->service_name, &local_port);
131 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr) 102 ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
132 << BootstrapErrorMessage(kr, "bootstrap_check_in"); 103 << BootstrapErrorMessage(kr, "bootstrap_check_in");
133 info_->local_port.reset(local_port); 104 info_->local_port.reset(local_port);
134
135 pid_t pid = fork();
136 ASSERT_GE(pid, 0) << ErrnoMessage("fork");
137
138 if (pid > 0) {
139 info_->child_pid = pid;
140
141 RunParent();
142
143 // Waiting for the child happens here instead of in RunParent() because even
144 // if RunParent() returns early due to a gtest fatal assertion failure, the
145 // child should still be reaped.
146
147 // This will make the parent hang up on the child as much as would be
148 // visible from the child’s perspective. The child’s side of the pipe will
149 // be broken, the child’s remote port will become a dead name, and an
150 // attempt by the child to look up the service will fail. If this weren’t
151 // done, the child might hang while waiting for a parent that has already
152 // triggered a fatal assertion failure to do something.
153 info.reset();
154 info_ = NULL;
155
156 int status;
157 pid_t wait_pid = waitpid(pid, &status, 0);
158 ASSERT_EQ(pid, wait_pid) << ErrnoMessage("waitpid");
159 if (status != 0) {
160 std::string message;
161 if (WIFEXITED(status)) {
162 message = base::StringPrintf("Child exited with code %d",
163 WEXITSTATUS(status));
164 } else if (WIFSIGNALED(status)) {
165 message = base::StringPrintf("Child terminated by signal %d (%s) %s",
166 WTERMSIG(status),
167 strsignal(WTERMSIG(status)),
168 WCOREDUMP(status) ? " (core dumped)" : "");
169 }
170 ASSERT_EQ(0, status) << message;
171 }
172 } else {
173 RunChild();
174 }
175 }
176
177 MachMultiprocess::~MachMultiprocess() {
178 }
179
180 pid_t MachMultiprocess::ChildPID() const {
181 EXPECT_NE(0, info_->child_pid);
182 return info_->child_pid;
183 }
184
185 int MachMultiprocess::ReadPipeFD() const {
186 EXPECT_NE(-1, info_->read_pipe_fd);
187 return info_->read_pipe_fd;
188 }
189
190 int MachMultiprocess::WritePipeFD() const {
191 EXPECT_NE(-1, info_->write_pipe_fd);
192 return info_->write_pipe_fd;
193 } 105 }
194 106
195 mach_port_t MachMultiprocess::LocalPort() const { 107 mach_port_t MachMultiprocess::LocalPort() const {
196 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->local_port); 108 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->local_port);
197 return info_->local_port; 109 return info_->local_port;
198 } 110 }
199 111
200 mach_port_t MachMultiprocess::RemotePort() const { 112 mach_port_t MachMultiprocess::RemotePort() const {
201 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->remote_port); 113 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->remote_port);
202 return info_->remote_port; 114 return info_->remote_port;
203 } 115 }
204 116
205 mach_port_t MachMultiprocess::ChildTask() const { 117 mach_port_t MachMultiprocess::ChildTask() const {
206 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->child_task); 118 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->child_task);
207 return info_->child_task; 119 return info_->child_task;
208 } 120 }
209 121
210 void MachMultiprocess::RunParent() { 122 void MachMultiprocess::MultiprocessParent() {
211 // The parent uses the read end of c2p and the write end of p2c.
212 info_->pipe_c2p_write.reset();
213 info_->read_pipe_fd = info_->pipe_c2p_read.get();
214 info_->pipe_p2c_read.reset();
215 info_->write_pipe_fd = info_->pipe_p2c_write.get();
216
217 ReceiveHelloMessage message = {}; 123 ReceiveHelloMessage message = {};
218 124
219 kern_return_t kr = 125 kern_return_t kr =
220 mach_msg(&message.header, 126 mach_msg(&message.header,
221 MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | 127 MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
222 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT), 128 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
223 0, 129 0,
224 sizeof(message), 130 sizeof(message),
225 info_->local_port, 131 info_->local_port,
226 MACH_MSG_TIMEOUT_NONE, 132 MACH_MSG_TIMEOUT_NONE,
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 // from the message body. 198 // from the message body.
293 info_->remote_port.reset(message.header.msgh_remote_port); 199 info_->remote_port.reset(message.header.msgh_remote_port);
294 info_->child_task.reset(message.port_descriptor.name); 200 info_->child_task.reset(message.port_descriptor.name);
295 201
296 // Verify that the child’s task port is what it purports to be. 202 // Verify that the child’s task port is what it purports to be.
297 int mach_pid; 203 int mach_pid;
298 kr = pid_for_task(info_->child_task, &mach_pid); 204 kr = pid_for_task(info_->child_task, &mach_pid);
299 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task"); 205 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
300 ASSERT_EQ(ChildPID(), mach_pid); 206 ASSERT_EQ(ChildPID(), mach_pid);
301 207
302 Parent(); 208 MachMultiprocessParent();
303 209
304 info_->remote_port.reset(); 210 info_->remote_port.reset();
305 info_->local_port.reset(); 211 info_->local_port.reset();
306
307 info_->read_pipe_fd = -1;
308 info_->pipe_c2p_read.reset();
309 info_->write_pipe_fd = -1;
310 info_->pipe_p2c_write.reset();
311 } 212 }
312 213
313 void MachMultiprocess::RunChild() { 214 void MachMultiprocess::MultiprocessChild() {
314 ScopedNotReached must_not_leave_this_scope; 215 ScopedForbidReturn forbid_return;;
315 216
316 // local_port is not valid in the forked child process. 217 // local_port is not valid in the forked child process.
317 ignore_result(info_->local_port.release()); 218 ignore_result(info_->local_port.release());
318 219
319 // The child uses the write end of c2p and the read end of p2c.
320 info_->pipe_c2p_read.reset();
321 info_->write_pipe_fd = info_->pipe_c2p_write.get();
322 info_->pipe_p2c_write.reset();
323 info_->read_pipe_fd = info_->pipe_p2c_read.get();
324
325 mach_port_t local_port; 220 mach_port_t local_port;
326 kern_return_t kr = mach_port_allocate( 221 kern_return_t kr = mach_port_allocate(
327 mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &local_port); 222 mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &local_port);
328 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_allocate"); 223 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_allocate");
329 info_->local_port.reset(local_port); 224 info_->local_port.reset(local_port);
330 225
331 // The remote port can be obtained from the bootstrap server. 226 // The remote port can be obtained from the bootstrap server.
332 mach_port_t remote_port; 227 mach_port_t remote_port;
333 kr = bootstrap_look_up( 228 kr = bootstrap_look_up(
334 bootstrap_port, info_->service_name.c_str(), &remote_port); 229 bootstrap_port, info_->service_name.c_str(), &remote_port);
(...skipping 18 matching lines...) Expand all
353 248
354 kr = mach_msg(&message.header, 249 kr = mach_msg(&message.header,
355 MACH_SEND_MSG, 250 MACH_SEND_MSG,
356 message.header.msgh_size, 251 message.header.msgh_size,
357 0, 252 0,
358 MACH_PORT_NULL, 253 MACH_PORT_NULL,
359 MACH_MSG_TIMEOUT_NONE, 254 MACH_MSG_TIMEOUT_NONE,
360 MACH_PORT_NULL); 255 MACH_PORT_NULL);
361 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); 256 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
362 257
363 Child(); 258 MachMultiprocessChild();
364 259
365 info_->remote_port.reset(); 260 info_->remote_port.reset();
366 info_->local_port.reset(); 261 info_->local_port.reset();
367 262
368 info_->write_pipe_fd = -1;
369 info_->pipe_c2p_write.reset();
370 info_->read_pipe_fd = -1;
371 info_->pipe_p2c_read.reset();
372
373 if (Test::HasFailure()) { 263 if (Test::HasFailure()) {
374 // Trigger the ScopedNotReached destructor. 264 // Trigger the ScopedForbidReturn destructor.
375 return; 265 return;
376 } 266 }
377 267
378 exit(0); 268 forbid_return.Disarm();
379 } 269 }
380 270
381 } // namespace test 271 } // namespace test
382 } // namespace crashpad 272 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/test/mac/mach_multiprocess.h ('k') | util/test/mac/mach_multiprocess_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698