1 /* 2 * Copyright (C) 2020 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 #ifndef INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 19 20 #include <condition_variable> 21 #include <functional> 22 #include <initializer_list> 23 #include <memory> 24 #include <mutex> 25 #include <optional> 26 #include <string> 27 #include <thread> 28 #include <vector> 29 30 #include "perfetto/base/build_config.h" 31 #include "perfetto/base/logging.h" 32 #include "perfetto/base/platform_handle.h" 33 #include "perfetto/base/proc_utils.h" 34 #include "perfetto/ext/base/event_fd.h" 35 #include "perfetto/ext/base/pipe.h" 36 #include "perfetto/ext/base/scoped_file.h" 37 38 namespace perfetto { 39 namespace base { 40 41 // Handles creation and lifecycle management of subprocesses, taking care of 42 // all subtleties involved in handling processes on UNIX. 43 // This class allows to deal with macro two use-cases: 44 // 1) fork() + exec() equivalent: for spawning a brand new process image. 45 // This happens when |args.exec_cmd| is not empty. 46 // This is safe to use even in a multi-threaded environment. 47 // 2) fork(): for spawning a process and running a function. 48 // This happens when |args.posix_entrypoint_for_testing| is not empty. 49 // This is intended only for tests as it is extremely subtle. 50 // This mode must be used with extreme care. Before the entrypoint is 51 // invoked all file descriptors other than stdin/out/err and the ones 52 // specified in |args.preserve_fds| will be closed, to avoid each process 53 // retaining a dupe of other subprocesses pipes. This however means that 54 // any non trivial calls (including logging) must be avoided as they might 55 // refer to FDs that are now closed. The entrypoint should really be used 56 // just to signal a pipe or similar for synchronizing sequencing in tests. 57 58 // 59 // This class allows to control stdin/out/err pipe redirection and takes care 60 // of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar 61 // fashion of python's subprocess.Communicate() 62 // stdin: is always piped and closed once the |args.input| buffer is written. 63 // stdout/err can be either: 64 // - dup()ed onto the parent process stdout/err. 65 // - redirected onto /dev/null. 66 // - piped onto a buffer (see output() method). There is only one output 67 // buffer in total. If both stdout and stderr are set to kBuffer mode, they 68 // will be merged onto the same. There doesn't seem any use case where they 69 // are needed distinctly. 70 // 71 // Some caveats worth mentioning: 72 // - It always waitpid()s, to avoid leaving zombies around. If the process is 73 // not terminated by the time the destructor is reached, the dtor will 74 // send a SIGKILL and wait for the termination. 75 // - After fork()-ing it will close all file descriptors, preserving only 76 // stdin/out/err and the fds listed in |args.preserve_fds|. 77 // - On Linux/Android, the child process will be SIGKILL-ed if the calling 78 // thread exists, even if the Subprocess is std::move()-d onto another thread. 79 // This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that 80 // child processes are leaked in the case of a crash of the parent (frequent 81 // in tests). However, the child process might still be leaked if execing 82 // a setuid/setgid binary (see man 2 prctl). 83 // 84 // Usage: 85 // base::Subprocess p({"/bin/cat", "-"}); 86 // (or equivalently: 87 // base::Subprocess p; 88 // p.args.exec_cmd.push_back("/bin/cat"); 89 // p.args.exec_cmd.push_back("-"); 90 // ) 91 // p.args.stdout_mode = base::Subprocess::kBuffer; 92 // p.args.stderr_mode = base::Subprocess::kInherit; 93 // p.args.input = "stdin contents"; 94 // p.Call(); 95 // (or equivalently: 96 // p.Start(); 97 // p.Wait(); 98 // ) 99 // EXPECT_EQ(p.status(), base::Subprocess::kTerminated); 100 // EXPECT_EQ(p.returncode(), 0); 101 class Subprocess { 102 public: 103 enum Status { 104 kNotStarted = 0, // Before calling Start() or Call(). 105 kRunning, // After calling Start(), before Wait(). 106 kTerminated, // The subprocess terminated, either successfully or not. 107 // This includes crashes or other signals on UNIX. 108 }; 109 110 enum class OutputMode { 111 kInherit = 0, // Inherit's the caller process stdout/stderr. 112 kDevNull, // dup() onto /dev/null. 113 kBuffer, // dup() onto a pipe and move it into the output() buffer. 114 kFd, // dup() onto the passed args.fd. 115 }; 116 117 enum class InputMode { 118 kBuffer = 0, // dup() onto a pipe and write args.input on it. 119 kDevNull, // dup() onto /dev/null. 120 }; 121 122 // Input arguments for configuring the subprocess behavior. 123 struct Args { exec_cmdArgs124 Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {} 125 Args(Args&&) noexcept; 126 Args& operator=(Args&&); 127 // If non-empty this will cause an exec() when Start()/Call() are called. 128 std::vector<std::string> exec_cmd; 129 130 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) 131 // If non-empty, it changes the argv[0] argument passed to exec. If 132 // unset, argv[0] == exec_cmd[0]. This is to handle cases like: 133 // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override". 134 std::string posix_argv0_override_for_testing; 135 136 // If non-empty this will be invoked on the fork()-ed child process, after 137 // stdin/out/err has been redirected and all other file descriptor are 138 // closed. It is valid to specify both |exec_cmd| AND 139 // |posix_entrypoint_for_testing|. In this case the latter will be invoked 140 // just before the exec() call, but after having closed all fds % stdin/o/e. 141 // This is for synchronization barriers in tests. 142 std::function<void()> posix_entrypoint_for_testing; 143 144 // When set, will will move the process to the given process group. If set 145 // and zero, it will create a new process group. Effectively this calls 146 // setpgid(0 /*self_pid*/, posix_proc_group_id). 147 // This can be used to avoid that subprocesses receive CTRL-C from the 148 // terminal, while still living in the same session. 149 std::optional<pid_t> posix_proc_group_id{}; 150 #endif 151 152 // If non-empty, replaces the environment passed to exec(). 153 std::vector<std::string> env; 154 155 // The file descriptors in this list will not be closed. 156 std::vector<int> preserve_fds; 157 158 // The data to push in the child process stdin, if input_mode == 159 // InputMode::kBuffer. 160 std::string input; 161 162 InputMode stdin_mode = InputMode::kBuffer; 163 OutputMode stdout_mode = OutputMode::kInherit; 164 OutputMode stderr_mode = OutputMode::kInherit; 165 166 base::ScopedPlatformHandle out_fd; 167 168 // Returns " ".join(exec_cmd), quoting arguments. 169 std::string GetCmdString() const; 170 }; 171 172 struct ResourceUsage { 173 uint32_t cpu_utime_ms = 0; 174 uint32_t cpu_stime_ms = 0; 175 uint32_t max_rss_kb = 0; 176 uint32_t min_page_faults = 0; 177 uint32_t maj_page_faults = 0; 178 uint32_t vol_ctx_switch = 0; 179 uint32_t invol_ctx_switch = 0; 180 cpu_time_msResourceUsage181 uint32_t cpu_time_ms() const { return cpu_utime_ms + cpu_stime_ms; } 182 }; 183 184 explicit Subprocess(std::initializer_list<std::string> exec_cmd = {}); 185 Subprocess(Subprocess&&) noexcept; 186 Subprocess& operator=(Subprocess&&); 187 ~Subprocess(); // It will KillAndWaitForTermination() if still alive. 188 189 // Starts the subprocess but doesn't wait for its termination. The caller 190 // is expected to either call Wait() or Poll() after this call. 191 void Start(); 192 193 // Wait for process termination. Can be called more than once. 194 // Args: 195 // |timeout_ms| = 0: wait indefinitely. 196 // |timeout_ms| > 0: wait for at most |timeout_ms|. 197 // Returns: 198 // True: The process terminated. See status() and returncode(). 199 // False: Timeout reached, the process is still running. In this case the 200 // process will be left in the kRunning state. 201 bool Wait(int timeout_ms = 0); 202 203 // Equivalent of Start() + Wait(); 204 // Returns true if the process exited cleanly with return code 0. False in 205 // any othe case. 206 bool Call(int timeout_ms = 0); 207 208 Status Poll(); 209 210 // Sends a signal (SIGKILL if not specified) and wait for process termination. 211 void KillAndWaitForTermination(int sig_num = 0); 212 pid()213 PlatformProcessId pid() const { return s_->pid; } 214 215 // The accessors below are updated only after a call to Poll(), Wait() or 216 // KillAndWaitForTermination(). 217 // In most cases you want to call Poll() rather than these accessors. 218 status()219 Status status() const { return s_->status; } returncode()220 int returncode() const { return s_->returncode; } timed_out()221 bool timed_out() const { return s_->timed_out; } 222 223 // This contains both stdout and stderr (if the corresponding _mode == 224 // OutputMode::kBuffer). It's non-const so the caller can std::move() it. output()225 std::string& output() { return s_->output; } output()226 const std::string& output() const { return s_->output; } 227 posix_rusage()228 const ResourceUsage& posix_rusage() const { return *s_->rusage; } 229 230 Args args; 231 232 private: 233 // The signal/exit code used when killing the process in case of a timeout. 234 static const int kTimeoutSignal; 235 236 Subprocess(const Subprocess&) = delete; 237 Subprocess& operator=(const Subprocess&) = delete; 238 239 // This is to deal robustly with the move operators, without having to 240 // manually maintain member-wise move instructions. 241 struct MovableState { 242 base::Pipe stdin_pipe; 243 base::Pipe stdouterr_pipe; 244 PlatformProcessId pid; 245 Status status = kNotStarted; 246 int returncode = -1; 247 std::string output; // Stdin+stderr. Only when OutputMode::kBuffer. 248 std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()}; 249 bool timed_out = false; 250 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) 251 std::thread stdouterr_thread; 252 std::thread stdin_thread; 253 ScopedPlatformHandle win_proc_handle; 254 ScopedPlatformHandle win_thread_handle; 255 256 base::EventFd stdouterr_done_event; 257 std::mutex mutex; // Protects locked_outerr_buf and the two pipes. 258 std::string locked_outerr_buf; 259 #else 260 base::Pipe exit_status_pipe; 261 size_t input_written = 0; 262 std::thread waitpid_thread; 263 #endif 264 }; 265 266 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) 267 static void StdinThread(MovableState*, std::string input); 268 static void StdoutErrThread(MovableState*); 269 #else 270 void TryPushStdin(); 271 void TryReadStdoutAndErr(); 272 void TryReadExitStatus(); 273 bool PollInternal(int poll_timeout_ms); 274 #endif 275 276 std::unique_ptr<MovableState> s_; 277 }; 278 279 } // namespace base 280 } // namespace perfetto 281 282 #endif // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 283