src/internal/hook_emission.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef DETOURMODKIT_INTERNAL_HOOK_EMISSION_HPP | ||
| 2 | #define DETOURMODKIT_INTERNAL_HOOK_EMISSION_HPP | ||
| 3 | |||
| 4 | /** | ||
| 5 | * @file internal/hook_emission.hpp | ||
| 6 | * @brief The shared hook lifecycle snapshot and emission sequence for the hook sibling TUs. | ||
| 7 | * @details src/hook.cpp and src/hook_toggle.cpp publish lifecycle events through this one sequence, so the | ||
| 8 | * population-then-emit order and the owned-name lifetime rule are stated exactly once. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "DetourModKit/diagnostics.hpp" | ||
| 12 | |||
| 13 | #include "internal/diagnostics_population.hpp" | ||
| 14 | |||
| 15 | #include <cstdint> | ||
| 16 | #include <string> | ||
| 17 | #include <string_view> | ||
| 18 | |||
| 19 | namespace DetourModKit::detail | ||
| 20 | { | ||
| 21 | struct RemovalPopulationState | ||
| 22 | { | ||
| 23 | bool was_active{false}; | ||
| 24 | bool remains_live{false}; | ||
| 25 | }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * @brief Owns the identity for one enable or disable lifecycle event. | ||
| 29 | * @details The owned name stays valid if a subscriber destroys the hook before synchronous emission ends. A | ||
| 30 | * failed name copy preserves the transition and publishes an empty name (HookLifecycleName.*). | ||
| 31 | */ | ||
| 32 | struct LifecycleSnapshot | ||
| 33 | { | ||
| 34 | std::string name; | ||
| 35 | std::uint64_t ledger_id{0}; | ||
| 36 | diagnostics::HookKind kind{diagnostics::HookKind::Inline}; | ||
| 37 | }; | ||
| 38 | |||
| 39 | [[nodiscard]] inline LifecycleSnapshot | ||
| 40 | 18139 | snapshot_lifecycle(const std::string &name, std::uint64_t ledger_id, bool is_inline) noexcept | |
| 41 | { | ||
| 42 | 18139 | LifecycleSnapshot snapshot; | |
| 43 | 18139 | snapshot.ledger_id = ledger_id; | |
| 44 | 18139 | snapshot.kind = is_inline ? diagnostics::HookKind::Inline : diagnostics::HookKind::Mid; | |
| 45 | try | ||
| 46 | { | ||
| 47 | 18139 | snapshot.name = name; | |
| 48 | } | ||
| 49 | 1 | catch (...) | |
| 50 | { | ||
| 51 | 1 | } | |
| 52 | 18139 | return snapshot; | |
| 53 | } | ||
| 54 | |||
| 55 | /// Emits one hook lifecycle event and contains a subscriber failure. It moves no population tally. | ||
| 56 | 56214 | inline void emit_lifecycle_event( | |
| 57 | std::string_view name, | ||
| 58 | std::uint64_t ledger_id, | ||
| 59 | diagnostics::HookKind kind, | ||
| 60 | diagnostics::HookTransition transition | ||
| 61 | ) noexcept | ||
| 62 | { | ||
| 63 | try | ||
| 64 | { | ||
| 65 | 56214 | diagnostics::hook_lifecycle().emit_safe( | |
| 66 | 56214 | diagnostics::HookLifecycleEvent{ | |
| 67 | .name = name, | ||
| 68 | .ledger_id = ledger_id, | ||
| 69 | .kind = kind, | ||
| 70 | .transition = transition, | ||
| 71 | } | ||
| 72 | ); | ||
| 73 | } | ||
| 74 | ✗ | catch (...) | |
| 75 | { | ||
| 76 | ✗ | } | |
| 77 | 56216 | } | |
| 78 | |||
| 79 | /** | ||
| 80 | * @brief Updates the live population tally, then emits the associated hook lifecycle event. | ||
| 81 | * @param removal Population state for a Removed event. Teardown must capture @c was_active before it forces its | ||
| 82 | * status to Disabled. Set @c remains_live when the detour stays reachable. A residue keeps | ||
| 83 | * the target tracked but leaves the live population. | ||
| 84 | * @details The tally moves first so a subscriber that calls collect() observes the completed transition. | ||
| 85 | */ | ||
| 86 | 47759 | inline void emit_lifecycle( | |
| 87 | std::string_view name, | ||
| 88 | std::uint64_t ledger_id, | ||
| 89 | diagnostics::HookKind kind, | ||
| 90 | diagnostics::HookTransition transition, | ||
| 91 | RemovalPopulationState removal = {} | ||
| 92 | ) noexcept | ||
| 93 | { | ||
| 94 | 47759 | switch (transition) | |
| 95 | { | ||
| 96 | 12706 | case diagnostics::HookTransition::Created: | |
| 97 | 12706 | DetourModKit::detail::hook_population::record_created(kind == diagnostics::HookKind::Vmt); | |
| 98 | 12706 | break; | |
| 99 | 18139 | case diagnostics::HookTransition::Enabled: | |
| 100 | case diagnostics::HookTransition::Disabled: | ||
| 101 | // The status store updates the count while it still holds the call gate. This code runs after unlock. | ||
| 102 | // Otherwise, two toggles can commit +1/-1 in an order opposite their serialized transitions. | ||
| 103 | 18139 | break; | |
| 104 | 16915 | case diagnostics::HookTransition::Removed: | |
| 105 | 16915 | if (!removal.remains_live) | |
| 106 | { | ||
| 107 | 16899 | DetourModKit::detail::hook_population::record_removed(removal.was_active); | |
| 108 | } | ||
| 109 | 16915 | break; | |
| 110 | } | ||
| 111 | 47759 | emit_lifecycle_event(name, ledger_id, kind, transition); | |
| 112 | 47755 | } | |
| 113 | } // namespace DetourModKit::detail | ||
| 114 | |||
| 115 | #endif // DETOURMODKIT_INTERNAL_HOOK_EMISSION_HPP | ||
| 116 |