| <!--* freshness: { reviewed: '2026-10-12' } *--> |
| |
| # Shared Debugger Utilities (`shared`) |
| |
| ## Overview |
| |
| `//src/developer/debug/shared` provides cross-platform abstractions and data |
| types shared between the host debugger (`zxdb`) and the target stub |
| (`debug_agent`). It includes the `fpromise`-integrated `MessageLoop`, buffered |
| I/O streams, CPU register metadata, `AddressRange`, binary `Serializer`, |
| `Status`/`Result` wrappers, RE2 `Regex`, and logging utilities. |
| |
| ## Interface |
| |
| Public headers and subdirectories expose these components: |
| |
| - [logging](./logging/README.md): Severity-based and category-filtered |
| hierarchical debug logging (`LOGS`, `DEBUG_LOG`, `FROM_HERE`, |
| `debug::LogSeverity`, `debug::LogStatement`, `debug::LogBackend`, |
| `debug::LogCategory`, `debug::SetDebugLogging`, `debug::DebugLogStatement`, |
| `debug::FileLineFunction`). |
| - Message loop (`message_loop.h`, `message_loop_fuchsia.h`, |
| `message_loop_linux.h`, `message_loop_poll.h`, `platform_message_loop.h`): |
| `debug::MessageLoop` (`WatchMode`, `WatchHandle`, `FDWatcher`), |
| `debug::MessageLoopContext`, `debug::MessageLoopFuchsia` (`WatchInfo`, |
| `WatchProcessConfig`, `WatchJobConfig`, `WatchType`, `WatchTypeToString`), |
| `debug::MessageLoopLinux` (`SignalWatcher`), `debug::MessageLoopPoll`, |
| `debug::PlatformMessageLoop`. |
| - Buffered streams (`stream_buffer.h`, `buffered_stream.h`, `buffered_fd.h`, |
| `buffered_zx_socket.h`, `buffered_bidi_pipe.h`): `debug::StreamBuffer` |
| (`StreamBuffer::Writer`), `debug::BufferedStream`, `debug::BufferedFD`, |
| `debug::BufferedZxSocket`, `debug::BufferedBidiPipe`. |
| - Address ranges (`address_range.h`): `debug::AddressRange`, |
| `debug::AddressRangeBeginCmp`, `debug::AddressRangeEndAddrCmp`, |
| `debug::AddressRangeEqualityCmp`. |
| - Architecture and registers (`arch.h`, `register_id.h`, `register_info.h`, |
| `register_value.h`): `debug::Arch`, `debug::ArchToString`, |
| `debug::RegisterID`, `debug::SpecialRegisterType`, `debug::RegisterFormat`, |
| `debug::RegisterCategory`, `debug::RegisterInfo`, `debug::InfoForRegister`, |
| `debug::RegisterIDToString`, and `debug::StringToRegisterID`. Also exposes |
| `debug::GetSpecialRegisterID`, `debug::GetSpecialRegisterType`, |
| `debug::DWARFToRegisterInfo`, `debug::GetArchForRegisterID`, |
| `debug::IsGeneralRegister`, `debug::GetRegisterData`, |
| `debug::RegisterCategoryToString`, `debug::RegisterIDToCategory`, and |
| `debug::RegisterValue`. |
| - Error handling and results (`status.h`, `result.h`, `zx_status.h`): |
| `debug::Status` (`Status::Type`), `debug::ZxStatus`, `debug::ErrnoStatus`, |
| `debug::Result`, `debug::ZxStatusToString`. |
| - Serialization and utilities (`serialization.h`, `regex.h`, `handle_info.h`): |
| `debug::Serializer` (`debug_ipc::Serializer`), `debug::Regex` |
| (`Regex::CompareType`), `debug::HandleTypeToString`, |
| `debug::CachePolicyToString`, `debug::HandleRightsToStrings`, |
| `debug::VmoFlagsToStrings`. |
| |
| ## Invariants |
| |
| Callers and implementations must preserve these invariants: |
| |
| - Only one `MessageLoop` may be active per thread between `Init()` and |
| `Cleanup()`, which must run on the same thread as `Run()`. Call `QuitNow()`, |
| `WatchFD()`, Zircon watch methods, and `WatchHandle` destructors only on the |
| `MessageLoop` thread. |
| - Watch each file descriptor at most once per `MessageLoop`. Do not unregister a |
| `MessageLoopFuchsia::WatchSocket` `WatchHandle` inside its callback because |
| the loop may invoke read and write callbacks back-to-back. |
| - Call `StreamBuffer::set_writer()` before using a `StreamBuffer`. Initialize a |
| `MessageLoop` on the current thread and pass valid handles or FDs before |
| calling `Start()` on `BufferedFD`, `BufferedZxSocket`, or `BufferedBidiPipe`. |
| - `AddressRange` enforces `end >= begin` via `FX_DCHECK` and uses half-open |
| `[begin, end)` bounds, so it cannot represent `UINT64_MAX`. |
| - `Arch` enum values appear in the wire protocol, so only append new values. |
| `RegisterID` partitions values into fixed ranges by architecture (`1000` to |
| `1399` for ARMv8, `2000` to `2699` for x64, `3000` to `3699` for riscv64). |
| - `RegisterInfo::bits` and `RegisterInfo::shift` for sub-register aliases must |
| be multiples of 8 for `GetRegisterData()`. `RegisterValue::data` stores bytes |
| in the target architecture's native endianness. |
| - `Status(Type, ...)` asserts if called with `kSuccess` or `kPlatformError`. Use |
| the default constructor or `ZxStatus`/`ErrnoStatus` helpers instead. |
| - `Result<T, E>::value()` and `take_value()` assert `!has_error()` via |
| `FX_DCHECK`, whereas `Result<T, E>::err()` asserts `has_error()`. |
| - On stream EOF or disconnect in `BufferedFD` and `BufferedStream`, check |
| `stream().IsAvailable(1)` and drain buffered bytes via the read callback |
| first. Draining before calling the error callback prevents dropping trailing |
| messages sent right before socket closure. |
| - To stop a `MessageLoop` watch, call `loop.StopWatching(watch_handle)` |
| explicitly instead of reassigning `watch_handle = WatchHandle()`. |
| |
| ## Side Effects |
| |
| These utilities produce the following OS, thread, and I/O side effects: |
| |
| - `MessageLoop` and its subclasses set thread-local loop pointers, lock task |
| queues, create OS wakeup pipes or Zircon `async::Loop` channels, and run |
| callbacks. |
| - `BufferedFD`, `BufferedZxSocket`, and `BufferedBidiPipe` own `fbl::unique_fd` |
| or `zx::socket` handles, register `MessageLoop` watches, perform non-blocking |
| I/O, and invoke stream callbacks. |
| - `Serializer::operator|` mutates arguments in place during deserialization |
| (resizing `std::vector` and updating `std::optional`) or writes bytes via |
| `SerializeBytes()`. |
| - Logging utilities (`LOGS`, `DEBUG_LOG`) record source locations and timing |
| metrics, and write formatted logs to `LogBackend` sinks or standard streams. |
| |
| ## Verification |
| |
| Run these commands to build and test `shared`: |
| |
| - Build: `fx build //src/developer/debug/shared` |
| - Test: `fx test zxdb_tests` |