xref: /aosp_15_r20/external/perfetto/src/base/subprocess_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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 <thread>
20 
21 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
22 #include <Windows.h>
23 #else
24 #include <signal.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #endif
28 
29 #include "perfetto/base/time.h"
30 #include "perfetto/ext/base/file_utils.h"
31 #include "perfetto/ext/base/pipe.h"
32 #include "perfetto/ext/base/temp_file.h"
33 #include "test/gtest_and_gmock.h"
34 
35 namespace perfetto {
36 namespace base {
37 namespace {
38 
GetOutput(const Subprocess & p)39 std::string GetOutput(const Subprocess& p) {
40   std::string output = p.output();
41 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
42   size_t pos = 0;
43   while ((pos = output.find("\r\n", pos)) != std::string::npos)
44     output.erase(pos, 1);
45 #endif
46   return output;
47 }
48 
GenLargeString()49 std::string GenLargeString() {
50   std::string contents;
51   for (int i = 0; i < 4096; i++) {
52     contents += "very long text " + std::to_string(i) + "\n";
53   }
54   // Make sure that |contents| is > the default pipe buffer on Linux (4 pages).
55   PERFETTO_DCHECK(contents.size() > 4096 * 4);
56   return contents;
57 }
58 
TEST(SubprocessTest,InvalidPath)59 TEST(SubprocessTest, InvalidPath) {
60   Subprocess p({"/usr/bin/invalid_1337"});
61   EXPECT_FALSE(p.Call());
62   EXPECT_EQ(p.status(), Subprocess::kTerminated);
63 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
64   EXPECT_EQ(p.returncode(), ERROR_FILE_NOT_FOUND);
65 #else
66   EXPECT_EQ(p.returncode(), 128);
67   EXPECT_EQ(GetOutput(p), "execve() failed\n");
68 #endif
69 }
70 
TEST(SubprocessTest,StdoutOnly)71 TEST(SubprocessTest, StdoutOnly) {
72 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
73   Subprocess p({"cmd", "/c", "(echo skip_err 1>&2) && echo out_only"});
74 #else
75   Subprocess p({"sh", "-c", "(echo skip_err >&2); echo out_only"});
76 #endif
77   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
78   p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
79 
80   EXPECT_TRUE(p.Call());
81   EXPECT_EQ(p.status(), Subprocess::kTerminated);
82   EXPECT_EQ(GetOutput(p), "out_only\n");
83 }
84 
TEST(SubprocessTest,StderrOnly)85 TEST(SubprocessTest, StderrOnly) {
86 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
87   Subprocess p({"cmd", "/c", "(echo err_only>&2) && echo skip_out"});
88 #else
89   Subprocess p({"sh", "-c", "(echo err_only >&2); echo skip_out"});
90 #endif
91   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
92   p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
93   EXPECT_TRUE(p.Call());
94   EXPECT_EQ(GetOutput(p), "err_only\n");
95 }
96 
TEST(SubprocessTest,BothStdoutAndStderr)97 TEST(SubprocessTest, BothStdoutAndStderr) {
98 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
99   Subprocess p({"cmd", "/c", "echo out&&(echo err>&2)&&echo out2"});
100 #else
101   Subprocess p({"sh", "-c", "echo out; (echo err >&2); echo out2"});
102 #endif
103   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
104   p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
105   EXPECT_TRUE(p.Call());
106   EXPECT_EQ(GetOutput(p), "out\nerr\nout2\n");
107 }
108 
TEST(SubprocessTest,CatInputModeDevNull)109 TEST(SubprocessTest, CatInputModeDevNull) {
110   std::string ignored_input = "ignored input";
111 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
112   Subprocess p({"cmd", "/C", "findstr . || exit 0"});
113 #else
114   Subprocess p({"cat", "-"});
115 #endif
116   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
117   p.args.input = ignored_input;
118   p.args.stdin_mode = Subprocess::InputMode::kDevNull;
119   EXPECT_TRUE(p.Call());
120   EXPECT_EQ(p.status(), Subprocess::kTerminated);
121   EXPECT_EQ(GetOutput(p), "");
122 }
123 
TEST(SubprocessTest,BothStdoutAndStderrInputModeDevNull)124 TEST(SubprocessTest, BothStdoutAndStderrInputModeDevNull) {
125 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
126   Subprocess p({"cmd", "/c", "echo out&&(echo err>&2)&&echo out2"});
127 #else
128   Subprocess p({"sh", "-c", "echo out; (echo err >&2); echo out2"});
129 #endif
130   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
131   p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
132   p.args.stdin_mode = Subprocess::InputMode::kDevNull;
133   EXPECT_TRUE(p.Call());
134   EXPECT_EQ(GetOutput(p), "out\nerr\nout2\n");
135 }
136 
TEST(SubprocessTest,AllDevNull)137 TEST(SubprocessTest, AllDevNull) {
138 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
139   Subprocess p({"cmd", "/c", "(exit 1)"});
140 #else
141   Subprocess p({"false"});
142 #endif
143   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
144   p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
145   p.args.stdin_mode = Subprocess::InputMode::kDevNull;
146   EXPECT_FALSE(p.Call());
147   EXPECT_EQ(p.status(), Subprocess::kTerminated);
148   EXPECT_EQ(p.returncode(), 1);
149 }
150 
TEST(SubprocessTest,BinTrue)151 TEST(SubprocessTest, BinTrue) {
152 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
153   Subprocess p({"cmd", "/c", "(exit 0)"});
154 #else
155   Subprocess p({"true"});
156 #endif
157   EXPECT_TRUE(p.Call());
158   EXPECT_EQ(p.status(), Subprocess::kTerminated);
159   EXPECT_EQ(p.returncode(), 0);
160 }
161 
TEST(SubprocessTest,BinFalse)162 TEST(SubprocessTest, BinFalse) {
163 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
164   Subprocess p({"cmd", "/c", "(exit 1)"});
165 #else
166   Subprocess p({"false"});
167 #endif
168   EXPECT_FALSE(p.Call());
169   EXPECT_EQ(p.status(), Subprocess::kTerminated);
170   EXPECT_EQ(p.returncode(), 1);
171 }
172 
TEST(SubprocessTest,Echo)173 TEST(SubprocessTest, Echo) {
174 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
175   Subprocess p({"cmd", "/c", "echo|set /p ignored_var=foobar"});
176 #else
177   Subprocess p({"echo", "-n", "foobar"});
178 #endif
179   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
180   EXPECT_TRUE(p.Call());
181   EXPECT_EQ(p.status(), Subprocess::kTerminated);
182   EXPECT_EQ(p.returncode(), 0);
183   EXPECT_EQ(GetOutput(p), "foobar");
184 }
185 
TEST(SubprocessTest,FeedbackLongInput)186 TEST(SubprocessTest, FeedbackLongInput) {
187   std::string contents = GenLargeString();
188 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
189   Subprocess p({"cmd", "/C", "findstr ."});
190 #else
191   Subprocess p({"cat", "-"});
192 #endif
193   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
194   p.args.input = contents;
195   EXPECT_TRUE(p.Call());
196   EXPECT_EQ(p.status(), Subprocess::kTerminated);
197   EXPECT_EQ(p.returncode(), 0);
198   EXPECT_EQ(GetOutput(p), contents);
199 }
200 
TEST(SubprocessTest,CatLargeFile)201 TEST(SubprocessTest, CatLargeFile) {
202   std::string contents = GenLargeString();
203   TempFile tf = TempFile::Create();
204   WriteAll(tf.fd(), contents.data(), contents.size());
205   FlushFile(tf.fd());
206 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
207   Subprocess p({"cmd", "/c", ("type \"" + tf.path() + "\"").c_str()});
208 #else
209   Subprocess p({"cat", tf.path().c_str()});
210 #endif
211   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
212   EXPECT_TRUE(p.Call());
213   EXPECT_EQ(GetOutput(p), contents);
214 }
215 
TEST(SubprocessTest,Timeout)216 TEST(SubprocessTest, Timeout) {
217 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
218   Subprocess p({"ping", "127.0.0.1", "-n", "60"});
219   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
220 #else
221   Subprocess p({"sleep", "60"});
222 #endif
223 
224   EXPECT_FALSE(p.Call(/*timeout_ms=*/1));
225   EXPECT_EQ(p.status(), Subprocess::kTerminated);
226   EXPECT_TRUE(p.timed_out());
227 }
228 
TEST(SubprocessTest,TimeoutNotHit)229 TEST(SubprocessTest, TimeoutNotHit) {
230 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
231   Subprocess p({"ping", "127.0.0.1", "-n", "1"});
232   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
233 #else
234   Subprocess p({"sleep", "0.01"});
235 #endif
236   EXPECT_TRUE(p.Call(/*timeout_ms=*/100000));
237   EXPECT_EQ(p.status(), Subprocess::kTerminated);
238 }
239 
TEST(SubprocessTest,TimeoutStopOutput)240 TEST(SubprocessTest, TimeoutStopOutput) {
241 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
242   Subprocess p({"cmd", "/c", "FOR /L %N IN () DO @echo stuff>NUL"});
243 #else
244   Subprocess p({"sh", "-c", "while true; do echo stuff; done"});
245 #endif
246   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
247   EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
248   EXPECT_EQ(p.status(), Subprocess::kTerminated);
249   EXPECT_TRUE(p.timed_out());
250 }
251 
TEST(SubprocessTest,ExitBeforeReadingStdin)252 TEST(SubprocessTest, ExitBeforeReadingStdin) {
253 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
254   Subprocess p({"ping", "127.0.0.1", "-n", "1"});
255 #else
256   // 'sh -c' is to avoid closing stdin (sleep closes it before sleeping).
257   Subprocess p({"sh", "-c", "sleep 0.01"});
258 #endif
259   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
260   p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
261   p.args.input = GenLargeString();
262   EXPECT_TRUE(p.Call());
263   EXPECT_EQ(p.status(), Subprocess::kTerminated);
264   EXPECT_EQ(p.returncode(), 0);
265 }
266 
TEST(SubprocessTest,StdinWriteStall)267 TEST(SubprocessTest, StdinWriteStall) {
268 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
269   Subprocess p({"ping", "127.0.0.1", "-n", "10"});
270 #else
271   // 'sh -c' is to avoid closing stdin (sleep closes it before sleeping).
272   // This causes a situation where the write on the stdin will stall because
273   // nobody reads it and the pipe buffer fills up. In this situation we should
274   // still handle the timeout properly.
275   Subprocess p({"sh", "-c", "sleep 10"});
276 #endif
277   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
278   p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
279   p.args.input = GenLargeString();
280   EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
281   EXPECT_EQ(p.status(), Subprocess::kTerminated);
282   EXPECT_TRUE(p.timed_out());
283 }
284 
TEST(SubprocessTest,StartAndWait)285 TEST(SubprocessTest, StartAndWait) {
286 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
287   Subprocess p({"ping", "127.0.0.1", "-n", "1000"});
288 #else
289   Subprocess p({"sleep", "1000"});
290 #endif
291   p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
292   p.Start();
293   EXPECT_EQ(p.Poll(), Subprocess::kRunning);
294   p.KillAndWaitForTermination();
295 
296   EXPECT_EQ(p.status(), Subprocess::kTerminated);
297   EXPECT_EQ(p.Poll(), Subprocess::kTerminated);
298 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
299   EXPECT_EQ(p.returncode(), static_cast<int>(STATUS_CONTROL_C_EXIT));
300 #else
301   EXPECT_EQ(p.returncode(), static_cast<int>(128 + SIGKILL));
302 #endif
303 }
304 
TEST(SubprocessTest,PollBehavesProperly)305 TEST(SubprocessTest, PollBehavesProperly) {
306   Pipe pipe = Pipe::Create();
307 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
308   Subprocess p({"cmd", "/c", "(exit 0)"});
309 #else
310   Subprocess p({"true"});
311 #endif
312   p.args.stdout_mode = Subprocess::OutputMode::kFd;
313   p.args.out_fd = std::move(pipe.wr);
314   p.Start();
315 
316   // Wait for EOF (which really means the child process has terminated).
317   std::string ignored;
318   ReadPlatformHandle(*pipe.rd, &ignored);
319 
320   // The kernel takes some time to detect the termination of the process. The
321   // best thing we can do here is check that we detect the termination within
322   // some reasonable time.
323   auto start_ms = GetWallTimeMs();
324   while (p.Poll() != Subprocess::kTerminated) {
325     auto elapsed_ms = GetWallTimeMs() - start_ms;
326     ASSERT_LT(elapsed_ms, TimeMillis(10000));
327     std::this_thread::sleep_for(TimeMillis(5));
328   }
329 
330   // At this point Poll() must detect the termination.
331   EXPECT_EQ(p.Poll(), Subprocess::kTerminated);
332   EXPECT_EQ(p.returncode(), 0);
333 }
334 
TEST(SubprocessTest,Wait)335 TEST(SubprocessTest, Wait) {
336 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
337   Subprocess p({"cmd", "/c", "echo exec_done && FOR /L %N IN () DO @echo>NUL"});
338 #else
339   Subprocess p({"sh", "-c", "echo exec_done; while true; do true; done"});
340 #endif
341   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
342   p.Start();
343 
344   // Wait for the fork()+exec() to complete.
345   while (p.output().find("exec_done") == std::string::npos) {
346     EXPECT_FALSE(p.Wait(1 /*ms*/));
347     EXPECT_EQ(p.status(), Subprocess::kRunning);
348   }
349 
350 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
351   ScopedPlatformHandle proc_handle(::OpenProcess(
352       PROCESS_TERMINATE, /*inherit=*/false, static_cast<DWORD>(p.pid())));
353   ASSERT_TRUE(proc_handle);
354   ASSERT_TRUE(::TerminateProcess(*proc_handle, DBG_CONTROL_BREAK));
355 #else
356   kill(p.pid(), SIGBUS);
357 #endif
358   EXPECT_TRUE(p.Wait(30000 /*ms*/));  // We shouldn't hit this.
359   EXPECT_TRUE(p.Wait());              // Should be a no-op.
360   EXPECT_EQ(p.status(), Subprocess::kTerminated);
361 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
362   EXPECT_EQ(p.returncode(), static_cast<int>(DBG_CONTROL_BREAK));
363 #else
364   EXPECT_EQ(p.returncode(), 128 + SIGBUS);
365 #endif
366 }
367 
TEST(SubprocessTest,KillOnDtor)368 TEST(SubprocessTest, KillOnDtor) {
369   auto is_process_alive = [](PlatformProcessId pid) {
370 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
371     DWORD ignored = 0;
372     return ProcessIdToSessionId(static_cast<DWORD>(pid), &ignored);
373 #else
374     // We use kill(SIGWINCH) as a way to tell if the process is still alive by
375     // looking at the kill(2) return value. SIGWINCH is one of the few signals
376     // that has default ignore disposition.
377     return kill(pid, SIGWINCH) == 0;
378 #endif
379   };
380 
381   PlatformProcessId pid;
382   {
383 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
384     Subprocess p({"ping", "127.0.0.1", "-n", "1000"});
385 #else
386     Subprocess p({"sleep", "1000"});
387 #endif
388     p.Start();
389     pid = p.pid();
390     EXPECT_TRUE(is_process_alive(pid));
391   }
392 
393   // Both on Windows and Linux, kill can take some time to free up the pid.
394   bool alive = true;
395   for (int attempt = 0; attempt < 1000 && alive; attempt++) {
396     alive = is_process_alive(pid);
397     std::this_thread::sleep_for(TimeMillis(5));
398   }
399   EXPECT_FALSE(alive);
400 }
401 
402 // Regression test for b/162505491.
TEST(SubprocessTest,MoveOperators)403 TEST(SubprocessTest, MoveOperators) {
404   {
405 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
406     Subprocess initial({"ping", "127.0.0.1", "-n", "100"});
407 #else
408     Subprocess initial = Subprocess({"sleep", "10000"});
409 #endif
410     initial.args.stdout_mode = Subprocess::OutputMode::kDevNull;
411     initial.Start();
412     Subprocess moved(std::move(initial));
413     EXPECT_EQ(moved.Poll(), Subprocess::kRunning);
414     EXPECT_EQ(initial.Poll(), Subprocess::kNotStarted);
415 
416     // Check that reuse works
417 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
418     initial = Subprocess({"cmd", "/c", "echo|set /p ignored_var=hello"});
419 #else
420     initial = Subprocess({"echo", "-n", "hello"});
421 #endif
422     initial.args.stdout_mode = Subprocess::OutputMode::kBuffer;
423     initial.Start();
424     initial.Wait(/*timeout_ms=*/5000);
425     EXPECT_EQ(initial.status(), Subprocess::kTerminated);
426     EXPECT_EQ(initial.returncode(), 0);
427     EXPECT_EQ(initial.output(), "hello");
428   }
429 
430   std::vector<Subprocess> v;
431   for (int i = 0; i < 10; i++) {
432 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
433     v.emplace_back(Subprocess({"ping", "127.0.0.1", "-n", "10"}));
434 #else
435     v.emplace_back(Subprocess({"sleep", "10"}));
436 #endif
437     v.back().args.stdout_mode = Subprocess::OutputMode::kDevNull;
438     v.back().Start();
439   }
440   for (auto& p : v)
441     EXPECT_EQ(p.Poll(), Subprocess::kRunning);
442 }
443 
444 // posix_entrypoint_for_testing is not supported on Windows.
445 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
446 
447 // Test the case of passing a lambda in |entrypoint| but no cmd.c
TEST(SubprocessTest,Entrypoint)448 TEST(SubprocessTest, Entrypoint) {
449   Subprocess p;
450   p.args.input = "ping\n";
451   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
452   p.args.posix_entrypoint_for_testing = [] {
453     char buf[32]{};
454     PERFETTO_CHECK(fgets(buf, sizeof(buf), stdin));
455     PERFETTO_CHECK(strcmp(buf, "ping\n") == 0);
456     printf("pong\n");
457     fflush(stdout);
458     _exit(42);
459   };
460   EXPECT_FALSE(p.Call());
461   EXPECT_EQ(p.returncode(), 42);
462   EXPECT_EQ(GetOutput(p), "pong\n");
463 }
464 
465 // Test the case of passing both a lambda entrypoint and a process to exec.
TEST(SubprocessTest,EntrypointAndExec)466 TEST(SubprocessTest, EntrypointAndExec) {
467   base::Pipe pipe1 = base::Pipe::Create();
468   base::Pipe pipe2 = base::Pipe::Create();
469   int pipe1_wr = *pipe1.wr;
470   int pipe2_wr = *pipe2.wr;
471 
472   Subprocess p({"echo", "123"});
473   p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
474   p.args.preserve_fds.push_back(pipe2_wr);
475   p.args.posix_entrypoint_for_testing = [pipe1_wr, pipe2_wr] {
476     base::ignore_result(write(pipe1_wr, "fail", 4));
477     base::ignore_result(write(pipe2_wr, "pass", 4));
478   };
479 
480   p.Start();
481   pipe1.wr.reset();
482   pipe2.wr.reset();
483 
484   char buf[8];
485   EXPECT_LE(read(*pipe1.rd, buf, sizeof(buf)), 0);
486   EXPECT_EQ(read(*pipe2.rd, buf, sizeof(buf)), 4);
487   buf[4] = '\0';
488   EXPECT_STREQ(buf, "pass");
489   EXPECT_TRUE(p.Wait());
490   EXPECT_EQ(p.status(), Subprocess::kTerminated);
491   EXPECT_EQ(GetOutput(p), "123\n");
492 }
493 
494 #endif
495 
496 }  // namespace
497 }  // namespace base
498 }  // namespace perfetto
499