mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jungseok Lee <jungseoklee85@gmail.com>
To: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: catalin.marinas@arm.com, will.deacon@arm.com,
	rostedt@goodmis.org, broonie@kernel.org, david.griego@linaro.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5 5/6] arm64: ftrace: add arch-specific stack tracer
Date: Thu, 12 Nov 2015 07:56:59 +0900	[thread overview]
Message-ID: <E26899F2-B934-45D1-BC45-141D2D2AAAED@gmail.com> (raw)
In-Reply-To: <5642CC2E.7010405@linaro.org>

On Nov 11, 2015, at 2:03 PM, AKASHI Takahiro wrote:
> Jungseok,
> 
> On 11/10/2015 11:05 PM, Jungseok Lee wrote:
>> On Nov 6, 2015, at 3:44 PM, AKASHI Takahiro wrote:
>> 
>> Hi Akashi,
>> 
>>> Stack tracer on arm64, check_stack(), is uniqeue in the following
>>> points:
>>> * analyze a function prologue of a traced function to estimate a more
>>>  accurate stack pointer value, replacing naive '<child's fp> + 0x10.'
>>> * use walk_stackframe(), instead of slurping stack contents as orignal
>>>  check_stack() does, to identify a stack frame and a stack index (height)
>>>  for every callsite.
>>> 
>>> Regarding a function prologue analyzer, there is no guarantee that we can
>>> handle all the possible patterns of function prologue as gcc does not use
>>> any fixed templates to generate them. 'Instruction scheduling' is another
>>> issue here.
>>> Nevertheless, the current version will surely cover almost all the cases
>>> in the kernel image and give us useful information on stack pointers.
>>> 
>>> So this patch utilizes a function prologue only for stack tracer, and
>>> does not affect the behaviors of existing stacktrace functions.
>>> 
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>> arch/arm64/include/asm/stacktrace.h |    4 +
>>> arch/arm64/kernel/ftrace.c          |   64 ++++++++++++
>>> arch/arm64/kernel/stacktrace.c      |  185 ++++++++++++++++++++++++++++++++++-
>>> 3 files changed, 250 insertions(+), 3 deletions(-)
>>> 
>>> diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
>>> index 7318f6d..47b4832 100644
>>> --- a/arch/arm64/include/asm/stacktrace.h
>>> +++ b/arch/arm64/include/asm/stacktrace.h
>>> @@ -25,5 +25,9 @@ struct stackframe {
>>> extern int unwind_frame(struct stackframe *frame);
>>> extern void walk_stackframe(struct stackframe *frame,
>>> 			    int (*fn)(struct stackframe *, void *), void *data);
>>> +#ifdef CONFIG_STACK_TRACER
>>> +struct stack_trace;
>>> +extern void save_stack_trace_sp(struct stack_trace *trace, unsigned long *sp);
>>> +#endif
>>> 
>>> #endif	/* __ASM_STACKTRACE_H */
>>> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
>>> index 314f82d..46bfe37 100644
>>> --- a/arch/arm64/kernel/ftrace.c
>>> +++ b/arch/arm64/kernel/ftrace.c
>>> @@ -9,6 +9,7 @@
>>>  * published by the Free Software Foundation.
>>>  */
>>> 
>>> +#include <linux/bug.h>
>>> #include <linux/ftrace.h>
>>> #include <linux/swab.h>
>>> #include <linux/uaccess.h>
>>> @@ -16,6 +17,7 @@
>>> #include <asm/cacheflush.h>
>>> #include <asm/ftrace.h>
>>> #include <asm/insn.h>
>>> +#include <asm/stacktrace.h>
>>> 
>>> #ifdef CONFIG_DYNAMIC_FTRACE
>>> /*
>>> @@ -173,3 +175,65 @@ int ftrace_disable_ftrace_graph_caller(void)
>>> }
>>> #endif /* CONFIG_DYNAMIC_FTRACE */
>>> #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
>>> +
>>> +#ifdef CONFIG_STACK_TRACER
>>> +static unsigned long stack_trace_sp[STACK_TRACE_ENTRIES];
>>> +static unsigned long raw_stack_trace_max_size;
>>> +
>>> +void check_stack(unsigned long ip, unsigned long *stack)
>>> +{
>>> +	unsigned long this_size, flags;
>>> +	unsigned long top;
>>> +	int i, j;
>>> +
>>> +	this_size = ((unsigned long)stack) & (THREAD_SIZE-1);
>>> +	this_size = THREAD_SIZE - this_size;
>>> +
>>> +	if (this_size <= raw_stack_trace_max_size)
>>> +		return;
>>> +
>>> +	/* we do not handle an interrupt stack yet */
>>> +	if (!object_is_on_stack(stack))
>>> +		return;
>>> +
>>> +	local_irq_save(flags);
>>> +	arch_spin_lock(&max_stack_lock);
>>> +
>>> +	/* check again */
>>> +	if (this_size <= raw_stack_trace_max_size)
>>> +		goto out;
>>> +
>>> +	/* find out stack frames */
>>> +	stack_trace_max.nr_entries = 0;
>>> +	stack_trace_max.skip = 0;
>>> +	save_stack_trace_sp(&stack_trace_max, stack_trace_sp);
>>> +	stack_trace_max.nr_entries--; /* for the last entry ('-1') */
>>> +
>>> +	/* calculate a stack index for each function */
>>> +	top = ((unsigned long)stack & ~(THREAD_SIZE-1)) + THREAD_SIZE;
>>> +	for (i = 0; i < stack_trace_max.nr_entries; i++)
>>> +		stack_trace_index[i] = top - stack_trace_sp[i];
>>> +	raw_stack_trace_max_size = this_size;
>>> +
>>> +	/* Skip over the overhead of the stack tracer itself */
>>> +	for (i = 0; i < stack_trace_max.nr_entries; i++)
>>> +		if (stack_trace_max.entries[i] == ip)
>>> +			break;
>>> +
>>> +	stack_trace_max.nr_entries -= i;
>>> +	for (j = 0; j < stack_trace_max.nr_entries; j++) {
>>> +		stack_trace_index[j] = stack_trace_index[j + i];
>>> +		stack_trace_max.entries[j] = stack_trace_max.entries[j + i];
>>> +	}
>>> +	stack_trace_max_size = stack_trace_index[0];
>>> +
>>> +	if (task_stack_end_corrupted(current)) {
>>> +		WARN(1, "task stack is corrupted.\n");
>>> +		stack_trace_print();
>>> +	}
>>> +
>>> + out:
>>> +	arch_spin_unlock(&max_stack_lock);
>>> +	local_irq_restore(flags);
>>> +}
>>> +#endif /* CONFIG_STACK_TRACER */
>>> diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
>>> index 5fd3477..4ee052a 100644
>>> --- a/arch/arm64/kernel/stacktrace.c
>>> +++ b/arch/arm64/kernel/stacktrace.c
>>> @@ -23,6 +23,144 @@
>>> 
>>> #include <asm/stacktrace.h>
>>> 
>>> +#ifdef CONFIG_STACK_TRACER
>>> +/*
>>> + * This function parses a function prologue of a traced function and
>>> + * determines its stack size.
>>> + * A return value indicates a location of @pc in a function prologue.
>>> + * @return value:
>>> + * <case 1>                       <case 1'>
>>> + * 1:
>>> + *     sub sp, sp, #XX            sub sp, sp, #XX
>>> + * 2:
>>> + *     stp x29, x30, [sp, #YY]    stp x29, x30, [sp, #--ZZ]!
>>> + * 3:
>>> + *     add x29, sp, #YY           mov x29, sp
>>> + * 0:
>>> + *
>>> + * <case 2>
>>> + * 1:
>>> + *     stp x29, x30, [sp, #-XX]!
>>> + * 3:
>>> + *     mov x29, sp
>>> + * 0:
>>> + *
>>> + * @size: sp offset from calller's sp (XX or XX + ZZ)
>>> + * @size2: fp offset from new sp (YY or 0)
>>> + */
>>> +static int analyze_function_prologue(unsigned long pc,
>>> +		unsigned long *size, unsigned long *size2)
>>> +{
>>> +	unsigned long offset;
>>> +	u32 *addr, insn;
>>> +	int pos = -1;
>>> +	enum aarch64_insn_register src, dst, reg1, reg2, base;
>>> +	int imm;
>>> +	enum aarch64_insn_variant variant;
>>> +	enum aarch64_insn_adsb_type adsb_type;
>>> +	enum aarch64_insn_ldst_type ldst_type;
>>> +
>>> +	*size = *size2 = 0;
>>> +
>>> +	if (!pc)
>>> +		goto out;
>>> +
>>> +	if (unlikely(!kallsyms_lookup_size_offset(pc, NULL, &offset)))
>>> +		goto out;
>>> +
>>> +	addr = (u32 *)(pc - offset);
>>> +#ifdef CONFIG_DYNAMIC_FTRACE
>>> +	if (addr == (u32 *)ftrace_call)
>>> +		addr = (u32 *)ftrace_caller;
>>> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
>>> +	else if (addr == (u32 *)ftrace_graph_caller)
>>> +#ifdef CONFIG_DYNAMIC_FTRACE
>>> +		addr = (u32 *)ftrace_caller;
>>> +#else
>>> +		addr = (u32 *)_mcount;
>>> +#endif
>>> +#endif
>>> +#endif
>>> +
>>> +	insn = *addr;
>>> +	pos = 1;
>>> +
>>> +	/* analyze a function prologue */
>>> +	while ((unsigned long)addr < pc) {
>>> +		if (aarch64_insn_is_branch_imm(insn) ||
>>> +		    aarch64_insn_is_br(insn) ||
>>> +		    aarch64_insn_is_blr(insn) ||
>>> +		    aarch64_insn_is_ret(insn) ||
>>> +		    aarch64_insn_is_eret(insn))
>>> +			/* exiting a basic block */
>>> +			goto out;
>>> +
>>> +		if (aarch64_insn_decode_add_sub_imm(insn, &dst, &src,
>>> +					&imm, &variant, &adsb_type)) {
>>> +			if ((adsb_type == AARCH64_INSN_ADSB_SUB) &&
>>> +				(dst == AARCH64_INSN_REG_SP) &&
>>> +				(src == AARCH64_INSN_REG_SP)) {
>>> +				/*
>>> +				 * Starting the following sequence:
>>> +				 *   sub sp, sp, #xx
>>> +				 *   stp x29, x30, [sp, #yy]
>>> +				 *   add x29, sp, #yy
>>> +				 */
>>> +				WARN_ON(pos != 1);
>>> +				pos = 2;
>>> +				*size += imm;
>>> +			} else if ((adsb_type == AARCH64_INSN_ADSB_ADD) &&
>>> +				(dst == AARCH64_INSN_REG_29) &&
>>> +				(src == AARCH64_INSN_REG_SP)) {
>>> +				/*
>>> +				 *   add x29, sp, #yy
>>> +				 * or
>>> +				 *   mov x29, sp
>>> +				 */
>>> +				WARN_ON(pos != 3);
>>> +				pos = 0;
>>> +				*size2 = imm;
>>> +
>>> +				break;
>>> +			}
>>> +		} else if (aarch64_insn_decode_load_store_pair(insn,
>>> +					&reg1, &reg2, &base, &imm,
>>> +					&variant, &ldst_type)) {
>>> +			if ((ldst_type ==
>>> +				AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX) &&
>>> +			    (reg1 == AARCH64_INSN_REG_29) &&
>>> +			    (reg2 == AARCH64_INSN_REG_30) &&
>>> +			    (base == AARCH64_INSN_REG_SP)) {
>>> +				/*
>>> +				 * Starting the following sequence:
>>> +				 *   stp x29, x30, [sp, #-xx]!
>>> +				 *   mov x29, sp
>>> +				 */
>>> +				WARN_ON(!((pos == 1) || (pos == 2)));
>>> +				pos = 3;
>>> +				*size += -imm;
>>> +			} else if ((ldst_type ==
>>> +				AARCH64_INSN_LDST_STORE_PAIR) &&
>>> +			    (reg1 == AARCH64_INSN_REG_29) &&
>>> +			    (reg2 == AARCH64_INSN_REG_30) &&
>>> +			    (base == AARCH64_INSN_REG_SP)) {
>>> +				/*
>>> +				 *   stp x29, x30, [sp, #yy]
>>> +				 */
>>> +				WARN_ON(pos != 2);
>>> +				pos = 3;
>>> +			}
>>> +		}
>>> +
>>> +		addr++;
>>> +		insn = *addr;
>>> +	}
>>> +
>>> +out:
>>> +	return pos;
>>> +}
>>> +#endif
>>> +
>> 
>> A small instruction decoder in software!
>> 
>> From a high level point of view, it makes sense to deal with only three main
>> cases. Since it is tough to deal with all function prologues in perspective
>> of computational overhead and implementation complexity, the above change looks
>> like a pretty good tradeoff. This is what I can say now. I'm also interested
>> in feedbacks from other folks.
> 
> I really appreciate your reviews and comments on the whole series of my patches
> (#2 to #5).
> May I add Reviewed-by (and Tested-by?) with your name if you like?

Sure. Tested-by is also okay.

Regarding patch6, the option would be useful if this analyzer is accepted. So,
like this patch5, I'm waiting for other folk's response. 

How about adding your document [1] to this series? I think it explains all
backgrounds, such as motivation, issue, approach, and TODO, on this series.

[1] http://www.spinics.net/lists/arm-kernel/msg444300.html

Best Regards
Jungseok Lee

  reply	other threads:[~2015-11-11 22:57 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-06  6:44 [PATCH v5 0/6] arm64: ftrace: fix incorrect output from " AKASHI Takahiro
2015-11-06  6:44 ` [PATCH v5 1/6] arm64: ftrace: modify a stack frame in a safe way AKASHI Takahiro
2015-11-06  6:44 ` [PATCH v5 2/6] arm64: ftrace: fix a stack tracer's output under function graph tracer AKASHI Takahiro
2015-11-09 14:04   ` Jungseok Lee
2015-11-10  2:42     ` AKASHI Takahiro
2015-11-13 15:01       ` Jungseok Lee
2015-11-16  9:23         ` AKASHI Takahiro
2015-11-06  6:44 ` [PATCH v5 3/6] ftrace: allow arch-specific stack tracer AKASHI Takahiro
2015-11-06 13:39   ` Steven Rostedt
2015-11-06  6:44 ` [PATCH v5 4/6] arm64: insn: add instruction decoders for ldp/stp and add/sub AKASHI Takahiro
2015-11-10 13:40   ` Jungseok Lee
2015-11-11  4:54     ` AKASHI Takahiro
2015-11-06  6:44 ` [PATCH v5 5/6] arm64: ftrace: add arch-specific stack tracer AKASHI Takahiro
2015-11-10 14:05   ` Jungseok Lee
2015-11-11  5:03     ` AKASHI Takahiro
2015-11-11 22:56       ` Jungseok Lee [this message]
2015-11-06  6:44 ` [PATCH v5 6/6] arm64: ftrace: add a test of function prologue analyzer AKASHI Takahiro
2015-11-09 14:24 ` [PATCH v5 0/6] arm64: ftrace: fix incorrect output from stack tracer Jungseok Lee
2015-11-10  2:58   ` AKASHI Takahiro
2015-11-10 13:32     ` Jungseok Lee

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=E26899F2-B934-45D1-BC45-141D2D2AAAED@gmail.com \
    --to=jungseoklee85@gmail.com \
    --cc=broonie@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=david.griego@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=takahiro.akashi@linaro.org \
    --cc=will.deacon@arm.com \
    /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®