7#include "../microfmt.hpp"
8#include "../reloco.hpp"
45 RELOCO_BEGIN_UNSAFE_CODE
47 void *destination,
size_t size)
noexcept {
48 return Read(context.get(), space, index, destination, size);
58 RELOCO_BEGIN_UNSAFE_CODE
60 const void *source,
size_t size)
noexcept {
61 return Write(context.get(), space, index, source, size);
66template <
typename State, auto Read, auto Write>
72 RELOCO_BEGIN_UNSAFE_CODE
74 void *destination,
size_t size)
noexcept {
75 return Read(context.get(), space, index, destination, size);
81 RELOCO_BEGIN_UNSAFE_CODE
83 const void *source,
size_t size)
noexcept {
84 return Write(context.get(), space, index, source, size);
93template <
typename Tag>
96 std::void_t<decltype(register_context_traits<Tag>::read_register(
97 std::declval<value_ref<const typename register_context_traits<Tag>::context_type>>(),
98 std::declval<address_space_ref>(), std::declval<uint32_t>(), std::declval<void *>(), std::declval<size_t>()))>>
103template <
typename Tag>
105 std::void_t<decltype(register_context_traits<Tag>::write_register(
106 std::declval<value_ref<typename register_context_traits<Tag>::context_type>>(),
107 std::declval<address_space_ref>(), std::declval<uint32_t>(),
108 std::declval<const void *>(), std::declval<size_t>()))>> : std::true_type {};
123 std::enable_if_t<std::is_convertible_v<State *, typename Traits::context_type *>,
int> = 0>
126 span<std::
byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
127 : state_(&state), mutable_state_(&state), vtable_(&s_vtable<Tag>), space_(space), scratch_(scratch) {}
129 template <
typename Tag,
typename State,
typename Traits = register_context_traits<Tag>,
130 std::enable_if_t<std::is_convertible_v<const State *, const
typename Traits::context_type *>,
int> = 0>
133 span<std::byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
134 : state_(&state), vtable_(&s_vtable<Tag>), space_(space), scratch_(scratch) {}
136 template <
typename Tag,
typename State, std::enable_if_t<!std::is_lvalue_reference_v<State>,
int> = 0>
139 [[nodiscard]]
constexpr bool is_null() const noexcept {
140 return !state_ || vtable_ ==
nullptr || (vtable_->read_register ==
nullptr && vtable_->write_register ==
nullptr);
143 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
return !is_null(); }
148 template <
typename T> [[nodiscard]]
bool read(uint32_t dwarf_reg_index, T &out_value)
const noexcept {
149 if (vtable_ ==
nullptr || vtable_->read_register ==
nullptr)
151 return vtable_->read_register(state_.get(), space_, dwarf_reg_index, &out_value,
sizeof(T));
157 [[nodiscard]]
bool read_raw(uint32_t dwarf_reg_index,
void *dest,
size_t size)
const noexcept {
158 if (vtable_ ==
nullptr || vtable_->read_register ==
nullptr)
160 return vtable_->read_register(state_.get(), space_, dwarf_reg_index, dest, size);
166 template <
typename T> [[nodiscard]]
bool write(uint32_t dwarf_reg_index,
const T &value)
const noexcept {
167 if (vtable_ ==
nullptr || vtable_->write_register ==
nullptr)
171 return vtable_->write_register(mutable_state_.get(), space_, dwarf_reg_index, &value,
sizeof(T));
177 [[nodiscard]]
bool write_raw(uint32_t dwarf_reg_index,
const void *src,
size_t size)
const noexcept {
178 if (vtable_ ==
nullptr || vtable_->write_register ==
nullptr)
182 return vtable_->write_register(mutable_state_.get(), space_, dwarf_reg_index, src, size);
186 [[nodiscard]]
constexpr span<std::byte>
scratch() const noexcept RELOCO_LIFETIMEBOUND {
return scratch_; }
190 bool (*read_register)(
const void *,
address_space_ref, uint32_t,
void *, size_t)
noexcept =
nullptr;
191 bool (*write_register)(
void *,
address_space_ref, uint32_t,
const void *, size_t)
noexcept =
nullptr;
194 template <
typename Tag> [[nodiscard]]
static constexpr auto read_entry() noexcept {
195 if constexpr (detail::has_register_context_read<Tag>::value) {
196 return +[](
const void *state, address_space_ref space, uint32_t index,
void *destination,
size_t size)
noexcept {
197 using context_type =
typename register_context_traits<Tag>::context_type;
198 const auto &context = *
static_cast<const context_type *
>(state);
199 return register_context_traits<Tag>::read_register(value_ref<const context_type>(context), space, index,
203 return static_cast<bool (*)(
const void *, address_space_ref, uint32_t,
void *,
size_t) noexcept
>(
nullptr);
207 template <
typename Tag> [[nodiscard]]
static constexpr auto write_entry() noexcept {
208 if constexpr (detail::has_register_context_write<Tag>::value) {
209 return +[](
void *state, address_space_ref space, uint32_t index,
const void *source,
size_t size)
noexcept {
210 using context_type =
typename register_context_traits<Tag>::context_type;
211 auto &context = *
static_cast<context_type *
>(state);
212 return register_context_traits<Tag>::write_register(value_ref<context_type>(context), space, index, source,
216 return static_cast<bool (*)(
void *, address_space_ref, uint32_t,
const void *,
size_t) noexcept
>(
nullptr);
220 template <
typename Tag>
static constexpr vtable s_vtable{read_entry<Tag>(), write_entry<Tag>()};
222 value_ptr<const void> state_{};
223 value_ptr<void> mutable_state_{};
224 const vtable *vtable_{
nullptr};
225 address_space_ref space_{};
226 span<std::byte> scratch_{};
229template <auto Read,
typename State>
230[[nodiscard]]
constexpr register_context_ref
232 span<std::byte> scratch RELOCO_LIFETIMEBOUND)
noexcept {
233 using state_type = std::remove_const_t<State>;
238template <auto Read, auto Write,
typename State>
239[[nodiscard]]
constexpr register_context_ref
241 span<std::byte> scratch RELOCO_LIFETIMEBOUND)
noexcept {
242 using state_type = std::remove_const_t<State>;
256 span<std::byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
257 : context_(std::move(context)), space_(space), scratch_(scratch) {}
259 [[nodiscard]]
constexpr value_ref<context_type>
context() &
noexcept RELOCO_LIFETIMEBOUND {
260 return value_ref<context_type>(context_);
263 [[nodiscard]]
constexpr value_ref<const context_type>
context() const & noexcept RELOCO_LIFETIMEBOUND {
264 return value_ref<const context_type>(context_);
275 value_ref<context_type>
context() && =
delete;
276 value_ref<const context_type>
context() const && = delete;
283 span<std::
byte> scratch_;
288template <
typename T>
struct register_member_pointer_traits;
290template <
typename Value,
typename State>
struct register_member_pointer_traits<Value State::*> {
291 using state_type = State;
292 using value_type = Value;
295template <
typename Field,
typename State,
typename =
void>
struct has_mutable_register_field_get : std::false_type {};
297template <
typename Field,
typename State>
298struct has_mutable_register_field_get<
299 Field, State, std::void_t<decltype(Field::get(std::declval<State &>(), std::declval<uint32_t>()))>>
300 : std::is_convertible<decltype(Field::get(std::declval<State &>(), std::declval<uint32_t>())),
301 typename Field::value_type *> {};
303template <
typename Field,
typename State,
typename =
void>
struct has_register_field_read : std::false_type {};
305template <
typename Field,
typename State>
306struct has_register_field_read<
308 std::void_t<decltype(Field::read(std::declval<const State &>(), std::declval<address_space_ref>(),
309 std::declval<uint32_t>(), std::declval<typename Field::value_type &>()))>>
310 : std::is_same<decltype(Field::read(std::declval<const State &>(), std::declval<address_space_ref>(),
311 std::declval<uint32_t>(), std::declval<typename Field::value_type &>())),
314template <
typename Field,
typename State,
typename =
void>
struct has_register_field_write : std::false_type {};
316template <
typename Field,
typename State>
317struct has_register_field_write<
319 std::void_t<decltype(Field::write(std::declval<State &>(), std::declval<address_space_ref>(),
320 std::declval<uint32_t>(), std::declval<const typename Field::value_type &>()))>>
321 : std::is_same<decltype(Field::write(std::declval<State &>(), std::declval<address_space_ref>(),
322 std::declval<uint32_t>(), std::declval<const typename Field::value_type &>())),
338 using member_traits = detail::register_member_pointer_traits<
decltype(Member)>;
342 static_assert(
sizeof...(Registers) != 0,
"a register member field must bind at least one register");
343 static_assert(std::is_trivially_copyable<value_type>::value,
"register fields must be trivially copyable");
345 [[nodiscard]]
static constexpr bool matches(uint32_t index)
noexcept {
return ((index == Registers) || ...); }
348 return &(state.*Member);
373 static_assert(
sizeof...(Registers) != 0,
"a selected register field must bind at least one register");
374 static_assert(std::is_trivially_copyable<value_type>::value,
"register fields must be trivially copyable");
375 static_assert(std::is_nothrow_default_constructible<Selector>::value,
376 "selector must be nothrow default constructible");
377 static_assert(std::is_nothrow_invocable_r<const value_type *, Selector, const state_type &, uint32_t>::value,
378 "selector must be noexcept and return a value_type pointer");
380 [[nodiscard]]
static constexpr bool matches(uint32_t index)
noexcept {
return ((index == Registers) || ...); }
383 return Selector{}(state, index);
386 [[nodiscard]]
static constexpr auto get(
state_type &state, uint32_t index)
noexcept
387 ->
decltype(Selector{}(state, index)) {
388 return Selector{}(state, index);
406template <
typename State,
typename Value,
auto ReadCallback,
auto WriteCallback, uint32_t... Registers>
411 static_assert(
sizeof...(Registers) != 0,
"a callback register field must bind at least one register");
412 static_assert(std::is_trivially_copyable<value_type>::value,
"register fields must be trivially copyable");
413 static_assert(std::is_nothrow_default_constructible<value_type>::value,
414 "callback register fields must be nothrow default "
418 "read callback must be noexcept and return bool");
419 static_assert(WriteCallback ==
nullptr ||
422 "write callback must be nullptr or noexcept and return bool");
424 [[nodiscard]]
static constexpr bool matches(uint32_t index)
noexcept {
return ((index == Registers) || ...); }
428 return ReadCallback(state, space, index, value);
431 template <auto Callback = WriteCallback, typename std::enable_if<Callback != nullptr, int>::type = 0>
434 return Callback(state, space, index, value);
459 static_assert(
sizeof...(FieldTraits) != 0,
"at least one register field trait is required");
460 static_assert((std::is_same<typename FieldTraits::state_type, State>::value && ...),
461 "all register field traits must bind the wrapper state type");
462 static_assert((std::is_trivially_copyable<typename FieldTraits::value_type>::value && ...),
463 "register field values must be trivially copyable");
467 span<std::byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS,
469 : state_(state), mutable_state_(&state), space_(space), scratch_(scratch), fallback_(fallback) {}
473 span<std::byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS,
475 : state_(state), space_(space), scratch_(scratch), fallback_(fallback) {}
491 [[nodiscard]]
explicit constexpr operator register_context_ref() &
noexcept RELOCO_LIFETIMEBOUND {
return ref(); }
499 RELOCO_BEGIN_UNSAFE_CODE
500 template <
typename Field>
501 [[nodiscard]]
bool try_read(uint32_t index,
void *destination,
size_t size,
bool &handled)
const noexcept {
502 if (handled || !Field::matches(index))
506 if (destination ==
nullptr || size !=
sizeof(
typename Field::value_type))
509 if constexpr (detail::has_register_field_read<Field, State>::value) {
510 typename Field::value_type value{};
511 if (!Field::read(*state_, space_, index, value))
513 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
514 std::memcpy(destination, &value,
sizeof(value));
515 RELOCO_END_UNSAFE_BUFFER_USAGE;
518 const auto *value = Field::get(*state_, index);
521 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
522 std::memcpy(destination, value,
sizeof(*value));
523 RELOCO_END_UNSAFE_BUFFER_USAGE;
531 RELOCO_BEGIN_UNSAFE_CODE
532 template <
typename Field>
533 [[nodiscard]]
bool try_write(uint32_t index,
const void *source,
size_t size,
bool &handled)
noexcept {
534 if (handled || !Field::matches(index))
538 if (!mutable_state_ || source ==
nullptr || size !=
sizeof(
typename Field::value_type))
541 if constexpr (detail::has_register_field_write<Field, State>::value) {
542 typename Field::value_type value{};
543 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
544 std::memcpy(&value, source,
sizeof(value));
545 RELOCO_END_UNSAFE_BUFFER_USAGE;
546 return Field::write(*mutable_state_, space_, index, value);
547 }
else if constexpr (detail::has_mutable_register_field_get<Field, State>::value) {
548 auto *value = Field::get(*mutable_state_, index);
551 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
552 std::memcpy(value, source,
sizeof(*value));
553 RELOCO_END_UNSAFE_BUFFER_USAGE;
561 [[nodiscard]]
bool read(uint32_t index,
void *destination,
size_t size)
const noexcept {
562 bool handled =
false;
564 using expand =
int[];
566 expand{0, (handled ? 0 : (result = try_read<FieldTraits>(index, destination, size, handled), 0))...});
569 return fallback_.read_raw(index, destination, size);
572 [[nodiscard]]
bool write(uint32_t index,
const void *source,
size_t size)
noexcept {
573 bool handled =
false;
575 using expand =
int[];
576 static_cast<void>(expand{0, (handled ? 0 : (result = try_write<FieldTraits>(index, source, size, handled), 0))...});
579 return fallback_.write_raw(index, source, size);
584 value_ref<const State> state_;
585 value_ptr<State> mutable_state_;
587 span<std::byte> scratch_;
591template <typename State, typename... FieldTraits>
597 RELOCO_BEGIN_UNSAFE_CODE
599 size_t size)
noexcept {
600 return context->read(index, destination, size);
606 RELOCO_BEGIN_UNSAFE_CODE
608 size_t size)
noexcept {
609 return context->write(index, source, size);
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
Typed owner that exposes a C/C++ register structure as a register_context_ref.
Definition register_context.hpp:457
register_context_ref_with(const State &&, address_space_ref, span< std::byte >, register_context_ref={})=delete
constexpr register_context_ref_with(const State &state RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, register_context_ref fallback={}) noexcept
Definition register_context.hpp:471
constexpr register_context_ref_with(State &state RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, register_context_ref fallback={}) noexcept
Definition register_context.hpp:465
register_context_ref_with(State &&, address_space_ref, span< std::byte >, register_context_ref={})=delete
constexpr register_context_ref ref() &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased reference borrowing this wrapper.
Definition register_context.hpp:486
register_context_ref ref() &&=delete
Zero-allocation, type-erased handle for inspecting and modifying CPU registers from a frame context.
Definition register_context.hpp:118
constexpr span< std::byte > scratch() const noexcept RELOCO_LIFETIMEBOUND
Definition register_context.hpp:186
bool read_raw(uint32_t dwarf_reg_index, void *dest, size_t size) const noexcept
Reads raw register bytes into a buffer.
Definition register_context.hpp:157
bool write_raw(uint32_t dwarf_reg_index, const void *src, size_t size) const noexcept
Writes raw bytes from a source buffer into a register.
Definition register_context.hpp:177
constexpr register_context_ref(Tag, const State &state RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition register_context.hpp:131
constexpr register_context_ref() noexcept=default
constexpr address_space_ref space() const noexcept
Definition register_context.hpp:185
constexpr bool is_null() const noexcept
Definition register_context.hpp:139
bool write(uint32_t dwarf_reg_index, const T &value) const noexcept
Writes a typed value into a register by its DWARF index.
Definition register_context.hpp:166
constexpr register_context_ref(Tag, State &&, address_space_ref, span< std::byte >)=delete
bool read(uint32_t dwarf_reg_index, T &out_value) const noexcept
Reads a register directly into a typed integer or float variable.
Definition register_context.hpp:148
Typed owner for a register-context traits specialization.
Definition register_context.hpp:250
constexpr value_ref< const context_type > context() const &noexcept RELOCO_LIFETIMEBOUND
Definition register_context.hpp:263
value_ref< context_type > context() &&=delete
typename traits_type::context_type context_type
Definition register_context.hpp:253
constexpr register_context_ref ref() const &noexcept RELOCO_LIFETIMEBOUND
Definition register_context.hpp:271
constexpr register_context_ref ref() &noexcept RELOCO_LIFETIMEBOUND
Definition register_context.hpp:267
value_ref< const context_type > context() const &&=delete
constexpr value_ref< context_type > context() &noexcept RELOCO_LIFETIMEBOUND
Definition register_context.hpp:259
constexpr register_context(context_type context, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition register_context.hpp:255
#define MICROFMT_API_CLASS
Definition compat.hpp:181
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
constexpr register_context_ref make_register_context_ref(State &state RELOCO_LIFETIMEBOUND, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND) noexcept
Definition register_context.hpp:240
constexpr register_context_ref make_read_only_register_context_ref(State &state RELOCO_LIFETIMEBOUND, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND) noexcept
Definition register_context.hpp:231
Definition register_context.hpp:91
Definition register_context.hpp:101
Definition register_context.hpp:110
Definition register_context.hpp:34
Definition register_context.hpp:28
Definition register_context.hpp:32
Compile-time register field queried through typed callbacks.
Definition register_context.hpp:407
State state_type
Definition register_context.hpp:408
static constexpr bool matches(uint32_t index) noexcept
Definition register_context.hpp:424
static bool write(state_type &state, address_space_ref space, uint32_t index, const value_type &value) noexcept
Definition register_context.hpp:432
static bool read(const state_type &state, address_space_ref space, uint32_t index, value_type &value) noexcept
Definition register_context.hpp:426
Value value_type
Definition register_context.hpp:409
RELOCO_END_UNSAFE_CODE static RELOCO_BEGIN_UNSAFE_CODE bool write_register(value_ref< context_type > context, address_space_ref, uint32_t index, const void *source, size_t size) noexcept
Definition register_context.hpp:607
static RELOCO_BEGIN_UNSAFE_CODE bool read_register(value_ref< const context_type > context, address_space_ref, uint32_t index, void *destination, size_t size) noexcept
Definition register_context.hpp:598
State context_type
Definition register_context.hpp:37
static RELOCO_BEGIN_UNSAFE_CODE bool read_register(value_ref< const context_type > context, address_space_ref space, uint32_t index, void *destination, size_t size) noexcept
Definition register_context.hpp:46
State context_type
Definition register_context.hpp:41
RELOCO_END_UNSAFE_CODE static RELOCO_BEGIN_UNSAFE_CODE bool write_register(value_ref< context_type > context, address_space_ref space, uint32_t index, const void *source, size_t size) noexcept
Definition register_context.hpp:82
static RELOCO_BEGIN_UNSAFE_CODE bool read_register(value_ref< const context_type > context, address_space_ref space, uint32_t index, void *destination, size_t size) noexcept
Definition register_context.hpp:73
State context_type
Definition register_context.hpp:68
State context_type
Definition register_context.hpp:54
static RELOCO_BEGIN_UNSAFE_CODE bool write_register(value_ref< context_type > context, address_space_ref space, uint32_t index, const void *source, size_t size) noexcept
Definition register_context.hpp:59
Static customization point for a register-context provider.
Definition register_context.hpp:26
Compile-time register-field trait backed by a pointer to member.
Definition register_context.hpp:337
static constexpr bool matches(uint32_t index) noexcept
Definition register_context.hpp:345
typename member_traits::value_type value_type
Definition register_context.hpp:340
typename member_traits::state_type state_type
Definition register_context.hpp:339
static constexpr const value_type * get(const state_type &state, uint32_t) noexcept
Definition register_context.hpp:347
static constexpr value_type * get(state_type &state, uint32_t) noexcept
Definition register_context.hpp:351
detail::register_member_pointer_traits< decltype(Member)> member_traits
Definition register_context.hpp:338
Compile-time field trait with state-dependent member selection.
Definition register_context.hpp:369
State state_type
Definition register_context.hpp:370
Value value_type
Definition register_context.hpp:371
static constexpr const value_type * get(const state_type &state, uint32_t index) noexcept
Definition register_context.hpp:382
static constexpr auto get(state_type &state, uint32_t index) noexcept -> decltype(Selector{}(state, index))
Definition register_context.hpp:386
static constexpr bool matches(uint32_t index) noexcept
Definition register_context.hpp:380
Definition register_context.hpp:30