sozu_lib/protocol/mux/
debug.rs1use std::collections::VecDeque;
9
10use kawa::ParsingPhase;
11use mio::Token;
12use sozu_command::{ready::Ready, state::ClusterId};
13
14use super::{BackendConnectionError, MuxResult, Readiness, StreamState};
15
16#[cfg(debug_assertions)]
19pub(super) const DEBUG_HISTORY_CAPACITY: usize = 512;
20
21#[derive(Default)]
22pub struct DebugHistory {
23 pub events: VecDeque<DebugEvent>,
26 pub is_interesting: bool,
27}
28impl DebugHistory {
29 pub fn new() -> Self {
30 Self::default()
31 }
32 pub fn push(&mut self, _event: DebugEvent) {
33 #[cfg(debug_assertions)]
34 {
35 if self.events.capacity() == 0 {
36 self.events.reserve_exact(DEBUG_HISTORY_CAPACITY);
37 }
38 if self.events.len() >= DEBUG_HISTORY_CAPACITY {
39 self.events.pop_front();
40 }
41 self.events.push_back(_event);
42 }
43 }
44 pub fn set_interesting(&mut self, _interesting: bool) {
45 #[cfg(debug_assertions)]
46 {
47 self.is_interesting = _interesting;
48 }
49 }
50 pub fn is_interesting(&self) -> bool {
51 #[cfg(debug_assertions)]
52 {
53 self.is_interesting
54 }
55 #[cfg(not(debug_assertions))]
56 {
57 false
58 }
59 }
60}
61
62#[derive(Debug)]
63pub enum DebugEvent {
64 EV(Token, Ready),
65 ReadyTimestamp(usize),
66 LoopStart,
67 LoopIteration(i32),
68 SR(Token, MuxResult, Readiness),
69 SW(Token, MuxResult, Readiness),
70 CW(Token, MuxResult, Readiness),
71 CR(Token, MuxResult, Readiness),
72 CC(usize, StreamState),
73 CCS(Token, ClusterId),
74 CCF(usize, BackendConnectionError),
75 CH(Token, Readiness),
76 S(u32, usize, ParsingPhase, usize, usize),
77 Str(String),
78 StreamEvent(usize, usize),
79 SocketIO(usize, usize, usize),
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::test_allocations::allocations;
86
87 #[test]
91 fn creating_a_debug_history_allocates_nothing() {
92 let before = allocations();
93 let history = DebugHistory::new();
94 let after = allocations();
95 assert_eq!(after - before, 0, "DebugHistory::new must not allocate");
96 assert_eq!(history.events.capacity(), 0);
97 }
98
99 #[cfg(debug_assertions)]
102 #[test]
103 fn debug_history_keeps_the_newest_events_up_to_its_capacity() {
104 let mut history = DebugHistory::new();
105 let before = allocations();
106 for i in 0..=DEBUG_HISTORY_CAPACITY as i32 {
107 history.push(DebugEvent::LoopIteration(i));
108 }
109 let after = allocations();
110 assert_eq!(
111 after - before,
112 1,
113 "the ring is allocated once, at full capacity"
114 );
115 assert!(history.events.capacity() >= DEBUG_HISTORY_CAPACITY);
116 assert_eq!(history.events.len(), DEBUG_HISTORY_CAPACITY);
117 assert!(matches!(
118 history.events.front(),
119 Some(DebugEvent::LoopIteration(1))
120 ));
121 assert!(matches!(
122 history.events.back(),
123 Some(DebugEvent::LoopIteration(i)) if *i == DEBUG_HISTORY_CAPACITY as i32
124 ));
125 history.set_interesting(true);
126 assert!(history.is_interesting());
127 let dump = format!("{:?}", history.events);
128 assert!(dump.starts_with("[LoopIteration(1), "), "{dump}");
129 }
130
131 #[cfg(not(debug_assertions))]
133 #[test]
134 fn release_history_records_nothing() {
135 let mut history = DebugHistory::new();
136 let before = allocations();
137 history.push(DebugEvent::LoopIteration(0));
138 history.set_interesting(true);
139 let after = allocations();
140 assert_eq!(after - before, 0);
141 assert!(history.events.is_empty());
142 assert!(!history.is_interesting());
143 }
144}