microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
remote_diagnostics.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "address_space.hpp"
11#include "symbol_resolver.hpp"
12#include <cstdint>
13#include <string_view>
14
15namespace microfmt {
16
17// ============================================================================
18// Diagnostic Formatter for Faults
19// ============================================================================
20
32inline void format_remote_fault(const sink &out, uintptr_t addr, symbol_resolver_ref resolver,
33 symbol_resolution_context &context) noexcept {
34 if (addr == 0) {
35 out.write("<fault:nullptr>");
36 return;
37 }
38
39 if (resolver) {
40 context.raw = {};
41 context.resolved = {};
42 if (resolver.resolve(addr, context) && ((context.resolved.has_symbol()) || context.resolved.has_image())) {
43 const auto &info = context.resolved;
44 out.write("<fault@");
45
46 if (info.has_symbol()) {
47 if (info.has_image()) {
48 out.write(info.image_name);
49 out.put('!');
50 }
51 microfmt::format_to(out, "{}", as_demangled(info.symbol_name));
52 if (info.offset_from_symbol > 0) {
53 out.write("+0x");
54 detail::format_unsigned<detail::radix::hex>(out, static_cast<uint64_t>(info.offset_from_symbol), false, 0);
55 }
56 } else {
57 // Module known, symbol stripped: e.g. "faulty_driver.ko+0x4200"
58 out.write(info.image_name);
59 out.write("+0x");
60 detail::format_unsigned<detail::radix::hex>(out, static_cast<uint64_t>(info.offset_from_image), false, 0);
61 }
62
63 out.write(">");
64 return;
65 }
66 }
67
68 // Fallback raw hex address
69 microfmt::format_to(out, "<fault:{:#x}>", addr);
70}
71
72// ============================================================================
73// Symbolic Remote Function / Code Pointer View
74// ============================================================================
75
82class MICROFMT_API_CLASS RELOCO_POINTER remote_fn_ptr {
83public:
87 constexpr remote_fn_ptr() noexcept = default;
88
95 constexpr remote_fn_ptr(uintptr_t addr, symbol_resolver_ref resolver,
96 symbol_resolution_context &context RELOCO_LIFETIMEBOUND) noexcept
97 : addr_(addr), resolver_(resolver), context_(&context) {}
98
103 [[nodiscard]] constexpr uintptr_t address() const noexcept { return addr_; }
108 [[nodiscard]] constexpr bool is_null() const noexcept { return addr_ == 0; }
113 [[nodiscard]] constexpr symbol_resolver_ref resolver() const noexcept { return resolver_; }
117 [[nodiscard]] constexpr symbol_resolution_context &context() const noexcept RELOCO_LIFETIMEBOUND { return *context_; }
118
119private:
121 uintptr_t addr_{0};
123 symbol_resolver_ref resolver_{};
125 value_ptr<symbol_resolution_context> context_{};
126};
127
134template <> struct formatter<remote_fn_ptr> {
138 char mode{'\0'};
139
144 constexpr void parse(format_parse_context &ctx) noexcept {
145 auto spec = ctx.spec();
146 if (!spec.empty()) {
147 mode = spec.front();
148 }
149 }
150
156 void format(const remote_fn_ptr &fn, const sink &out) const noexcept {
157 if (fn.is_null()) {
158 out.write("(null)");
159 return;
160 }
161
162 if (mode == 'x' || mode == 'p' || !fn.resolver()) {
163 microfmt::format_to(out, "{:#x}", fn.address());
164 return;
165 }
166
167 auto sym_view = remote_symbol_view(fn.address(), fn.resolver(), fn.context(), true);
168 if (mode == '#') {
169 microfmt::format_to(out, "{:#}", sym_view);
170 } else {
171 microfmt::format_to(out, "{}", sym_view);
172 }
173 }
174};
175
176// ============================================================================
177// Diagnostics-Aware Remote Struct View
178// ============================================================================
179
185template <typename T> class RELOCO_POINTER remote_diag_ref {
186public:
195 constexpr remote_diag_ref(uintptr_t addr, address_space_ref space, symbol_resolver_ref resolver,
196 span<std::byte> scratch RELOCO_LIFETIMEBOUND,
197 symbol_resolution_context &symbol_context RELOCO_LIFETIMEBOUND) noexcept
198 : addr_(addr), space_(space), resolver_(resolver), scratch_(scratch), symbol_context_(&symbol_context) {}
199
204 [[nodiscard]] constexpr uintptr_t address() const noexcept { return addr_; }
209 [[nodiscard]] constexpr bool is_null() const noexcept { return addr_ == 0; }
210
215 [[nodiscard]] expected<T *, remote_load_error> load() const noexcept RELOCO_LIFETIMEBOUND {
216 if (addr_ == 0)
217 return unexpected(remote_load_error::null_address);
218 if (scratch_.size() < sizeof(T))
219 return unexpected(remote_load_error::scratch_too_small);
220 if (reinterpret_cast<uintptr_t>(scratch_.data()) % alignof(T) != 0)
221 return unexpected(remote_load_error::scratch_misaligned);
222 if (!space_)
223 return unexpected(remote_load_error::invalid_address_space);
224
225 auto *out_ptr = static_cast<T *>(static_cast<void *>(scratch_.data()));
226 if (!space_.read_bytes(addr_, out_ptr, sizeof(T)))
227 return unexpected(remote_load_error::read_failed);
228 return out_ptr;
229 }
230
235 [[nodiscard]] constexpr address_space_ref space() const noexcept { return space_; }
240 [[nodiscard]] constexpr symbol_resolver_ref resolver() const noexcept { return resolver_; }
241 [[nodiscard]] constexpr symbol_resolution_context &symbol_context() const noexcept RELOCO_LIFETIMEBOUND {
242 return *symbol_context_;
243 }
244
245private:
247 uintptr_t addr_{0};
249 address_space_ref space_{};
251 symbol_resolver_ref resolver_{};
253 span<std::byte> scratch_{};
255 value_ptr<symbol_resolution_context> symbol_context_{};
256};
257
265template <typename T> struct formatter<remote_diag_ref<T>> {
269 microfmt::string_view spec_{""};
270
275 constexpr void parse(format_parse_context &ctx) noexcept { spec_ = ctx.spec(); }
276
282 void format(const remote_diag_ref<T> &view, const sink &out) const noexcept {
283 if (view.is_null()) {
284 out.write("(null)");
285 return;
286 }
287
288 auto staged = view.load();
289 if (!staged) {
290 format_remote_fault(out, view.address(), view.resolver(), view.symbol_context());
291 return;
292 }
293
294 formatter<T> elem_fmt;
295 format_parse_context pctx(spec_);
296 elem_fmt.parse(pctx);
297 elem_fmt.format(**staged, out);
298 }
299};
300
301} // namespace microfmt
Type-erased remote address-space access and remote string/object views.
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Definition microfmt.hpp:881
Lazily-loaded remote object reference with diagnostics support.
Definition remote_diagnostics.hpp:185
constexpr remote_diag_ref(uintptr_t addr, address_space_ref space, symbol_resolver_ref resolver, span< std::byte > scratch RELOCO_LIFETIMEBOUND, symbol_resolution_context &symbol_context RELOCO_LIFETIMEBOUND) noexcept
Constructs a diagnostics-aware remote object reference.
Definition remote_diagnostics.hpp:195
constexpr bool is_null() const noexcept
Reports whether the reference points to address zero.
Definition remote_diagnostics.hpp:209
expected< T *, remote_load_error > load() const noexcept RELOCO_LIFETIMEBOUND
Loads the object into the scratch buffer.
Definition remote_diagnostics.hpp:215
constexpr address_space_ref space() const noexcept
Returns the address space handle.
Definition remote_diagnostics.hpp:235
constexpr symbol_resolution_context & symbol_context() const noexcept RELOCO_LIFETIMEBOUND
Definition remote_diagnostics.hpp:241
constexpr uintptr_t address() const noexcept
Returns the remote object address.
Definition remote_diagnostics.hpp:204
constexpr symbol_resolver_ref resolver() const noexcept
Returns the symbol resolver handle.
Definition remote_diagnostics.hpp:240
Symbolic view of a remote function / code pointer.
Definition remote_diagnostics.hpp:82
constexpr symbol_resolution_context & context() const noexcept RELOCO_LIFETIMEBOUND
Returns caller-owned symbol-resolution temporaries.
Definition remote_diagnostics.hpp:117
constexpr uintptr_t address() const noexcept
Returns the code address.
Definition remote_diagnostics.hpp:103
constexpr bool is_null() const noexcept
Reports whether the address is null.
Definition remote_diagnostics.hpp:108
constexpr symbol_resolver_ref resolver() const noexcept
Returns the symbol resolver handle.
Definition remote_diagnostics.hpp:113
constexpr remote_fn_ptr() noexcept=default
Constructs an empty (null) view.
Formattable view resolving and rendering a remote code address.
Definition symbol_resolver.hpp:327
Type-erased, two-word handle to a symbol resolver.
Definition symbol_resolver.hpp:132
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
void format_remote_fault(const sink &out, uintptr_t addr, symbol_resolver_ref resolver, symbol_resolution_context &context) noexcept
Formats a human-readable description of a faulting address.
Definition remote_diagnostics.hpp:32
constexpr auto as_demangled(microfmt::string_view sym) noexcept
Wraps a symbol for demangled formatting.
Definition demangle.hpp:523
void format(const remote_diag_ref< T > &view, const sink &out) const noexcept
Loads and renders the remote object (or a fault description).
Definition remote_diagnostics.hpp:282
constexpr void parse(format_parse_context &ctx) noexcept
Captures the specifier for the element formatter.
Definition remote_diagnostics.hpp:275
constexpr void parse(format_parse_context &ctx) noexcept
Parses the leading mode character.
Definition remote_diagnostics.hpp:144
void format(const remote_fn_ptr &fn, const sink &out) const noexcept
Renders the remote code pointer.
Definition remote_diagnostics.hpp:156
Definition microfmt.hpp:985
Caller-owned temporary storage for one symbol resolution.
Definition symbol_resolver.hpp:100
Type-erased symbol resolution for remote addresses and formattable symbol views.