LCOV - code coverage report
Current view: top level - wirestead/diagnostics - runtime_stats_counter.hpp (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 100.0 % 76 76
Test Date: 2026-08-30 10:35:09 Functions: 100.0 % 11 11
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 75.0 % 12 9

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright 2025 Jinwoo Sung
       3                 :             :  *
       4                 :             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :             :  * you may not use this file except in compliance with the License.
       6                 :             :  * You may obtain a copy of the License at
       7                 :             :  *
       8                 :             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :             :  *
      10                 :             :  * Unless required by applicable law or agreed to in writing, software
      11                 :             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :             :  * See the License for the specific language governing permissions and
      14                 :             :  * limitations under the License.
      15                 :             :  */
      16                 :             : 
      17                 :             : #pragma once
      18                 :             : 
      19                 :             : #include <atomic>
      20                 :             : #include <chrono>
      21                 :             : #include <cstddef>
      22                 :             : #include <cstdint>
      23                 :             : 
      24                 :             : #include "wirestead/diagnostics/logger.hpp"
      25                 :             : #include "wirestead/wrapper/runtime_stats.hpp"
      26                 :             : 
      27                 :             : namespace wirestead {
      28                 :             : namespace diagnostics {
      29                 :             : 
      30                 :             : struct RuntimeStatsCounters {
      31                 :             :   std::atomic<uint64_t> bytes_accepted{0};
      32                 :             :   std::atomic<uint64_t> messages_accepted{0};
      33                 :             : 
      34                 :             :   std::atomic<uint64_t> bytes_sent{0};
      35                 :             :   std::atomic<uint64_t> messages_sent{0};
      36                 :             : 
      37                 :             :   std::atomic<uint64_t> bytes_received{0};
      38                 :             :   std::atomic<uint64_t> messages_received{0};
      39                 :             :   // Steady-clock nanoseconds at the last receive; 0 means nothing has arrived.
      40                 :             :   std::atomic<int64_t> last_receive_ns{0};
      41                 :             : 
      42                 :             :   std::atomic<uint64_t> failed_sends{0};
      43                 :             : 
      44                 :             :   std::atomic<uint64_t> dropped_messages{0};
      45                 :             :   std::atomic<uint64_t> dropped_bytes{0};
      46                 :             : 
      47                 :             :   std::atomic<uint64_t> backpressure_events{0};
      48                 :             : 
      49                 :             :   std::atomic<size_t> max_queued_bytes{0};
      50                 :             : 
      51                 :             :   // Latched, deliberately not cleared by reset() - the warning below is meant
      52                 :             :   // to fire once in a process's life, not once per measurement window.
      53                 :             :   std::atomic<bool> drop_warned{false};
      54                 :             : 
      55                 :      429360 :   void record_accepted(size_t bytes) {
      56                 :      429360 :     messages_accepted.fetch_add(1, std::memory_order_relaxed);
      57                 :      429360 :     bytes_accepted.fetch_add(bytes, std::memory_order_relaxed);
      58                 :      429360 :   }
      59                 :             : 
      60                 :        1553 :   void record_sent(size_t bytes) {
      61                 :        1553 :     messages_sent.fetch_add(1, std::memory_order_relaxed);
      62                 :        1553 :     bytes_sent.fetch_add(bytes, std::memory_order_relaxed);
      63                 :        1553 :   }
      64                 :             : 
      65                 :        2944 :   void record_received(size_t bytes) {
      66                 :        2944 :     messages_received.fetch_add(1, std::memory_order_relaxed);
      67                 :        2944 :     bytes_received.fetch_add(bytes, std::memory_order_relaxed);
      68                 :             :     // Steady rather than system clock: this is only ever read as an age, and
      69                 :             :     // an age computed across a clock step is worse than no age at all.
      70                 :        2944 :     last_receive_ns.store(steady_now_ns(), std::memory_order_relaxed);
      71                 :        2944 :   }
      72                 :             : 
      73                 :       88573 :   void record_failed_send() { failed_sends.fetch_add(1, std::memory_order_relaxed); }
      74                 :             : 
      75                 :        3028 :   static int64_t steady_now_ns() {
      76                 :        6056 :     return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch())
      77                 :        3028 :         .count();
      78                 :             :   }
      79                 :             : 
      80                 :             :   // Every drop in the library routes through here. BestEffort discards by
      81                 :             :   // design and can do it hundreds of thousands of times a second, so the
      82                 :             :   // running total is the counter to watch - but a caller who never looks at it
      83                 :             :   // would otherwise lose data in complete silence, which is what makes the
      84                 :             :   // first one worth saying out loud exactly once.
      85                 :          24 :   void record_dropped(size_t messages, size_t bytes) {
      86                 :          24 :     dropped_messages.fetch_add(static_cast<uint64_t>(messages), std::memory_order_relaxed);
      87                 :          24 :     dropped_bytes.fetch_add(static_cast<uint64_t>(bytes), std::memory_order_relaxed);
      88         [ +  + ]:          24 :     if (!drop_warned.exchange(true, std::memory_order_relaxed)) {
      89                 :          13 :       WIRESTEAD_LOG_WARNING("runtime_stats", "record_dropped",
      90                 :             :                             "Dropping queued data (BestEffort). Watch RuntimeStats::dropped_bytes - "
      91                 :             :                             "on_backpressure() is not a reliable loss signal under this strategy.");
      92                 :             :     }
      93                 :          24 :   }
      94                 :             : 
      95                 :         139 :   void record_backpressure_event() { backpressure_events.fetch_add(1, std::memory_order_relaxed); }
      96                 :             : 
      97                 :      701588 :   void observe_queue(size_t queued) {
      98                 :      701588 :     size_t current = max_queued_bytes.load(std::memory_order_relaxed);
      99   [ +  +  -  +  :     1069364 :     while (queued > current && !max_queued_bytes.compare_exchange_weak(current, queued, std::memory_order_relaxed,
                   -  + ]
     100                 :             :                                                                        std::memory_order_relaxed)) {
     101                 :             :     }
     102                 :      701588 :   }
     103                 :             : 
     104                 :             :   // Folds a finished contributor's totals in, so a server's counters outlive
     105                 :             :   // the session that produced them. The cumulative fields add; max_queued_bytes
     106                 :             :   // takes the deeper of the two peaks, matching how a server aggregates it
     107                 :             :   // across live sessions.
     108                 :             :   //
     109                 :             :   // queued_bytes, pending_bytes and backpressure_active are deliberately left
     110                 :             :   // out: they describe a live queue, and a contributor being absorbed no longer
     111                 :             :   // has one.
     112                 :         132 :   void absorb(const wrapper::RuntimeStats& other) {
     113                 :         132 :     bytes_accepted.fetch_add(other.bytes_accepted, std::memory_order_relaxed);
     114                 :         132 :     messages_accepted.fetch_add(other.messages_accepted, std::memory_order_relaxed);
     115                 :         132 :     bytes_sent.fetch_add(other.bytes_sent, std::memory_order_relaxed);
     116                 :         132 :     messages_sent.fetch_add(other.messages_sent, std::memory_order_relaxed);
     117                 :         132 :     bytes_received.fetch_add(other.bytes_received, std::memory_order_relaxed);
     118                 :         132 :     messages_received.fetch_add(other.messages_received, std::memory_order_relaxed);
     119                 :         132 :     failed_sends.fetch_add(other.failed_sends, std::memory_order_relaxed);
     120                 :         132 :     dropped_messages.fetch_add(other.dropped_messages, std::memory_order_relaxed);
     121                 :         132 :     dropped_bytes.fetch_add(other.dropped_bytes, std::memory_order_relaxed);
     122                 :         132 :     backpressure_events.fetch_add(other.backpressure_events, std::memory_order_relaxed);
     123                 :         132 :     observe_queue(other.max_queued_bytes);
     124                 :         132 :   }
     125                 :             : 
     126                 :        4032 :   wrapper::RuntimeStats snapshot(size_t queued_bytes, size_t pending_bytes, bool backpressure_active) const {
     127                 :        4032 :     wrapper::RuntimeStats stats;
     128                 :        4032 :     stats.bytes_accepted = bytes_accepted.load(std::memory_order_relaxed);
     129                 :        4032 :     stats.messages_accepted = messages_accepted.load(std::memory_order_relaxed);
     130                 :        4032 :     stats.bytes_sent = bytes_sent.load(std::memory_order_relaxed);
     131                 :        4032 :     stats.messages_sent = messages_sent.load(std::memory_order_relaxed);
     132                 :        4032 :     stats.bytes_received = bytes_received.load(std::memory_order_relaxed);
     133                 :        4032 :     stats.messages_received = messages_received.load(std::memory_order_relaxed);
     134                 :        4032 :     stats.failed_sends = failed_sends.load(std::memory_order_relaxed);
     135                 :        4032 :     stats.dropped_messages = dropped_messages.load(std::memory_order_relaxed);
     136                 :        4032 :     stats.dropped_bytes = dropped_bytes.load(std::memory_order_relaxed);
     137                 :        4032 :     stats.backpressure_events = backpressure_events.load(std::memory_order_relaxed);
     138                 :        4032 :     stats.queued_bytes = queued_bytes;
     139                 :        4032 :     stats.pending_bytes = pending_bytes;
     140                 :        4032 :     stats.max_queued_bytes = max_queued_bytes.load(std::memory_order_relaxed);
     141                 :        4032 :     stats.backpressure_active = backpressure_active;
     142                 :        4032 :     const int64_t last = last_receive_ns.load(std::memory_order_relaxed);
     143         [ +  + ]:        4032 :     if (last != 0) {
     144                 :          84 :       const int64_t age = steady_now_ns() - last;
     145         [ +  - ]:          84 :       stats.last_receive_age_ms = static_cast<uint64_t>(age > 0 ? age / 1'000'000 : 0);
     146                 :             :     }
     147                 :        4032 :     return stats;
     148                 :             :   }
     149                 :             : 
     150                 :         177 :   void reset(size_t current_queued_bytes) {
     151                 :         177 :     bytes_accepted.store(0, std::memory_order_relaxed);
     152                 :         177 :     messages_accepted.store(0, std::memory_order_relaxed);
     153                 :         177 :     bytes_sent.store(0, std::memory_order_relaxed);
     154                 :         177 :     messages_sent.store(0, std::memory_order_relaxed);
     155                 :         177 :     bytes_received.store(0, std::memory_order_relaxed);
     156                 :         177 :     messages_received.store(0, std::memory_order_relaxed);
     157                 :         177 :     last_receive_ns.store(0, std::memory_order_relaxed);
     158                 :         177 :     failed_sends.store(0, std::memory_order_relaxed);
     159                 :         177 :     dropped_messages.store(0, std::memory_order_relaxed);
     160                 :         177 :     dropped_bytes.store(0, std::memory_order_relaxed);
     161                 :         177 :     backpressure_events.store(0, std::memory_order_relaxed);
     162                 :         177 :     max_queued_bytes.store(current_queued_bytes, std::memory_order_relaxed);
     163                 :         177 :   }
     164                 :             : };
     165                 :             : 
     166                 :             : }  // namespace diagnostics
     167                 :             : }  // namespace wirestead
        

Generated by: LCOV version 2.0-1