mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jiri Olsa <jolsa@redhat.com>
To: acme@redhat.com, a.p.zijlstra@chello.nl, mingo@elte.hu,
	paulus@samba.org, cjashfor@linux.vnet.ibm.com,
	fweisbec@gmail.com
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH 4/5] unwind, api: Add unwind interface and implementation for x86_64
Date: Fri, 10 Feb 2012 12:25:18 +0100	[thread overview]
Message-ID: <1328873119-21553-5-git-send-email-jolsa@redhat.com> (raw)
In-Reply-To: <1328873119-21553-1-git-send-email-jolsa@redhat.com>

Adding unwind interface with x86_64 implementation.
The interface consists of following functions:

struct unw_t;
- single backtrace handle

void unw_init(struct unw_t *u);
- initialize the handle

void unw_regs(struct unw_t *u, struct pt_regs *regs);
- returns current struct pt_regs registers data

int  unw_step(struct unw_t *u);
- makes single backtrace step

void unw_backtrace(void);
- runs the backtrace unwind and printk it out

The example usage is shown in unw_backtrace function.
---
 arch/x86/include/asm/unwind.h    |   14 +++
 arch/x86/kernel/Makefile         |    1 +
 arch/x86/kernel/unwind_init_64.S |   31 +++++++
 include/linux/unwind.h           |   23 +++++
 kernel/Makefile                  |    1 +
 kernel/unwind.c                  |  180 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/include/asm/unwind.h
 create mode 100644 arch/x86/kernel/unwind_init_64.S
 create mode 100644 include/linux/unwind.h
 create mode 100644 kernel/unwind.c

diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
new file mode 100644
index 0000000..beab2f7
--- /dev/null
+++ b/arch/x86/include/asm/unwind.h
@@ -0,0 +1,14 @@
+#ifndef _ARCH_X86_KERNEL_UNWIND_H
+#define _ARCH_X86_KERNEL_UNWIND_H
+
+#include <asm/ptrace-abi.h>
+
+#ifdef __ASSEMBLY__
+#ifdef __i386__
+#define UNW_X86_CFA_OFF 	(FRAME_SIZE + 0x0)
+#else
+#define UNW_X86_64_CFA_OFF 	(FRAME_SIZE + 0x0)
+#endif /* __i386__ */
+#endif /* __ASSEMBLY__ */
+
+#endif  /* _ARCH_X86_KERNEL_UNWIND_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 8a7c0ec..a77fff3 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
 obj-$(CONFIG_SWIOTLB)			+= pci-swiotlb.o
 obj-$(CONFIG_OF)			+= devicetree.o
 obj-$(CONFIG_UNWIND)			+= dwarf.o
+obj-$(CONFIG_UNWIND)			+= unwind_init_$(BITS).o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/unwind_init_64.S b/arch/x86/kernel/unwind_init_64.S
new file mode 100644
index 0000000..4c8c9ed
--- /dev/null
+++ b/arch/x86/kernel/unwind_init_64.S
@@ -0,0 +1,31 @@
+
+#include <linux/linkage.h>
+#include <asm/ptrace-abi.h>
+#include <asm/unwind.h>
+
+	.code64
+ENTRY(unw_init)
+	/* Callee saved: RBX, RBP, R12-R15  */
+	movq %r12, R12(%rdi)
+	movq %r13, R13(%rdi)
+	movq %r14, R14(%rdi)
+	movq %r15, R15(%rdi)
+	movq %rbp, RBP(%rdi)
+	movq %rbx, RBX(%rdi)
+
+	movq %r8,  R8(%rdi)
+	movq %r9,  R9(%rdi)
+	movq %rdi, RDI(%rdi)
+	movq %rsi, RSI(%rdi)
+	movq %rdx, RDX(%rdi)
+	movq %rax, RAX(%rdi)
+	movq %rcx, RCX(%rdi)
+
+	leaq 8(%rsp), %rax /* exclude this call.  */
+	movq %rax, UNW_X86_64_CFA_OFF(%rdi)
+	movq 0(%rsp), %rax
+	movq %rax, RIP(%rdi)
+
+	xorq %rax, %rax
+	retq
+END(unw_init)
diff --git a/include/linux/unwind.h b/include/linux/unwind.h
new file mode 100644
index 0000000..d99b028
--- /dev/null
+++ b/include/linux/unwind.h
@@ -0,0 +1,23 @@
+#ifndef UNWIND_H
+#define UNWIND_H
+
+#include <linux/ptrace.h>
+#include <asm/unwind.h>
+#include <linux/dwarf.h>
+
+struct unw_t {
+	struct pt_regs regs;
+	dwarf_word_t cfa;
+
+	/*
+	 * First 2 items are touched by assembly code,
+	 * do not move them.
+	 */
+};
+
+void unw_init(struct unw_t *u);
+void unw_regs(struct unw_t *u, struct pt_regs *regs);
+int  unw_step(struct unw_t *u);
+void unw_backtrace(void);
+
+#endif /* UNWIND_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 3ddbc72..d472f4e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_UNWIND) += dwarf-read.o
 obj-$(CONFIG_UNWIND) += dwarf-cfi.o
 obj-$(CONFIG_UNWIND) += dwarf-expression.o
 obj-$(CONFIG_UNWIND) += dwarf-fde.o
+obj-$(CONFIG_UNWIND) += unwind.o
 
 $(obj)/configs.o: $(obj)/config_data.h
 
diff --git a/kernel/unwind.c b/kernel/unwind.c
new file mode 100644
index 0000000..f5191d5
--- /dev/null
+++ b/kernel/unwind.c
@@ -0,0 +1,180 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/unwind.h>
+#include <linux/dwarf.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+
+struct table_entry {
+	int32_t start_ip_offset;
+	int32_t fde_offset;
+};
+
+static struct table_entry* 	table_data;
+static dwarf_word_t 		table_count;
+static dwarf_word_t		table_base;
+
+static struct table_entry*
+lookup(dwarf_word_t ip)
+{
+	struct table_entry *fde = NULL;
+	unsigned long lo, hi, mid;
+
+	ip -= table_base;
+
+	/* Do a binary search for right entry. */
+	for (lo = 0, hi = table_count; lo < hi;)
+	{
+		mid = (lo + hi) / 2;
+		fde = table_data + mid;
+
+		if (ip < fde->start_ip_offset)
+			hi = mid;
+		else
+			lo = mid + 1;
+	}
+
+	if (hi <= 0)
+		return NULL;
+
+	fde = table_data + hi - 1;
+	return fde;
+}
+
+#ifdef CONFIG_UNWIND_EH_FRAME
+extern char __eh_frame_hdr_start[];
+extern char __eh_frame_hdr_end[];
+extern char __eh_frame_start[];
+extern char __eh_frame_end[];
+
+struct eh_frame_hdr {
+	unsigned char version;
+	unsigned char eh_frame_ptr_enc;
+	unsigned char fde_count_enc;
+	unsigned char table_enc;
+};
+
+static int __init eh_frame_init(void)
+{
+	struct eh_frame_hdr *hdr;
+	dwarf_word_t addr, eh_frame_start;
+
+	hdr = (struct eh_frame_hdr *) __eh_frame_hdr_start;
+	addr = (dwarf_word_t) (hdr + 1);
+
+	if (dwarf_read_pointer(&addr, hdr->eh_frame_ptr_enc,
+			       &eh_frame_start)) {
+		printk("unwind failed to read eh_frame_start\n");
+		goto failed;
+	}
+
+	if (dwarf_read_pointer(&addr, hdr->fde_count_enc,
+			       &table_count)) {
+		printk("unwind failed to read fde_count\n");
+		goto failed;
+	}
+
+	if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+		printk("unwind unexpected table_enc\n");
+		goto failed;
+	}
+
+	table_data = (struct table_entry *) addr;
+	table_base = (dwarf_word_t) hdr;
+
+	printk("unwind __eh_frame_hdr_start  %p\n", __eh_frame_hdr_start);
+	printk("unwind __eh_frame_hdr_end    %p\n", __eh_frame_hdr_end);
+	printk("unwind __eh_frame_start      %p\n", __eh_frame_start);
+	printk("unwind __eh_frame_en         %p\n", __eh_frame_end);
+	printk("unwind version               %x\n", hdr->version);
+	printk("unwind eh_frame_ptr_enc      %x\n", hdr->eh_frame_ptr_enc);
+	printk("unwind fde_count_enc         %x\n", hdr->fde_count_enc);
+	printk("unwind table_enc             %x\n", hdr->table_enc);
+	printk("unwind table_data            %p\n", table_data);
+	printk("unwind table_count           %llx\n", table_count);
+	printk("unwind table_base            %llx\n", table_base);
+
+	printk("unwind eh_frame table initialized\n");
+	return 0;
+
+ failed:
+	printk("unwind table initialization failed\n");
+	return -EINVAL;
+}
+#endif /* CONFIG_UNWIND_EH_FRAME */
+
+static int __init unw_init_table(void)
+{
+#ifdef CONFIG_UNWIND_EH_FRAME
+	return eh_frame_init();
+#endif
+	return -EINVAL;
+}
+
+pure_initcall(unw_init_table);
+
+__weak void unw_init(struct unw_t *u)
+{
+}
+
+static void dwarf_regs_get(struct unw_t *u, struct dwarf_regs *regs)
+{
+	regs->cfa = u->cfa;
+	dwarf_regs_pt2dwarf(&u->regs, regs);
+}
+
+static void dwarf_regs_set(struct unw_t *u, struct dwarf_regs *regs)
+{
+	u->cfa = regs->cfa;
+	dwarf_regs_dwarf2pt(regs, &u->regs);
+}
+
+int unw_step(struct unw_t *u)
+{
+	struct table_entry *entry;
+	struct dwarf_fde fde;
+	struct dwarf_regs regs;
+	void *data;
+	int ret;
+
+	entry = lookup(u->regs.ip);
+	if (!entry)
+		return -EINVAL;
+
+	data = (void *) (table_base + entry->fde_offset);
+
+	ret = dwarf_fde_init(&fde, data);
+	if (ret)
+		return ret;
+
+	dwarf_regs_get(u, &regs);
+
+	ret = dwarf_fde_process(&fde, &regs);
+	if (!ret)
+		dwarf_regs_set(u, &regs);
+
+	return ret;
+}
+
+void unw_regs(struct unw_t *u, struct pt_regs *regs)
+{
+	memcpy(regs, &u->regs, sizeof(u->regs));
+}
+
+void unw_backtrace(void)
+{
+	struct unw_t unw;
+	struct pt_regs regs;
+
+	unw_init(&unw);
+
+	printk("unwind backtrace:\n");
+
+	do {
+		unw_regs(&unw, &regs);
+		printk("    [0x%lx] %pS\n", regs.ip, (void *) regs.ip);
+	} while (!unw_step(&unw));
+
+}
-- 
1.7.1


  parent reply	other threads:[~2012-02-10 11:26 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-10 11:25 [RFC 0/5] kernel: backtrace unwind support Jiri Olsa
2012-02-10 11:25 ` [PATCH 1/5] unwind, kconfig: Adding UNWIND* options Jiri Olsa
2012-02-10 11:25 ` [PATCH 2/5] unwind, x86: Generate exception frames data for UNWIND_EH_FRAME option Jiri Olsa
2012-02-10 11:25 ` [PATCH 3/5] unwind, dwarf: Add dwarf unwind support Jiri Olsa
2012-02-10 11:25 ` Jiri Olsa [this message]
2012-02-10 11:25 ` [PATCH 5/5] unwind, test: Add backtrace unwind test code Jiri Olsa
2012-02-10 17:43 ` [RFC 0/5] kernel: backtrace unwind support Peter Zijlstra
2012-02-10 18:59   ` Linus Torvalds
2012-02-10 19:27     ` Arnaldo Carvalho de Melo
2012-02-10 19:32       ` Linus Torvalds
2012-02-10 19:39         ` Arnaldo Carvalho de Melo
2012-02-10 19:42           ` Arnaldo Carvalho de Melo
2012-02-10 19:44       ` Ingo Molnar
2012-02-10 20:18         ` Jiri Olsa
2012-02-10 20:37           ` Linus Torvalds
2012-02-14  2:22             ` Benjamin Herrenschmidt
2012-02-11 14:38           ` Ingo Molnar
2012-02-11 23:36             ` Jiri Olsa
2012-02-11  3:25         ` Frederic Weisbecker

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=1328873119-21553-5-git-send-email-jolsa@redhat.com \
    --to=jolsa@redhat.com \
    --cc=a.p.zijlstra@chello.nl \
    --cc=acme@redhat.com \
    --cc=cjashfor@linux.vnet.ibm.com \
    --cc=fweisbec@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    --cc=paulus@samba.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

all inboxes | Powered by JetHome®