forked from lix-project/lix
gc: Start libproc-based Darwin roots
This commit is contained in:
parent
d8d1dd1b83
commit
80361ab769
|
@ -1,6 +1,7 @@
|
|||
#include "derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "logging.hh"
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
|
@ -21,6 +22,11 @@
|
|||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/proc_info.h>
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
@ -357,12 +363,9 @@ static void readFileRoots(const char * path, UncheckedRoots & roots)
|
|||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
static void readProcfsRoots(const Path & storeDir, UncheckedRoots & unchecked)
|
||||
{
|
||||
UncheckedRoots unchecked;
|
||||
|
||||
auto procDir = AutoCloseDir{opendir("/proc")};
|
||||
if (procDir) {
|
||||
struct dirent * ent;
|
||||
|
@ -418,8 +421,99 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
|||
if (errno)
|
||||
throw SysError("iterating /proc");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(__linux__)
|
||||
#ifdef __APPLE__
|
||||
static void readLibprocRoots(const Path & storeDir, UncheckedRoots & unchecked)
|
||||
{
|
||||
int pidBufSize = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
|
||||
if (pidBufSize <= 0 || pidBufSize % sizeof(int) != 0)
|
||||
throw SysError("Listing PIDs");
|
||||
|
||||
std::vector<int> pids(pidBufSize / sizeof(int));
|
||||
pidBufSize = proc_listpids(PROC_ALL_PIDS, 0, pids.data(), pidBufSize);
|
||||
if (pidBufSize <= 0 || pidBufSize % sizeof(int) != 0)
|
||||
throw SysError("Listing PIDs");
|
||||
pids.resize(pidBufSize / sizeof(int));
|
||||
|
||||
for (auto pid: pids) {
|
||||
// It doesn't make sense to ask about the kernel
|
||||
if (pid == 0)
|
||||
continue;
|
||||
|
||||
// Process cwd/root directory
|
||||
struct proc_vnodepathinfo vnodeInfo;
|
||||
if (proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vnodeInfo, sizeof(vnodeInfo)) <= 0) {
|
||||
if (errno == ESRCH) continue;
|
||||
throw SysError("Getting PID info");
|
||||
};
|
||||
unchecked[std::string(vnodeInfo.pvi_cdir.vip_path)].emplace("{libproc/cwd}");
|
||||
unchecked[std::string(vnodeInfo.pvi_rdir.vip_path)].emplace("{libproc/rootdir}");
|
||||
|
||||
// File descriptors
|
||||
int fdBufSize = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, nullptr, 0);
|
||||
if (fdBufSize <= 0 || fdBufSize % sizeof(struct proc_fdinfo) != 0)
|
||||
throw SysError("Listing PID's file descriptors");
|
||||
std::vector<struct proc_fdinfo> fds(fdBufSize / sizeof(struct proc_fdinfo));
|
||||
fdBufSize = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds.data(), fdBufSize);
|
||||
if (fdBufSize <= 0)
|
||||
throw SysError("Listing PID's file descriptors");
|
||||
fds.resize(fdBufSize / sizeof(struct proc_fdinfo));
|
||||
for (auto fd: fds) {
|
||||
// While there are names for other FD types, only vnodes are in the filesystem
|
||||
if (fd.proc_fdtype != PROX_FDTYPE_VNODE)
|
||||
continue;
|
||||
|
||||
struct vnode_fdinfowithpath fdInfo;
|
||||
if (proc_pidfdinfo(pid, fd.proc_fd, PROC_PIDFDVNODEPATHINFO, &fdInfo, sizeof(fdInfo)) <= 0)
|
||||
throw SysError("Getting file descriptor path");
|
||||
|
||||
unchecked[std::string(fdInfo.pvip.vip_path)].emplace("{libproc/fd}");
|
||||
}
|
||||
|
||||
// Regions (e.g. mmapped files, executables, shared libraries)
|
||||
struct proc_regionwithpathinfo regionInfo;
|
||||
uint64_t nextAddr = 0;
|
||||
while (true) {
|
||||
// Seriously, what are you doing XNU?
|
||||
// There's 3 flavors of PROC_PIDREGIONPATHINFO:
|
||||
// * PROC_PIDREGIONPATHINFO includes all regions
|
||||
// * PROC_PIDREGIONPATHINFO2 includes regions backed by a vnode
|
||||
// * PROC_PIDREGIONPATHINFO3 includes regions backed by a vnode on a specified filesystem
|
||||
// Only PROC_PIDREGIONPATHINFO is documented.
|
||||
// Unfortunately, using it would make finding gcroots take about 50x as long
|
||||
// and tests would fail from timeout.
|
||||
// According to the Frida source code, PROC_PIDREGIONPATHINFO2 has been available
|
||||
// since XNU 2782.1.97 in OS X 10.10
|
||||
//
|
||||
// This is PROC_PIDREGIONPATHINFO2
|
||||
if (proc_pidinfo(pid, 22, nextAddr, ®ionInfo, sizeof(regionInfo)) <= 0) {
|
||||
if ((errno == ESRCH) || (errno == EINVAL))
|
||||
break;
|
||||
throw SysError("Getting region path");
|
||||
}
|
||||
|
||||
unchecked[std::string(regionInfo.prp_vip.vip_path)].emplace("{libproc/region}");
|
||||
|
||||
nextAddr = regionInfo.prp_prinfo.pri_address + regionInfo.prp_prinfo.pri_size;
|
||||
}
|
||||
|
||||
// TODO: per-thread cwds (apparently exist but I don't see them much)
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
{
|
||||
UncheckedRoots unchecked;
|
||||
|
||||
#ifdef __APPLE__
|
||||
readLibprocRoots(storeDir, unchecked);
|
||||
#elif defined(__linux__)
|
||||
readProcfsRoots(storeDir, unchecked);
|
||||
#else
|
||||
try {
|
||||
std::regex lsofRegex(R"(^n(/.*)$)");
|
||||
auto lsofLines =
|
||||
|
|
Loading…
Reference in a new issue