| 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 | |