Path: blob/main/contrib/llvm-project/lldb/source/Host/common/Host.cpp
39606 views
//===-- Host.cpp ----------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78// C includes9#include <cerrno>10#include <climits>11#include <cstdlib>12#include <sys/types.h>13#ifndef _WIN3214#include <dlfcn.h>15#include <grp.h>16#include <netdb.h>17#include <pwd.h>18#include <sys/stat.h>19#include <unistd.h>20#endif2122#if defined(__APPLE__)23#include <mach-o/dyld.h>24#include <mach/mach_init.h>25#include <mach/mach_port.h>26#endif2728#if defined(__linux__) || defined(__FreeBSD__) || \29defined(__FreeBSD_kernel__) || defined(__APPLE__) || \30defined(__NetBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)31#if !defined(__ANDROID__)32#include <spawn.h>33#endif34#include <sys/syscall.h>35#include <sys/wait.h>36#endif3738#if defined(__FreeBSD__)39#include <pthread_np.h>40#endif4142#if defined(__NetBSD__)43#include <lwp.h>44#endif4546#include <csignal>4748#include "lldb/Host/FileAction.h"49#include "lldb/Host/FileSystem.h"50#include "lldb/Host/Host.h"51#include "lldb/Host/HostInfo.h"52#include "lldb/Host/HostProcess.h"53#include "lldb/Host/MonitoringProcessLauncher.h"54#include "lldb/Host/ProcessLaunchInfo.h"55#include "lldb/Host/ProcessLauncher.h"56#include "lldb/Host/ThreadLauncher.h"57#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"58#include "lldb/Utility/FileSpec.h"59#include "lldb/Utility/LLDBLog.h"60#include "lldb/Utility/Log.h"61#include "lldb/Utility/Predicate.h"62#include "lldb/Utility/Status.h"63#include "lldb/lldb-private-forward.h"64#include "llvm/ADT/SmallString.h"65#include "llvm/Support/Errno.h"66#include "llvm/Support/FileSystem.h"6768#if defined(_WIN32)69#include "lldb/Host/windows/ConnectionGenericFileWindows.h"70#include "lldb/Host/windows/ProcessLauncherWindows.h"71#else72#include "lldb/Host/posix/ProcessLauncherPosixFork.h"73#endif7475#if defined(__APPLE__)76#ifndef _POSIX_SPAWN_DISABLE_ASLR77#define _POSIX_SPAWN_DISABLE_ASLR 0x010078#endif7980extern "C" {81int __pthread_chdir(const char *path);82int __pthread_fchdir(int fildes);83}8485#endif8687using namespace lldb;88using namespace lldb_private;8990#if !defined(__APPLE__)91#if !defined(_WIN32)92#include <syslog.h>93void Host::SystemLog(Severity severity, llvm::StringRef message) {94static llvm::once_flag g_openlog_once;95llvm::call_once(g_openlog_once, [] {96openlog("lldb", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);97});98int level = LOG_DEBUG;99switch (severity) {100case lldb::eSeverityInfo:101level = LOG_INFO;102break;103case lldb::eSeverityWarning:104level = LOG_WARNING;105break;106case lldb::eSeverityError:107level = LOG_ERR;108break;109}110syslog(level, "%s", message.data());111}112#else113void Host::SystemLog(Severity severity, llvm::StringRef message) {114switch (severity) {115case lldb::eSeverityInfo:116case lldb::eSeverityWarning:117llvm::outs() << message;118break;119case lldb::eSeverityError:120llvm::errs() << message;121break;122}123}124#endif125#endif126127#if !defined(__APPLE__) && !defined(_WIN32)128static thread_result_t129MonitorChildProcessThreadFunction(::pid_t pid,130Host::MonitorChildProcessCallback callback);131132llvm::Expected<HostThread> Host::StartMonitoringChildProcess(133const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {134char thread_name[256];135::snprintf(thread_name, sizeof(thread_name),136"<lldb.host.wait4(pid=%" PRIu64 ")>", pid);137assert(pid <= UINT32_MAX);138return ThreadLauncher::LaunchThread(thread_name, [pid, callback] {139return MonitorChildProcessThreadFunction(pid, callback);140});141}142143#ifndef __linux__144// Scoped class that will disable thread canceling when it is constructed, and145// exception safely restore the previous value it when it goes out of scope.146class ScopedPThreadCancelDisabler {147public:148ScopedPThreadCancelDisabler() {149// Disable the ability for this thread to be cancelled150int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state);151if (err != 0)152m_old_state = -1;153}154155~ScopedPThreadCancelDisabler() {156// Restore the ability for this thread to be cancelled to what it157// previously was.158if (m_old_state != -1)159::pthread_setcancelstate(m_old_state, 0);160}161162private:163int m_old_state; // Save the old cancelability state.164};165#endif // __linux__166167#ifdef __linux__168static thread_local volatile sig_atomic_t g_usr1_called;169170static void SigUsr1Handler(int) { g_usr1_called = 1; }171#endif // __linux__172173static bool CheckForMonitorCancellation() {174#ifdef __linux__175if (g_usr1_called) {176g_usr1_called = 0;177return true;178}179#else180::pthread_testcancel();181#endif182return false;183}184185static thread_result_t186MonitorChildProcessThreadFunction(::pid_t pid,187Host::MonitorChildProcessCallback callback) {188Log *log = GetLog(LLDBLog::Process);189LLDB_LOG(log, "pid = {0}", pid);190191int status = -1;192193#ifdef __linux__194// This signal is only used to interrupt the thread from waitpid195struct sigaction sigUsr1Action;196memset(&sigUsr1Action, 0, sizeof(sigUsr1Action));197sigUsr1Action.sa_handler = SigUsr1Handler;198::sigaction(SIGUSR1, &sigUsr1Action, nullptr);199#endif // __linux__200201while (true) {202log = GetLog(LLDBLog::Process);203LLDB_LOG(log, "::waitpid({0}, &status, 0)...", pid);204205if (CheckForMonitorCancellation())206return nullptr;207208const ::pid_t wait_pid = ::waitpid(pid, &status, 0);209210LLDB_LOG(log, "::waitpid({0}, &status, 0) => pid = {1}, status = {2:x}", pid,211wait_pid, status);212213if (CheckForMonitorCancellation())214return nullptr;215216if (wait_pid != -1)217break;218if (errno != EINTR) {219LLDB_LOG(log, "pid = {0}, thread exiting because waitpid failed ({1})...",220pid, llvm::sys::StrError());221return nullptr;222}223}224225int signal = 0;226int exit_status = 0;227if (WIFEXITED(status)) {228exit_status = WEXITSTATUS(status);229} else if (WIFSIGNALED(status)) {230signal = WTERMSIG(status);231exit_status = -1;232} else {233llvm_unreachable("Unknown status");234}235236// Scope for pthread_cancel_disabler237{238#ifndef __linux__239ScopedPThreadCancelDisabler pthread_cancel_disabler;240#endif241242if (callback)243callback(pid, signal, exit_status);244}245246LLDB_LOG(GetLog(LLDBLog::Process), "pid = {0} thread exiting...", pid);247return nullptr;248}249250#endif // #if !defined (__APPLE__) && !defined (_WIN32)251252lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); }253254#ifndef _WIN32255256lldb::thread_t Host::GetCurrentThread() {257return lldb::thread_t(pthread_self());258}259260const char *Host::GetSignalAsCString(int signo) {261switch (signo) {262case SIGHUP:263return "SIGHUP"; // 1 hangup264case SIGINT:265return "SIGINT"; // 2 interrupt266case SIGQUIT:267return "SIGQUIT"; // 3 quit268case SIGILL:269return "SIGILL"; // 4 illegal instruction (not reset when caught)270case SIGTRAP:271return "SIGTRAP"; // 5 trace trap (not reset when caught)272case SIGABRT:273return "SIGABRT"; // 6 abort()274#if defined(SIGPOLL)275#if !defined(SIGIO) || (SIGPOLL != SIGIO)276// Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to277// fail with 'multiple define cases with same value'278case SIGPOLL:279return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)280#endif281#endif282#if defined(SIGEMT)283case SIGEMT:284return "SIGEMT"; // 7 EMT instruction285#endif286case SIGFPE:287return "SIGFPE"; // 8 floating point exception288case SIGKILL:289return "SIGKILL"; // 9 kill (cannot be caught or ignored)290case SIGBUS:291return "SIGBUS"; // 10 bus error292case SIGSEGV:293return "SIGSEGV"; // 11 segmentation violation294case SIGSYS:295return "SIGSYS"; // 12 bad argument to system call296case SIGPIPE:297return "SIGPIPE"; // 13 write on a pipe with no one to read it298case SIGALRM:299return "SIGALRM"; // 14 alarm clock300case SIGTERM:301return "SIGTERM"; // 15 software termination signal from kill302case SIGURG:303return "SIGURG"; // 16 urgent condition on IO channel304case SIGSTOP:305return "SIGSTOP"; // 17 sendable stop signal not from tty306case SIGTSTP:307return "SIGTSTP"; // 18 stop signal from tty308case SIGCONT:309return "SIGCONT"; // 19 continue a stopped process310case SIGCHLD:311return "SIGCHLD"; // 20 to parent on child stop or exit312case SIGTTIN:313return "SIGTTIN"; // 21 to readers pgrp upon background tty read314case SIGTTOU:315return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP)316#if defined(SIGIO)317case SIGIO:318return "SIGIO"; // 23 input/output possible signal319#endif320case SIGXCPU:321return "SIGXCPU"; // 24 exceeded CPU time limit322case SIGXFSZ:323return "SIGXFSZ"; // 25 exceeded file size limit324case SIGVTALRM:325return "SIGVTALRM"; // 26 virtual time alarm326case SIGPROF:327return "SIGPROF"; // 27 profiling time alarm328#if defined(SIGWINCH)329case SIGWINCH:330return "SIGWINCH"; // 28 window size changes331#endif332#if defined(SIGINFO)333case SIGINFO:334return "SIGINFO"; // 29 information request335#endif336case SIGUSR1:337return "SIGUSR1"; // 30 user defined signal 1338case SIGUSR2:339return "SIGUSR2"; // 31 user defined signal 2340default:341break;342}343return nullptr;344}345346#endif347348#if !defined(__APPLE__) // see Host.mm349350bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) {351bundle.Clear();352return false;353}354355bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; }356#endif357358#ifndef _WIN32359360FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) {361FileSpec module_filespec;362#if !defined(__ANDROID__)363Dl_info info;364if (::dladdr(host_addr, &info)) {365if (info.dli_fname) {366module_filespec.SetFile(info.dli_fname, FileSpec::Style::native);367FileSystem::Instance().Resolve(module_filespec);368}369}370#endif371return module_filespec;372}373374#endif375376#if !defined(__linux__)377bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {378return false;379}380#endif381382struct ShellInfo {383ShellInfo() : process_reaped(false) {}384385lldb_private::Predicate<bool> process_reaped;386lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;387int signo = -1;388int status = -1;389};390391static void392MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid,393int signo, // Zero for no signal394int status) // Exit value of process if signal is zero395{396shell_info->pid = pid;397shell_info->signo = signo;398shell_info->status = status;399// Let the thread running Host::RunShellCommand() know that the process400// exited and that ShellInfo has been filled in by broadcasting to it401shell_info->process_reaped.SetValue(true, eBroadcastAlways);402}403404Status Host::RunShellCommand(llvm::StringRef command,405const FileSpec &working_dir, int *status_ptr,406int *signo_ptr, std::string *command_output_ptr,407const Timeout<std::micro> &timeout,408bool run_in_shell, bool hide_stderr) {409return RunShellCommand(llvm::StringRef(), Args(command), working_dir,410status_ptr, signo_ptr, command_output_ptr, timeout,411run_in_shell, hide_stderr);412}413414Status Host::RunShellCommand(llvm::StringRef shell_path,415llvm::StringRef command,416const FileSpec &working_dir, int *status_ptr,417int *signo_ptr, std::string *command_output_ptr,418const Timeout<std::micro> &timeout,419bool run_in_shell, bool hide_stderr) {420return RunShellCommand(shell_path, Args(command), working_dir, status_ptr,421signo_ptr, command_output_ptr, timeout, run_in_shell,422hide_stderr);423}424425Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir,426int *status_ptr, int *signo_ptr,427std::string *command_output_ptr,428const Timeout<std::micro> &timeout,429bool run_in_shell, bool hide_stderr) {430return RunShellCommand(llvm::StringRef(), args, working_dir, status_ptr,431signo_ptr, command_output_ptr, timeout, run_in_shell,432hide_stderr);433}434435Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,436const FileSpec &working_dir, int *status_ptr,437int *signo_ptr, std::string *command_output_ptr,438const Timeout<std::micro> &timeout,439bool run_in_shell, bool hide_stderr) {440Status error;441ProcessLaunchInfo launch_info;442launch_info.SetArchitecture(HostInfo::GetArchitecture());443if (run_in_shell) {444// Run the command in a shell445FileSpec shell = HostInfo::GetDefaultShell();446if (!shell_path.empty())447shell.SetPath(shell_path);448449launch_info.SetShell(shell);450launch_info.GetArguments().AppendArguments(args);451const bool will_debug = false;452const bool first_arg_is_full_shell_command = false;453launch_info.ConvertArgumentsForLaunchingInShell(454error, will_debug, first_arg_is_full_shell_command, 0);455} else {456// No shell, just run it457const bool first_arg_is_executable = true;458launch_info.SetArguments(args, first_arg_is_executable);459}460461launch_info.GetEnvironment() = Host::GetEnvironment();462463if (working_dir)464launch_info.SetWorkingDirectory(working_dir);465llvm::SmallString<64> output_file_path;466467if (command_output_ptr) {468// Create a temporary file to get the stdout/stderr and redirect the output469// of the command into this file. We will later read this file if all goes470// well and fill the data into "command_output_ptr"471if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {472tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%");473llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),474output_file_path);475} else {476llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "",477output_file_path);478}479}480481FileSpec output_file_spec(output_file_path.str());482// Set up file descriptors.483launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);484if (output_file_spec)485launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false,486true);487else488launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);489490if (output_file_spec && !hide_stderr)491launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);492else493launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);494495std::shared_ptr<ShellInfo> shell_info_sp(new ShellInfo());496launch_info.SetMonitorProcessCallback(497std::bind(MonitorShellCommand, shell_info_sp, std::placeholders::_1,498std::placeholders::_2, std::placeholders::_3));499500error = LaunchProcess(launch_info);501const lldb::pid_t pid = launch_info.GetProcessID();502503if (error.Success() && pid == LLDB_INVALID_PROCESS_ID)504error.SetErrorString("failed to get process ID");505506if (error.Success()) {507if (!shell_info_sp->process_reaped.WaitForValueEqualTo(true, timeout)) {508error.SetErrorString("timed out waiting for shell command to complete");509510// Kill the process since it didn't complete within the timeout specified511Kill(pid, SIGKILL);512// Wait for the monitor callback to get the message513shell_info_sp->process_reaped.WaitForValueEqualTo(514true, std::chrono::seconds(1));515} else {516if (status_ptr)517*status_ptr = shell_info_sp->status;518519if (signo_ptr)520*signo_ptr = shell_info_sp->signo;521522if (command_output_ptr) {523command_output_ptr->clear();524uint64_t file_size =525FileSystem::Instance().GetByteSize(output_file_spec);526if (file_size > 0) {527if (file_size > command_output_ptr->max_size()) {528error.SetErrorStringWithFormat(529"shell command output is too large to fit into a std::string");530} else {531WritableDataBufferSP Buffer =532FileSystem::Instance().CreateWritableDataBuffer(533output_file_spec);534if (error.Success())535command_output_ptr->assign(536reinterpret_cast<char *>(Buffer->GetBytes()),537Buffer->GetByteSize());538}539}540}541}542}543544llvm::sys::fs::remove(output_file_spec.GetPath());545return error;546}547548// The functions below implement process launching for non-Apple-based549// platforms550#if !defined(__APPLE__)551Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) {552std::unique_ptr<ProcessLauncher> delegate_launcher;553#if defined(_WIN32)554delegate_launcher.reset(new ProcessLauncherWindows());555#else556delegate_launcher.reset(new ProcessLauncherPosixFork());557#endif558MonitoringProcessLauncher launcher(std::move(delegate_launcher));559560Status error;561HostProcess process = launcher.LaunchProcess(launch_info, error);562563// TODO(zturner): It would be better if the entire HostProcess were returned564// instead of writing it into this structure.565launch_info.SetProcessID(process.GetProcessId());566567return error;568}569#endif // !defined(__APPLE__)570571#ifndef _WIN32572void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); }573574#endif575576#if !defined(__APPLE__)577llvm::Error Host::OpenFileInExternalEditor(llvm::StringRef editor,578const FileSpec &file_spec,579uint32_t line_no) {580return llvm::errorCodeToError(581std::error_code(ENOTSUP, std::system_category()));582}583584bool Host::IsInteractiveGraphicSession() { return false; }585#endif586587std::unique_ptr<Connection> Host::CreateDefaultConnection(llvm::StringRef url) {588#if defined(_WIN32)589if (url.starts_with("file://"))590return std::unique_ptr<Connection>(new ConnectionGenericFile());591#endif592return std::unique_ptr<Connection>(new ConnectionFileDescriptor());593}594595#if defined(LLVM_ON_UNIX)596WaitStatus WaitStatus::Decode(int wstatus) {597if (WIFEXITED(wstatus))598return {Exit, uint8_t(WEXITSTATUS(wstatus))};599else if (WIFSIGNALED(wstatus))600return {Signal, uint8_t(WTERMSIG(wstatus))};601else if (WIFSTOPPED(wstatus))602return {Stop, uint8_t(WSTOPSIG(wstatus))};603llvm_unreachable("Unknown wait status");604}605#endif606607void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS,608raw_ostream &OS,609StringRef Options) {610if (Options == "g") {611char type;612switch (WS.type) {613case WaitStatus::Exit:614type = 'W';615break;616case WaitStatus::Signal:617type = 'X';618break;619case WaitStatus::Stop:620type = 'S';621break;622}623OS << formatv("{0}{1:x-2}", type, WS.status);624return;625}626627assert(Options.empty());628const char *desc;629switch(WS.type) {630case WaitStatus::Exit:631desc = "Exited with status";632break;633case WaitStatus::Signal:634desc = "Killed by signal";635break;636case WaitStatus::Stop:637desc = "Stopped by signal";638break;639}640OS << desc << " " << int(WS.status);641}642643uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,644ProcessInstanceInfoList &process_infos) {645return FindProcessesImpl(match_info, process_infos);646}647648char SystemLogHandler::ID;649650SystemLogHandler::SystemLogHandler() {}651652void SystemLogHandler::Emit(llvm::StringRef message) {653Host::SystemLog(lldb::eSeverityInfo, message);654}655656657