mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-03-03 18:28:01 +01:00
rust_binder: add binder_transaction tracepoint
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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
c1093b8589
commit
c1ea31205e
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user