• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 3;
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         // Write the debug store header information
127         let uptime_now = uptimeMillis();
128         write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?;
129 
130         // Join events with a separator
131         write!(
132             f,
133             "{}",
134             self.event_store.rfold(String::new(), |mut acc, event| {
135                 if !acc.is_empty() {
136                     acc.push_str("||");
137                 }
138                 acc.push_str(&event.to_string());
139                 acc
140             })
141         )?;
142 
143         // Write the debug store footer
144         write!(f, ";;")
145     }
146 }
147 
148 #[cfg(test)]
149 mod tests {
150     use super::*;
151 
152     #[test]
test_begin_event()153     fn test_begin_event() {
154         let debug_store = DebugStore::new();
155         let _event_id = debug_store.begin("test_event".to_string(), vec![]);
156         let output = debug_store.to_string();
157         assert!(
158             output.contains("test_event"),
159             "The output should contain the event name 'test_event'"
160         );
161     }
162 
163     #[test]
test_unique_event_ids()164     fn test_unique_event_ids() {
165         let debug_store = DebugStore::new();
166         let event_id1 = debug_store.begin("event1".to_string(), vec![]);
167         let event_id2 = debug_store.begin("event2".to_string(), vec![]);
168         assert_ne!(event_id1, event_id2, "Event IDs should be unique");
169     }
170 
171     #[test]
test_end_event()172     fn test_end_event() {
173         let debug_store = DebugStore::new();
174         let event_id = debug_store.begin("test_event".to_string(), vec![]);
175         debug_store.end(event_id, vec![]);
176         let output = debug_store.to_string();
177 
178         let id_pattern = format!("ID:{},", event_id);
179         assert!(
180             output.contains("test_event"),
181             "The output should contain the event name 'test_event'"
182         );
183         assert_eq!(
184             output.matches(&id_pattern).count(),
185             2,
186             "The output should contain two events (start and end) associated with the given ID"
187         );
188     }
189 
190     #[test]
test_event_data_handling()191     fn test_event_data_handling() {
192         let debug_store = DebugStore::new();
193         debug_store
194             .record("data_event".to_string(), vec![("key".to_string(), "value".to_string())]);
195         let output = debug_store.to_string();
196         assert!(
197             output.contains("data_event"),
198             "The output should contain the event name 'data_event'"
199         );
200         assert!(
201             output.contains("key=value"),
202             "The output should contain the event data 'key=value'"
203         );
204     }
205 }
206