1use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use crate::perf::lockless_ring_buffer::LocklessRingBuffer;
9use crate::task::Kernel;
10use crate::vfs::OutputBuffer;
11use fuchsia_rcu::RcuDroppableArc;
12use starnix_logging::log_error;
13use starnix_uapi::errors::Errno;
14use zerocopy::native_endian::{I32, U16, U32, U64};
15use zerocopy::{Immutable, IntoBytes, Unaligned};
16use zx::BootTimeline;
17
18const DEFAULT_RING_BUFFER_SIZE_BYTES: usize = 2097152;
21
22const FTRACE_PRINT_ID: U16 = U16::new(5);
24
25const DROPPED_PAGES: &str = "dropped_pages";
27
28const MAX_TIME_DELTA_NANOS: u32 = (1 << 27) - 1;
29
30#[repr(C)]
31#[derive(Debug, Default, IntoBytes, Immutable, Unaligned)]
32struct PrintEventHeader {
33 common_type: U16,
34 common_flags: u8,
35 common_preempt_count: u8,
36 common_pid: I32,
37 ip: U64,
38}
39
40#[repr(C)]
41#[derive(Debug, IntoBytes, Immutable, Unaligned)]
42struct PrintEvent {
43 header: PrintEventHeader,
44}
45
46impl PrintEvent {
47 fn new(pid: i32) -> Self {
48 Self {
49 header: PrintEventHeader {
50 common_type: FTRACE_PRINT_ID,
51 common_pid: I32::new(pid),
52 ..Default::default()
54 },
55 }
56 }
57
58 fn size(&self) -> usize {
59 std::mem::size_of::<PrintEventHeader>()
60 }
61}
62
63#[repr(C)]
64#[derive(Debug, Default, IntoBytes, PartialEq, Immutable, Unaligned)]
65struct TraceEventHeader {
66 time_delta: U32,
70
71 data: U32,
74}
75
76impl TraceEventHeader {
77 fn new(size: usize) -> Self {
78 let size = (std::mem::size_of::<u32>() + size) as u32;
81 Self { time_delta: U32::new(0), data: U32::new(size) }
82 }
83
84 fn set_time_delta(&mut self, nanos: u32) {
85 let saturated_nanos = nanos.min(MAX_TIME_DELTA_NANOS);
89 self.time_delta = U32::new(saturated_nanos << 5);
91 }
92}
93
94#[repr(C)]
95#[derive(Debug, IntoBytes, Immutable, Unaligned)]
96pub struct TraceEvent {
97 header: TraceEventHeader, event: PrintEvent,
105}
106
107impl TraceEvent {
108 pub fn new(pid: i32, data_len: usize) -> Self {
109 let event = PrintEvent::new(pid);
110 let header = TraceEventHeader::new(event.size() + data_len + 1);
112 Self { header, event }
113 }
114
115 fn size(&self) -> usize {
116 std::mem::size_of::<u32>() + self.header.data.get() as usize
118 }
119}
120
121pub struct TraceEventQueue {
123 ring_buffer: RcuDroppableArc<LocklessRingBuffer>,
125
126 pub async_id_read: fuchsia_trace::Id,
128
129 pub async_id_write: fuchsia_trace::Id,
131
132 pub cpu_id: u32,
134}
135
136const STATIC_READ_TRACK_NAMES: [&str; 8] = [
137 "Tracefs read 0",
138 "Tracefs read 1",
139 "Tracefs read 2",
140 "Tracefs read 3",
141 "Tracefs read 4",
142 "Tracefs read 5",
143 "Tracefs read 6",
144 "Tracefs read 7",
145];
146
147const STATIC_WRITE_TRACK_NAMES: [&str; 8] = [
148 "Tracefs write 0",
149 "Tracefs write 1",
150 "Tracefs write 2",
151 "Tracefs write 3",
152 "Tracefs write 4",
153 "Tracefs write 5",
154 "Tracefs write 6",
155 "Tracefs write 7",
156];
157
158impl<'a> TraceEventQueue {
159 pub(crate) fn new(cpu_id: u32) -> Result<Self, Errno> {
160 let async_id_read = fuchsia_trace::Id::new();
161 let async_id_write = fuchsia_trace::Id::new();
162 let ring_buffer = Arc::new(
163 LocklessRingBuffer::new(DEFAULT_RING_BUFFER_SIZE_BYTES, true, async_id_write)
164 .map_err(|_| starnix_uapi::errno!(ENOMEM))?,
165 );
166 ring_buffer.disable()?;
167
168 Ok(Self {
169 ring_buffer: RcuDroppableArc::new(ring_buffer),
170 async_id_read,
171 async_id_write,
172 cpu_id,
173 })
174 }
175
176 pub fn read_track_name(&self) -> std::borrow::Cow<'static, str> {
177 if let Some(&name) = STATIC_READ_TRACK_NAMES.get(self.cpu_id as usize) {
178 std::borrow::Cow::Borrowed(name)
179 } else {
180 std::borrow::Cow::Owned(format!("Tracefs read {}", self.cpu_id))
181 }
182 }
183
184 pub fn write_track_name(&self) -> std::borrow::Cow<'static, str> {
185 if let Some(&name) = STATIC_WRITE_TRACK_NAMES.get(self.cpu_id as usize) {
186 std::borrow::Cow::Borrowed(name)
187 } else {
188 std::borrow::Cow::Owned(format!("Tracefs write {}", self.cpu_id))
189 }
190 }
191
192 fn enable(&self) -> Result<zx::BootInstant, Errno> {
193 self.ring_buffer.read().enable()
194 }
195
196 fn disable(&self) -> Result<u64, Errno> {
199 self.ring_buffer.read().disable()
200 }
201
202 pub fn read(&self, buf: &mut dyn OutputBuffer) -> Result<usize, Errno> {
207 self.ring_buffer.read().read(buf)
208 }
209
210 pub fn push_event(
217 &self,
218 mut event: TraceEvent,
219 data: &[u8],
220 ) -> Result<zx::Duration<BootTimeline>, Errno> {
221 let size = event.size();
222 let ring_buffer = self.ring_buffer.read();
223
224 let (res, _timestamp, delta) = match ring_buffer.reserve(size) {
225 Ok(res) => res,
226 Err(e) if e == starnix_uapi::errno!(EINVAL) => {
227 log_error!("Invalid reservation size: {}", size);
228 return Err(starnix_uapi::errno!(EINVAL));
229 }
230 Err(e) if e == starnix_uapi::errno!(ENOSPC) => {
231 log_error!("Ring buffer full, dropping event of size: {}", size);
232 return Err(starnix_uapi::errno!(ENOSPC));
233 }
234 Err(e) => return Err(e),
235 };
236
237 let nanos = delta.into_nanos().try_into().unwrap_or(u32::MAX);
238 event.header.set_time_delta(nanos);
239
240 let bytes = event.as_bytes();
241 res.write_at(0, bytes);
242 res.write_at(bytes.len(), data);
243 res.write_at(bytes.len() + data.len(), b"\n");
244
245 ring_buffer.commit(res);
246
247 Ok(delta)
248 }
249}
250
251pub struct TraceEventQueueList {
252 pub queues: Vec<Arc<TraceEventQueue>>,
253 tracing_enabled: Arc<AtomicBool>,
254 tracefs_node: fuchsia_inspect::Node,
255}
256
257impl TraceEventQueueList {
258 pub fn from(kernel: &Kernel) -> Arc<Self> {
259 kernel.expando.get_or_init(|| {
260 let num_cpus = zx::system_get_num_cpus();
261 let tracing_enabled = Arc::new(AtomicBool::new(false));
262 let tracefs_node = kernel.inspect_node.create_child("tracefs");
263
264 let mut queues = vec![];
265 for cpu in 0..num_cpus {
266 let queue = Arc::new(TraceEventQueue::new(cpu as u32).expect("create queue"));
267 queues.push(queue);
268 }
269 Self { queues, tracing_enabled, tracefs_node }
270 })
271 }
272
273 pub fn is_enabled(&self) -> bool {
274 self.tracing_enabled.load(Ordering::Acquire)
275 }
276
277 pub fn enable(&self) -> Result<(), Errno> {
278 let mut first_error = None;
279 for queue in &self.queues {
280 if let Err(e) = queue.enable() {
281 if first_error.is_none() {
282 first_error = Some(e);
283 }
284 }
285 }
286 if let Some(e) = first_error {
287 return Err(e);
288 }
289 self.tracing_enabled.store(true, Ordering::Release);
290 Ok(())
291 }
292
293 pub fn disable(&self) -> Result<(), Errno> {
294 self.tracing_enabled.store(false, Ordering::Release);
296 let mut first_error = None;
297 let mut total_dropped = 0;
298 for queue in &self.queues {
299 match queue.disable() {
300 Ok(dropped) => total_dropped += dropped,
301 Err(e) => {
302 if first_error.is_none() {
303 first_error = Some(e);
304 }
305 }
306 }
307 }
308
309 self.tracefs_node.record_uint(DROPPED_PAGES, total_dropped);
310
311 if let Some(e) = first_error {
312 return Err(e);
313 }
314 Ok(())
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::{DEFAULT_RING_BUFFER_SIZE_BYTES, LocklessRingBuffer, TraceEvent, TraceEventQueue};
321 use crate::vfs::OutputBuffer;
322 use crate::vfs::buffers::VecOutputBuffer;
323
324 use starnix_types::PAGE_SIZE;
325 use starnix_uapi::error;
326
327 #[fuchsia::test]
328 fn trace_event_queue_empty_errors() {
329 let queue = TraceEventQueue::new(0).unwrap();
330
331 let mut buffer = VecOutputBuffer::new(*PAGE_SIZE as usize);
332 assert_eq!(queue.read(&mut buffer), error!(EAGAIN));
333
334 let data = b"B|1234|slice_name";
335 let event = TraceEvent::new(1234, data.len());
336 assert_eq!(queue.push_event(event, data), error!(ENOMEM));
337 }
338
339 #[fuchsia::test]
340 fn read_empty_queue() {
341 let queue = TraceEventQueue::new(0).expect("create queue");
342 let mut buffer = VecOutputBuffer::new(*PAGE_SIZE as usize);
343 assert_eq!(queue.read(&mut buffer), error!(EAGAIN));
344 }
345
346 #[fuchsia::test]
347 fn enable_disable_queue() {
348 let queue = TraceEventQueue::new(0).expect("create queue");
349 assert!(!queue.ring_buffer.read().is_enabled());
350
351 assert!(queue.enable().is_ok());
353 assert_eq!(queue.ring_buffer.read().size_bytes(), DEFAULT_RING_BUFFER_SIZE_BYTES);
354
355 let data = b"B|1234|slice_name";
357 let event = TraceEvent::new(1234, data.len());
358 let result = queue.push_event(event, data);
359
360 assert!(result.is_ok());
361 assert_eq!(result.as_ref().unwrap().into_nanos(), 0);
362
363 assert!(queue.disable().is_ok());
365 assert!(!queue.ring_buffer.read().is_enabled());
366 }
367
368 #[fuchsia::test]
369 fn create_trace_event() {
370 let event = TraceEvent::new(1234, b"B|1234|slice_name".len());
372 let event_size = event.size();
373 assert_eq!(event_size, 42);
374 }
375
376 #[fuchsia::test]
378 fn single_trace_event_fails_read() {
379 let queue = TraceEventQueue::new(0).expect("create queue");
380 queue.enable().expect("enable queue");
381 let data = b"B|1234|slice_name";
383 let event = TraceEvent::new(1234, data.len());
384
385 let result = queue.push_event(event, data);
387 assert!(result.is_ok());
388 assert_eq!(result.ok().expect("delta").into_nanos(), 0);
389
390 let mut buffer = VecOutputBuffer::new(*PAGE_SIZE as usize);
391 assert_eq!(queue.read(&mut buffer), error!(EAGAIN));
392 }
393
394 #[fuchsia::test]
395 fn page_overflow() {
396 let queue = TraceEventQueue::new(0).expect("create queue");
397 let queue_start_timestamp = queue.enable().expect("enable queue");
398
399 let pid = 1234;
400 let data = b"B|1234|loooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\
401 ooooooooooooooooooooooooooooooooooooooooooooooooooooooooongevent";
402 let expected_event = TraceEvent::new(pid, data.len());
403 assert_eq!(expected_event.size(), 155);
404
405 let events_per_page =
406 (*PAGE_SIZE as usize - LocklessRingBuffer::PAGE_HEADER_SIZE) / expected_event.size();
407
408 for i in 0..=events_per_page {
410 let event = TraceEvent::new(pid, data.len());
411 let result = queue.push_event(event, data);
412 assert!(result.is_ok());
413 let delta = result.ok().expect("delta").into_nanos();
414 if i == 0 || i == events_per_page {
418 assert_eq!(delta, 0);
419 } else {
420 assert!(delta > 0);
421 }
422 }
423
424 let mut buffer = VecOutputBuffer::new(*PAGE_SIZE as usize);
426 assert_eq!(queue.read(&mut buffer), Ok(*PAGE_SIZE as usize));
427 assert_eq!(buffer.bytes_written() as u64, *PAGE_SIZE);
428
429 let actual_ts_bytes = &buffer.data()[0..8];
431 let actual_ts = u64::from_le_bytes(actual_ts_bytes.try_into().unwrap());
432 assert!(actual_ts >= queue_start_timestamp.into_nanos() as u64);
433
434 let actual_size_bytes = &buffer.data()[8..16];
436 let expected_size_bytes = &((expected_event.size() * events_per_page) as u64).to_le_bytes();
437 assert_eq!(actual_size_bytes, expected_size_bytes);
438
439 let mut buffer = VecOutputBuffer::new(*PAGE_SIZE as usize);
441 assert_eq!(queue.read(&mut buffer), error!(EAGAIN));
442 }
443}