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