linux-toolchains.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Indu Bhagat <indu.bhagat@oracle.com>
To: linux-toolchains@vger.kernel.org, rostedt@goodmis.org,
	peterz@infradead.org
Cc: Indu Bhagat <indu.bhagat@oracle.com>
Subject: [POC,V2 5/5] x86_64: invoke SFrame based stack tracer for user space
Date: Thu, 25 May 2023 22:32:15 -0700	[thread overview]
Message-ID: <20230526053215.3617580-6-indu.bhagat@oracle.com> (raw)
In-Reply-To: <20230526053215.3617580-1-indu.bhagat@oracle.com>

[Changes in V2]
  - Code changes to use the sframe_unwind.h from its new location
    (include/linux/sframe_unwind.h).
  - No further changes yet. ATM, it is understood that this POC is
    broken because accessing SFrame sections may fault while the
    perf event is being handled in the NMI context. The changes in this
    patch are expected to be reworked.
[End of changes in V2]

The task's sframe_state is allocated and initialized if a phdr with type
PT_GNU_SFRAME is encountered for the binary.

perf_callchain_user() will fall back on the frame pointer based stack
trace approach if:
  - SFrame section for the main program is not found.
  - SFrame state for the task is either not setup or stale and cannot
  be refreshed.

Finally, the sframe_state is cleaned up in release_task().

Signed-off-by: Indu Bhagat <indu.bhagat@oracle.com>
---
 arch/x86/events/core.c | 51 ++++++++++++++++++++++++++++++++++++++++++
 fs/binfmt_elf.c        | 37 ++++++++++++++++++++++++++++++
 kernel/exit.c          |  9 ++++++++
 3 files changed, 97 insertions(+)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index d096b04bf80e..0351e3c444a3 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2860,11 +2860,54 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *ent
 }
 #endif
 
+#ifdef CONFIG_USER_UNWINDER_SFRAME
+
+#include <linux/sframe_unwind.h>
+
+/* Check if the specified task has SFrame unwind state set up.  */
+static inline bool check_sframe_state_p(struct task_struct *task)
+{
+	bool sframe_ok = false;
+
+	/*
+	 * FIXME TODO - only current task can be unwinded at this time.
+	 * Even for current tasks, following unknowns remain and hence, not
+	 * handled:
+	 *    - dlopen / dlclose detection and update of sframe_state,
+	 *    - in general, any change in memory mappings.
+	 */
+	if (task != current)
+		return false;
+
+	if (!task->sframe_state)
+		return false;
+
+	sframe_ok = unwind_sframe_state_ready_p(task->sframe_state);
+
+	return sframe_ok;
+}
+
+#else
+/* Check if the specified task has SFrame unwind state set up. */
+static inline bool check_sframe_state_p(struct task_struct *task)
+{
+	return false;
+}
+
+static inline int sframe_callchain_user(struct task_struct *task,
+					struct perf_callchain_entry_ctx *entry,
+					struct pt_regs *regs)
+{
+	return 0;
+}
+#endif
+
 void
 perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
 {
 	struct stack_frame frame;
 	const struct stack_frame __user *fp;
+	bool sframe_avail;
 
 	if (perf_guest_state()) {
 		/* TODO: We don't support guest os callchain now */
@@ -2887,7 +2930,15 @@ perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs
 	if (perf_callchain_user32(regs, entry))
 		return;
 
+	sframe_avail = check_sframe_state_p(current);
+
 	pagefault_disable();
+
+	if (sframe_avail && !sframe_callchain_user(current, entry, regs)) {
+		pagefault_enable();
+		return;
+	}
+
 	while (entry->nr < entry->max_stack) {
 		if (!valid_user_frame(fp, sizeof(frame)))
 			break;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 1033fbdfdbec..750f9d397c1e 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -820,6 +820,32 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
 	return ret == -ENOENT ? 0 : ret;
 }
 
+#ifdef CONFIG_USER_UNWINDER_SFRAME
+
+#include <linux/sframe_unwind.h>
+
+static inline int sframe_state_setup(void)
+{
+	int ret;
+
+	/* Allocate the SFrame state (per task) if NULL. */
+	if (!current->sframe_state)
+		current->sframe_state = unwind_sframe_state_alloc(current);
+
+	if (!current->sframe_state)
+		return -ENOMEM;
+
+	ret = unwind_sframe_state_update(current);
+
+	return ret;
+}
+#else
+static inline int sframe_state_setup(void)
+{
+	return 0;
+}
+#endif
+
 static int load_elf_binary(struct linux_binprm *bprm)
 {
 	struct file *interpreter = NULL; /* to shut gcc up */
@@ -842,6 +868,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
 	struct mm_struct *mm;
 	struct pt_regs *regs;
+	bool sframe_avail = false;
 
 	retval = -ENOEXEC;
 	/* First of all, some simple consistency checks */
@@ -861,6 +888,14 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	if (!elf_phdata)
 		goto out;
 
+	elf_ppnt = elf_phdata;
+	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
+		if (elf_ppnt->p_type == PT_GNU_SFRAME) {
+			sframe_avail = true;
+			break;
+		}
+	}
+
 	elf_ppnt = elf_phdata;
 	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
 		char *elf_interpreter;
@@ -1342,6 +1377,8 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	 */
 	ELF_PLAT_INIT(regs, reloc_func_desc);
 #endif
+	if (sframe_avail && sframe_state_setup())
+		goto out;
 
 	finalize_exec(bprm);
 	START_THREAD(elf_ex, regs, elf_entry, bprm->p);
diff --git a/kernel/exit.c b/kernel/exit.c
index 34b90e2e7cf7..14c57ecd03ea 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -70,6 +70,10 @@
 #include <linux/sysfs.h>
 #include <linux/user_events.h>
 
+#ifdef CONFIG_USER_UNWINDER_SFRAME
+#include <linux/sframe_unwind.h>
+#endif
+
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/mmu_context.h>
@@ -241,6 +245,11 @@ void release_task(struct task_struct *p)
 	struct task_struct *leader;
 	struct pid *thread_pid;
 	int zap_leader;
+
+#ifdef CONFIG_USER_UNWINDER_SFRAME
+	unwind_sframe_state_cleanup(p);
+#endif
+
 repeat:
 	/* don't need to get the RCU readlock here - the process is dead and
 	 * can't be modifying its own credentials. But shut RCU-lockdep up */
-- 
2.39.2


  parent reply	other threads:[~2023-05-26  5:32 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-26  5:32 [POC,V2 0/5] SFrame based stack tracer for user space in the kernel Indu Bhagat
2023-05-26  5:32 ` [POC,V2 1/5] Kconfig: x86: Add new config options for userspace unwinder Indu Bhagat
2023-05-26  5:32 ` [POC,V2 2/5] task_struct : add additional member for sframe state Indu Bhagat
2023-05-26  5:32 ` [POC,V2 3/5] sframe: add new SFrame library Indu Bhagat
2023-05-26  5:32 ` [POC,V2 4/5] sframe: add an SFrame format stack tracer Indu Bhagat
2023-05-26  5:32 ` Indu Bhagat [this message]
2023-05-26  7:56 ` [POC,V2 0/5] SFrame based stack tracer for user space in the kernel Steven Rostedt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230526053215.3617580-6-indu.bhagat@oracle.com \
    --to=indu.bhagat@oracle.com \
    --cc=linux-toolchains@vger.kernel.org \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).