1 | //===-- HostTest.cpp ------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Host/Host.h" |
10 | #include "TestingSupport/SubsystemRAII.h" |
11 | #include "lldb/Host/FileSystem.h" |
12 | #include "lldb/Host/HostInfo.h" |
13 | #include "lldb/Host/Pipe.h" |
14 | #include "lldb/Host/ProcessLaunchInfo.h" |
15 | #include "lldb/Utility/ProcessInfo.h" |
16 | #include "llvm/ADT/ScopeExit.h" |
17 | #include "llvm/ADT/Twine.h" |
18 | #include "llvm/Support/CommandLine.h" |
19 | #include "llvm/Support/FileSystem.h" |
20 | #include "llvm/Testing/Support/Error.h" |
21 | #include "gtest/gtest.h" |
22 | #include <future> |
23 | #include <thread> |
24 | |
25 | using namespace lldb_private; |
26 | using namespace llvm; |
27 | |
28 | // From TestMain.cpp. |
29 | extern const char *TestMainArgv0; |
30 | |
31 | static cl::opt<uint64_t> test_arg("test-arg" ); |
32 | |
33 | TEST(Host, WaitStatusFormat) { |
34 | EXPECT_EQ("W01" , formatv("{0:g}" , WaitStatus{WaitStatus::Exit, 1}).str()); |
35 | EXPECT_EQ("X02" , formatv("{0:g}" , WaitStatus{WaitStatus::Signal, 2}).str()); |
36 | EXPECT_EQ("S03" , formatv("{0:g}" , WaitStatus{WaitStatus::Stop, 3}).str()); |
37 | EXPECT_EQ("Exited with status 4" , |
38 | formatv("{0}" , WaitStatus{WaitStatus::Exit, 4}).str()); |
39 | } |
40 | |
41 | TEST(Host, GetEnvironment) { |
42 | putenv(string: const_cast<char *>("LLDB_TEST_ENVIRONMENT_VAR=Host::GetEnvironment" )); |
43 | ASSERT_EQ("Host::GetEnvironment" , |
44 | Host::GetEnvironment().lookup("LLDB_TEST_ENVIRONMENT_VAR" )); |
45 | } |
46 | |
47 | TEST(Host, ProcessInstanceInfoCumulativeUserTimeIsValid) { |
48 | ProcessInstanceInfo info; |
49 | info.SetCumulativeUserTime(ProcessInstanceInfo::timespec{.tv_sec: 0, .tv_usec: 0}); |
50 | EXPECT_FALSE(info.CumulativeUserTimeIsValid()); |
51 | info.SetCumulativeUserTime(ProcessInstanceInfo::timespec{.tv_sec: 0, .tv_usec: 1}); |
52 | EXPECT_TRUE(info.CumulativeUserTimeIsValid()); |
53 | info.SetCumulativeUserTime(ProcessInstanceInfo::timespec{.tv_sec: 1, .tv_usec: 0}); |
54 | EXPECT_TRUE(info.CumulativeUserTimeIsValid()); |
55 | } |
56 | |
57 | TEST(Host, ProcessInstanceInfoCumulativeSystemTimeIsValid) { |
58 | ProcessInstanceInfo info; |
59 | info.SetCumulativeSystemTime(ProcessInstanceInfo::timespec{.tv_sec: 0, .tv_usec: 0}); |
60 | EXPECT_FALSE(info.CumulativeSystemTimeIsValid()); |
61 | info.SetCumulativeSystemTime(ProcessInstanceInfo::timespec{.tv_sec: 0, .tv_usec: 1}); |
62 | EXPECT_TRUE(info.CumulativeSystemTimeIsValid()); |
63 | info.SetCumulativeSystemTime(ProcessInstanceInfo::timespec{.tv_sec: 1, .tv_usec: 0}); |
64 | EXPECT_TRUE(info.CumulativeSystemTimeIsValid()); |
65 | } |
66 | |
67 | TEST(Host, LaunchProcessSetsArgv0) { |
68 | SubsystemRAII<FileSystem> subsystems; |
69 | |
70 | static constexpr StringLiteral TestArgv0 = "HelloArgv0" ; |
71 | if (test_arg != 0) { |
72 | // In subprocess |
73 | if (TestMainArgv0 != TestArgv0) { |
74 | errs() << formatv(Fmt: "Got '{0}' for argv[0]\n" , Vals&: TestMainArgv0); |
75 | exit(status: 1); |
76 | } |
77 | exit(status: 0); |
78 | } |
79 | |
80 | ProcessLaunchInfo info; |
81 | info.SetExecutableFile( |
82 | exe_file: FileSpec(llvm::sys::fs::getMainExecutable(argv0: TestMainArgv0, MainExecAddr: &test_arg)), |
83 | /*add_exe_file_as_first_arg=*/false); |
84 | info.GetArguments().AppendArgument(arg_str: "HelloArgv0" ); |
85 | info.GetArguments().AppendArgument( |
86 | arg_str: "--gtest_filter=Host.LaunchProcessSetsArgv0" ); |
87 | info.GetArguments().AppendArgument(arg_str: "--test-arg=47" ); |
88 | std::promise<int> exit_status; |
89 | info.SetMonitorProcessCallback([&](lldb::pid_t pid, int signal, int status) { |
90 | exit_status.set_value(status); |
91 | }); |
92 | ASSERT_THAT_ERROR(Host::LaunchProcess(info).takeError(), Succeeded()); |
93 | ASSERT_THAT(exit_status.get_future().get(), 0); |
94 | } |
95 | |
96 | TEST(Host, FindProcesses) { |
97 | SubsystemRAII<FileSystem, HostInfo> subsystems; |
98 | |
99 | if (test_arg != 0) { |
100 | // Give the parent time to retrieve information about self. |
101 | // It will kill self when it is done. |
102 | std::this_thread::sleep_for(rtime: std::chrono::seconds(10)); |
103 | exit(status: 0); |
104 | } |
105 | |
106 | bool foundPID = false; |
107 | ProcessLaunchInfo info; |
108 | ProcessInstanceInfoList processes; |
109 | ProcessInstanceInfoMatch match(TestMainArgv0, NameMatch::Equals); |
110 | info.SetExecutableFile(exe_file: FileSpec(TestMainArgv0), |
111 | /*add_exe_file_as_first_arg=*/true); |
112 | info.GetArguments().AppendArgument(arg_str: "--gtest_filter=Host.FindProcesses" ); |
113 | info.GetArguments().AppendArgument(arg_str: "--test-arg=48" ); |
114 | std::promise<int> exit_status; |
115 | info.SetMonitorProcessCallback([&](lldb::pid_t pid, int signal, int status) { |
116 | exit_status.set_value(status); |
117 | }); |
118 | ASSERT_THAT_ERROR(Host::LaunchProcess(info).takeError(), Succeeded()); |
119 | ASSERT_TRUE(Host::FindProcesses(match, processes)); |
120 | for (const auto &process : processes) { |
121 | if (process.GetProcessID() == info.GetProcessID()) { |
122 | ASSERT_EQ(process.GetExecutableFile().GetFilename(), |
123 | info.GetExecutableFile().GetFilename()); |
124 | foundPID = true; |
125 | } |
126 | } |
127 | ASSERT_TRUE(foundPID); |
128 | auto clean_up = llvm::make_scope_exit(F: [&] { |
129 | Host::Kill(pid: info.GetProcessID(), SIGKILL); |
130 | exit_status.get_future().get(); |
131 | }); |
132 | } |
133 | |
134 | TEST(Host, LaunchProcessDuplicatesHandle) { |
135 | static constexpr llvm::StringLiteral test_msg("Hello subprocess!" ); |
136 | |
137 | SubsystemRAII<FileSystem> subsystems; |
138 | |
139 | if (test_arg) { |
140 | Pipe pipe(LLDB_INVALID_PIPE, (lldb::pipe_t)test_arg.getValue()); |
141 | llvm::Expected<size_t> bytes_written = |
142 | pipe.Write(buf: test_msg.data(), size: test_msg.size()); |
143 | if (bytes_written && *bytes_written == test_msg.size()) |
144 | exit(status: 0); |
145 | exit(status: 1); |
146 | } |
147 | Pipe pipe; |
148 | ASSERT_THAT_ERROR(pipe.CreateNew(/*child_process_inherit=*/false).takeError(), |
149 | llvm::Succeeded()); |
150 | SCOPED_TRACE(llvm::formatv("Pipe handles are: {0}/{1}" , |
151 | (uint64_t)pipe.GetReadPipe(), |
152 | (uint64_t)pipe.GetWritePipe()) |
153 | .str()); |
154 | ProcessLaunchInfo info; |
155 | info.SetExecutableFile(exe_file: FileSpec(TestMainArgv0), |
156 | /*add_exe_file_as_first_arg=*/true); |
157 | info.GetArguments().AppendArgument( |
158 | arg_str: "--gtest_filter=Host.LaunchProcessDuplicatesHandle" ); |
159 | info.GetArguments().AppendArgument( |
160 | arg_str: ("--test-arg=" + llvm::Twine((uint64_t)pipe.GetWritePipe())).str()); |
161 | info.AppendDuplicateFileAction(fd: (uint64_t)pipe.GetWritePipe(), |
162 | dup_fd: (uint64_t)pipe.GetWritePipe()); |
163 | info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback); |
164 | ASSERT_THAT_ERROR(Host::LaunchProcess(info).takeError(), llvm::Succeeded()); |
165 | pipe.CloseWriteFileDescriptor(); |
166 | |
167 | char msg[100]; |
168 | llvm::Expected<size_t> bytes_read = |
169 | pipe.Read(buf: msg, size: sizeof(msg), timeout: std::chrono::seconds(10)); |
170 | ASSERT_THAT_EXPECTED(bytes_read, llvm::Succeeded()); |
171 | ASSERT_EQ(llvm::StringRef(msg, *bytes_read), test_msg); |
172 | } |
173 | |