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