blob: ebed48a5494814c1f862869c7af0ae4a3ef56d6e [file] [view]
<!--* freshness: { reviewed: '2026-10-12' } *-->
# 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`