1#![cfg_attr(not(test), no_std)]
3#![deny(missing_docs)]
4#![deny(clippy::unwrap_used)]
5
6#[cfg(any(test, target_arch = "bpf"))]
9use aya_metrics_common::{Counter, Meter, BPF_COUNTERS_MAX_ENTRIES};
10
11#[cfg(target_arch = "bpf")]
14mod bpf {
15 use super::*;
16 use aya_ebpf::macros::map;
17 use aya_ebpf::maps::PerCpuArray;
18
19 #[map(name = "COUNTERS")]
21 pub static mut COUNTERS: PerCpuArray<u64> =
22 PerCpuArray::<u64>::with_max_entries(BPF_COUNTERS_MAX_ENTRIES as u32, 0);
23}
24
25#[cfg(target_arch = "bpf")]
28use bpf::*;
29
30#[cfg(any(test, target_arch = "bpf"))]
41#[inline(always)]
42pub fn counter<T: Counter>(counter: T, value: u64) {
43 if let Some(counter) = unsafe { COUNTERS.get_ptr_mut(Meter::index(&counter)) } {
47 unsafe { *counter += value };
48 }
49}
50
51#[cfg(test)]
56mod bpf_mocks {
57 use std::cell::Cell;
58
59 use super::BPF_COUNTERS_MAX_ENTRIES;
60
61 pub struct PerCpuArray<T, const N: usize> {
62 pub data: Cell<[T; N]>,
63 _t: core::marker::PhantomData<T>,
64 }
65
66 impl<const N: usize> PerCpuArray<u64, N> {
67 pub const fn new() -> PerCpuArray<u64, N> {
68 PerCpuArray {
69 data: Cell::new([0u64; N]),
70 _t: core::marker::PhantomData,
71 }
72 }
73
74 pub fn get_ptr_mut(&mut self, index: u32) -> Option<*mut u64> {
75 let data = self.data.get_mut();
76 let ptr = data as *mut u64;
77 let ptr_at = unsafe { ptr.offset(index.try_into().unwrap()) };
78 Some(ptr_at)
79 }
80 }
81
82 pub static mut COUNTERS: PerCpuArray<u64, BPF_COUNTERS_MAX_ENTRIES> =
83 PerCpuArray::<u64, BPF_COUNTERS_MAX_ENTRIES>::new();
84}
85
86#[cfg(test)]
88use bpf_mocks::*;
89
90#[cfg(test)]
91mod test {
92 use super::*;
93
94 #[derive(Copy, Clone, Debug)]
95 enum MockCounter {
96 Test1,
97 Test2,
98 }
99
100 impl Counter for MockCounter {
101 fn name(self) -> String {
102 match self {
103 MockCounter::Test1 => "test1".to_string(),
104 MockCounter::Test2 => "test2".to_string(),
105 }
106 }
107
108 fn index(&self) -> u32 {
109 match self {
110 MockCounter::Test1 => 0,
111 MockCounter::Test2 => BPF_COUNTERS_MAX_ENTRIES as u32 - 1,
112 }
113 }
114 }
115
116 #[test]
117 fn test_counter() {
118 let mut expected = [0u64; BPF_COUNTERS_MAX_ENTRIES];
119
120 let actual = unsafe { COUNTERS.data.get() };
121 assert_eq!(actual, expected);
122
123 counter(MockCounter::Test1, 1);
125 counter(MockCounter::Test2, 42);
126 let actual = unsafe { COUNTERS.data.get() };
127 *expected.first_mut().unwrap() = 1;
128 *expected.last_mut().unwrap() = 42;
129 assert_eq!(actual, expected);
130
131 counter(MockCounter::Test1, 0);
133 counter(MockCounter::Test2, 0);
134 let actual = unsafe { COUNTERS.data.get() };
135 assert_eq!(actual, expected);
136
137 counter(MockCounter::Test1, 1);
139 counter(MockCounter::Test2, 1);
140 let actual = unsafe { COUNTERS.data.get() };
141 *expected.first_mut().unwrap() = 2;
142 *expected.last_mut().unwrap() = 43;
143 assert_eq!(actual, expected);
144 }
145}