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