blob: 40a094840ab1c73d47080eb7215de3c9370aa358 [file] [view]
<!--* 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`