Skip to main content

omena_parser/
instrumentation.rs

1//! Lightweight instrumentation for parser materialization.
2//!
3//! The counters in this module support regression gates that verify parser
4//! consumers do not accidentally rematerialize token streams.
5
6use std::cell::Cell;
7#[cfg(any(test, feature = "test-support"))]
8use std::cell::RefCell;
9#[cfg(test)]
10use std::sync::atomic::{AtomicUsize, Ordering};
11
12use serde::Serialize;
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub struct OmenaParserLexInstrumentationSnapshotV0 {
17    pub lex_invocation_count: u64,
18    pub lex_token_count: u64,
19}
20
21#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct OmenaParserParseInstrumentationSnapshotV0 {
24    pub parse_invocation_count: u64,
25    pub parse_token_count: u64,
26}
27
28#[cfg(any(test, feature = "test-support"))]
29#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct OmenaParserFactCollectionInstrumentationSnapshotV0 {
32    pub traversal_entry_count: u64,
33    pub families: Vec<&'static str>,
34    pub registered_family_count: u64,
35    pub registered_families: Vec<&'static str>,
36}
37
38thread_local! {
39    static LEX_INSTRUMENTATION: Cell<Option<OmenaParserLexInstrumentationSnapshotV0>> =
40        const { Cell::new(None) };
41    static PARSE_INSTRUMENTATION: Cell<Option<OmenaParserParseInstrumentationSnapshotV0>> =
42        const { Cell::new(None) };
43    #[cfg(test)]
44    static SYNTAX_ROOT_MATERIALIZATION_COUNT: AtomicUsize =
45        const { AtomicUsize::new(0) };
46    #[cfg(feature = "test-support")]
47    static CLOSED_WORLD_BUNDLE_CONSTRUCTION_COUNT: Cell<usize> = const { Cell::new(0) };
48    #[cfg(any(test, feature = "test-support"))]
49    static FACT_COLLECTION_INSTRUMENTATION:
50        RefCell<Option<OmenaParserFactCollectionInstrumentationSnapshotV0>> =
51            const { RefCell::new(None) };
52}
53
54pub fn with_omena_parser_lex_instrumentation<T>(
55    operation: impl FnOnce() -> T,
56) -> (T, OmenaParserLexInstrumentationSnapshotV0) {
57    LEX_INSTRUMENTATION.with(|instrumentation| {
58        let previous =
59            instrumentation.replace(Some(OmenaParserLexInstrumentationSnapshotV0::default()));
60        let value = operation();
61        let snapshot = instrumentation
62            .replace(previous)
63            .unwrap_or_else(OmenaParserLexInstrumentationSnapshotV0::default);
64        (value, snapshot)
65    })
66}
67
68pub fn with_omena_parser_parse_instrumentation<T>(
69    operation: impl FnOnce() -> T,
70) -> (T, OmenaParserParseInstrumentationSnapshotV0) {
71    PARSE_INSTRUMENTATION.with(|instrumentation| {
72        let previous =
73            instrumentation.replace(Some(OmenaParserParseInstrumentationSnapshotV0::default()));
74        let value = operation();
75        let snapshot = instrumentation
76            .replace(previous)
77            .unwrap_or_else(OmenaParserParseInstrumentationSnapshotV0::default);
78        (value, snapshot)
79    })
80}
81
82pub(crate) fn record_omena_parser_lex_materialization(token_count: usize) {
83    LEX_INSTRUMENTATION.with(|instrumentation| {
84        if let Some(mut snapshot) = instrumentation.get() {
85            snapshot.lex_invocation_count += 1;
86            snapshot.lex_token_count += token_count as u64;
87            instrumentation.set(Some(snapshot));
88        }
89    });
90}
91
92pub(crate) fn record_omena_parser_parse_materialization(token_count: usize) {
93    PARSE_INSTRUMENTATION.with(|instrumentation| {
94        if let Some(mut snapshot) = instrumentation.get() {
95            snapshot.parse_invocation_count += 1;
96            snapshot.parse_token_count += token_count as u64;
97            instrumentation.set(Some(snapshot));
98        }
99    });
100}
101
102#[cfg(any(test, feature = "test-support"))]
103pub fn with_omena_parser_fact_collection_instrumentation<T>(
104    operation: impl FnOnce() -> T,
105) -> (T, OmenaParserFactCollectionInstrumentationSnapshotV0) {
106    FACT_COLLECTION_INSTRUMENTATION.with(|instrumentation| {
107        let previous = instrumentation.replace(Some(
108            OmenaParserFactCollectionInstrumentationSnapshotV0::default(),
109        ));
110        let value = operation();
111        let snapshot = instrumentation
112            .replace(previous)
113            .unwrap_or_else(OmenaParserFactCollectionInstrumentationSnapshotV0::default);
114        (value, snapshot)
115    })
116}
117
118#[cfg(any(test, feature = "test-support"))]
119pub(crate) fn record_omena_parser_fact_collection_traversal(family: &'static str) {
120    FACT_COLLECTION_INSTRUMENTATION.with(|instrumentation| {
121        let mut instrumentation = instrumentation.borrow_mut();
122        if let Some(snapshot) = instrumentation.as_mut() {
123            snapshot.traversal_entry_count += 1;
124            snapshot.families.push(family);
125        }
126    });
127}
128
129#[cfg(any(test, feature = "test-support"))]
130pub(crate) fn record_omena_parser_fact_collection_registrations(families: &'static [&'static str]) {
131    FACT_COLLECTION_INSTRUMENTATION.with(|instrumentation| {
132        let mut instrumentation = instrumentation.borrow_mut();
133        if let Some(snapshot) = instrumentation.as_mut() {
134            snapshot.registered_family_count = families.len() as u64;
135            snapshot.registered_families = families.to_vec();
136        }
137    });
138}
139
140#[cfg(not(any(test, feature = "test-support")))]
141pub(crate) fn record_omena_parser_fact_collection_registrations(
142    _families: &'static [&'static str],
143) {
144}
145
146#[cfg(not(any(test, feature = "test-support")))]
147pub(crate) fn record_omena_parser_fact_collection_traversal(_family: &'static str) {}
148
149#[cfg(feature = "test-support")]
150pub(crate) fn record_closed_world_bundle_construction_for_test() {
151    CLOSED_WORLD_BUNDLE_CONSTRUCTION_COUNT.with(|counter| counter.set(counter.get() + 1));
152}
153
154#[cfg(feature = "test-support")]
155pub fn reset_closed_world_bundle_construction_count_for_test() {
156    CLOSED_WORLD_BUNDLE_CONSTRUCTION_COUNT.with(|counter| counter.set(0));
157}
158
159#[cfg(feature = "test-support")]
160pub fn closed_world_bundle_construction_count_for_test() -> usize {
161    CLOSED_WORLD_BUNDLE_CONSTRUCTION_COUNT.with(Cell::get)
162}
163
164#[cfg(test)]
165pub(crate) fn record_omena_parser_syntax_root_materialization() {
166    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.fetch_add(1, Ordering::SeqCst));
167}
168
169#[cfg(not(test))]
170pub(crate) fn record_omena_parser_syntax_root_materialization() {}
171
172#[cfg(test)]
173pub(crate) fn reset_omena_parser_syntax_root_materialization_count() {
174    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.store(0, Ordering::SeqCst));
175}
176
177#[cfg(test)]
178pub(crate) fn omena_parser_syntax_root_materialization_count() -> usize {
179    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.load(Ordering::SeqCst))
180}