forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			445 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			445 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
// C Includes
 | 
						|
// C++ Includes
 | 
						|
#include <climits>
 | 
						|
 | 
						|
// Other libraries and framework includes
 | 
						|
// Project includes
 | 
						|
#include "lldb/Core/Debugger.h"
 | 
						|
#include "lldb/Host/Config.h"
 | 
						|
#include "lldb/Host/FileSystem.h"
 | 
						|
#include "lldb/Host/HostInfo.h"
 | 
						|
#include "lldb/Target/FileAction.h"
 | 
						|
#include "lldb/Target/ProcessLaunchInfo.h"
 | 
						|
#include "lldb/Target/Target.h"
 | 
						|
#include "lldb/Utility/Log.h"
 | 
						|
#include "lldb/Utility/StreamString.h"
 | 
						|
 | 
						|
#include "llvm/Support/ConvertUTF.h"
 | 
						|
#include "llvm/Support/FileSystem.h"
 | 
						|
 | 
						|
#if !defined(_WIN32)
 | 
						|
#include <limits.h>
 | 
						|
#endif
 | 
						|
 | 
						|
using namespace lldb;
 | 
						|
using namespace lldb_private;
 | 
						|
 | 
						|
//----------------------------------------------------------------------------
 | 
						|
// ProcessLaunchInfo member functions
 | 
						|
//----------------------------------------------------------------------------
 | 
						|
 | 
						|
ProcessLaunchInfo::ProcessLaunchInfo()
 | 
						|
    : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
 | 
						|
      m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0),
 | 
						|
      m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr),
 | 
						|
      m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() {}
 | 
						|
 | 
						|
ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
 | 
						|
                                     const FileSpec &stdout_file_spec,
 | 
						|
                                     const FileSpec &stderr_file_spec,
 | 
						|
                                     const FileSpec &working_directory,
 | 
						|
                                     uint32_t launch_flags)
 | 
						|
    : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
 | 
						|
      m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0),
 | 
						|
      m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr),
 | 
						|
      m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() {
 | 
						|
  if (stdin_file_spec) {
 | 
						|
    FileAction file_action;
 | 
						|
    const bool read = true;
 | 
						|
    const bool write = false;
 | 
						|
    if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
 | 
						|
      AppendFileAction(file_action);
 | 
						|
  }
 | 
						|
  if (stdout_file_spec) {
 | 
						|
    FileAction file_action;
 | 
						|
    const bool read = false;
 | 
						|
    const bool write = true;
 | 
						|
    if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
 | 
						|
      AppendFileAction(file_action);
 | 
						|
  }
 | 
						|
  if (stderr_file_spec) {
 | 
						|
    FileAction file_action;
 | 
						|
    const bool read = false;
 | 
						|
    const bool write = true;
 | 
						|
    if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
 | 
						|
      AppendFileAction(file_action);
 | 
						|
  }
 | 
						|
  if (working_directory)
 | 
						|
    SetWorkingDirectory(working_directory);
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
 | 
						|
  FileAction file_action;
 | 
						|
  if (file_action.Close(fd)) {
 | 
						|
    AppendFileAction(file_action);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
 | 
						|
  FileAction file_action;
 | 
						|
  if (file_action.Duplicate(fd, dup_fd)) {
 | 
						|
    AppendFileAction(file_action);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
 | 
						|
                                             bool read, bool write) {
 | 
						|
  FileAction file_action;
 | 
						|
  if (file_action.Open(fd, file_spec, read, write)) {
 | 
						|
    AppendFileAction(file_action);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
 | 
						|
                                                 bool write) {
 | 
						|
  FileAction file_action;
 | 
						|
  if (file_action.Open(fd, FileSpec{FileSystem::DEV_NULL, false}, read,
 | 
						|
                       write)) {
 | 
						|
    AppendFileAction(file_action);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
 | 
						|
  if (idx < m_file_actions.size())
 | 
						|
    return &m_file_actions[idx];
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
 | 
						|
  for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {
 | 
						|
    if (m_file_actions[idx].GetFD() == fd)
 | 
						|
      return &m_file_actions[idx];
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
 | 
						|
  return m_working_dir;
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
 | 
						|
  m_working_dir = working_dir;
 | 
						|
}
 | 
						|
 | 
						|
const char *ProcessLaunchInfo::GetProcessPluginName() const {
 | 
						|
  return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str());
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
 | 
						|
  m_plugin_name = plugin;
 | 
						|
}
 | 
						|
 | 
						|
const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
 | 
						|
  m_shell = shell;
 | 
						|
  if (m_shell) {
 | 
						|
    m_shell.ResolveExecutableLocation();
 | 
						|
    m_flags.Set(lldb::eLaunchFlagLaunchInShell);
 | 
						|
  } else
 | 
						|
    m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
 | 
						|
  if (separate)
 | 
						|
    m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
 | 
						|
  else
 | 
						|
    m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
 | 
						|
  if (expand)
 | 
						|
    m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
 | 
						|
  else
 | 
						|
    m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::Clear() {
 | 
						|
  ProcessInfo::Clear();
 | 
						|
  m_working_dir.Clear();
 | 
						|
  m_plugin_name.clear();
 | 
						|
  m_shell.Clear();
 | 
						|
  m_flags.Clear();
 | 
						|
  m_file_actions.clear();
 | 
						|
  m_resume_count = 0;
 | 
						|
  m_listener_sp.reset();
 | 
						|
  m_hijack_listener_sp.reset();
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetMonitorProcessCallback(
 | 
						|
    const Host::MonitorChildProcessCallback &callback, bool monitor_signals) {
 | 
						|
  m_monitor_callback = callback;
 | 
						|
  m_monitor_signals = monitor_signals;
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::MonitorProcess() const {
 | 
						|
  if (m_monitor_callback && ProcessIDIsValid()) {
 | 
						|
    Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID(),
 | 
						|
                                      m_monitor_signals);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::SetDetachOnError(bool enable) {
 | 
						|
  if (enable)
 | 
						|
    m_flags.Set(lldb::eLaunchFlagDetachOnError);
 | 
						|
  else
 | 
						|
    m_flags.Clear(lldb::eLaunchFlagDetachOnError);
 | 
						|
}
 | 
						|
 | 
						|
void ProcessLaunchInfo::FinalizeFileActions(Target *target,
 | 
						|
                                            bool default_to_use_pty) {
 | 
						|
  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
 | 
						|
 | 
						|
  // If nothing for stdin or stdout or stderr was specified, then check the
 | 
						|
  // process for any default
 | 
						|
  // settings that were set with "settings set"
 | 
						|
  if (GetFileActionForFD(STDIN_FILENO) == nullptr ||
 | 
						|
      GetFileActionForFD(STDOUT_FILENO) == nullptr ||
 | 
						|
      GetFileActionForFD(STDERR_FILENO) == nullptr) {
 | 
						|
    if (log)
 | 
						|
      log->Printf("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr "
 | 
						|
                  "was not set, evaluating default handling",
 | 
						|
                  __FUNCTION__);
 | 
						|
 | 
						|
    if (m_flags.Test(eLaunchFlagLaunchInTTY)) {
 | 
						|
      // Do nothing, if we are launching in a remote terminal
 | 
						|
      // no file actions should be done at all.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_flags.Test(eLaunchFlagDisableSTDIO)) {
 | 
						|
      if (log)
 | 
						|
        log->Printf("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding "
 | 
						|
                    "suppression action for stdin, stdout and stderr",
 | 
						|
                    __FUNCTION__);
 | 
						|
      AppendSuppressFileAction(STDIN_FILENO, true, false);
 | 
						|
      AppendSuppressFileAction(STDOUT_FILENO, false, true);
 | 
						|
      AppendSuppressFileAction(STDERR_FILENO, false, true);
 | 
						|
    } else {
 | 
						|
      // Check for any values that might have gotten set with any of:
 | 
						|
      // (lldb) settings set target.input-path
 | 
						|
      // (lldb) settings set target.output-path
 | 
						|
      // (lldb) settings set target.error-path
 | 
						|
      FileSpec in_file_spec;
 | 
						|
      FileSpec out_file_spec;
 | 
						|
      FileSpec err_file_spec;
 | 
						|
      if (target) {
 | 
						|
        // Only override with the target settings if we don't already have
 | 
						|
        // an action for in, out or error
 | 
						|
        if (GetFileActionForFD(STDIN_FILENO) == nullptr)
 | 
						|
          in_file_spec = target->GetStandardInputPath();
 | 
						|
        if (GetFileActionForFD(STDOUT_FILENO) == nullptr)
 | 
						|
          out_file_spec = target->GetStandardOutputPath();
 | 
						|
        if (GetFileActionForFD(STDERR_FILENO) == nullptr)
 | 
						|
          err_file_spec = target->GetStandardErrorPath();
 | 
						|
      }
 | 
						|
 | 
						|
      if (log)
 | 
						|
        log->Printf("ProcessLaunchInfo::%s target stdin='%s', target "
 | 
						|
                    "stdout='%s', stderr='%s'",
 | 
						|
                    __FUNCTION__,
 | 
						|
                    in_file_spec ? in_file_spec.GetCString() : "<null>",
 | 
						|
                    out_file_spec ? out_file_spec.GetCString() : "<null>",
 | 
						|
                    err_file_spec ? err_file_spec.GetCString() : "<null>");
 | 
						|
 | 
						|
      if (in_file_spec) {
 | 
						|
        AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false);
 | 
						|
        if (log)
 | 
						|
          log->Printf(
 | 
						|
              "ProcessLaunchInfo::%s appended stdin open file action for %s",
 | 
						|
              __FUNCTION__, in_file_spec.GetCString());
 | 
						|
      }
 | 
						|
 | 
						|
      if (out_file_spec) {
 | 
						|
        AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true);
 | 
						|
        if (log)
 | 
						|
          log->Printf(
 | 
						|
              "ProcessLaunchInfo::%s appended stdout open file action for %s",
 | 
						|
              __FUNCTION__, out_file_spec.GetCString());
 | 
						|
      }
 | 
						|
 | 
						|
      if (err_file_spec) {
 | 
						|
        AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true);
 | 
						|
        if (log)
 | 
						|
          log->Printf(
 | 
						|
              "ProcessLaunchInfo::%s appended stderr open file action for %s",
 | 
						|
              __FUNCTION__, err_file_spec.GetCString());
 | 
						|
      }
 | 
						|
 | 
						|
      if (default_to_use_pty &&
 | 
						|
          (!in_file_spec || !out_file_spec || !err_file_spec)) {
 | 
						|
        if (log)
 | 
						|
          log->Printf("ProcessLaunchInfo::%s default_to_use_pty is set, and at "
 | 
						|
                      "least one stdin/stderr/stdout is unset, so generating a "
 | 
						|
                      "pty to use for it",
 | 
						|
                      __FUNCTION__);
 | 
						|
 | 
						|
        int open_flags = O_RDWR | O_NOCTTY;
 | 
						|
#if !defined(_WIN32)
 | 
						|
        // We really shouldn't be specifying platform specific flags
 | 
						|
        // that are intended for a system call in generic code.  But
 | 
						|
        // this will have to do for now.
 | 
						|
        open_flags |= O_CLOEXEC;
 | 
						|
#endif
 | 
						|
        if (m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) {
 | 
						|
          const FileSpec slave_file_spec{m_pty->GetSlaveName(nullptr, 0),
 | 
						|
                                         false};
 | 
						|
 | 
						|
          // Only use the slave tty if we don't have anything specified for
 | 
						|
          // input and don't have an action for stdin
 | 
						|
          if (!in_file_spec && GetFileActionForFD(STDIN_FILENO) == nullptr) {
 | 
						|
            AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false);
 | 
						|
          }
 | 
						|
 | 
						|
          // Only use the slave tty if we don't have anything specified for
 | 
						|
          // output and don't have an action for stdout
 | 
						|
          if (!out_file_spec && GetFileActionForFD(STDOUT_FILENO) == nullptr) {
 | 
						|
            AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true);
 | 
						|
          }
 | 
						|
 | 
						|
          // Only use the slave tty if we don't have anything specified for
 | 
						|
          // error and don't have an action for stderr
 | 
						|
          if (!err_file_spec && GetFileActionForFD(STDERR_FILENO) == nullptr) {
 | 
						|
            AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
 | 
						|
    Status &error, bool localhost, bool will_debug,
 | 
						|
    bool first_arg_is_full_shell_command, int32_t num_resumes) {
 | 
						|
  error.Clear();
 | 
						|
 | 
						|
  if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
 | 
						|
    if (m_shell) {
 | 
						|
      std::string shell_executable = m_shell.GetPath();
 | 
						|
 | 
						|
      const char **argv = GetArguments().GetConstArgumentVector();
 | 
						|
      if (argv == nullptr || argv[0] == nullptr)
 | 
						|
        return false;
 | 
						|
      Args shell_arguments;
 | 
						|
      std::string safe_arg;
 | 
						|
      shell_arguments.AppendArgument(shell_executable);
 | 
						|
      const llvm::Triple &triple = GetArchitecture().GetTriple();
 | 
						|
      if (triple.getOS() == llvm::Triple::Win32 &&
 | 
						|
          !triple.isWindowsCygwinEnvironment())
 | 
						|
        shell_arguments.AppendArgument(llvm::StringRef("/C"));
 | 
						|
      else
 | 
						|
        shell_arguments.AppendArgument(llvm::StringRef("-c"));
 | 
						|
 | 
						|
      StreamString shell_command;
 | 
						|
      if (will_debug) {
 | 
						|
        // Add a modified PATH environment variable in case argv[0]
 | 
						|
        // is a relative path.
 | 
						|
        const char *argv0 = argv[0];
 | 
						|
        FileSpec arg_spec(argv0, false);
 | 
						|
        if (arg_spec.IsRelative()) {
 | 
						|
          // We have a relative path to our executable which may not work if
 | 
						|
          // we just try to run "a.out" (without it being converted to
 | 
						|
          // "./a.out")
 | 
						|
          FileSpec working_dir = GetWorkingDirectory();
 | 
						|
          // Be sure to put quotes around PATH's value in case any paths have
 | 
						|
          // spaces...
 | 
						|
          std::string new_path("PATH=\"");
 | 
						|
          const size_t empty_path_len = new_path.size();
 | 
						|
 | 
						|
          if (working_dir) {
 | 
						|
            new_path += working_dir.GetPath();
 | 
						|
          } else {
 | 
						|
            llvm::SmallString<64> cwd;
 | 
						|
            if (! llvm::sys::fs::current_path(cwd))
 | 
						|
              new_path += cwd;
 | 
						|
          }
 | 
						|
          std::string curr_path;
 | 
						|
          if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
 | 
						|
            if (new_path.size() > empty_path_len)
 | 
						|
              new_path += ':';
 | 
						|
            new_path += curr_path;
 | 
						|
          }
 | 
						|
          new_path += "\" ";
 | 
						|
          shell_command.PutCString(new_path);
 | 
						|
        }
 | 
						|
 | 
						|
        if (triple.getOS() != llvm::Triple::Win32 ||
 | 
						|
            triple.isWindowsCygwinEnvironment())
 | 
						|
          shell_command.PutCString("exec");
 | 
						|
 | 
						|
        // Only Apple supports /usr/bin/arch being able to specify the
 | 
						|
        // architecture
 | 
						|
        if (GetArchitecture().IsValid() && // Valid architecture
 | 
						|
            GetArchitecture().GetTriple().getVendor() ==
 | 
						|
                llvm::Triple::Apple && // Apple only
 | 
						|
            GetArchitecture().GetCore() !=
 | 
						|
                ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
 | 
						|
        {
 | 
						|
          shell_command.Printf(" /usr/bin/arch -arch %s",
 | 
						|
                               GetArchitecture().GetArchitectureName());
 | 
						|
          // Set the resume count to 2:
 | 
						|
          // 1 - stop in shell
 | 
						|
          // 2 - stop in /usr/bin/arch
 | 
						|
          // 3 - then we will stop in our program
 | 
						|
          SetResumeCount(num_resumes + 1);
 | 
						|
        } else {
 | 
						|
          // Set the resume count to 1:
 | 
						|
          // 1 - stop in shell
 | 
						|
          // 2 - then we will stop in our program
 | 
						|
          SetResumeCount(num_resumes);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (first_arg_is_full_shell_command) {
 | 
						|
        // There should only be one argument that is the shell command itself to
 | 
						|
        // be used as is
 | 
						|
        if (argv[0] && !argv[1])
 | 
						|
          shell_command.Printf("%s", argv[0]);
 | 
						|
        else
 | 
						|
          return false;
 | 
						|
      } else {
 | 
						|
        for (size_t i = 0; argv[i] != nullptr; ++i) {
 | 
						|
          const char *arg =
 | 
						|
              Args::GetShellSafeArgument(m_shell, argv[i], safe_arg);
 | 
						|
          shell_command.Printf(" %s", arg);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      shell_arguments.AppendArgument(shell_command.GetString());
 | 
						|
      m_executable = m_shell;
 | 
						|
      m_arguments = shell_arguments;
 | 
						|
      return true;
 | 
						|
    } else {
 | 
						|
      error.SetErrorString("invalid shell path");
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    error.SetErrorString("not launching in shell");
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
ListenerSP ProcessLaunchInfo::GetListenerForProcess(Debugger &debugger) {
 | 
						|
  if (m_listener_sp)
 | 
						|
    return m_listener_sp;
 | 
						|
  else
 | 
						|
    return debugger.GetListener();
 | 
						|
}
 |