-
Notifications
You must be signed in to change notification settings - Fork 2
Implement dynamic debug logging mask for Rust Binder #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: rust-binder-base
Are you sure you want to change the base?
Changes from all commits
c5b4867
a7e1f76
63a61d3
db6071e
e2a8641
4c049db
83ca110
de7ee67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // SPDX-License-Identifier: GPL-2.0 | ||
| // Copyright (C) 2026 Google LLC. | ||
|
|
||
| //! Binder debugging helpers. | ||
|
|
||
| #![allow(dead_code)] | ||
|
|
||
| kernel::impl_flags!( | ||
| /// Represents multiple debug mask flags. | ||
| #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] | ||
| pub struct DebugMasks(u32); | ||
|
|
||
| /// Represents a single debug mask category. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub enum DebugMask { | ||
| UserError = kernel::bits::bit_u32(0), | ||
| FailedTransaction = kernel::bits::bit_u32(1), | ||
| DeadTransaction = kernel::bits::bit_u32(2), | ||
| OpenClose = kernel::bits::bit_u32(3), | ||
| DeadBinder = kernel::bits::bit_u32(4), | ||
| DeathNotification = kernel::bits::bit_u32(5), | ||
| ReadWrite = kernel::bits::bit_u32(6), | ||
| UserRefs = kernel::bits::bit_u32(7), | ||
| Threads = kernel::bits::bit_u32(8), | ||
| Transaction = kernel::bits::bit_u32(9), | ||
| TransactionComplete = kernel::bits::bit_u32(10), | ||
| FreeBuffer = kernel::bits::bit_u32(11), | ||
| InternalRefs = kernel::bits::bit_u32(12), | ||
| PriorityCap = kernel::bits::bit_u32(13), | ||
| Spinlocks = kernel::bits::bit_u32(14), | ||
| } | ||
| ); | ||
|
|
||
| #[no_mangle] | ||
| pub(crate) static rust_binder_debug_mask: kernel::sync::atomic::Atomic<u32> = | ||
| kernel::sync::atomic::Atomic::new( | ||
| (DebugMask::UserError as u32) | ||
| | (DebugMask::FailedTransaction as u32) | ||
| | (DebugMask::DeadTransaction as u32), | ||
| ); | ||
|
|
||
| /// Checks if the given debug logging category is enabled in the mask. | ||
| pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool { | ||
| let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed); | ||
| DebugMasks(current_mask).contains(mask) | ||
| } | ||
|
|
||
| /// Prints a debug log if the specified mask category is enabled. | ||
| #[macro_export] | ||
| macro_rules! binder_debug { | ||
| // Rule to explicitly specify a PID (used in kworkers). | ||
| (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { | ||
| if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { | ||
| kernel::pr_info!( | ||
| "{}: {}\n", | ||
| $pid, | ||
| kernel::prelude::fmt!($($arg)*) | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| // Default rule (automatically prepends "PID:TID" of the current calling thread). | ||
| ($mask:ident, $($arg:tt)*) => { | ||
| if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { | ||
| let thread = kernel::current!(); | ||
| kernel::pr_info!( | ||
| "{}:{} {}\n", | ||
| thread.tgid(), | ||
| thread.pid(), | ||
|
Darksonn marked this conversation as resolved.
|
||
| kernel::prelude::fmt!($($arg)*) | ||
| ); | ||
| } | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,7 @@ type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>; | |
| /// Represents a notification that the freeze state has changed. | ||
| pub(crate) struct FreezeMessage { | ||
| cookie: FreezeCookie, | ||
| pid: i32, | ||
| } | ||
|
Comment on lines
61
to
64
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For commit |
||
|
|
||
| kernel::list::impl_list_arc_safe! { | ||
|
|
@@ -73,8 +74,8 @@ impl FreezeMessage { | |
| UniqueArc::new_uninit(flags) | ||
| } | ||
|
|
||
| fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> { | ||
| match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) { | ||
| fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> { | ||
| match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) { | ||
| Ok(msg) => ListArc::from(msg), | ||
| Err(err) => match err {}, | ||
| } | ||
|
|
@@ -140,7 +141,14 @@ impl DeliverToRead for FreezeMessage { | |
| } | ||
| } | ||
|
|
||
| fn cancel(self: DArc<Self>) {} | ||
| fn cancel(self: DArc<Self>) { | ||
| binder_debug!( | ||
| pid = self.pid, | ||
| DeadTransaction, | ||
| "undelivered freeze notification, {:016x}", | ||
| self.cookie.0 | ||
| ); | ||
| } | ||
|
|
||
| fn should_sync_wakeup(&self) -> bool { | ||
| false | ||
|
|
@@ -182,20 +190,23 @@ impl Process { | |
| info = match node_refs.by_handle.get_mut(&handle) { | ||
| Some(info) => info, | ||
| None => { | ||
| pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}" | ||
| ); | ||
| return Err(EINVAL); | ||
| } | ||
| }; | ||
| if info.freeze().is_some() { | ||
| pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); | ||
| binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set"); | ||
| return Err(EINVAL); | ||
| } | ||
| let node_ref = info.node_ref(); | ||
| freeze_entry = node_refs.freeze_listeners.entry(cookie); | ||
|
|
||
| if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { | ||
| if !dupe.get().allow_duplicate(&node_ref.node) { | ||
| pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); | ||
| binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie"); | ||
| return Err(EINVAL); | ||
| } | ||
| } | ||
|
|
@@ -248,7 +259,7 @@ impl Process { | |
| } | ||
|
|
||
| *info.freeze() = Some(cookie); | ||
| let msg = FreezeMessage::init(msg, cookie); | ||
| let msg = FreezeMessage::init(msg, cookie, self.task.pid()); | ||
| drop(node_refs_guard); | ||
| let _ = self.push_work(msg); | ||
| Ok(()) | ||
|
|
@@ -260,26 +271,31 @@ impl Process { | |
| let mut node_refs_guard = self.node_refs.lock(); | ||
| let node_refs = &mut *node_refs_guard; | ||
| let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else { | ||
| pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_FREEZE_NOTIFICATION_DONE {:016x} not found", | ||
| cookie.0 | ||
| ); | ||
| return Err(EINVAL); | ||
| }; | ||
| let mut clear_msg = None; | ||
| if freeze.num_pending_duplicates > 0 { | ||
| clear_msg = Some(FreezeMessage::init(alloc, cookie)); | ||
| clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); | ||
| freeze.num_pending_duplicates -= 1; | ||
| freeze.num_cleared_duplicates += 1; | ||
| } else { | ||
| if !freeze.is_pending { | ||
| pr_warn!( | ||
| "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n", | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending", | ||
| cookie.0 | ||
| ); | ||
| return Err(EINVAL); | ||
| } | ||
| let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen(); | ||
| if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) { | ||
| // Immediately send another FreezeMessage. | ||
| clear_msg = Some(FreezeMessage::init(alloc, cookie)); | ||
| clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); | ||
| } | ||
| freeze.is_pending = false; | ||
| } | ||
|
|
@@ -300,27 +316,39 @@ impl Process { | |
| let mut node_refs_guard = self.node_refs.lock(); | ||
| let node_refs = &mut *node_refs_guard; | ||
| let Some(info) = node_refs.by_handle.get_mut(&handle) else { | ||
| pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}" | ||
| ); | ||
| return Err(EINVAL); | ||
| }; | ||
| let Some(info_cookie) = info.freeze() else { | ||
| pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n"); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active" | ||
| ); | ||
| return Err(EINVAL); | ||
| }; | ||
| if *info_cookie != cookie { | ||
| pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n"); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch" | ||
| ); | ||
| return Err(EINVAL); | ||
| } | ||
| let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else { | ||
| pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle); | ||
| binder_debug!( | ||
| UserError, | ||
| "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}" | ||
| ); | ||
| return Err(EINVAL); | ||
| }; | ||
| listener.is_clearing = true; | ||
| _to_free_fl = listener.node.remove_freeze_listener(self); | ||
| *info.freeze() = None; | ||
| let mut msg = None; | ||
| if !listener.is_pending { | ||
| msg = Some(FreezeMessage::init(alloc, cookie)); | ||
| msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); | ||
| } | ||
| drop(node_refs_guard); | ||
|
|
||
|
|
@@ -400,7 +428,7 @@ impl Process { | |
| continue; | ||
| }; | ||
| let msg_alloc = FreezeMessage::new(GFP_KERNEL)?; | ||
| let msg = FreezeMessage::init(msg_alloc, cookie); | ||
| let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid()); | ||
| batch.push((proc, msg), GFP_KERNEL)?; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's import
kernel::bits::bit_u32andkernel::sync::atomic::Atomicinstead of using the full path every time.