]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rust_binder: add binder_transaction tracepoint
authorAlice Ryhl <aliceryhl@google.com>
Wed, 3 Dec 2025 14:48:09 +0000 (14:48 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 29 Dec 2025 11:14:52 +0000 (12:14 +0100)
This patch adds the binder_transaction tracepoint to Rust Binder. This
was chosen as the next tracepoint to add as it is the most complex
tracepoint. (And it's also an important tracepoint known to perfetto.)

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20251203-binder-trace1-v1-2-22d3ffddb44e@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/android/binder/node.rs
drivers/android/binder/process.rs
drivers/android/binder/rust_binder.h
drivers/android/binder/rust_binder_events.h
drivers/android/binder/rust_binder_main.rs
drivers/android/binder/thread.rs
drivers/android/binder/trace.rs
drivers/android/binder/transaction.rs

index c26d113ede96686bd783bf6dcba35c47015967de..69f757ff7461e0fb6b2b36b5ade1deb53c0c9339 100644 (file)
@@ -178,6 +178,14 @@ struct NodeInner {
     refs: List<NodeRefInfo, { NodeRefInfo::LIST_NODE }>,
 }
 
+use kernel::bindings::rb_node_layout;
+use mem::offset_of;
+pub(crate) const NODE_LAYOUT: rb_node_layout = rb_node_layout {
+    arc_offset: Arc::<Node>::DATA_OFFSET + offset_of!(DTRWrap<Node>, wrapped),
+    debug_id: offset_of!(Node, debug_id),
+    ptr: offset_of!(Node, ptr),
+};
+
 #[pin_data]
 pub(crate) struct Node {
     pub(crate) debug_id: usize,
index 132055b4790f0ec69a87635b498909df2bf475e2..62b8279b711feb86e4cf407764186a49156dc92c 100644 (file)
@@ -418,6 +418,13 @@ impl ProcessNodeRefs {
     }
 }
 
+use core::mem::offset_of;
+use kernel::bindings::rb_process_layout;
+pub(crate) const PROCESS_LAYOUT: rb_process_layout = rb_process_layout {
+    arc_offset: Arc::<Process>::DATA_OFFSET,
+    task: offset_of!(Process, task),
+};
+
 /// A process using binder.
 ///
 /// Strictly speaking, there can be multiple of these per process. There is one for each binder fd
index 31806890ed1a278793ae7178f9d76ca4d591a954..e68ba7a23c34de4688fa7d34341b0585b5dba7e9 100644 (file)
@@ -20,4 +20,83 @@ struct inode;
 struct dentry *rust_binderfs_create_proc_file(struct inode *nodp, int pid);
 void rust_binderfs_remove_file(struct dentry *dentry);
 
+/*
+ * The internal data types in the Rust Binder driver are opaque to C, so we use
+ * void pointer typedefs for these types.
+ */
+
+typedef void *rust_binder_transaction;
+typedef void *rust_binder_process;
+typedef void *rust_binder_node;
+
+struct rb_process_layout {
+       size_t arc_offset;
+       size_t task;
+};
+
+struct rb_transaction_layout {
+       size_t debug_id;
+       size_t code;
+       size_t flags;
+       size_t from_thread;
+       size_t to_proc;
+       size_t target_node;
+};
+
+struct rb_node_layout {
+       size_t arc_offset;
+       size_t debug_id;
+       size_t ptr;
+};
+
+struct rust_binder_layout {
+       struct rb_transaction_layout t;
+       struct rb_process_layout p;
+       struct rb_node_layout n;
+};
+
+extern const struct rust_binder_layout RUST_BINDER_LAYOUT;
+
+static inline size_t rust_binder_transaction_debug_id(rust_binder_transaction t)
+{
+       return *(size_t *) (t + RUST_BINDER_LAYOUT.t.debug_id);
+}
+
+static inline u32 rust_binder_transaction_code(rust_binder_transaction t)
+{
+       return *(u32 *) (t + RUST_BINDER_LAYOUT.t.code);
+}
+
+static inline u32 rust_binder_transaction_flags(rust_binder_transaction t)
+{
+       return *(u32 *) (t + RUST_BINDER_LAYOUT.t.flags);
+}
+
+// Nullable!
+static inline rust_binder_node rust_binder_transaction_target_node(rust_binder_transaction t)
+{
+       void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.target_node);
+
+       if (p)
+               p = p + RUST_BINDER_LAYOUT.n.arc_offset;
+       return NULL;
+}
+
+static inline rust_binder_process rust_binder_transaction_to_proc(rust_binder_transaction t)
+{
+       void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.to_proc);
+
+       return p + RUST_BINDER_LAYOUT.p.arc_offset;
+}
+
+static inline struct task_struct *rust_binder_process_task(rust_binder_process t)
+{
+       return *(struct task_struct **) (t + RUST_BINDER_LAYOUT.p.task);
+}
+
+static inline size_t rust_binder_node_debug_id(rust_binder_node t)
+{
+       return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id);
+}
+
 #endif
index 2f3efbf9dba68e6415f0f09ff5c8255dc5c5bc00..8ad785c6bd0f5783df1e764a8827fed41afbcb00 100644 (file)
@@ -30,6 +30,36 @@ TRACE_EVENT(rust_binder_ioctl,
        TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
 );
 
+TRACE_EVENT(rust_binder_transaction,
+       TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread),
+       TP_ARGS(reply, t, thread),
+       TP_STRUCT__entry(
+               __field(int, debug_id)
+               __field(int, target_node)
+               __field(int, to_proc)
+               __field(int, to_thread)
+               __field(int, reply)
+               __field(unsigned int, code)
+               __field(unsigned int, flags)
+       ),
+       TP_fast_assign(
+               rust_binder_process to = rust_binder_transaction_to_proc(t);
+               rust_binder_node target_node = rust_binder_transaction_target_node(t);
+
+               __entry->debug_id = rust_binder_transaction_debug_id(t);
+               __entry->target_node = target_node ? rust_binder_node_debug_id(target_node) : 0;
+               __entry->to_proc = rust_binder_process_task(to)->pid;
+               __entry->to_thread = thread ? thread->pid : 0;
+               __entry->reply = reply;
+               __entry->code = rust_binder_transaction_code(t);
+               __entry->flags = rust_binder_transaction_flags(t);
+       ),
+       TP_printk("transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x code=0x%x",
+                 __entry->debug_id, __entry->target_node,
+                 __entry->to_proc, __entry->to_thread,
+                 __entry->reply, __entry->flags, __entry->code)
+);
+
 #endif /* _RUST_BINDER_TRACE_H */
 
 /* This part must be outside protection */
index c79a9e7422401fd50078c1902774573b8ba0076c..c04b88936584a8080db1ddc4985c0db929c2ddbd 100644 (file)
@@ -89,6 +89,14 @@ module! {
     license: "GPL",
 }
 
+use kernel::bindings::rust_binder_layout;
+#[no_mangle]
+static RUST_BINDER_LAYOUT: rust_binder_layout = rust_binder_layout {
+    t: transaction::TRANSACTION_LAYOUT,
+    p: process::PROCESS_LAYOUT,
+    n: node::NODE_LAYOUT,
+};
+
 fn next_debug_id() -> usize {
     static NEXT_DEBUG_ID: AtomicUsize = AtomicUsize::new(0);
 
index 1a8e6fdc0dc42369ee078e720aa02b2554fb7332..6e8f29e50cacb227028e35dff90a38c1a76cd78c 100644 (file)
@@ -1117,6 +1117,7 @@ impl Thread {
         transaction: &DArc<Transaction>,
     ) -> bool {
         if let Ok(transaction) = &reply {
+            crate::trace::trace_transaction(true, transaction, Some(&self.task));
             transaction.set_outstanding(&mut self.process.inner.lock());
         }
 
index af0e4392805e7ef2a39b7c1e557ea233dcd810ab..9839901c7151590defed297b091f1339603c09df 100644 (file)
@@ -2,11 +2,21 @@
 
 // Copyright (C) 2025 Google LLC.
 
+use crate::transaction::Transaction;
+
+use kernel::bindings::{rust_binder_transaction, task_struct};
 use kernel::ffi::{c_uint, c_ulong};
+use kernel::task::Task;
 use kernel::tracepoint::declare_trace;
 
 declare_trace! {
     unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong);
+    unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct);
+}
+
+#[inline]
+fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
+    t as *const Transaction as rust_binder_transaction
 }
 
 #[inline]
@@ -14,3 +24,14 @@ pub(crate) fn trace_ioctl(cmd: u32, arg: usize) {
     // SAFETY: Always safe to call.
     unsafe { rust_binder_ioctl(cmd, arg as c_ulong) }
 }
+
+#[inline]
+pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) {
+    let thread = match thread {
+        Some(thread) => thread.as_ptr(),
+        None => core::ptr::null_mut(),
+    };
+    // SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is
+    // valid or null.
+    unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) }
+}
index 4bd3c0e417eb93d5d62d9c20daadde1fb0e4990f..cd8d8202e52d16fb787946d8d21d7830638ff9c3 100644 (file)
@@ -24,6 +24,17 @@ use crate::{
     BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
 };
 
+use core::mem::offset_of;
+use kernel::bindings::rb_transaction_layout;
+pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout {
+    debug_id: offset_of!(Transaction, debug_id),
+    code: offset_of!(Transaction, code),
+    flags: offset_of!(Transaction, flags),
+    from_thread: offset_of!(Transaction, from),
+    to_proc: offset_of!(Transaction, to),
+    target_node: offset_of!(Transaction, target_node),
+};
+
 #[pin_data(PinnedDrop)]
 pub(crate) struct Transaction {
     pub(crate) debug_id: usize,
@@ -249,6 +260,7 @@ impl Transaction {
 
         if oneway {
             if let Some(target_node) = self.target_node.clone() {
+                crate::trace::trace_transaction(false, &self, None);
                 if process_inner.is_frozen.is_frozen() {
                     process_inner.async_recv = true;
                     if self.flags & TF_UPDATE_TXN != 0 {
@@ -286,11 +298,13 @@ impl Transaction {
         }
 
         let res = if let Some(thread) = self.find_target_thread() {
+            crate::trace::trace_transaction(false, &self, Some(&thread.task));
             match thread.push_work(self) {
                 PushWorkRes::Ok => Ok(()),
                 PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
             }
         } else {
+            crate::trace::trace_transaction(false, &self, None);
             process_inner.push_work(self)
         };
         drop(process_inner);