312 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- source/Host/linux/Host.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
 | 
						|
#include <dirent.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/utsname.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
// C++ Includes
 | 
						|
// Other libraries and framework includes
 | 
						|
#include "llvm/Support/ScopedPrinter.h"
 | 
						|
// Project includes
 | 
						|
#include "lldb/Target/Process.h"
 | 
						|
#include "lldb/Utility/Log.h"
 | 
						|
#include "lldb/Utility/Status.h"
 | 
						|
 | 
						|
#include "lldb/Host/Host.h"
 | 
						|
#include "lldb/Host/HostInfo.h"
 | 
						|
#include "lldb/Host/linux/Support.h"
 | 
						|
#include "lldb/Utility/DataBufferHeap.h"
 | 
						|
#include "lldb/Utility/DataExtractor.h"
 | 
						|
 | 
						|
#include "lldb/Core/ModuleSpec.h"
 | 
						|
#include "lldb/Symbol/ObjectFile.h"
 | 
						|
 | 
						|
using namespace lldb;
 | 
						|
using namespace lldb_private;
 | 
						|
 | 
						|
namespace {
 | 
						|
enum class ProcessState {
 | 
						|
  Unknown,
 | 
						|
  DiskSleep,
 | 
						|
  Paging,
 | 
						|
  Running,
 | 
						|
  Sleeping,
 | 
						|
  TracedOrStopped,
 | 
						|
  Zombie,
 | 
						|
};
 | 
						|
}
 | 
						|
 | 
						|
static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
 | 
						|
                          ProcessState &State, ::pid_t &TracerPid) {
 | 
						|
  auto BufferOrError = getProcFile(Pid, "status");
 | 
						|
  if (!BufferOrError)
 | 
						|
    return false;
 | 
						|
 | 
						|
  llvm::StringRef Rest = BufferOrError.get()->getBuffer();
 | 
						|
  while(!Rest.empty()) {
 | 
						|
    llvm::StringRef Line;
 | 
						|
    std::tie(Line, Rest) = Rest.split('\n');
 | 
						|
 | 
						|
    if (Line.consume_front("Gid:")) {
 | 
						|
      // Real, effective, saved set, and file system GIDs. Read the first two.
 | 
						|
      Line = Line.ltrim();
 | 
						|
      uint32_t RGid, EGid;
 | 
						|
      Line.consumeInteger(10, RGid);
 | 
						|
      Line = Line.ltrim();
 | 
						|
      Line.consumeInteger(10, EGid);
 | 
						|
 | 
						|
      ProcessInfo.SetGroupID(RGid);
 | 
						|
      ProcessInfo.SetEffectiveGroupID(EGid);
 | 
						|
    } else if (Line.consume_front("Uid:")) {
 | 
						|
      // Real, effective, saved set, and file system UIDs. Read the first two.
 | 
						|
      Line = Line.ltrim();
 | 
						|
      uint32_t RUid, EUid;
 | 
						|
      Line.consumeInteger(10, RUid);
 | 
						|
      Line = Line.ltrim();
 | 
						|
      Line.consumeInteger(10, EUid);
 | 
						|
 | 
						|
      ProcessInfo.SetUserID(RUid);
 | 
						|
      ProcessInfo.SetEffectiveUserID(EUid);
 | 
						|
    } else if (Line.consume_front("PPid:")) {
 | 
						|
      ::pid_t PPid;
 | 
						|
      Line.ltrim().consumeInteger(10, PPid);
 | 
						|
      ProcessInfo.SetParentProcessID(PPid);
 | 
						|
    } else if (Line.consume_front("State:")) {
 | 
						|
      char S = Line.ltrim().front();
 | 
						|
      switch (S) {
 | 
						|
      case 'R':
 | 
						|
        State = ProcessState::Running;
 | 
						|
        break;
 | 
						|
      case 'S':
 | 
						|
        State = ProcessState::Sleeping;
 | 
						|
        break;
 | 
						|
      case 'D':
 | 
						|
        State = ProcessState::DiskSleep;
 | 
						|
        break;
 | 
						|
      case 'Z':
 | 
						|
        State = ProcessState::Zombie;
 | 
						|
        break;
 | 
						|
      case 'T':
 | 
						|
        State = ProcessState::TracedOrStopped;
 | 
						|
        break;
 | 
						|
      case 'W':
 | 
						|
        State = ProcessState::Paging;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    } else if (Line.consume_front("TracerPid:")) {
 | 
						|
      Line = Line.ltrim();
 | 
						|
      Line.consumeInteger(10, TracerPid);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool IsDirNumeric(const char *dname) {
 | 
						|
  for (; *dname; dname++) {
 | 
						|
    if (!isdigit(*dname))
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool GetELFProcessCPUType(llvm::StringRef exe_path,
 | 
						|
                                 ProcessInstanceInfo &process_info) {
 | 
						|
  // Clear the architecture.
 | 
						|
  process_info.GetArchitecture().Clear();
 | 
						|
 | 
						|
  ModuleSpecList specs;
 | 
						|
  FileSpec filespec(exe_path, false);
 | 
						|
  const size_t num_specs =
 | 
						|
      ObjectFile::GetModuleSpecifications(filespec, 0, 0, specs);
 | 
						|
  // GetModuleSpecifications() could fail if the executable has been deleted or
 | 
						|
  // is locked.
 | 
						|
  // But it shouldn't return more than 1 architecture.
 | 
						|
  assert(num_specs <= 1 && "Linux plugin supports only a single architecture");
 | 
						|
  if (num_specs == 1) {
 | 
						|
    ModuleSpec module_spec;
 | 
						|
    if (specs.GetModuleSpecAtIndex(0, module_spec) &&
 | 
						|
        module_spec.GetArchitecture().IsValid()) {
 | 
						|
      process_info.GetArchitecture() = module_spec.GetArchitecture();
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool GetProcessAndStatInfo(::pid_t pid,
 | 
						|
                                  ProcessInstanceInfo &process_info,
 | 
						|
                                  ProcessState &State, ::pid_t &tracerpid) {
 | 
						|
  tracerpid = 0;
 | 
						|
  process_info.Clear();
 | 
						|
 | 
						|
  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
 | 
						|
 | 
						|
  // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
 | 
						|
  llvm::SmallString<64> ProcExe;
 | 
						|
  (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);
 | 
						|
  std::string ExePath(PATH_MAX, '\0');
 | 
						|
 | 
						|
  ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
 | 
						|
  if (len <= 0) {
 | 
						|
    LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
 | 
						|
             Status(errno, eErrorTypePOSIX));
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  ExePath.resize(len);
 | 
						|
 | 
						|
  // If the binary has been deleted, the link name has " (deleted)" appended.
 | 
						|
  // Remove if there.
 | 
						|
  llvm::StringRef PathRef = ExePath;
 | 
						|
  PathRef.consume_back(" (deleted)");
 | 
						|
 | 
						|
  GetELFProcessCPUType(PathRef, process_info);
 | 
						|
 | 
						|
  // Get the process environment.
 | 
						|
  auto BufferOrError = getProcFile(pid, "environ");
 | 
						|
  if (!BufferOrError)
 | 
						|
    return false;
 | 
						|
  std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);
 | 
						|
 | 
						|
  // Get the command line used to start the process.
 | 
						|
  BufferOrError = getProcFile(pid, "cmdline");
 | 
						|
  if (!BufferOrError)
 | 
						|
    return false;
 | 
						|
  std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);
 | 
						|
 | 
						|
  // Get User and Group IDs and get tracer pid.
 | 
						|
  if (!GetStatusInfo(pid, process_info, State, tracerpid))
 | 
						|
    return false;
 | 
						|
 | 
						|
  process_info.SetProcessID(pid);
 | 
						|
  process_info.GetExecutableFile().SetFile(PathRef, false);
 | 
						|
  process_info.GetArchitecture().MergeFrom(HostInfo::GetArchitecture());
 | 
						|
 | 
						|
  llvm::StringRef Rest = Environ->getBuffer();
 | 
						|
  while (!Rest.empty()) {
 | 
						|
    llvm::StringRef Var;
 | 
						|
    std::tie(Var, Rest) = Rest.split('\0');
 | 
						|
    process_info.GetEnvironmentEntries().AppendArgument(Var);
 | 
						|
  }
 | 
						|
 | 
						|
  llvm::StringRef Arg0;
 | 
						|
  std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
 | 
						|
  process_info.SetArg0(Arg0);
 | 
						|
  while (!Rest.empty()) {
 | 
						|
    llvm::StringRef Arg;
 | 
						|
    std::tie(Arg, Rest) = Rest.split('\0');
 | 
						|
    process_info.GetArguments().AppendArgument(Arg);
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
 | 
						|
                             ProcessInstanceInfoList &process_infos) {
 | 
						|
  static const char procdir[] = "/proc/";
 | 
						|
 | 
						|
  DIR *dirproc = opendir(procdir);
 | 
						|
  if (dirproc) {
 | 
						|
    struct dirent *direntry = NULL;
 | 
						|
    const uid_t our_uid = getuid();
 | 
						|
    const lldb::pid_t our_pid = getpid();
 | 
						|
    bool all_users = match_info.GetMatchAllUsers();
 | 
						|
 | 
						|
    while ((direntry = readdir(dirproc)) != NULL) {
 | 
						|
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
 | 
						|
        continue;
 | 
						|
 | 
						|
      lldb::pid_t pid = atoi(direntry->d_name);
 | 
						|
 | 
						|
      // Skip this process.
 | 
						|
      if (pid == our_pid)
 | 
						|
        continue;
 | 
						|
 | 
						|
      ::pid_t tracerpid;
 | 
						|
      ProcessState State;
 | 
						|
      ProcessInstanceInfo process_info;
 | 
						|
 | 
						|
      if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
 | 
						|
        continue;
 | 
						|
 | 
						|
      // Skip if process is being debugged.
 | 
						|
      if (tracerpid != 0)
 | 
						|
        continue;
 | 
						|
 | 
						|
      if (State == ProcessState::Zombie)
 | 
						|
        continue;
 | 
						|
 | 
						|
      // Check for user match if we're not matching all users and not running as
 | 
						|
      // root.
 | 
						|
      if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
 | 
						|
        continue;
 | 
						|
 | 
						|
      if (match_info.Matches(process_info)) {
 | 
						|
        process_infos.Append(process_info);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    closedir(dirproc);
 | 
						|
  }
 | 
						|
 | 
						|
  return process_infos.GetSize();
 | 
						|
}
 | 
						|
 | 
						|
bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
 | 
						|
  bool tids_changed = false;
 | 
						|
  static const char procdir[] = "/proc/";
 | 
						|
  static const char taskdir[] = "/task/";
 | 
						|
  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
 | 
						|
  DIR *dirproc = opendir(process_task_dir.c_str());
 | 
						|
 | 
						|
  if (dirproc) {
 | 
						|
    struct dirent *direntry = NULL;
 | 
						|
    while ((direntry = readdir(dirproc)) != NULL) {
 | 
						|
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
 | 
						|
        continue;
 | 
						|
 | 
						|
      lldb::tid_t tid = atoi(direntry->d_name);
 | 
						|
      TidMap::iterator it = tids_to_attach.find(tid);
 | 
						|
      if (it == tids_to_attach.end()) {
 | 
						|
        tids_to_attach.insert(TidPair(tid, false));
 | 
						|
        tids_changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    closedir(dirproc);
 | 
						|
  }
 | 
						|
 | 
						|
  return tids_changed;
 | 
						|
}
 | 
						|
 | 
						|
bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
 | 
						|
  ::pid_t tracerpid;
 | 
						|
  ProcessState State;
 | 
						|
  return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
 | 
						|
}
 | 
						|
 | 
						|
size_t Host::GetEnvironment(StringList &env) {
 | 
						|
  char **host_env = environ;
 | 
						|
  char *env_entry;
 | 
						|
  size_t i;
 | 
						|
  for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
 | 
						|
    env.AppendString(env_entry);
 | 
						|
  return i;
 | 
						|
}
 | 
						|
 | 
						|
Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
 | 
						|
  return Status("unimplemented");
 | 
						|
}
 |