starnix_core/task/
tracing.rs1use crate::task::{Kernel, PidTable};
6use starnix_logging::{log_debug, log_error, log_info};
7use starnix_sync::RwLock;
8use starnix_uapi::{pid_t, tid_t};
9use std::collections::HashMap;
10use std::sync::{Arc, Weak};
11use zx::Koid;
12
13#[derive(Debug, Clone)]
14pub struct KoidPair {
15 pub process: Option<Koid>,
16 pub thread: Option<Koid>,
17}
18
19pub type PidToKoidMap = Arc<RwLock<HashMap<tid_t, KoidPair>>>;
21
22pub struct TracePerformanceEventManager {
23 map: PidToKoidMap,
27
28 local_map: HashMap<tid_t, KoidPair>,
32
33 weak_kernel: Weak<Kernel>,
37}
38
39impl Drop for TracePerformanceEventManager {
40 fn drop(&mut self) {
41 self.stop();
43 }
44}
45
46impl TracePerformanceEventManager {
47 pub fn new() -> Self {
48 Self { map: PidToKoidMap::default(), local_map: HashMap::new(), weak_kernel: Weak::new() }
49 }
50
51 pub fn start(&mut self, kernel: &Arc<Kernel>) {
63 if self.weak_kernel.upgrade().is_some() {
67 log_error!(
68 "TracePerformanceEventManager has already been started. Re-initializing mapping"
69 );
70 }
71
72 self.weak_kernel = Arc::downgrade(kernel);
73 *kernel.pid_to_koid_mapping.write() = Some(self.map.clone());
74
75 let kernel_pids = kernel.pids.read();
76 let existing_pid_map = Self::read_existing_pid_map(&*kernel_pids);
77 self.map.write().extend(existing_pid_map);
78 }
79
80 pub fn stop(&mut self) {
83 if let Some(kernel) = self.weak_kernel.upgrade() {
84 log_info!("Stopping trace pid mapping. Notifier set to None.");
85 *kernel.pid_to_koid_mapping.write() = None;
86 self.weak_kernel = Weak::new();
87 }
88 }
89
90 pub fn clear(&mut self) {
93 self.map.write().clear();
94 self.local_map.clear();
95 }
96
97 fn get_mapping(&mut self, pid: pid_t) -> &KoidPair {
101 if self.local_map.is_empty() {
102 let shared_map = self.map.read().clone();
103 self.local_map.extend(shared_map);
104 }
105
106 if self.local_map.contains_key(&pid) {
107 return self.local_map.get(&pid).expect("pid should always have a KoidPair.");
108 }
109
110 let shared_map = self.map.read();
114 if let Some(koid_pair) = shared_map.get(&pid) {
115 self.local_map.insert(pid, koid_pair.clone());
116 return self.local_map.get(&pid).expect("pid should always have a KoidPair.");
117 }
118
119 unreachable!("all pids including {pid} should have mappings")
120 }
121
122 pub fn map_pid_to_koid(&mut self, pid: pid_t) -> Koid {
124 self.get_mapping(pid).process.expect("all pids should have a process koid.")
125 }
126
127 pub fn map_tid_to_koid(&mut self, tid: tid_t) -> Koid {
129 self.get_mapping(tid).thread.expect("all tids should have a thread koid.")
130 }
131
132 fn read_existing_pid_map(pid_table: &PidTable) -> HashMap<tid_t, KoidPair> {
134 let mut pid_map = HashMap::new();
135
136 let ids = pid_table.task_ids();
137 for tid in &ids {
138 let task_ref = pid_table.get_task(*tid);
139 let task = task_ref.upgrade().expect("Empty mapping for {tid}.");
140 let live = task.live().expect("tid {tid} is not live.");
141 let pair = KoidPair {
142 process: task.thread_group().get_process_koid().ok(),
143 thread: live.thread.read().as_ref().and_then(|t| t.koid().ok()),
144 };
145 if pair.process.is_some() || pair.thread.is_some() {
147 pid_map.insert(*tid, pair);
148 }
149 }
150
151 log_debug!("Initialized {} pid mappings. From {} ids", pid_map.len(), ids.len());
152 pid_map
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use crate::testing::{create_task, spawn_kernel_and_run};
160 use futures::channel::oneshot;
161
162 #[fuchsia::test]
163 async fn test_initialize_pid_map() {
164 let (sender, receiver) = oneshot::channel();
165 spawn_kernel_and_run(async move |locked, current_task| {
166 let kernel = current_task.kernel();
167 let pid = current_task.task.tid;
168 let tkoid = current_task.live().thread.read().as_ref().and_then(|t| t.koid().ok());
169 let pkoid = current_task.thread_group().get_process_koid().ok();
170
171 let _another_current = create_task(locked, &kernel, "another-task");
172
173 let pid_map = TracePerformanceEventManager::read_existing_pid_map(&*kernel.pids.read());
174
175 assert!(tkoid.is_some());
176 assert_eq!(pid_map.len(), 2, "Expected 2 entries in pid_map got {pid_map:?}");
177 assert!(pid_map.contains_key(&pid));
178
179 let pair = pid_map.get(&pid).unwrap();
180 assert_eq!(pair.process, pkoid);
181 assert_eq!(pair.thread, tkoid);
182 sender.send(()).unwrap();
183 })
184 .await;
185 receiver.await.unwrap();
186 }
187
188 #[fuchsia::test]
189 fn test_mapping() {
190 let mut manager = TracePerformanceEventManager::new();
191 let mut map = HashMap::new();
192 map.insert(
193 1,
194 KoidPair { process: Some(Koid::from_raw(101)), thread: Some(Koid::from_raw(201)) },
195 );
196 map.insert(2, KoidPair { process: Some(Koid::from_raw(102)), thread: None });
197 manager.map.write().extend(map);
198
199 assert_eq!(manager.map_pid_to_koid(1), Koid::from_raw(101));
200 assert_eq!(manager.map_tid_to_koid(1), Koid::from_raw(201));
201 assert_eq!(manager.map_pid_to_koid(2), Koid::from_raw(102));
202 }
203
204 #[fuchsia::test]
205 #[should_panic]
206 fn test_unmapped_tid() {
207 let mut manager = TracePerformanceEventManager::new();
208
209 manager.map_tid_to_koid(2);
210 }
211
212 #[fuchsia::test]
213 async fn test_lifecycle() {
214 let (sender, receiver) = oneshot::channel();
215 spawn_kernel_and_run(async move |locked, current_task| {
216 let kernel = current_task.kernel();
217 let mut manager = TracePerformanceEventManager::new();
218
219 manager.start(&kernel);
220
221 let pid_map = manager.map.read().clone();
222 assert_eq!(pid_map.len(), 1, "Expected 1 entry in pid_map got {pid_map:?}");
223
224 let another_current = create_task(locked, &kernel, "another-task");
226 let test_thread = another_current
227 .thread_group()
228 .process
229 .create_thread(b"my-new-test-thread")
230 .expect("test thread");
231
232 {
233 let another_current_live = another_current.live();
234 let mut thread = another_current_live.thread.write();
235 *thread = Some(Arc::new(test_thread));
236 drop(thread);
237 }
238
239 let pid_map = manager.map.read().clone();
240 let pid_dump = format!("{pid_map:?}");
241 assert_eq!(pid_map.len(), 1, "Expected 1 entry in pid_map got {pid_dump}");
242
243 another_current.record_pid_koid_mapping();
245
246 let pid_map = manager.map.read().clone();
248 let pid_dump = format!("{pid_map:?}");
249 assert_eq!(pid_map.len(), 2, "Expected 2 entries in pid_map got {pid_dump}");
250
251 let _ = manager.map_pid_to_koid(another_current.task.get_pid());
253 let _ = manager.map_pid_to_koid(another_current.task.get_tid());
254
255 manager.stop();
256
257 manager.clear();
258 assert!(manager.map.read().is_empty());
259 sender.send(()).unwrap();
260 })
261 .await;
262 receiver.await.unwrap();
263 }
264}