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