| <!--* 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` |