Home | History | Annotate | Download | only in test
      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 : &empty;
    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