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

Side by Side Diff: util/test/multiprocess.cc

Issue 880763002: Reorganize Multiprocess and implement for Windows (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: fixes 2 Created 5 years, 10 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/multiprocess.h ('k') | util/test/multiprocess_exec.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/test/multiprocess.h"
16
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <sys/wait.h>
20
21 #include <string>
22
23 #include "base/auto_reset.h"
24 #include "base/files/scoped_file.h"
25 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/posix/eintr_wrapper.h"
28 #include "base/strings/stringprintf.h"
29 #include "gtest/gtest.h"
30 #include "util/misc/scoped_forbid_return.h"
31 #include "util/test/errors.h"
32
33 namespace crashpad {
34 namespace test {
35
36 namespace internal {
37
38 struct MultiprocessInfo {
39 MultiprocessInfo()
40 : pipe_c2p_read(-1),
41 pipe_c2p_write(-1),
42 pipe_p2c_read(-1),
43 pipe_p2c_write(-1),
44 child_pid(0) {}
45
46 base::ScopedFD pipe_c2p_read; // child to parent
47 base::ScopedFD pipe_c2p_write; // child to parent
48 base::ScopedFD pipe_p2c_read; // parent to child
49 base::ScopedFD pipe_p2c_write; // parent to child
50 pid_t child_pid; // valid only in parent
51 };
52
53 } // namespace internal
54
55 Multiprocess::Multiprocess()
56 : info_(nullptr),
57 code_(EXIT_SUCCESS),
58 reason_(kTerminationNormal) {
59 }
60
61 void Multiprocess::Run() {
62 ASSERT_EQ(nullptr, info_);
63 scoped_ptr<internal::MultiprocessInfo> info(new internal::MultiprocessInfo);
64 base::AutoReset<internal::MultiprocessInfo*> reset_info(&info_, info.get());
65
66 ASSERT_NO_FATAL_FAILURE(PreFork());
67
68 pid_t pid = fork();
69 ASSERT_GE(pid, 0) << ErrnoMessage("fork");
70
71 if (pid > 0) {
72 info_->child_pid = pid;
73
74 RunParent();
75
76 // Waiting for the child happens here instead of in RunParent() because even
77 // if RunParent() returns early due to a gtest fatal assertion failure, the
78 // child should still be reaped.
79
80 // This will make the parent hang up on the child as much as would be
81 // visible from the child’s perspective. The child’s side of the pipe will
82 // be broken, the child’s remote port will become a dead name, and an
83 // attempt by the child to look up the service will fail. If this weren’t
84 // done, the child might hang while waiting for a parent that has already
85 // triggered a fatal assertion failure to do something.
86 info.reset();
87 info_ = nullptr;
88
89 int status;
90 pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
91 ASSERT_EQ(pid, wait_pid) << ErrnoMessage("waitpid");
92
93 TerminationReason reason;
94 int code;
95 std::string message;
96 if (WIFEXITED(status)) {
97 reason = kTerminationNormal;
98 code = WEXITSTATUS(status);
99 message = base::StringPrintf("Child exited with code %d, expected", code);
100 } else if (WIFSIGNALED(status)) {
101 reason = kTerminationSignal;
102 code = WTERMSIG(status);
103 message =
104 base::StringPrintf("Child terminated by signal %d (%s)%s, expected",
105 code,
106 strsignal(code),
107 WCOREDUMP(status) ? " (core dumped)" : "");
108 } else {
109 FAIL() << "Unknown termination reason";
110 }
111
112 if (reason_ == kTerminationNormal) {
113 message += base::StringPrintf(" exit with code %d", code_);
114 } else if (reason == kTerminationSignal) {
115 message += base::StringPrintf(" termination by signal %d", code_);
116 }
117
118 if (reason != reason_ || code != code_) {
119 ADD_FAILURE() << message;
120 }
121 } else {
122 RunChild();
123 }
124 }
125
126 void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
127 int code) {
128 reason_ = reason;
129 code_ = code;
130 }
131
132 Multiprocess::~Multiprocess() {
133 }
134
135 void Multiprocess::PreFork() {
136 int pipe_fds_c2p[2];
137 int rv = pipe(pipe_fds_c2p);
138 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
139
140 info_->pipe_c2p_read.reset(pipe_fds_c2p[0]);
141 info_->pipe_c2p_write.reset(pipe_fds_c2p[1]);
142
143 int pipe_fds_p2c[2];
144 rv = pipe(pipe_fds_p2c);
145 ASSERT_EQ(0, rv) << ErrnoMessage("pipe");
146
147 info_->pipe_p2c_read.reset(pipe_fds_p2c[0]);
148 info_->pipe_p2c_write.reset(pipe_fds_p2c[1]);
149 }
150
151 pid_t Multiprocess::ChildPID() const {
152 EXPECT_NE(0, info_->child_pid);
153 return info_->child_pid;
154 }
155
156 int Multiprocess::ReadPipeFD() const {
157 int fd = info_->child_pid ? info_->pipe_c2p_read.get()
158 : info_->pipe_p2c_read.get();
159 CHECK_NE(fd, -1);
160 return fd;
161 }
162
163 int Multiprocess::WritePipeFD() const {
164 int fd = info_->child_pid ? info_->pipe_p2c_write.get()
165 : info_->pipe_c2p_write.get();
166 CHECK_NE(fd, -1);
167 return fd;
168 }
169
170 void Multiprocess::CloseReadPipe() {
171 if (info_->child_pid) {
172 info_->pipe_c2p_read.reset();
173 } else {
174 info_->pipe_p2c_read.reset();
175 }
176 }
177
178 void Multiprocess::CloseWritePipe() {
179 if (info_->child_pid) {
180 info_->pipe_p2c_write.reset();
181 } else {
182 info_->pipe_c2p_write.reset();
183 }
184 }
185
186 void Multiprocess::RunParent() {
187 // The parent uses the read end of c2p and the write end of p2c.
188 info_->pipe_c2p_write.reset();
189 info_->pipe_p2c_read.reset();
190
191 MultiprocessParent();
192
193 info_->pipe_c2p_read.reset();
194 info_->pipe_p2c_write.reset();
195 }
196
197 void Multiprocess::RunChild() {
198 ScopedForbidReturn forbid_return;
199
200 // The child uses the write end of c2p and the read end of p2c.
201 info_->pipe_c2p_read.reset();
202 info_->pipe_p2c_write.reset();
203
204 MultiprocessChild();
205
206 info_->pipe_c2p_write.reset();
207 info_->pipe_p2c_read.reset();
208
209 if (testing::Test::HasFailure()) {
210 // Trigger the ScopedForbidReturn destructor.
211 return;
212 }
213
214 exit(0);
215 }
216
217 } // namespace test
218 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/test/multiprocess.h ('k') | util/test/multiprocess_exec.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698