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. 4 5 #include "base/test/multiprocess_test.h" 6 7 #include <errno.h> 8 #include <string.h> 9 #include <sys/types.h> 10 #include <sys/socket.h> 11 #include <unistd.h> 12 13 #include <memory> 14 #include <utility> 15 #include <vector> 16 17 #include "base/base_switches.h" 18 #include "base/command_line.h" 19 #include "base/containers/hash_tables.h" 20 #include "base/lazy_instance.h" 21 #include "base/logging.h" 22 #include "base/macros.h" 23 #include "base/pickle.h" 24 #include "base/posix/global_descriptors.h" 25 #include "base/posix/unix_domain_socket_linux.h" 26 #include "testing/multiprocess_func_list.h" 27 28 namespace base { 29 30 namespace { 31 32 const int kMaxMessageSize = 1024 * 1024; 33 const int kFragmentSize = 4096; 34 35 // Message sent between parent process and helper child process. 36 enum class MessageType : uint32_t { 37 START_REQUEST, 38 START_RESPONSE, 39 WAIT_REQUEST, 40 WAIT_RESPONSE, 41 }; 42 43 struct MessageHeader { 44 uint32_t size; 45 MessageType type; 46 }; 47 48 struct StartProcessRequest { 49 MessageHeader header = 50 {sizeof(StartProcessRequest), MessageType::START_REQUEST}; 51 52 uint32_t num_args = 0; 53 uint32_t num_fds = 0; 54 }; 55 56 struct StartProcessResponse { 57 MessageHeader header = 58 {sizeof(StartProcessResponse), MessageType::START_RESPONSE}; 59 60 pid_t child_pid; 61 }; 62 63 struct WaitProcessRequest { 64 MessageHeader header = 65 {sizeof(WaitProcessRequest), MessageType::WAIT_REQUEST}; 66 67 pid_t pid; 68 uint64_t timeout_ms; 69 }; 70 71 struct WaitProcessResponse { 72 MessageHeader header = 73 {sizeof(WaitProcessResponse), MessageType::WAIT_RESPONSE}; 74 75 bool success = false; 76 int32_t exit_code = 0; 77 }; 78 79 // Helper class that implements an alternate test child launcher for 80 // multi-process tests. The default implementation doesn't work if the child is 81 // launched after starting threads. However, for some tests (i.e. Mojo), this 82 // is necessary. This implementation works around that issue by forking a helper 83 // process very early in main(), before any real work is done. Then, when a 84 // child needs to be spawned, a message is sent to that helper process, which 85 // then forks and returns the result to the parent. The forked child then calls 86 // main() and things look as though a brand new process has been fork/exec'd. 87 class LaunchHelper { 88 public: 89 using MainFunction = int (*)(int, char**); 90 91 LaunchHelper() {} 92 93 // Initialise the alternate test child implementation. 94 void Init(MainFunction main); 95 96 // Starts a child test helper process. 97 Process StartChildTestHelper(const std::string& procname, 98 const CommandLine& base_command_line, 99 const LaunchOptions& options); 100 101 // Waits for a child test helper process. 102 bool WaitForChildExitWithTimeout(const Process& process, TimeDelta timeout, 103 int* exit_code); 104 105 bool IsReady() const { return child_fd_ != -1; } 106 bool IsChild() const { return is_child_; } 107 108 private: 109 // Wrappers around sendmsg/recvmsg that supports message fragmentation. 110 void Send(int fd, const MessageHeader* msg, const std::vector<int>& fds); 111 ssize_t Recv(int fd, void* buf, std::vector<ScopedFD>* fds); 112 113 // Parent process implementation. 114 void DoParent(int fd); 115 // Helper process implementation. 116 void DoHelper(int fd); 117 118 void StartProcessInHelper(const StartProcessRequest* request, 119 std::vector<ScopedFD> fds); 120 void WaitForChildInHelper(const WaitProcessRequest* request); 121 122 bool is_child_ = false; 123 124 // Parent vars. 125 int child_fd_ = -1; 126 127 // Helper vars. 128 int parent_fd_ = -1; 129 MainFunction main_ = nullptr; 130 131 DISALLOW_COPY_AND_ASSIGN(LaunchHelper); 132 }; 133 134 void LaunchHelper::Init(MainFunction main) { 135 main_ = main; 136 137 // Create a communication channel between the parent and child launch helper. 138 // fd[0] belongs to the parent, fd[1] belongs to the child. 139 int fds[2] = {-1, -1}; 140 int rv = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds); 141 PCHECK(rv == 0); 142 CHECK_NE(-1, fds[0]); 143 CHECK_NE(-1, fds[1]); 144 145 pid_t pid = fork(); 146 PCHECK(pid >= 0) << "Fork failed"; 147 if (pid) { 148 // Parent. 149 rv = close(fds[1]); 150 PCHECK(rv == 0); 151 DoParent(fds[0]); 152 } else { 153 // Helper. 154 rv = close(fds[0]); 155 PCHECK(rv == 0); 156 DoHelper(fds[1]); 157 NOTREACHED(); 158 _exit(0); 159 } 160 } 161 162 void LaunchHelper::Send( 163 int fd, const MessageHeader* msg, const std::vector<int>& fds) { 164 uint32_t bytes_remaining = msg->size; 165 const char* buf = reinterpret_cast<const char*>(msg); 166 while (bytes_remaining) { 167 size_t send_size = 168 (bytes_remaining > kFragmentSize) ? kFragmentSize : bytes_remaining; 169 bool success = UnixDomainSocket::SendMsg( 170 fd, buf, send_size, 171 (bytes_remaining == msg->size) ? fds : std::vector<int>()); 172 CHECK(success); 173 bytes_remaining -= send_size; 174 buf += send_size; 175 } 176 } 177 178 ssize_t LaunchHelper::Recv(int fd, void* buf, std::vector<ScopedFD>* fds) { 179 ssize_t size = UnixDomainSocket::RecvMsg(fd, buf, kFragmentSize, fds); 180 if (size <= 0) 181 return size; 182 183 const MessageHeader* header = reinterpret_cast<const MessageHeader*>(buf); 184 CHECK(header->size < kMaxMessageSize); 185 uint32_t bytes_remaining = header->size - size; 186 char* buffer = reinterpret_cast<char*>(buf); 187 buffer += size; 188 while (bytes_remaining) { 189 std::vector<ScopedFD> dummy_fds; 190 size = UnixDomainSocket::RecvMsg(fd, buffer, kFragmentSize, &dummy_fds); 191 if (size <= 0) 192 return size; 193 194 CHECK(dummy_fds.empty()); 195 CHECK(size == kFragmentSize || 196 static_cast<size_t>(size) == bytes_remaining); 197 bytes_remaining -= size; 198 buffer += size; 199 } 200 return header->size; 201 } 202 203 void LaunchHelper::DoParent(int fd) { 204 child_fd_ = fd; 205 } 206 207 void LaunchHelper::DoHelper(int fd) { 208 parent_fd_ = fd; 209 is_child_ = true; 210 std::unique_ptr<char[]> buf(new char[kMaxMessageSize]); 211 while (true) { 212 // Wait for a message from the parent. 213 std::vector<ScopedFD> fds; 214 ssize_t size = Recv(parent_fd_, buf.get(), &fds); 215 if (size == 0 || (size < 0 && errno == ECONNRESET)) { 216 _exit(0); 217 } 218 PCHECK(size > 0); 219 220 const MessageHeader* header = 221 reinterpret_cast<const MessageHeader*>(buf.get()); 222 CHECK_EQ(static_cast<ssize_t>(header->size), size); 223 switch (header->type) { 224 case MessageType::START_REQUEST: 225 StartProcessInHelper( 226 reinterpret_cast<const StartProcessRequest*>(buf.get()), 227 std::move(fds)); 228 break; 229 case MessageType::WAIT_REQUEST: 230 WaitForChildInHelper( 231 reinterpret_cast<const WaitProcessRequest*>(buf.get())); 232 break; 233 default: 234 LOG(FATAL) << "Unsupported message type: " 235 << static_cast<uint32_t>(header->type); 236 } 237 } 238 } 239 240 void LaunchHelper::StartProcessInHelper(const StartProcessRequest* request, 241 std::vector<ScopedFD> fds) { 242 pid_t pid = fork(); 243 PCHECK(pid >= 0) << "Fork failed"; 244 if (pid) { 245 // Helper. 246 StartProcessResponse resp; 247 resp.child_pid = pid; 248 Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp), 249 std::vector<int>()); 250 } else { 251 // Child. 252 PCHECK(close(parent_fd_) == 0); 253 parent_fd_ = -1; 254 CommandLine::Reset(); 255 256 Pickle serialised_extra(reinterpret_cast<const char*>(request + 1), 257 request->header.size - sizeof(StartProcessRequest)); 258 PickleIterator iter(serialised_extra); 259 std::vector<std::string> args; 260 for (size_t i = 0; i < request->num_args; i++) { 261 std::string arg; 262 CHECK(iter.ReadString(&arg)); 263 args.push_back(std::move(arg)); 264 } 265 266 CHECK_EQ(request->num_fds, fds.size()); 267 for (size_t i = 0; i < request->num_fds; i++) { 268 int new_fd; 269 CHECK(iter.ReadInt(&new_fd)); 270 int old_fd = fds[i].release(); 271 if (new_fd != old_fd) { 272 if (dup2(old_fd, new_fd) < 0) { 273 PLOG(FATAL) << "dup2"; 274 } 275 PCHECK(close(old_fd) == 0); 276 } 277 } 278 279 // argv has argc+1 elements, where the last element is NULL. 280 std::unique_ptr<char*[]> argv(new char*[args.size() + 1]); 281 for (size_t i = 0; i < args.size(); i++) { 282 argv[i] = const_cast<char*>(args[i].c_str()); 283 } 284 argv[args.size()] = nullptr; 285 _exit(main_(args.size(), argv.get())); 286 NOTREACHED(); 287 } 288 } 289 290 void LaunchHelper::WaitForChildInHelper(const WaitProcessRequest* request) { 291 Process process(request->pid); 292 TimeDelta timeout = TimeDelta::FromMilliseconds(request->timeout_ms); 293 int exit_code = -1; 294 bool success = process.WaitForExitWithTimeout(timeout, &exit_code); 295 296 WaitProcessResponse resp; 297 resp.exit_code = exit_code; 298 resp.success = success; 299 Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp), 300 std::vector<int>()); 301 } 302 303 Process LaunchHelper::StartChildTestHelper(const std::string& procname, 304 const CommandLine& base_command_line, 305 const LaunchOptions& options) { 306 307 CommandLine command_line(base_command_line); 308 if (!command_line.HasSwitch(switches::kTestChildProcess)) 309 command_line.AppendSwitchASCII(switches::kTestChildProcess, procname); 310 311 StartProcessRequest request; 312 Pickle serialised_extra; 313 const CommandLine::StringVector& argv = command_line.argv(); 314 for (const auto& arg : argv) 315 CHECK(serialised_extra.WriteString(arg)); 316 request.num_args = argv.size(); 317 318 std::vector<int> fds_to_send; 319 if (options.fds_to_remap) { 320 for (auto p : *options.fds_to_remap) { 321 CHECK(serialised_extra.WriteInt(p.second)); 322 fds_to_send.push_back(p.first); 323 } 324 request.num_fds = options.fds_to_remap->size(); 325 } 326 327 size_t buf_size = sizeof(StartProcessRequest) + serialised_extra.size(); 328 request.header.size = buf_size; 329 std::unique_ptr<char[]> buffer(new char[buf_size]); 330 memcpy(buffer.get(), &request, sizeof(StartProcessRequest)); 331 memcpy(buffer.get() + sizeof(StartProcessRequest), serialised_extra.data(), 332 serialised_extra.size()); 333 334 // Send start message. 335 Send(child_fd_, reinterpret_cast<const MessageHeader*>(buffer.get()), 336 fds_to_send); 337 338 // Synchronously get response. 339 StartProcessResponse response; 340 std::vector<ScopedFD> recv_fds; 341 ssize_t resp_size = Recv(child_fd_, &response, &recv_fds); 342 PCHECK(resp_size == sizeof(StartProcessResponse)); 343 344 return Process(response.child_pid); 345 } 346 347 bool LaunchHelper::WaitForChildExitWithTimeout( 348 const Process& process, TimeDelta timeout, int* exit_code) { 349 350 WaitProcessRequest request; 351 request.pid = process.Handle(); 352 request.timeout_ms = timeout.InMilliseconds(); 353 354 Send(child_fd_, reinterpret_cast<const MessageHeader*>(&request), 355 std::vector<int>()); 356 357 WaitProcessResponse response; 358 std::vector<ScopedFD> recv_fds; 359 ssize_t resp_size = Recv(child_fd_, &response, &recv_fds); 360 PCHECK(resp_size == sizeof(WaitProcessResponse)); 361 362 if (!response.success) 363 return false; 364 365 *exit_code = response.exit_code; 366 return true; 367 } 368 369 LazyInstance<LaunchHelper>::Leaky g_launch_helper; 370 371 } // namespace 372 373 void InitAndroidMultiProcessTestHelper(int (*main)(int, char**)) { 374 DCHECK(main); 375 // Don't allow child processes to themselves create new child processes. 376 if (g_launch_helper.Get().IsChild()) 377 return; 378 g_launch_helper.Get().Init(main); 379 } 380 381 bool AndroidIsChildProcess() { 382 return g_launch_helper.Get().IsChild(); 383 } 384 385 bool AndroidWaitForChildExitWithTimeout( 386 const Process& process, TimeDelta timeout, int* exit_code) { 387 CHECK(g_launch_helper.Get().IsReady()); 388 return g_launch_helper.Get().WaitForChildExitWithTimeout( 389 process, timeout, exit_code); 390 } 391 392 // A very basic implementation for Android. On Android tests can run in an APK 393 // and we don't have an executable to exec*. This implementation does the bare 394 // minimum to execute the method specified by procname (in the child process). 395 // - All options except |fds_to_remap| are ignored. 396 Process SpawnMultiProcessTestChild(const std::string& procname, 397 const CommandLine& base_command_line, 398 const LaunchOptions& options) { 399 if (g_launch_helper.Get().IsReady()) { 400 return g_launch_helper.Get().StartChildTestHelper( 401 procname, base_command_line, options); 402 } 403 404 // TODO(viettrungluu): The FD-remapping done below is wrong in the presence of 405 // cycles (e.g., fd1 -> fd2, fd2 -> fd1). crbug.com/326576 406 FileHandleMappingVector empty; 407 const FileHandleMappingVector* fds_to_remap = 408 options.fds_to_remap ? options.fds_to_remap : ∅ 409 410 pid_t pid = fork(); 411 412 if (pid < 0) { 413 PLOG(ERROR) << "fork"; 414 return Process(); 415 } 416 if (pid > 0) { 417 // Parent process. 418 return Process(pid); 419 } 420 // Child process. 421 base::hash_set<int> fds_to_keep_open; 422 for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin(); 423 it != fds_to_remap->end(); ++it) { 424 fds_to_keep_open.insert(it->first); 425 } 426 // Keep standard FDs (stdin, stdout, stderr, etc.) open since this 427 // is not meant to spawn a daemon. 428 int base = GlobalDescriptors::kBaseDescriptor; 429 for (int fd = base; fd < sysconf(_SC_OPEN_MAX); ++fd) { 430 if (fds_to_keep_open.find(fd) == fds_to_keep_open.end()) { 431 close(fd); 432 } 433 } 434 for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin(); 435 it != fds_to_remap->end(); ++it) { 436 int old_fd = it->first; 437 int new_fd = it->second; 438 if (dup2(old_fd, new_fd) < 0) { 439 PLOG(FATAL) << "dup2"; 440 } 441 close(old_fd); 442 } 443 CommandLine::Reset(); 444 CommandLine::Init(0, nullptr); 445 CommandLine* command_line = CommandLine::ForCurrentProcess(); 446 command_line->InitFromArgv(base_command_line.argv()); 447 if (!command_line->HasSwitch(switches::kTestChildProcess)) 448 command_line->AppendSwitchASCII(switches::kTestChildProcess, procname); 449 450 _exit(multi_process_function_list::InvokeChildProcessTest(procname)); 451 return Process(); 452 } 453 454 } // namespace base 455