POSIX Utilities (posix)

Overview

//src/developer/debug/debug_agent/posix provides low-level, allocation-free POSIX and Linux system call utilities for debug_agent. It includes signal-safe EINTR retry macros, a non-allocating Linux directory reader (getdents64), and an injective file-descriptor table shuffler safe for post-fork execution.

Interface

Public headers expose these types, macros, and functions:

  • linux::linux_dirent: 64-bit directory entry structure for getdents64 (dir_reader_linux.h).
  • linux::DirReaderLinux: Non-copyable, fixed-buffer Linux directory reader (DirReaderLinux(const char* directory_path), IsValid(), Next(), name(), fd(), IsFallback()) (dir_reader_linux.h).
  • HANDLE_EINTR(x): Macro that retries POSIX expression x while it returns -1 with errno == EINTR (eintr_wrapper.h).
  • IGNORE_EINTR(x): Macro that evaluates POSIX expression x once and converts a -1 return with errno == EINTR to 0 (eintr_wrapper.h).
  • posix::InjectionDelegate: Abstract interface (Duplicate, Move, Close) for file-descriptor table operations (file_descriptor_shuffle.h).
  • posix::FileDescriptorTableInjection: Default InjectionDelegate implementation backed by dup, dup2, and close (file_descriptor_shuffle.h).
  • posix::InjectionArc / posix::InjectiveMultimap: Mapping element (source, dest, close_source) and vector defining an injective file-descriptor remapping (file_descriptor_shuffle.h).
  • posix::PerformInjectiveMultimap(const InjectiveMultimap& map, InjectionDelegate* delegate): Non-destructive wrapper that copies map and executes the descriptor remapping (file_descriptor_shuffle.h).
  • posix::PerformInjectiveMultimapDestructive(InjectiveMultimap* map, InjectionDelegate* delegate): Allocation-free, lock-free file-descriptor remapping algorithm (file_descriptor_shuffle.h).
  • posix::ShuffleFileDescriptors(InjectiveMultimap* map): Entry point that applies PerformInjectiveMultimapDestructive with FileDescriptorTableInjection (file_descriptor_shuffle.h).

Invariants

Callers and implementations must preserve these invariants:

  • linux::DirReaderLinux is non-copyable and performs no dynamic memory allocations. It uses a fixed 512-byte buffer aligned to linux_dirent so it remains safe in post-fork child processes.
  • linux::DirReaderLinux::name() returns nullptr before the first successful call to Next() (when size_ == 0).
  • HANDLE_EINTR(x) retries x while x returns -1 with errno == EINTR. In non-NDEBUG builds, HANDLE_EINTR(x) caps retries at 100 iterations to catch stuck signal loops.
  • IGNORE_EINTR(x) evaluates x once without retrying and converts a -1 return with errno == EINTR into 0.
  • posix::PerformInjectiveMultimapDestructive and posix::ShuffleFileDescriptors never allocate memory or acquire locks, making them safe in post-fork contexts.
  • posix::PerformInjectiveMultimapDestructive requires unique destination descriptors in InjectiveMultimap (FX_DCHECK(i->dest != j->dest)).
  • posix::FileDescriptorTableInjection::Close asserts via FX_DCHECK that IGNORE_EINTR(close(fd)) returns 0.

Side Effects

These utilities produce the following OS and memory side effects:

  • linux::DirReaderLinux opens directory_path with O_RDONLY | O_DIRECTORY on construction, calls syscall(__NR_getdents64, ...) in Next(), and closes fd_ on destruction.
  • posix::PerformInjectiveMultimapDestructive and posix::ShuffleFileDescriptors mutate the passed InjectiveMultimap in place as they update temporary descriptors and close flags.
  • posix::FileDescriptorTableInjection mutates the process file-descriptor table via dup, dup2, and close system calls.

Verification

Run these commands to build and test posix:

  • Build: fx build //src/developer/debug/debug_agent:lib
  • Test: fx test --host debug_agent_unit_tests