starnix_logging/
logging.rs

1// Copyright 2021 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 starnix_task_command::TaskCommand;
6use starnix_uapi::errors::Errno;
7use starnix_uapi::{pid_t, tid_t};
8use std::cell::RefCell;
9use std::fmt;
10
11// This needs to be available to the macros in this module without clients having to depend on
12// log themselves.
13#[doc(hidden)]
14pub use log as __log;
15
16pub use log::kv::{ToValue, Value};
17pub use log::{Level, Record, logger};
18
19/// Used to track the current thread's logical context.
20enum TaskDebugInfo {
21    /// The thread with this set is used for internal logic within the starnix kernel.
22    Kernel,
23    /// The thread with this set is used to service syscalls for a specific user thread, and this
24    /// describes the user thread's identity.
25    User { pid: pid_t, tid: tid_t, command: TaskCommand, leader_command: TaskCommand },
26    /// Unknown info. This happens when trying to log while in the destructor of a thread local
27    /// variable.
28    Unknown,
29}
30
31impl TaskDebugInfo {
32    pub fn leader_command(&self) -> TaskCommand {
33        match self {
34            Self::Kernel => TaskCommand::new(b"kthreadd"),
35            Self::User { leader_command, .. } => leader_command.clone(),
36            Self::Unknown => TaskCommand::new(b"<unknown>"),
37        }
38    }
39}
40
41thread_local! {
42    /// When a thread in this kernel is started, it is a kthread by default. Once the thread
43    /// becomes aware of the user-level task it is executing, this thread-local should be set to
44    /// include that info.
45    static CURRENT_TASK_INFO: RefCell<TaskDebugInfo> = const { RefCell::new(TaskDebugInfo::Kernel) } ;
46}
47
48impl fmt::Display for TaskDebugInfo {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::Kernel => write!(f, "kthread"),
52            Self::User { pid, tid, command, .. } => write!(f, "{pid}:{tid}[{command}]"),
53            Self::Unknown => write!(f, "unknown"),
54        }
55    }
56}
57
58#[inline]
59pub const fn trace_debug_logs_enabled() -> bool {
60    // Allow trace and debug logs if we are in a debug (non-release) build
61    // or feature `trace_and_debug_logs_in_release` is enabled.
62    cfg!(debug_assertions) || cfg!(feature = "trace_and_debug_logs_in_release")
63}
64
65#[macro_export]
66macro_rules! log_trace {
67    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => {
68        if $crate::trace_debug_logs_enabled() {
69            $crate::with_current_task_info(|_task_info| {
70                $crate::__log::trace!(
71                    tag:% = _task_info,
72                    $($key $(:$capture)* $(= $value)*),+;
73                    $($arg)*
74                );
75            });
76        }
77    };
78    ($($arg:tt)*) => {
79        if $crate::trace_debug_logs_enabled() {
80            $crate::with_current_task_info(|_task_info| {
81                $crate::__log::trace!(tag:% = _task_info; $($arg)*);
82            });
83        }
84    };
85}
86
87#[macro_export]
88macro_rules! log_syscall {
89    ($current_task:expr, $($arg:tt)*) => {
90        if $crate::trace_debug_logs_enabled() {
91            $crate::log!(
92                $current_task.task.thread_group.syscall_log_level(),
93                $($arg)*
94            );
95        }
96    }
97}
98
99#[macro_export]
100macro_rules! log_debug {
101    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => {
102        if $crate::trace_debug_logs_enabled() {
103            $crate::with_current_task_info(|_task_info| {
104                $crate::__log::debug!(
105                    tag:% = _task_info,
106                    $($key $(:$capture)* $(= $value)*),+;
107                    $($arg)*
108                );
109            });
110        }
111    };
112    ($($arg:tt)*) => {
113        if $crate::trace_debug_logs_enabled() {
114            $crate::with_current_task_info(|_task_info| {
115                $crate::__log::debug!(tag:% = _task_info; $($arg)*);
116            });
117        }
118    };
119}
120
121#[macro_export]
122macro_rules! log_info {
123    ($($arg:tt)*) => {
124        $crate::log!($crate::__log::Level::Info, $($arg)*);
125    };
126}
127
128#[macro_export]
129macro_rules! log_warn {
130    ($($arg:tt)*) => {
131        $crate::log!($crate::__log::Level::Warn, $($arg)*);
132    };
133}
134
135#[macro_export]
136macro_rules! log_error {
137    ($($arg:tt)*) => {
138        $crate::log!($crate::__log::Level::Error, $($arg)*);
139    };
140}
141
142#[macro_export]
143macro_rules! log {
144    ($lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => {
145        $crate::with_current_task_info(|_task_info| {
146            $crate::__log::log!(
147                $lvl,
148                tag:% = _task_info,
149                $($key $(:$capture)* $(= $value)*),+;
150                $($arg)*
151            );
152        });
153    };
154    ($lvl:expr, $($arg:tt)+) => {
155        $crate::with_current_task_info(|_task_info| {
156            $crate::__log::log!($lvl, tag:% = _task_info; $($arg)*);
157        });
158    };
159}
160
161// Call this when you get an error that should "never" happen, i.e. if it does that means the
162// kernel was updated to produce some other error after this match was written.
163#[track_caller]
164pub fn impossible_error(status: zx::Status) -> Errno {
165    panic!("encountered impossible error: {status}");
166}
167
168pub fn set_zx_name(obj: &impl zx::AsHandleRef, name: impl AsRef<[u8]>) {
169    obj.as_handle_ref()
170        .set_name(&zx::Name::from_bytes_lossy(name.as_ref()))
171        .map_err(impossible_error)
172        .unwrap();
173}
174
175pub fn with_zx_name<O: zx::AsHandleRef>(obj: O, name: impl AsRef<[u8]>) -> O {
176    set_zx_name(&obj, name);
177    obj
178}
179
180/// Set the context for log messages from this thread. Should only be called when a thread has been
181/// created to execute a user-level task, and should only be called once at the start of that
182/// thread's execution.
183pub fn set_current_task_info(
184    command: TaskCommand,
185    leader_command: TaskCommand,
186    pid: pid_t,
187    tid: tid_t,
188) {
189    CURRENT_TASK_INFO.with(|task_info| {
190        *task_info.borrow_mut() = TaskDebugInfo::User { pid, tid, command, leader_command };
191    });
192}
193
194/// Access this thread's task info for debugging. Intended for use internally by Starnix's log
195/// macros.
196///
197/// *Do not use this for kernel logic.* If you need access to the current pid/tid/etc for the
198/// purposes of writing kernel logic beyond logging for debugging purposes, those should be accessed
199/// through the `CurrentTask` type as an argument explicitly passed to your function.
200#[doc(hidden)]
201pub fn with_current_task_info<T>(f: impl Fn(&dyn fmt::Display) -> T) -> T {
202    match CURRENT_TASK_INFO.try_with(|task_info| f(&task_info.borrow())) {
203        Ok(value) => value,
204        Err(_) => f(&TaskDebugInfo::Unknown),
205    }
206}
207
208pub(crate) fn get_current_leader_command() -> flyweights::FlyByteStr {
209    match CURRENT_TASK_INFO.try_with(|task_info| task_info.borrow().leader_command()) {
210        Ok(value) => value.into(),
211        Err(_) => flyweights::FlyByteStr::new(b"<unknown>"),
212    }
213}
214
215/// A filter for syscall logging.
216#[derive(Debug, Clone, PartialEq, Eq, Hash)]
217pub struct SyscallLogFilter {
218    match_string: String,
219}
220
221impl SyscallLogFilter {
222    pub fn new(match_string: String) -> Self {
223        Self { match_string }
224    }
225
226    pub fn matches(&self, command: &TaskCommand) -> bool {
227        let matcher = self.match_string.as_bytes();
228        command.as_bytes().windows(matcher.len()).any(|w| w == matcher)
229    }
230}