1use super::tracing_directory::TraceMarkerFile;
6use starnix_core::perf::{TraceEventQueue, TraceEventQueueList};
7use starnix_core::task::CurrentTask;
8use starnix_core::vfs::pseudo::dynamic_file::ConstFile;
9use starnix_core::vfs::pseudo::simple_directory::SimpleDirectory;
10use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps, SimpleFileNode};
11use starnix_core::vfs::{
12 CacheMode, FileObject, FileOps, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions,
13 FsNodeOps, FsStr, OutputBuffer,
14};
15use starnix_core::{fileops_impl_nonseekable, fileops_impl_noop_sync};
16use starnix_logging::{CATEGORY_TRACE_META, track_stub};
17use starnix_sync::{
18 FileOpsCore, LockDepMutex, LockEqualOrBefore, Locked, TraceFsDataLock, Unlocked,
19};
20use starnix_types::PAGE_SIZE;
21use starnix_types::vfs::default_statfs;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::file_mode::mode;
24use starnix_uapi::{TRACEFS_MAGIC, errno, error, statfs};
25use std::borrow::Cow;
26use std::sync::{Arc, LazyLock};
27
28pub fn trace_fs(
29 locked: &mut Locked<Unlocked>,
30 current_task: &CurrentTask,
31 options: FileSystemOptions,
32) -> Result<FileSystemHandle, Errno> {
33 struct TraceFsHandle(FileSystemHandle);
34
35 let handle = current_task.kernel().expando.get_or_init(|| {
36 TraceFsHandle(
37 TraceFs::new_fs(locked, current_task, options)
38 .expect("tracefs constructed with valid options"),
39 )
40 });
41 Ok(handle.0.clone())
42}
43
44pub struct TraceFs;
45
46impl FileSystemOps for TraceFs {
47 fn statfs(
48 &self,
49 _locked: &mut Locked<FileOpsCore>,
50 _fs: &FileSystem,
51 _current_task: &CurrentTask,
52 ) -> Result<statfs, Errno> {
53 Ok(default_statfs(TRACEFS_MAGIC))
54 }
55
56 fn name(&self) -> &'static FsStr {
57 "tracefs".into()
58 }
59}
60
61impl TraceFs {
62 pub fn new_fs<L>(
63 locked: &mut Locked<L>,
64 current_task: &CurrentTask,
65 options: FileSystemOptions,
66 ) -> Result<FileSystemHandle, Errno>
67 where
68 L: LockEqualOrBefore<FileOpsCore>,
69 {
70 let kernel = current_task.kernel();
71 let event_queue_collection = TraceEventQueueList::from(kernel);
72 let fs = FileSystem::new(locked, kernel, CacheMode::Uncached, TraceFs, options)?;
73 let dir = SimpleDirectory::new();
74 dir.edit(&fs, |dir| {
75 dir.entry("trace", TraceFile::new_node(), mode!(IFREG, 0o755));
76 dir.subdir("per_cpu", 0o755, |dir| {
77 static CPU_DIR_NAMES: LazyLock<Vec<String>> = LazyLock::new(|| {
79 (0..zx::system_get_num_cpus()).map(|cpu| format!("cpu{}", cpu)).collect()
80 });
81 for (cpu, dir_name) in CPU_DIR_NAMES.iter().map(|s| s.as_str()).enumerate() {
82 dir.subdir(dir_name, 0o755, |dir| {
83 let ops: Box<dyn FsNodeOps> = Box::new(TraceRawFile::new_node(
84 event_queue_collection.queues[cpu].clone(),
85 ));
86 dir.entry("trace_pipe_raw", ops, mode!(IFREG, 0o444));
87 dir.entry("trace", TraceFile::new_node(), mode!(IFREG, 0o444));
88 });
89 }
90 });
91 dir.subdir("events", 0o755, |dir| {
92 dir.entry("header_page", EventsHeaderPage::new_node(), mode!(IFREG, 0o444));
93 dir.subdir("ftrace", 0o755, |dir| {
94 dir.subdir("print", 0o755, |dir| {
95 dir.entry("format", FtracePrintFormatFile::new_node(), mode!(IFREG, 0o444));
96 });
97 if kernel.features.selinux_test_suite {
98 dir.subdir("function", 0o755, |dir| {
101 dir.entry(
102 "id",
103 BytesFile::new_node(b"1\n".to_vec()),
104 mode!(IFREG, 0o444),
105 );
106 });
107 }
108 });
109 dir.entry("enable", TraceBytesFile::new_node(), mode!(IFREG, 0o755));
110 });
111 dir.subdir("options", 0o755, |dir| {
112 dir.entry("overwrite", TraceBytesFile::new_node(), mode!(IFREG, 0o444));
113 });
114 dir.entry(
115 "tracing_on",
116 TracingOnFile::new_node(event_queue_collection.clone()),
117 mode!(IFREG, 0o666),
118 );
119 dir.entry("current_tracer", ConstFile::new_node("nop".into()), mode!(IFREG, 0o755));
120 dir.entry(
121 "trace_marker",
122 TraceMarkerFile::new_node(event_queue_collection.clone()),
123 mode!(IFREG, 0o222),
124 );
125 dir.entry("printk_formats", TraceBytesFile::new_node(), mode!(IFREG, 0o755));
126 dir.entry(
127 "trace_clock",
128 ConstFile::new_node(
129 "[local] global counter uptime perf mono mono_raw boot tai x86-tsc ".into(),
130 ),
131 mode!(IFREG, 0o755),
132 );
133 dir.entry("buffer_size_kb", ConstFile::new_node("7".into()), mode!(IFREG, 0o755));
134 });
135
136 let root_ino = fs.allocate_ino();
137 fs.create_root(root_ino, dir);
138 Ok(fs)
139 }
140}
141
142#[derive(Default)]
143struct TraceBytesFile {
144 data: LockDepMutex<Vec<u8>, TraceFsDataLock>,
145}
146
147impl TraceBytesFile {
148 #[track_caller]
149 fn new_node() -> impl FsNodeOps {
150 SimpleFileNode::new(move |_, _| Ok(BytesFile::new(Self::default())))
151 }
152}
153
154impl BytesFileOps for TraceBytesFile {
155 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
156 *self.data.lock() = data;
157 Ok(())
158 }
159 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
160 let data = self.data.lock().clone();
161 Ok(data.into())
162 }
163}
164
165#[derive(Clone)]
166struct TraceRawFile {
167 queue: Arc<TraceEventQueue>,
168}
169
170impl TraceRawFile {
171 pub fn new_node(queue: Arc<TraceEventQueue>) -> impl FsNodeOps {
172 SimpleFileNode::new(move |_, _| Ok(Self { queue: queue.clone() }))
173 }
174}
175
176impl FileOps for TraceRawFile {
177 fileops_impl_nonseekable!();
178 fileops_impl_noop_sync!();
179
180 fn read(
181 &self,
182 _locked: &mut Locked<FileOpsCore>,
183 _file: &FileObject,
184 current_task: &CurrentTask,
185 _offset: usize,
186 data: &mut dyn OutputBuffer,
187 ) -> Result<usize, Errno> {
188 if data.available() as u64 != *PAGE_SIZE {
190 return error!(EINVAL);
191 }
192 let _guard = fuchsia_trace::async_enter!(
193 self.queue.async_id_read,
194 CATEGORY_TRACE_META,
195 self.queue.read_track_name(),
196 "tid" => current_task.tid
197 );
198 self.queue.read(data)
199 }
200
201 fn write(
202 &self,
203 _locked: &mut Locked<FileOpsCore>,
204 _file: &FileObject,
205 _current_task: &CurrentTask,
206 _offset: usize,
207 _data: &mut dyn starnix_core::vfs::InputBuffer,
208 ) -> Result<usize, Errno> {
209 error!(ENOSYS)
210 }
211}
212
213#[derive(Clone)]
214struct TracingOnFile {
215 event_queue_collection: Arc<TraceEventQueueList>,
216}
217
218impl TracingOnFile {
219 pub fn new_node(event_queue_collection: Arc<TraceEventQueueList>) -> impl FsNodeOps {
220 BytesFile::new_node(Self { event_queue_collection })
221 }
222}
223
224impl BytesFileOps for TracingOnFile {
225 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
226 let state_str = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
227 let clean_state_str = state_str.split('\n').next().unwrap_or("");
228 match clean_state_str {
229 "0" => self.event_queue_collection.disable(),
230 "1" => self.event_queue_collection.enable(),
231 _ => error!(EINVAL),
232 }
233 }
234
235 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
236 if self.event_queue_collection.is_enabled() {
237 Ok(Cow::Borrowed(&b"1\n"[..]))
238 } else {
239 Ok(Cow::Borrowed(&b"0\n"[..]))
240 }
241 }
242}
243
244#[derive(Clone)]
245struct EventsHeaderPage;
246impl EventsHeaderPage {
247 fn new_node() -> impl FsNodeOps {
248 ConstFile::new_node(
249 "\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0\n\
250 \tfield: local_t commit;\toffset:8;\tsize:8;\tsigned:1\n\
251 \tfield: int overwrite;\toffset:8;\tsize:1;\tsigned:1\n\
252 \tfield: char data;\toffset:16;\tsize:4080;\tsigned:0\n"
253 .into(),
254 )
255 }
256}
257
258#[derive(Clone)]
259struct FtracePrintFormatFile;
260impl FtracePrintFormatFile {
261 fn new_node() -> impl FsNodeOps {
262 ConstFile::new_node(
263 "name: print\n\
264 ID: 5\n\
265 format:\n\
266 \tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n\
267 \tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n\
268 \tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n\
269 \tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n\
270 \n\
271 \tfield:unsigned long ip;\toffset:8;\tsize:8;\tsigned:0;\n\
272 \tfield:char buf[];\toffset:16;\tsize:0;\tsigned:0;\n\
273 \n\
274 print fmt: \"%ps: %s\", (void *)REC->ip, REC- >buf\n"
275 .into(),
276 )
277 }
278}
279
280#[derive(Clone)]
281struct TraceFile;
282impl TraceFile {
283 fn new_node() -> impl FsNodeOps {
284 track_stub!(TODO("https://fxbug.dev/357665908"), "/sys/kernel/tracing/trace");
285 ConstFile::new_node("# tracer: nop\n".into())
286 }
287}