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