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