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::{
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                /// A name for each cpu directory, cached to provide a 'static lifetime.
78                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                        // Necessary for the perf_event SELinux testsuite case.
99                        // See https://fxbug.dev/398663320.
100                        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        // TODO(b/505532201): Consider supporting larger buffers.
189        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}