Skip to main content

starnix_modules_tracefs/
fs.rs

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