[PATCH 2/3] rust_binder: add Rust binder tracepoints

From: Mohamad Alsadhan

Date: Sat Mar 07 2026 - 00:43:47 EST


New tracepoints: `{ioctl,read,write}_done`, `wait_for_work`,
`transaction_received`, `transaction_fd_{send,recv}`,
`{alloc,free}_lru_{start,end}`, `alloc_page_{start,end}`,
`unmap_{user,kernel}_{start,end}`, `command` and `return`.

Link: https://github.com/Rust-for-Linux/linux/issues/1226
Suggested-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
Signed-off-by: Mohamad Alsadhan <mo@xxxxxxx>
---
drivers/android/binder/rust_binder.h | 28 +++--
drivers/android/binder/rust_binder_events.h | 172 ++++++++++++++++++++++++++++
drivers/android/binder/trace.rs | 111 +++++++++++++++++-
3 files changed, 299 insertions(+), 12 deletions(-)

diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h
index d2284726c..b27283fc9 100644
--- a/drivers/android/binder/rust_binder.h
+++ b/drivers/android/binder/rust_binder.h
@@ -59,44 +59,52 @@ 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);
+ 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);
+ 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);
+ return *(u32 *)(t + RUST_BINDER_LAYOUT.t.flags);
}

// Nullable!
-static inline rust_binder_node rust_binder_transaction_target_node(rust_binder_transaction t)
+static inline rust_binder_node
+rust_binder_transaction_target_node(rust_binder_transaction t)
{
- void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.target_node);
+ void *p = *(void **)(t + RUST_BINDER_LAYOUT.t.target_node);

if (p)
p = p + RUST_BINDER_LAYOUT.n.arc_offset;
return p;
}

-static inline rust_binder_process rust_binder_transaction_to_proc(rust_binder_transaction t)
+static inline rust_binder_process
+rust_binder_transaction_to_proc(rust_binder_transaction t)
{
- void *p = *(void **) (t + RUST_BINDER_LAYOUT.t.to_proc);
+ 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)
+static inline struct task_struct *
+rust_binder_process_task(rust_binder_process t)
{
- return *(struct task_struct **) (t + RUST_BINDER_LAYOUT.p.task);
+ 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);
+ return *(size_t *)(t + RUST_BINDER_LAYOUT.n.debug_id);
+}
+
+static inline binder_uintptr_t rust_binder_node_ptr(rust_binder_node t)
+{
+ return *(binder_uintptr_t *)(t + RUST_BINDER_LAYOUT.n.ptr);
}

#endif
diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h
index e3adfb931..6725453f5 100644
--- a/drivers/android/binder/rust_binder_events.h
+++ b/drivers/android/binder/rust_binder_events.h
@@ -30,6 +30,45 @@ TRACE_EVENT(binder_ioctl,
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);

+DECLARE_EVENT_CLASS(binder_function_return_class,
+ TP_PROTO(int ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret=%d", __entry->ret)
+);
+
+#define DEFINE_RBINDER_FUNCTION_RETURN_EVENT(name) \
+DEFINE_EVENT(binder_function_return_class, name, \
+ TP_PROTO(int ret), \
+ TP_ARGS(ret))
+
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done);
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done);
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done);
+
+TRACE_EVENT(binder_wait_for_work,
+ TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo),
+ TP_ARGS(proc_work, transaction_stack, thread_todo),
+ TP_STRUCT__entry(
+ __field(bool, proc_work)
+ __field(bool, transaction_stack)
+ __field(bool, thread_todo)
+ ),
+ TP_fast_assign(
+ __entry->proc_work = proc_work;
+ __entry->transaction_stack = transaction_stack;
+ __entry->thread_todo = thread_todo;
+ ),
+ TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d",
+ __entry->proc_work, __entry->transaction_stack,
+ __entry->thread_todo)
+);
+
TRACE_EVENT(binder_transaction,
TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread),
TP_ARGS(reply, t, thread),
@@ -60,6 +99,139 @@ TRACE_EVENT(binder_transaction,
__entry->reply, __entry->flags, __entry->code)
);

+TRACE_EVENT(binder_transaction_received,
+ TP_PROTO(rust_binder_transaction t),
+ TP_ARGS(t),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = rust_binder_transaction_debug_id(t);
+ ),
+ TP_printk("transaction=%d", __entry->debug_id)
+);
+
+TRACE_EVENT(binder_transaction_fd_send,
+ TP_PROTO(int t_debug_id, int fd, size_t offset),
+ TP_ARGS(t_debug_id, fd, offset),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ __field(int, fd)
+ __field(size_t, offset)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = t_debug_id;
+ __entry->fd = fd;
+ __entry->offset = offset;
+ ),
+ TP_printk("transaction=%d src_fd=%d offset=%zu",
+ __entry->debug_id, __entry->fd, __entry->offset)
+);
+
+TRACE_EVENT(binder_transaction_fd_recv,
+ TP_PROTO(int t_debug_id, int fd, size_t offset),
+ TP_ARGS(t_debug_id, fd, offset),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ __field(int, fd)
+ __field(size_t, offset)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = t_debug_id;
+ __entry->fd = fd;
+ __entry->offset = offset;
+ ),
+ TP_printk("transaction=%d dest_fd=%d offset=%zu",
+ __entry->debug_id, __entry->fd, __entry->offset)
+);
+
+DECLARE_EVENT_CLASS(binder_lru_page_class,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index),
+ TP_STRUCT__entry(
+ __field(int, proc)
+ __field(size_t, page_index)
+ ),
+ TP_fast_assign(
+ __entry->proc = pid;
+ __entry->page_index = page_index;
+ ),
+ TP_printk("proc=%d page_index=%zu",
+ __entry->proc, __entry->page_index)
+);
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_start,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_end,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_free_lru_start,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_free_lru_end,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_start,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_end,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_start,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_end,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_start,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_end,
+ TP_PROTO(int pid, size_t page_index),
+ TP_ARGS(pid, page_index));
+
+TRACE_EVENT(binder_command,
+ TP_PROTO(uint32_t cmd),
+ TP_ARGS(cmd),
+ TP_STRUCT__entry(
+ __field(uint32_t, cmd)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ ),
+ TP_printk("cmd=0x%x %s",
+ __entry->cmd,
+ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ?
+ binder_command_strings[_IOC_NR(__entry->cmd)] :
+ "unknown")
+);
+
+TRACE_EVENT(binder_return,
+ TP_PROTO(uint32_t cmd),
+ TP_ARGS(cmd),
+ TP_STRUCT__entry(
+ __field(uint32_t, cmd)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ ),
+ TP_printk("cmd=0x%x %s",
+ __entry->cmd,
+ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ?
+ binder_return_strings[_IOC_NR(__entry->cmd)] :
+ "unknown")
+);
+
#endif /* _RUST_BINDER_TRACE_H */

/* This part must be outside protection */
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
index d54b18ab7..e270d08a5 100644
--- a/drivers/android/binder/trace.rs
+++ b/drivers/android/binder/trace.rs
@@ -5,13 +5,33 @@
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::error::Result;
+use kernel::ffi::{c_int, c_uint, c_ulong};
+use kernel::task::{Pid, Task};
use kernel::tracepoint::declare_trace;

declare_trace! {
unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong);
+ unsafe fn binder_ioctl_done(ret: c_int);
+ unsafe fn binder_read_done(ret: c_int);
+ unsafe fn binder_write_done(ret: c_int);
+ unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool);
unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct);
+ unsafe fn binder_transaction_received(t: rust_binder_transaction);
+ unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize);
+ unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize);
+ unsafe fn binder_alloc_lru_start(pid: c_int, page_index: usize);
+ unsafe fn binder_alloc_lru_end(pid: c_int, page_index: usize);
+ unsafe fn binder_free_lru_start(pid: c_int, page_index: usize);
+ unsafe fn binder_free_lru_end(pid: c_int, page_index: usize);
+ unsafe fn binder_alloc_page_start(pid: c_int, page_index: usize);
+ unsafe fn binder_alloc_page_end(pid: c_int, page_index: usize);
+ unsafe fn binder_unmap_user_start(pid: c_int, page_index: usize);
+ unsafe fn binder_unmap_user_end(pid: c_int, page_index: usize);
+ unsafe fn binder_unmap_kernel_start(pid: c_int, page_index: usize);
+ unsafe fn binder_unmap_kernel_end(pid: c_int, page_index: usize);
+ unsafe fn binder_command(cmd: u32);
+ unsafe fn binder_return(ret: u32);
}

#[inline]
@@ -19,12 +39,44 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
t as *const Transaction as rust_binder_transaction
}

+#[inline]
+fn to_errno(ret: Result) -> i32 {
+ match ret {
+ Ok(()) => 0,
+ Err(err) => err.to_errno(),
+ }
+}
+
#[inline]
pub(crate) fn trace_ioctl(cmd: u32, arg: usize) {
// SAFETY: Always safe to call.
unsafe { binder_ioctl(cmd, arg as c_ulong) }
}

+#[inline]
+pub(crate) fn trace_ioctl_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_ioctl_done(to_errno(ret)) }
+}
+#[inline]
+pub(crate) fn trace_read_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_read_done(to_errno(ret)) }
+}
+#[inline]
+pub(crate) fn trace_write_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_write_done(to_errno(ret)) }
+}
+
+#[inline]
+pub(crate) fn trace_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_wait_for_work(proc_work, transaction_stack, thread_todo) }
+}
+
+
+
#[inline]
pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) {
let thread = match thread {
@@ -35,3 +87,58 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta
// valid or null.
unsafe { binder_transaction(reply, raw_transaction(t), thread) }
}
+
+#[inline]
+pub(crate) fn trace_transaction_received(t: &Transaction) {
+ // SAFETY: The raw transaction is valid for the duration of this call.
+ unsafe { binder_transaction_received(raw_transaction(t)) }
+}
+
+#[inline]
+pub(crate) fn trace_transaction_fd_send(t_debug_id: usize, fd: u32, offset: usize) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_transaction_fd_send(t_debug_id as c_int, fd as c_int, offset) }
+}
+#[inline]
+pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usize) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) }
+}
+
+macro_rules! define_wrapper_lru_page_class {
+ ($($name:ident),* $(,)?) => {
+ $(
+ kernel::macros::paste! {
+ #[inline]
+ pub(crate) fn [<trace_ $name>](pid: Pid, page_index: usize) {
+ // SAFETY: Always safe to call.
+ unsafe { [<binder_ $name>](pid as c_int, page_index) }
+ }
+ }
+ )*
+ };
+}
+
+define_wrapper_lru_page_class!(
+ alloc_lru_start,
+ alloc_lru_end,
+ free_lru_start,
+ free_lru_end,
+ alloc_page_start,
+ alloc_page_end,
+ unmap_user_start,
+ unmap_user_end,
+ unmap_kernel_start,
+ unmap_kernel_end,
+);
+
+#[inline]
+pub(crate) fn trace_command(cmd: u32) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_command(cmd) }
+}
+#[inline]
+pub(crate) fn trace_return(ret: u32) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_return(ret) }
+}

--
2.52.0