@@ -7,6 +7,8 @@
TryNewListArc,
},
prelude::*,
+ seq_file::SeqFile,
+ seq_print,
sync::lock::{spinlock::SpinLockBackend, Guard},
sync::{Arc, LockedBy, SpinLock},
user_ptr::UserSlicePtrWriter,
@@ -111,6 +113,41 @@ pub(crate) fn new(
})
}
+ #[inline(never)]
+ pub(crate) fn debug_print(&self, m: &mut SeqFile) -> Result<()> {
+ let weak;
+ let strong;
+ let has_weak;
+ let has_strong;
+ let active_inc_refs;
+ {
+ let mut guard = self.owner.inner.lock();
+ let inner = self.inner.access_mut(&mut guard);
+ weak = inner.weak.count;
+ has_weak = inner.weak.has_count;
+ strong = inner.strong.count;
+ has_strong = inner.strong.has_count;
+ active_inc_refs = inner.active_inc_refs;
+ }
+
+ let has_weak = if has_weak { "Y" } else { "N" };
+ let has_strong = if has_strong { "Y" } else { "N" };
+
+ seq_print!(
+ m,
+ "node gid:{},ptr:{:#x},cookie:{:#x}: strong{}{} weak{}{} active{}\n",
+ self.global_id,
+ self.ptr,
+ self.cookie,
+ strong,
+ has_strong,
+ weak,
+ has_weak,
+ active_inc_refs
+ );
+ Ok(())
+ }
+
pub(crate) fn get_id(&self) -> (usize, usize) {
(self.ptr, self.cookie)
}
@@ -20,6 +20,8 @@
pages::Pages,
prelude::*,
rbtree::RBTree,
+ seq_file::SeqFile,
+ seq_print,
sync::{
lock::Guard, Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
},
@@ -405,6 +407,79 @@ fn new(ctx: Arc<Context>, cred: ARef<Credential>) -> Result<Arc<Self>> {
Ok(process)
}
+ #[inline(never)]
+ pub(crate) fn debug_print(&self, m: &mut SeqFile) -> Result<()> {
+ seq_print!(m, "pid: {}\n", self.task.pid_in_current_ns());
+
+ let is_manager;
+ let started_threads;
+ let has_proc_work;
+ let mut ready_threads = Vec::new();
+ let mut all_threads = Vec::new();
+ let mut all_nodes = Vec::new();
+ loop {
+ let inner = self.inner.lock();
+ let ready_threads_len = inner.ready_threads.iter().count();
+ let all_threads_len = inner.threads.values().count();
+ let all_nodes_len = inner.nodes.values().count();
+
+ let resize_ready_threads = ready_threads_len > ready_threads.capacity();
+ let resize_all_threads = all_threads_len > all_threads.capacity();
+ let resize_all_nodes = all_nodes_len > all_nodes.capacity();
+ if resize_ready_threads || resize_all_threads || resize_all_nodes {
+ drop(inner);
+ ready_threads.try_reserve(ready_threads_len)?;
+ all_threads.try_reserve(all_threads_len)?;
+ all_nodes.try_reserve(all_nodes_len)?;
+ continue;
+ }
+
+ is_manager = inner.is_manager;
+ started_threads = inner.started_thread_count;
+ has_proc_work = !inner.work.is_empty();
+
+ for thread in &inner.ready_threads {
+ assert!(ready_threads.len() < ready_threads.capacity());
+ ready_threads.try_push(thread.id)?;
+ }
+
+ for thread in inner.threads.values() {
+ assert!(all_threads.len() < all_threads.capacity());
+ all_threads.try_push(thread.clone())?;
+ }
+
+ for node in inner.nodes.values() {
+ assert!(all_nodes.len() < all_nodes.capacity());
+ all_nodes.try_push(node.clone())?;
+ }
+
+ break;
+ }
+
+ seq_print!(m, "is_manager: {}\n", is_manager);
+ seq_print!(m, "started_threads: {}\n", started_threads);
+ seq_print!(m, "has_proc_work: {}\n", has_proc_work);
+ if ready_threads.is_empty() {
+ seq_print!(m, "ready_thread_ids: none\n");
+ } else {
+ seq_print!(m, "ready_thread_ids:");
+ for thread_id in ready_threads {
+ seq_print!(m, " {}", thread_id);
+ }
+ seq_print!(m, "\n");
+ }
+
+ for node in all_nodes {
+ node.debug_print(m)?;
+ }
+
+ seq_print!(m, "all threads:\n");
+ for thread in all_threads {
+ thread.debug_print(m);
+ }
+ Ok(())
+ }
+
/// Attempts to fetch a work item from the process queue.
pub(crate) fn get_work(&self) -> Option<DLArc<dyn DeliverToRead>> {
self.inner.lock().work.pop_front()
@@ -10,6 +10,8 @@
HasListLinks, ListArc, ListArcSafe, ListItem, ListLinks, ListLinksSelfPtr, TryNewListArc,
},
prelude::*,
+ seq_file::SeqFile,
+ seq_print,
sync::Arc,
types::ForeignOwnable,
user_ptr::UserSlicePtrWriter,
@@ -347,7 +349,13 @@ unsafe impl<T> Sync for AssertSync<T> {}
}
#[no_mangle]
-unsafe extern "C" fn rust_binder_state_show(_: *mut seq_file) -> core::ffi::c_int {
+unsafe extern "C" fn rust_binder_state_show(ptr: *mut seq_file) -> core::ffi::c_int {
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(ptr) };
+ if let Err(err) = rust_binder_state_show_impl(m) {
+ seq_print!(m, "failed to generate state: {:?}\n", err);
+ }
0
}
@@ -360,3 +368,16 @@ unsafe impl<T> Sync for AssertSync<T> {}
unsafe extern "C" fn rust_binder_transaction_log_show(_: *mut seq_file) -> core::ffi::c_int {
0
}
+
+fn rust_binder_state_show_impl(m: &mut SeqFile) -> Result<()> {
+ let contexts = context::get_all_contexts()?;
+ for ctx in contexts {
+ let procs = ctx.get_all_procs()?;
+ seq_print!(m, "context {}: ({} processes)\n", &*ctx.name, procs.len());
+ for proc in procs {
+ proc.debug_print(m)?;
+ seq_print!(m, "\n");
+ }
+ }
+ Ok(())
+}
@@ -15,6 +15,8 @@
},
prelude::*,
security,
+ seq_file::SeqFile,
+ seq_print,
sync::{Arc, SpinLock},
types::Either,
user_ptr::{UserSlicePtr, UserSlicePtrWriter},
@@ -447,6 +449,72 @@ pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
}))
}
+ #[inline(never)]
+ pub(crate) fn debug_print(&self, m: &mut SeqFile) {
+ let looper_flags;
+ let looper_need_return;
+ let is_dead;
+ let has_work;
+ let process_work_list;
+ let current_transaction;
+ {
+ let inner = self.inner.lock();
+ looper_flags = inner.looper_flags;
+ looper_need_return = inner.looper_need_return;
+ is_dead = inner.is_dead;
+ has_work = !inner.work_list.is_empty();
+ process_work_list = inner.process_work_list;
+ current_transaction = inner.current_transaction.clone();
+ }
+ seq_print!(m, " tid: {}\n", self.id);
+ seq_print!(m, " state:");
+ if is_dead {
+ seq_print!(m, " dead");
+ }
+ if looper_need_return {
+ seq_print!(m, " pending_flush_wakeup");
+ }
+ if has_work && process_work_list {
+ seq_print!(m, " has_work");
+ }
+ if has_work && !process_work_list {
+ seq_print!(m, " has_deferred_work");
+ }
+ if looper_flags & LOOPER_REGISTERED != 0 {
+ seq_print!(m, " registered");
+ }
+ if looper_flags & LOOPER_ENTERED != 0 {
+ seq_print!(m, " entered");
+ }
+ if looper_flags & LOOPER_EXITED != 0 {
+ seq_print!(m, " exited");
+ }
+ if looper_flags & LOOPER_INVALID != 0 {
+ seq_print!(m, " invalid");
+ }
+ if looper_flags & LOOPER_WAITING != 0 {
+ if looper_flags & LOOPER_WAITING_PROC != 0 {
+ seq_print!(m, " in_get_work");
+ } else {
+ seq_print!(m, " in_get_work_local");
+ }
+ }
+ if looper_flags & LOOPER_POLL != 0 {
+ seq_print!(m, " poll_is_initialized");
+ }
+ seq_print!(m, "\n");
+ if current_transaction.is_some() {
+ seq_print!(m, " tstack:");
+ let mut t = current_transaction;
+ while let Some(tt) = t.as_ref() {
+ seq_print!(m, " ");
+ tt.debug_print(m);
+ t = tt.clone_next();
+ }
+ seq_print!(m, "\n");
+ }
+ }
+
pub(crate) fn get_extended_error(&self, data: UserSlicePtr) -> Result {
let mut writer = data.writer();
let ee = self.inner.lock().extended_error;
@@ -5,6 +5,8 @@
io_buffer::IoBufferWriter,
list::ListArcSafe,
prelude::*,
+ seq_file::SeqFile,
+ seq_print,
sync::{Arc, SpinLock},
task::Kuid,
types::{Either, ScopeGuard},
@@ -140,6 +142,29 @@ pub(crate) fn new_reply(
}))?)
}
+ #[inline(never)]
+ pub(crate) fn debug_print(&self, m: &mut SeqFile) {
+ let from_pid = self.from.process.task.pid_in_current_ns();
+ let to_pid = self.to.task.pid_in_current_ns();
+ let from_tid = self.from.id;
+ match self.target_node.as_ref() {
+ Some(target_node) => {
+ let node_id = target_node.global_id;
+ seq_print!(
+ m,
+ "{}(tid:{})->{}(nid:{})",
+ from_pid,
+ from_tid,
+ to_pid,
+ node_id
+ );
+ }
+ None => {
+ seq_print!(m, "{}(tid:{})->{}(nid:_)", from_pid, from_tid, to_pid);
+ }
+ }
+ }
+
/// Determines if the transaction is stacked on top of the given transaction.
pub(crate) fn is_stacked_on(&self, onext: &Option<DArc<Self>>) -> bool {
match (&self.stack_next, onext) {
@@ -15,6 +15,7 @@
#include <linux/pid_namespace.h>
#include <linux/poll.h>
#include <linux/security.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/rust_binder.h>
@@ -48,6 +48,7 @@
pub mod print;
pub mod rbtree;
pub mod security;
+pub mod seq_file;
mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
new file mode 100644
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Seq file bindings.
+//!
+//! C header: [`include/linux/seq_file.h`](../../../../include/linux/seq_file.h)
+
+use crate::{bindings, c_str, types::Opaque};
+
+/// A helper for implementing special files, where the complete contents can be generated on each
+/// access.
+pub struct SeqFile(Opaque<bindings::seq_file>);
+
+impl SeqFile {
+ /// Creates a new [`SeqFile`] from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that, for the duration of 'a, the pointer must point at a valid
+ /// `seq_file` and that it will not be accessed via anything other than the returned reference.
+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::seq_file) -> &'a mut SeqFile {
+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the
+ // `Credential` type being transparent makes the cast ok.
+ unsafe { &mut *ptr.cast() }
+ }
+
+ /// Used by the [`seq_print`] macro.
+ ///
+ /// [`seq_print`]: crate::seq_print
+ pub fn call_printf(&mut self, args: core::fmt::Arguments<'_>) {
+ // SAFETY: Passing a void pointer to `Arguments` is valid for `%pA`.
+ unsafe {
+ bindings::seq_printf(
+ self.0.get(),
+ c_str!("%pA").as_char_ptr(),
+ &args as *const _ as *const core::ffi::c_void,
+ );
+ }
+ }
+}
+
+/// Use for writing to a [`SeqFile`] with the ordinary Rust formatting syntax.
+#[macro_export]
+macro_rules! seq_print {
+ ($m:expr, $($arg:tt)+) => (
+ $m.call_printf(format_args!($($arg)+))
+ );
+}