perf events, x86/stacktrace: Fix performance/softlockup by providing a special frame pointer-only stack walker
It's just wasteful for stacktrace users like perf to walk through every entries on the stack whereas these only accept reliable ones, ie: that the frame pointer validates. Since perf requires pure reliable stacktraces, it needs a stack walker based on frame pointers-only to optimize the stacktrace processing. This might solve some near-lockup scenarios that can be triggered by call-graph tracing timer events. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1261024834-5336-2-git-send-regression-fweisbec@gmail.com> [ v2: fix for modular builds and small detail tidyup ] Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
61c1917f47
commit
06d65bda75
3 changed files with 33 additions and 3 deletions
|
@ -22,6 +22,12 @@ print_context_stack(struct thread_info *tinfo,
|
||||||
const struct stacktrace_ops *ops, void *data,
|
const struct stacktrace_ops *ops, void *data,
|
||||||
unsigned long *end, int *graph);
|
unsigned long *end, int *graph);
|
||||||
|
|
||||||
|
extern unsigned long
|
||||||
|
print_context_stack_bp(struct thread_info *tinfo,
|
||||||
|
unsigned long *stack, unsigned long bp,
|
||||||
|
const struct stacktrace_ops *ops, void *data,
|
||||||
|
unsigned long *end, int *graph);
|
||||||
|
|
||||||
/* Generic stack tracer with callbacks */
|
/* Generic stack tracer with callbacks */
|
||||||
|
|
||||||
struct stacktrace_ops {
|
struct stacktrace_ops {
|
||||||
|
|
|
@ -2336,7 +2336,7 @@ static const struct stacktrace_ops backtrace_ops = {
|
||||||
.warning_symbol = backtrace_warning_symbol,
|
.warning_symbol = backtrace_warning_symbol,
|
||||||
.stack = backtrace_stack,
|
.stack = backtrace_stack,
|
||||||
.address = backtrace_address,
|
.address = backtrace_address,
|
||||||
.walk_stack = print_context_stack,
|
.walk_stack = print_context_stack_bp,
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "../dumpstack.h"
|
#include "../dumpstack.h"
|
||||||
|
|
|
@ -109,6 +109,30 @@ print_context_stack(struct thread_info *tinfo,
|
||||||
}
|
}
|
||||||
return bp;
|
return bp;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(print_context_stack);
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
print_context_stack_bp(struct thread_info *tinfo,
|
||||||
|
unsigned long *stack, unsigned long bp,
|
||||||
|
const struct stacktrace_ops *ops, void *data,
|
||||||
|
unsigned long *end, int *graph)
|
||||||
|
{
|
||||||
|
struct stack_frame *frame = (struct stack_frame *)bp;
|
||||||
|
unsigned long *ret_addr = &frame->return_address;
|
||||||
|
|
||||||
|
while (valid_stack_ptr(tinfo, ret_addr, sizeof(*ret_addr), end)) {
|
||||||
|
unsigned long addr = *ret_addr;
|
||||||
|
|
||||||
|
if (__kernel_text_address(addr)) {
|
||||||
|
ops->address(data, addr, 1);
|
||||||
|
frame = frame->next_frame;
|
||||||
|
ret_addr = &frame->return_address;
|
||||||
|
print_ftrace_graph_addr(addr, data, ops, tinfo, graph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (unsigned long)frame;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(print_context_stack_bp);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -143,8 +167,8 @@ static void print_trace_address(void *data, unsigned long addr, int reliable)
|
||||||
static const struct stacktrace_ops print_trace_ops = {
|
static const struct stacktrace_ops print_trace_ops = {
|
||||||
.warning = print_trace_warning,
|
.warning = print_trace_warning,
|
||||||
.warning_symbol = print_trace_warning_symbol,
|
.warning_symbol = print_trace_warning_symbol,
|
||||||
.stack = print_trace_stack,
|
.stack = print_trace_stack,
|
||||||
.address = print_trace_address,
|
.address = print_trace_address,
|
||||||
.walk_stack = print_context_stack,
|
.walk_stack = print_context_stack,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue