xref: /aosp_15_r20/frameworks/native/libs/debugstore/rust/src/core.rs (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
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 use super::event::Event;
17 use super::event_type::EventType;
18 use super::storage::Storage;
19 use crate::cxxffi::uptimeMillis;
20 use std::fmt;
21 use std::sync::{
22     atomic::{AtomicU64, Ordering},
23     LazyLock,
24 };
25 
26 //  Lazily initialized static instance of DebugStore.
27 static INSTANCE: LazyLock<DebugStore> = LazyLock::new(DebugStore::new);
28 
29 /// The `DebugStore` struct is responsible for managing debug events and data.
30 pub struct DebugStore {
31     /// Atomic counter for generating unique event IDs.
32     id_generator: AtomicU64,
33     /// Non-blocking storage for debug events.
34     event_store: Storage<Event, { DebugStore::DEFAULT_EVENT_LIMIT }>,
35 }
36 
37 impl DebugStore {
38     /// The default limit for the number of events that can be stored.
39     ///
40     /// This limit is used to initialize the storage for debug events.
41     const DEFAULT_EVENT_LIMIT: usize = 16;
42     /// A designated identifier used for events that cannot be closed.
43     ///
44     /// This ID is used for point/instantaneous events, or events do not have
45     ///  a distinct end.
46     const NON_CLOSABLE_ID: u64 = 0;
47     /// The version number for the encoding of debug store data.
48     ///
49     /// This constant is used as a part of the debug store's data format,
50     /// allowing for version tracking and compatibility checks.
51     const ENCODE_VERSION: u32 = 1;
52 
53     /// Creates a new instance of `DebugStore` with specified event limit and maximum delay.
new() -> Self54     fn new() -> Self {
55         Self { id_generator: AtomicU64::new(1), event_store: Storage::new() }
56     }
57 
58     /// Returns a shared instance of `DebugStore`.
59     ///
60     /// This method provides a singleton pattern access to `DebugStore`.
get_instance() -> &'static DebugStore61     pub fn get_instance() -> &'static DebugStore {
62         &INSTANCE
63     }
64 
65     /// Begins a new debug event with the given name and data.
66     ///
67     /// This method logs the start of a debug event, assigning it a unique ID and marking its start time.
68     /// - `name`: The name of the debug event.
69     /// - `data`: Associated data as key-value pairs.
70     /// - Returns: A unique ID for the debug event.
begin(&self, name: String, data: Vec<(String, String)>) -> u6471     pub fn begin(&self, name: String, data: Vec<(String, String)>) -> u64 {
72         let id = self.generate_id();
73         self.event_store.insert(Event::new(
74             id,
75             Some(name),
76             uptimeMillis(),
77             EventType::DurationStart,
78             data,
79         ));
80         id
81     }
82 
83     /// Records a debug event without a specific duration, with the given name and data.
84     ///
85     /// This method logs an instantaneous debug event, useful for events that don't have a duration but are significant.
86     /// - `name`: The name of the debug event.
87     /// - `data`: Associated data as key-value pairs.
record(&self, name: String, data: Vec<(String, String)>)88     pub fn record(&self, name: String, data: Vec<(String, String)>) {
89         self.event_store.insert(Event::new(
90             Self::NON_CLOSABLE_ID,
91             Some(name),
92             uptimeMillis(),
93             EventType::Point,
94             data,
95         ));
96     }
97 
98     /// Ends a debug event that was previously started with the given ID.
99     ///
100     /// This method marks the end of a debug event, completing its lifecycle.
101     /// - `id`: The unique ID of the debug event to end.
102     /// - `data`: Additional data to log at the end of the event.
end(&self, id: u64, data: Vec<(String, String)>)103     pub fn end(&self, id: u64, data: Vec<(String, String)>) {
104         if id != Self::NON_CLOSABLE_ID {
105             self.event_store.insert(Event::new(
106                 id,
107                 None,
108                 uptimeMillis(),
109                 EventType::DurationEnd,
110                 data,
111             ));
112         }
113     }
114 
generate_id(&self) -> u64115     fn generate_id(&self) -> u64 {
116         let mut id = self.id_generator.fetch_add(1, Ordering::Relaxed);
117         while id == Self::NON_CLOSABLE_ID {
118             id = self.id_generator.fetch_add(1, Ordering::Relaxed);
119         }
120         id
121     }
122 }
123 
124 impl fmt::Display for DebugStore {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result125     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126         let uptime_now = uptimeMillis();
127         write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?;
128 
129         write!(
130             f,
131             "{}",
132             self.event_store.fold(String::new(), |mut acc, event| {
133                 if !acc.is_empty() {
134                     acc.push_str("||");
135                 }
136                 acc.push_str(&event.to_string());
137                 acc
138             })
139         )
140     }
141 }
142 
143 #[cfg(test)]
144 mod tests {
145     use super::*;
146 
147     #[test]
test_begin_event()148     fn test_begin_event() {
149         let debug_store = DebugStore::new();
150         let _event_id = debug_store.begin("test_event".to_string(), vec![]);
151         let output = debug_store.to_string();
152         assert!(
153             output.contains("test_event"),
154             "The output should contain the event name 'test_event'"
155         );
156     }
157 
158     #[test]
test_unique_event_ids()159     fn test_unique_event_ids() {
160         let debug_store = DebugStore::new();
161         let event_id1 = debug_store.begin("event1".to_string(), vec![]);
162         let event_id2 = debug_store.begin("event2".to_string(), vec![]);
163         assert_ne!(event_id1, event_id2, "Event IDs should be unique");
164     }
165 
166     #[test]
test_end_event()167     fn test_end_event() {
168         let debug_store = DebugStore::new();
169         let event_id = debug_store.begin("test_event".to_string(), vec![]);
170         debug_store.end(event_id, vec![]);
171         let output = debug_store.to_string();
172 
173         let id_pattern = format!("ID:{},", event_id);
174         assert!(
175             output.contains("test_event"),
176             "The output should contain the event name 'test_event'"
177         );
178         assert_eq!(
179             output.matches(&id_pattern).count(),
180             2,
181             "The output should contain two events (start and end) associated with the given ID"
182         );
183     }
184 
185     #[test]
test_event_data_handling()186     fn test_event_data_handling() {
187         let debug_store = DebugStore::new();
188         debug_store
189             .record("data_event".to_string(), vec![("key".to_string(), "value".to_string())]);
190         let output = debug_store.to_string();
191         assert!(
192             output.contains("data_event"),
193             "The output should contain the event name 'data_event'"
194         );
195         assert!(
196             output.contains("key=value"),
197             "The output should contain the event data 'key=value'"
198         );
199     }
200 }
201