brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.4 KiB · b5f0504 Raw
433 lines · cpp
1//===-- source/Host/linux/Host.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-exception6//7//===----------------------------------------------------------------------===//8 9#include <cerrno>10#include <cstdio>11#include <cstring>12#include <dirent.h>13#include <fcntl.h>14#include <optional>15#include <sys/stat.h>16#include <sys/types.h>17#include <sys/utsname.h>18#include <unistd.h>19 20#include "llvm/ADT/StringSwitch.h"21#include "llvm/Object/ELF.h"22#include "llvm/Support/ScopedPrinter.h"23 24#include "lldb/Utility/LLDBLog.h"25#include "lldb/Utility/Log.h"26#include "lldb/Utility/ProcessInfo.h"27#include "lldb/Utility/Status.h"28 29#include "lldb/Host/FileSystem.h"30#include "lldb/Host/Host.h"31#include "lldb/Host/HostInfo.h"32#include "lldb/Host/linux/Host.h"33#include "lldb/Host/posix/Support.h"34#include "lldb/Utility/DataExtractor.h"35 36using namespace lldb;37using namespace lldb_private;38 39namespace {40 41enum class ProcessState {42  Unknown,43  Dead,44  DiskSleep,45  Idle,46  Paging,47  Parked,48  Running,49  Sleeping,50  TracedOrStopped,51  Zombie,52};53 54struct StatFields {55  ::pid_t pid = LLDB_INVALID_PROCESS_ID;56  // comm57  char state;58  ::pid_t ppid = LLDB_INVALID_PROCESS_ID;59  ::pid_t pgrp = LLDB_INVALID_PROCESS_ID;60  ::pid_t session = LLDB_INVALID_PROCESS_ID;61  int tty_nr;62  int tpgid;63  unsigned flags;64  long unsigned minflt;65  long unsigned cminflt;66  long unsigned majflt;67  long unsigned cmajflt;68  long unsigned utime;69  long unsigned stime;70  long cutime;71  long cstime;72  // In proc_pid_stat(5) this field is specified as priority73  // but documented as realtime priority. To keep with the adopted74  // nomenclature in ProcessInstanceInfo, we adopt the documented75  // naming here.76  long realtime_priority;77  long priority;78  // .... other things. We don't need them below79};80}81 82namespace lldb_private {83class ProcessLaunchInfo;84}85 86static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,87                          ProcessState &State, ::pid_t &TracerPid,88                          ::pid_t &Tgid) {89  Log *log = GetLog(LLDBLog::Host);90 91  auto BufferOrError = getProcFile(Pid, "stat");92  if (!BufferOrError)93    return false;94 95  llvm::StringRef Rest = BufferOrError.get()->getBuffer();96  if (Rest.empty())97    return false;98  StatFields stat_fields;99  if (sscanf(100          Rest.data(),101          "%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld",102          &stat_fields.pid, /* comm, */ &stat_fields.state,103          &stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session,104          &stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags,105          &stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt,106          &stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime,107          &stat_fields.cutime, &stat_fields.cstime,108          &stat_fields.realtime_priority, &stat_fields.priority) < 0) {109    return false;110  }111 112  auto convert = [sc_clk_ticks = sysconf(_SC_CLK_TCK)](auto time_in_ticks) {113    ProcessInstanceInfo::timespec ts;114    if (sc_clk_ticks <= 0) {115      return ts;116    }117    ts.tv_sec = time_in_ticks / sc_clk_ticks;118    double remainder =119        (static_cast<double>(time_in_ticks) / sc_clk_ticks) - ts.tv_sec;120    ts.tv_usec =121        std::chrono::microseconds{std::lround(1e+6 * remainder)}.count();122    return ts;123  };124 125  // Priority (nice) values run from 19 to -20 inclusive (in linux). In the126  // prpsinfo struct pr_nice is a char.127  auto priority_value = static_cast<int8_t>(128      (stat_fields.priority < 0 ? 0x80 : 0x00) | (stat_fields.priority & 0x7f));129 130  ProcessInfo.SetParentProcessID(stat_fields.ppid);131  ProcessInfo.SetProcessGroupID(stat_fields.pgrp);132  ProcessInfo.SetProcessSessionID(stat_fields.session);133  ProcessInfo.SetUserTime(convert(stat_fields.utime));134  ProcessInfo.SetSystemTime(convert(stat_fields.stime));135  ProcessInfo.SetCumulativeUserTime(convert(stat_fields.cutime));136  ProcessInfo.SetCumulativeSystemTime(convert(stat_fields.cstime));137  ProcessInfo.SetPriorityValue(priority_value);138  switch (stat_fields.state) {139  case 'R':140    State = ProcessState::Running;141    break;142  case 'S':143    State = ProcessState::Sleeping;144    break;145  case 'D':146    State = ProcessState::DiskSleep;147    break;148  case 'Z':149    State = ProcessState::Zombie;150    break;151  case 'X':152    State = ProcessState::Dead;153    break;154  case 'P':155    State = ProcessState::Parked;156    break;157  case 'W':158    State = ProcessState::Paging;159    break;160  case 'I':161    State = ProcessState::Idle;162    break;163  case 'T': // Stopped on a signal or (before Linux 2.6.33) trace stopped164    [[fallthrough]];165  case 't':166    State = ProcessState::TracedOrStopped;167    break;168  default:169    State = ProcessState::Unknown;170    break;171  }172  ProcessInfo.SetIsZombie(State == ProcessState::Zombie);173 174  if (State == ProcessState::Unknown) {175    LLDB_LOG(log, "Unknown process state {0}", stat_fields.state);176  }177 178  BufferOrError = getProcFile(Pid, "status");179  if (!BufferOrError)180    return false;181 182  Rest = BufferOrError.get()->getBuffer();183  if (Rest.empty())184    return false;185 186  while (!Rest.empty()) {187    llvm::StringRef Line;188    std::tie(Line, Rest) = Rest.split('\n');189 190    if (Line.consume_front("Gid:")) {191      // Real, effective, saved set, and file system GIDs. Read the first two.192      Line = Line.ltrim();193      uint32_t RGid, EGid;194      Line.consumeInteger(10, RGid);195      Line = Line.ltrim();196      Line.consumeInteger(10, EGid);197 198      ProcessInfo.SetGroupID(RGid);199      ProcessInfo.SetEffectiveGroupID(EGid);200    } else if (Line.consume_front("Uid:")) {201      // Real, effective, saved set, and file system UIDs. Read the first two.202      Line = Line.ltrim();203      uint32_t RUid, EUid;204      Line.consumeInteger(10, RUid);205      Line = Line.ltrim();206      Line.consumeInteger(10, EUid);207 208      ProcessInfo.SetUserID(RUid);209      ProcessInfo.SetEffectiveUserID(EUid);210    } else if (Line.consume_front("TracerPid:")) {211      Line = Line.ltrim();212      Line.consumeInteger(10, TracerPid);213    } else if (Line.consume_front("Tgid:")) {214      Line = Line.ltrim();215      Line.consumeInteger(10, Tgid);216    } else if (Line.consume_front("CoreDumping:")) {217      uint32_t coredumping;218      Line = Line.ltrim();219      if (!Line.consumeInteger(2, coredumping))220        ProcessInfo.SetIsCoreDumping(coredumping);221    }222  }223  return true;224}225 226static bool IsDirNumeric(const char *dname) {227  for (; *dname; dname++) {228    if (!isdigit(*dname))229      return false;230  }231  return true;232}233 234static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {235  Log *log = GetLog(LLDBLog::Host);236 237  auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);238  if (!buffer_sp)239    return ArchSpec();240 241  uint8_t exe_class =242      llvm::object::getElfArchType(243          {reinterpret_cast<const char *>(buffer_sp->GetBytes()),244           size_t(buffer_sp->GetByteSize())})245          .first;246 247  switch (exe_class) {248  case llvm::ELF::ELFCLASS32:249    return HostInfo::GetArchitecture(HostInfo::eArchKind32);250  case llvm::ELF::ELFCLASS64:251    return HostInfo::GetArchitecture(HostInfo::eArchKind64);252  default:253    LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);254    return ArchSpec();255  }256}257 258static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) {259  auto BufferOrError = getProcFile(pid, "cmdline");260  if (!BufferOrError)261    return;262  std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);263 264  llvm::StringRef Arg0, Rest;265  std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');266  process_info.SetArg0(Arg0);267  while (!Rest.empty()) {268    llvm::StringRef Arg;269    std::tie(Arg, Rest) = Rest.split('\0');270    process_info.GetArguments().AppendArgument(Arg);271  }272}273 274static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) {275  Log *log = GetLog(LLDBLog::Process);276  std::string ExePath(PATH_MAX, '\0');277 278  // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.279  llvm::SmallString<64> ProcExe;280  (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);281 282  ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);283  if (len > 0) {284    ExePath.resize(len);285  } else {286    LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,287             Status(errno, eErrorTypePOSIX));288    ExePath.resize(0);289  }290  // If the binary has been deleted, the link name has " (deleted)" appended.291  // Remove if there.292  llvm::StringRef PathRef = ExePath;293  PathRef.consume_back(" (deleted)");294 295  if (!PathRef.empty()) {296    process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);297    process_info.SetArchitecture(GetELFProcessCPUType(PathRef));298  }299}300 301static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) {302  // Get the process environment.303  auto BufferOrError = getProcFile(pid, "environ");304  if (!BufferOrError)305    return;306 307  std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);308  llvm::StringRef Rest = Environ->getBuffer();309  while (!Rest.empty()) {310    llvm::StringRef Var;311    std::tie(Var, Rest) = Rest.split('\0');312    process_info.GetEnvironment().insert(Var);313  }314}315 316static bool GetProcessAndStatInfo(::pid_t pid,317                                  ProcessInstanceInfo &process_info,318                                  ProcessState &State, ::pid_t &tracerpid) {319  ::pid_t tgid;320  tracerpid = 0;321  process_info.Clear();322 323  process_info.SetProcessID(pid);324 325  GetExePathAndArch(pid, process_info);326  GetProcessArgs(pid, process_info);327  GetProcessEnviron(pid, process_info);328 329  // Get User and Group IDs and get tracer pid.330  if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))331    return false;332 333  return true;334}335 336uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,337                                 ProcessInstanceInfoList &process_infos) {338  static const char procdir[] = "/proc/";339 340  DIR *dirproc = opendir(procdir);341  if (dirproc) {342    struct dirent *direntry = nullptr;343    const uid_t our_uid = getuid();344    const lldb::pid_t our_pid = getpid();345    bool all_users = match_info.GetMatchAllUsers();346 347    while ((direntry = readdir(dirproc)) != nullptr) {348      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))349        continue;350 351      lldb::pid_t pid = atoi(direntry->d_name);352 353      // Skip this process.354      if (pid == our_pid)355        continue;356 357      ::pid_t tracerpid;358      ProcessState State;359      ProcessInstanceInfo process_info;360 361      if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))362        continue;363 364      // Skip if process is being debugged.365      if (tracerpid != 0)366        continue;367 368      if (State == ProcessState::Zombie)369        continue;370 371      // Check for user match if we're not matching all users and not running372      // as root.373      if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))374        continue;375 376      if (match_info.Matches(process_info)) {377        process_infos.push_back(process_info);378      }379    }380 381    closedir(dirproc);382  }383 384  return process_infos.size();385}386 387bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {388  bool tids_changed = false;389  static const char procdir[] = "/proc/";390  static const char taskdir[] = "/task/";391  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;392  DIR *dirproc = opendir(process_task_dir.c_str());393 394  if (dirproc) {395    struct dirent *direntry = nullptr;396    while ((direntry = readdir(dirproc)) != nullptr) {397      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))398        continue;399 400      lldb::tid_t tid = atoi(direntry->d_name);401      TidMap::iterator it = tids_to_attach.find(tid);402      if (it == tids_to_attach.end()) {403        tids_to_attach.insert(TidPair(tid, false));404        tids_changed = true;405      }406    }407    closedir(dirproc);408  }409 410  return tids_changed;411}412 413bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {414  ::pid_t tracerpid;415  ProcessState State;416  return GetProcessAndStatInfo(pid, process_info, State, tracerpid);417}418 419Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {420  return Status::FromErrorString("unimplemented");421}422 423std::optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) {424  ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;425  ProcessInstanceInfo process_info;426  ProcessState state;427 428  if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) ||429      tgid == LLDB_INVALID_PROCESS_ID)430    return std::nullopt;431  return tgid;432}433