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 #include "perfetto/ext/base/subprocess.h"
18
19 #include "perfetto/base/build_config.h"
20
21 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
22
23 #include <stdio.h>
24
25 #include <algorithm>
26 #include <mutex>
27 #include <tuple>
28
29 #include <Windows.h>
30
31 #include "perfetto/base/logging.h"
32 #include "perfetto/base/time.h"
33 #include "perfetto/ext/base/pipe.h"
34 #include "perfetto/ext/base/utils.h"
35
36 namespace perfetto {
37 namespace base {
38
39 // static
40 const int Subprocess::kTimeoutSignal = static_cast<int>(STATUS_TIMEOUT);
41
Start()42 void Subprocess::Start() {
43 if (args.exec_cmd.empty()) {
44 PERFETTO_ELOG("Subprocess.exec_cmd cannot be empty on Windows");
45 return;
46 }
47
48 // Quote arguments but only when ambiguous. When quoting, CreateProcess()
49 // assumes that the command is an absolute path and does not search in the
50 // %PATH%. If non quoted, instead, CreateProcess() tries both. This is to
51 // allow Subprocess("cmd.exe", "/c", "shell command").
52 std::string cmd;
53 for (const auto& part : args.exec_cmd) {
54 if (part.find(" ") != std::string::npos) {
55 cmd += "\"" + part + "\" ";
56 } else {
57 cmd += part + " ";
58 }
59 }
60 // Remove trailing space.
61 if (!cmd.empty())
62 cmd.resize(cmd.size() - 1);
63
64 s_->stdin_pipe = Pipe::Create();
65 // Allow the child process to inherit the other end of the pipe.
66 PERFETTO_CHECK(
67 ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
68
69 if (args.stderr_mode == kBuffer || args.stdout_mode == kBuffer) {
70 s_->stdouterr_pipe = Pipe::Create();
71 PERFETTO_CHECK(
72 ::SetHandleInformation(*s_->stdouterr_pipe.wr, HANDLE_FLAG_INHERIT, 1));
73 }
74
75 ScopedPlatformHandle nul_handle;
76 if (args.stderr_mode == kDevNull || args.stdout_mode == kDevNull) {
77 nul_handle.reset(::CreateFileA("NUL", GENERIC_WRITE, FILE_SHARE_WRITE,
78 nullptr, OPEN_EXISTING,
79 FILE_ATTRIBUTE_NORMAL, nullptr));
80 PERFETTO_CHECK(::SetHandleInformation(*nul_handle, HANDLE_FLAG_INHERIT, 1));
81 }
82
83 PROCESS_INFORMATION proc_info{};
84 STARTUPINFOA start_info{};
85 start_info.cb = sizeof(STARTUPINFOA);
86
87 if (args.stderr_mode == kInherit) {
88 start_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
89 } else if (args.stderr_mode == kBuffer) {
90 start_info.hStdError = *s_->stdouterr_pipe.wr;
91 } else if (args.stderr_mode == kDevNull) {
92 start_info.hStdError = *nul_handle;
93 } else if (args.stderr_mode == kFd) {
94 PERFETTO_CHECK(
95 ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
96 start_info.hStdError = *args.out_fd;
97 } else {
98 PERFETTO_CHECK(false);
99 }
100
101 if (args.stdout_mode == kInherit) {
102 start_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
103 } else if (args.stdout_mode == kBuffer) {
104 start_info.hStdOutput = *s_->stdouterr_pipe.wr;
105 } else if (args.stdout_mode == kDevNull) {
106 start_info.hStdOutput = *nul_handle;
107 } else if (args.stdout_mode == kFd) {
108 PERFETTO_CHECK(
109 ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
110 start_info.hStdOutput = *args.out_fd;
111 } else {
112 PERFETTO_CHECK(false);
113 }
114
115 start_info.hStdInput = *s_->stdin_pipe.rd;
116 start_info.dwFlags |= STARTF_USESTDHANDLES;
117
118 // Create the child process.
119 bool success =
120 ::CreateProcessA(nullptr, // App name. Needs to be null to use PATH.
121 &cmd[0], // Command line.
122 nullptr, // Process security attributes.
123 nullptr, // Primary thread security attributes.
124 true, // Handles are inherited.
125 0, // Flags.
126 nullptr, // Use parent's environment.
127 nullptr, // Use parent's current directory.
128 &start_info, // STARTUPINFO pointer.
129 &proc_info); // Receives PROCESS_INFORMATION.
130
131 // Close on our side the pipe ends that we passed to the child process.
132 s_->stdin_pipe.rd.reset();
133 s_->stdouterr_pipe.wr.reset();
134 args.out_fd.reset();
135
136 if (!success) {
137 s_->returncode = ERROR_FILE_NOT_FOUND;
138 s_->status = kTerminated;
139 s_->stdin_pipe.wr.reset();
140 s_->stdouterr_pipe.rd.reset();
141 PERFETTO_ELOG("CreateProcess failed: %lx, cmd: %s", GetLastError(),
142 &cmd[0]);
143 return;
144 }
145
146 s_->pid = proc_info.dwProcessId;
147 s_->win_proc_handle = ScopedPlatformHandle(proc_info.hProcess);
148 s_->win_thread_handle = ScopedPlatformHandle(proc_info.hThread);
149 s_->status = kRunning;
150
151 MovableState* s = s_.get();
152 s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
153
154 if (args.stderr_mode == kBuffer || args.stdout_mode == kBuffer) {
155 PERFETTO_DCHECK(s_->stdouterr_pipe.rd);
156 s_->stdouterr_thread = std::thread(&Subprocess::StdoutErrThread, s);
157 }
158 }
159
160 // static
StdinThread(MovableState * s,std::string input)161 void Subprocess::StdinThread(MovableState* s, std::string input) {
162 size_t input_written = 0;
163 while (input_written < input.size()) {
164 DWORD wsize = 0;
165 if (::WriteFile(*s->stdin_pipe.wr, input.data() + input_written,
166 static_cast<DWORD>(input.size() - input_written), &wsize,
167 nullptr)) {
168 input_written += wsize;
169 } else {
170 // ERROR_BROKEN_PIPE is WAI when the child just closes stdin and stops
171 // accepting input.
172 auto err = ::GetLastError();
173 if (err != ERROR_BROKEN_PIPE)
174 PERFETTO_PLOG("Subprocess WriteFile(stdin) failed %lx", err);
175 break;
176 }
177 } // while(...)
178 std::unique_lock<std::mutex> lock(s->mutex);
179 s->stdin_pipe.wr.reset();
180 }
181
182 // static
StdoutErrThread(MovableState * s)183 void Subprocess::StdoutErrThread(MovableState* s) {
184 char buf[4096];
185 for (;;) {
186 DWORD rsize = 0;
187 bool res =
188 ::ReadFile(*s->stdouterr_pipe.rd, buf, sizeof(buf), &rsize, nullptr);
189 if (!res) {
190 auto err = GetLastError();
191 if (err != ERROR_BROKEN_PIPE)
192 PERFETTO_PLOG("Subprocess ReadFile(stdouterr) failed %ld", err);
193 }
194
195 if (rsize > 0) {
196 std::unique_lock<std::mutex> lock(s->mutex);
197 s->locked_outerr_buf.append(buf, static_cast<size_t>(rsize));
198 } else { // EOF or some error.
199 break;
200 }
201 } // For(..)
202
203 // Close the stdouterr_pipe. The main loop looks at the pipe closure to
204 // determine whether the stdout/err thread has completed.
205 {
206 std::unique_lock<std::mutex> lock(s->mutex);
207 s->stdouterr_pipe.rd.reset();
208 }
209 s->stdouterr_done_event.Notify();
210 }
211
Poll()212 Subprocess::Status Subprocess::Poll() {
213 if (s_->status != kRunning)
214 return s_->status; // Nothing to poll.
215 Wait(1 /*ms*/);
216 return s_->status;
217 }
218
Wait(int timeout_ms)219 bool Subprocess::Wait(int timeout_ms) {
220 PERFETTO_CHECK(s_->status != kNotStarted);
221 const bool wait_forever = timeout_ms == 0;
222 const int64_t wait_start_ms = base::GetWallTimeMs().count();
223
224 // Break out of the loop only after both conditions are satisfied:
225 // - All stdout/stderr data has been read (if kBuffer).
226 // - The process exited.
227 // Note that the two events can happen arbitrary order. After the process
228 // exits, there might be still data in the pipe buffer, which we want to
229 // read fully.
230 // Note also that stdout/err might be "complete" before starting, if neither
231 // is operating in kBuffer mode. In that case we just want to wait for the
232 // process termination.
233 //
234 // Instead, don't wait on the stdin to be fully written. The child process
235 // might exit prematurely (or crash). If that happens, we can end up in a
236 // state where the write(stdin_pipe_.wr) will never unblock.
237 bool stdouterr_complete = false;
238 for (;;) {
239 HANDLE wait_handles[2]{};
240 DWORD num_handles = 0;
241
242 // Check if the process exited.
243 bool process_exited = !s_->win_proc_handle;
244 if (!process_exited) {
245 DWORD exit_code = STILL_ACTIVE;
246 PERFETTO_CHECK(::GetExitCodeProcess(*s_->win_proc_handle, &exit_code));
247 if (exit_code != STILL_ACTIVE) {
248 s_->returncode = static_cast<int>(exit_code);
249 s_->status = kTerminated;
250 s_->win_proc_handle.reset();
251 s_->win_thread_handle.reset();
252 process_exited = true;
253 }
254 } else {
255 PERFETTO_DCHECK(s_->status != kRunning);
256 }
257 if (!process_exited) {
258 wait_handles[num_handles++] = *s_->win_proc_handle;
259 }
260
261 // Check if there is more output and if the stdout/err pipe has been closed.
262 {
263 std::unique_lock<std::mutex> lock(s_->mutex);
264 // Move the output from the internal buffer shared with the
265 // stdouterr_thread to the final buffer exposed to the client.
266 if (!s_->locked_outerr_buf.empty()) {
267 s_->output.append(std::move(s_->locked_outerr_buf));
268 s_->locked_outerr_buf.clear();
269 }
270 stdouterr_complete = !s_->stdouterr_pipe.rd;
271 if (!stdouterr_complete) {
272 wait_handles[num_handles++] = s_->stdouterr_done_event.fd();
273 }
274 } // lock(s_->mutex)
275
276 if (num_handles == 0) {
277 PERFETTO_DCHECK(process_exited && stdouterr_complete);
278 break;
279 }
280
281 DWORD wait_ms; // Note: DWORD is unsigned.
282 if (wait_forever) {
283 wait_ms = INFINITE;
284 } else {
285 const int64_t now = GetWallTimeMs().count();
286 const int64_t wait_left_ms = timeout_ms - (now - wait_start_ms);
287 if (wait_left_ms <= 0)
288 return false; // Timed out
289 wait_ms = static_cast<DWORD>(wait_left_ms);
290 }
291
292 auto wait_res =
293 ::WaitForMultipleObjects(num_handles, wait_handles, false, wait_ms);
294 PERFETTO_CHECK(wait_res != WAIT_FAILED);
295 }
296
297 PERFETTO_DCHECK(!s_->win_proc_handle);
298 PERFETTO_DCHECK(!s_->win_thread_handle);
299
300 if (s_->stdin_thread.joinable()) // Might not exist if CreateProcess failed.
301 s_->stdin_thread.join();
302 if (s_->stdouterr_thread.joinable())
303 s_->stdouterr_thread.join();
304
305 // The stdin pipe is closed by the dedicated stdin thread. However if that is
306 // not started (e.g. because of no redirection) force close it now. Needs to
307 // happen after the join() to be thread safe.
308 s_->stdin_pipe.wr.reset();
309 s_->stdouterr_pipe.rd.reset();
310
311 return true;
312 }
313
KillAndWaitForTermination(int exit_code)314 void Subprocess::KillAndWaitForTermination(int exit_code) {
315 auto code = exit_code ? static_cast<DWORD>(exit_code) : STATUS_CONTROL_C_EXIT;
316 ::TerminateProcess(*s_->win_proc_handle, code);
317 Wait();
318 // TryReadExitStatus must have joined the threads.
319 PERFETTO_DCHECK(!s_->stdin_thread.joinable());
320 PERFETTO_DCHECK(!s_->stdouterr_thread.joinable());
321 }
322
323 } // namespace base
324 } // namespace perfetto
325
326 #endif // PERFETTO_OS_WIN
327