GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 14 / 0 / 14
Functions: 100.0% 1 / 0 / 1
Branches: 100.0% 14 / 0 / 14

include/DetourModKit/async_logger_config.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_ASYNC_LOGGER_CONFIG_HPP
2 #define DETOURMODKIT_ASYNC_LOGGER_CONFIG_HPP
3
4 /**
5 * @file async_logger_config.hpp
6 * @brief Lightweight, public async-logger configuration surface.
7 * @details Carries only the control-plane configuration types plus their default constants, and none of the
8 * async-logger plumbing, so ModInfo can embed an AsyncLoggerConfig by value without forcing every consumer
9 * translation unit to compile the queue and pool.
10 */
11
12 #include <chrono>
13 #include <cstddef>
14 #include <cstdint>
15 #include <string>
16 #include <string_view>
17
18 namespace DetourModKit
19 {
20 /// Default strftime-style timestamp format for the async sink.
21 inline constexpr std::string_view DEFAULT_ASYNC_TIMESTAMP_FORMAT{"%Y-%m-%d %H:%M:%S"};
22 /// Default capacity (slot count) of the bounded MPMC message queue.
23 inline constexpr std::size_t DEFAULT_QUEUE_CAPACITY = 8192;
24 /// Default number of messages the writer drains per write batch.
25 inline constexpr std::size_t DEFAULT_BATCH_SIZE = 64;
26 /// Default interval between periodic writer flushes.
27 inline constexpr auto DEFAULT_FLUSH_INTERVAL = std::chrono::milliseconds(100);
28 /// Default spin-backoff iteration count before a producer yields/parks.
29 inline constexpr std::size_t DEFAULT_SPIN_BACKOFF_ITERATIONS = 32;
30
31 /**
32 * @enum OverflowPolicy
33 * @brief Defines the action that AsyncLogger::enqueue takes when the bounded queue is full.
34 * @warning On a callback path, use only DropNewest. For callback-safe use, keep the new record within
35 * @ref LOG_INLINE_MESSAGE_SIZE. The other policies have these hazards:
36 * - DropOldest can take the string-pool lock when it evicts an older long record.
37 * - Block parks the producer.
38 * - SyncFallback writes synchronously.
39 * @note The drop policies change acceptance, not producer latency. Under a saturating burst, DropNewest
40 * accepted under 3 percent of the records and DropOldest accepted almost all, at the same p50
41 * (`docs/analysis/hot_path_bench_v4/`).
42 */
43 enum class OverflowPolicy : std::uint8_t
44 {
45 /// Drops the new message when the queue is full and performs no wait at the overflow step.
46 DropNewest,
47 /**
48 * @brief Evicts the oldest queued message and performs no queue-capacity wait.
49 * @note The eviction can take the string-pool lock when the old record exceeds the inline bound.
50 */
51 DropOldest,
52 /// Parks the producer until space frees or block_timeout_ms elapses and can stall the caller.
53 Block,
54 /**
55 * @brief Applies the synchronous fallback policy.
56 * @details The policy has these effects:
57 * - It writes on the producer thread.
58 * - It takes the sink lock.
59 * - It performs I/O.
60 */
61 SyncFallback
62 };
63
64 /**
65 * @struct AsyncLoggerConfig
66 * @brief Configuration for the async logger.
67 * @details The default queue holds DEFAULT_QUEUE_CAPACITY (8192) slots; each slot embeds a LogMessage with a
68 * LOG_INLINE_MESSAGE_SIZE (512) byte inline buffer, so the ring buffer's resident footprint is on the
69 * order of a few MiB at the default capacity (queue_capacity must stay a power of two). The overflow
70 * string pool (for messages larger than the inline buffer) is a separate, lazily grown allocation behind
71 * the AsyncLogger pimpl (detail::StringPool). Shrink queue_capacity for memory-constrained hosts.
72 */
73 struct AsyncLoggerConfig
74 {
75 std::size_t queue_capacity = DEFAULT_QUEUE_CAPACITY;
76 /// Records the writer drains per write batch; clamped to @ref queue_capacity, which is all the queue can hold.
77 std::size_t batch_size = DEFAULT_BATCH_SIZE;
78 std::chrono::milliseconds flush_interval = DEFAULT_FLUSH_INTERVAL;
79 OverflowPolicy overflow_policy = OverflowPolicy::DropOldest;
80 std::size_t spin_backoff_iterations = DEFAULT_SPIN_BACKOFF_ITERATIONS;
81 std::chrono::milliseconds block_timeout_ms{16};
82 std::size_t block_max_spin_iterations{1000};
83 /**
84 * @brief strftime-style date/time format for the async sink; empty selects
85 * @ref DEFAULT_ASYNC_TIMESTAMP_FORMAT.
86 * @details The empty default keeps value construction allocation-free. The async writer materializes the
87 * effective format in its owned configuration before starting; Logger::enable_async_mode replaces it
88 * with the Logger's format so both sinks stay identical. The writer appends the millisecond fraction.
89 */
90 std::string timestamp_format{};
91
92 /**
93 * @brief Reports whether every field satisfies its documented bound.
94 * @return true when queue_capacity is a power of two of at least 2 and each duration or count is positive.
95 */
96 417 [[nodiscard]] constexpr bool validate() const noexcept
97 {
98
4/4
✓ Branch 2 → 3 taken 415 times.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 414 times.
417 if (queue_capacity < 2 || (queue_capacity & (queue_capacity - 1)) != 0)
99 3 return false;
100
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 413 times.
414 if (batch_size == 0)
101 1 return false;
102
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 411 times.
413 if (flush_interval.count() <= 0)
103 2 return false;
104
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 410 times.
411 if (spin_backoff_iterations == 0)
105 1 return false;
106
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 408 times.
410 if (block_timeout_ms.count() <= 0)
107 2 return false;
108
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 407 times.
408 if (block_max_spin_iterations == 0)
109 1 return false;
110 407 return true;
111 }
112 };
113
114 // Compile-time validation: the default queue capacity must be a power of 2 and at least 2.
115 static_assert(
116 DEFAULT_QUEUE_CAPACITY >= 2 && (DEFAULT_QUEUE_CAPACITY & (DEFAULT_QUEUE_CAPACITY - 1)) == 0,
117 "DEFAULT_QUEUE_CAPACITY must be a power of 2 and at least 2"
118 );
119
120 } // namespace DetourModKit
121
122 #endif // DETOURMODKIT_ASYNC_LOGGER_CONFIG_HPP
123