workload.cpp revision 8acc1fc01c91734a51ecdac25dcbc7733ea1b7f9
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "workload.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <sys/wait.h>
22#include <unistd.h>
23
24#include <base/logging.h>
25
26std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) {
27  std::unique_ptr<Workload> workload(new Workload(args));
28  if (workload != nullptr && workload->CreateNewProcess()) {
29    return workload;
30  }
31  return nullptr;
32}
33
34Workload::~Workload() {
35  if (work_pid_ != -1 && work_state_ != NotYetCreateNewProcess) {
36    if (!Workload::WaitChildProcess(false)) {
37      kill(work_pid_, SIGKILL);
38      Workload::WaitChildProcess(true);
39    }
40  }
41  if (start_signal_fd_ != -1) {
42    close(start_signal_fd_);
43  }
44  if (exec_child_fd_ != -1) {
45    close(exec_child_fd_);
46  }
47}
48
49static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd);
50
51bool Workload::CreateNewProcess() {
52  CHECK_EQ(work_state_, NotYetCreateNewProcess);
53
54  int start_signal_pipe[2];
55  if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) {
56    PLOG(ERROR) << "pipe2() failed";
57    return false;
58  }
59
60  int exec_child_pipe[2];
61  if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) {
62    PLOG(ERROR) << "pipe2() failed";
63    close(start_signal_pipe[0]);
64    close(start_signal_pipe[1]);
65    return false;
66  }
67
68  pid_t pid = fork();
69  if (pid == -1) {
70    PLOG(ERROR) << "fork() failed";
71    close(start_signal_pipe[0]);
72    close(start_signal_pipe[1]);
73    close(exec_child_pipe[0]);
74    close(exec_child_pipe[1]);
75    return false;
76  } else if (pid == 0) {
77    // In child process.
78    close(start_signal_pipe[1]);
79    close(exec_child_pipe[0]);
80    ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]);
81  }
82  // In parent process.
83  close(start_signal_pipe[0]);
84  close(exec_child_pipe[1]);
85  start_signal_fd_ = start_signal_pipe[1];
86  exec_child_fd_ = exec_child_pipe[0];
87  work_pid_ = pid;
88  work_state_ = NotYetStartNewProcess;
89  return true;
90}
91
92static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) {
93  std::vector<char*> argv(args.size() + 1);
94  for (size_t i = 0; i < args.size(); ++i) {
95    argv[i] = &args[i][0];
96  }
97  argv[args.size()] = nullptr;
98
99  char start_signal = 0;
100  ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1));
101  if (nread == 1 && start_signal == 1) {
102    close(start_signal_fd);
103    execvp(argv[0], argv.data());
104    // If execvp() succeed, we will not arrive here. But if it failed, we need to
105    // report the failure to the parent process by writing 1 to exec_child_fd.
106    int saved_errno = errno;
107    char exec_child_failed = 1;
108    TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
109    close(exec_child_fd);
110    errno = saved_errno;
111    PLOG(ERROR) << "execvp(" << argv[0] << ") failed";
112  } else {
113    PLOG(DEBUG) << "child process failed to receive start_signal, nread = " << nread;
114  }
115  exit(1);
116}
117
118bool Workload::Start() {
119  CHECK_EQ(work_state_, NotYetStartNewProcess);
120  char start_signal = 1;
121  ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1));
122  if (nwrite != 1) {
123    PLOG(ERROR) << "write start signal failed";
124    return false;
125  }
126  char exec_child_failed;
127  ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
128  if (nread != 0) {
129    ((nread == -1) ? PLOG(ERROR) : LOG(ERROR)) << "exec child failed, nread = " << nread;
130    return false;
131  }
132  work_state_ = Started;
133  return true;
134}
135
136bool Workload::WaitChildProcess(bool wait_forever) {
137  bool finished = false;
138  int status;
139  pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (wait_forever ? 0 : WNOHANG)));
140  if (result == work_pid_) {
141    finished = true;
142    if (WIFSIGNALED(status)) {
143      LOG(WARNING) << "child process was terminated by signal " << strsignal(WTERMSIG(status));
144    } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
145      LOG(WARNING) << "child process exited with exit code " << WEXITSTATUS(status);
146    }
147  } else if (result == -1) {
148    PLOG(ERROR) << "waitpid() failed";
149  }
150  return finished;
151}
152