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