*
*/
+/* on 4.9 walk_stackframe was unexported so use save_stack_trace instead */
+#if defined(MODULE) && defined(__aarch64__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
+#define GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME 0
+#else
+#define GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME 1
+#endif
+
+#if (!GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME) && !defined(CONFIG_STACKTRACE)
+#error "CONFIG_STACKTRACE is required for kernel unwinding"
+#endif
+
/*
* EABI backtrace stores {fp,lr} on the stack.
*/
#if defined(__arm__) || defined(__aarch64__)
#include <asm/stacktrace.h>
+#if !GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME
+#include <linux/stacktrace.h>
+#endif
+
+#endif /* defined(__arm__) || defined(__aarch64__) */
+
+#if GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME
+#if defined(__arm__) || defined(__aarch64__)
static int report_trace(struct stackframe *frame, void *d)
{
unsigned int *depth = d, cookie = NO_COOKIE;
return *depth == 0;
}
-#endif
/* Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile */
/* #define GATOR_KERNEL_STACK_UNWINDING */
#endif
}
+#else /* GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME */
+
+/* -------------------------- KERNEL UNWINDING USING save_stack_trace_regs -------------------------- */
+
+#define GATOR_KMOD_STACK_MAX_SIZE 32
+struct gator_kmod_stack { unsigned long addresses[GATOR_KMOD_STACK_MAX_SIZE]; };
+static DEFINE_PER_CPU(struct gator_kmod_stack, gator_kmod_stack);
+
+static void report_trace(unsigned int cpu, struct stack_trace * trace)
+{
+ unsigned int cookie = NO_COOKIE;
+ unsigned int index;
+
+ for (index = 0; index < trace->nr_entries; ++index) {
+ unsigned long addr = trace->entries[index];
+#if defined(MODULE)
+ struct module * mod = __module_address(addr);
+ if (mod) {
+ cookie = get_cookie(cpu, current, mod->name, false);
+ addr = addr -
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
+ (unsigned long) mod->module_core;
+#else
+ (unsigned long) mod->core_layout.base;
+#endif
+ }
+ else {
+ cookie = NO_COOKIE;
+ }
+#endif
+ marshal_backtrace(addr & ~1, cookie, 1);
+ }
+}
+
+static void kernel_backtrace(int cpu, struct pt_regs *const regs)
+{
+ struct stack_trace trace;
+ trace.skip = 0;
+ trace.nr_entries = 0;
+ trace.entries = per_cpu(gator_kmod_stack, cpu).addresses;
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+ trace.max_entries = gator_backtrace_depth;
+#else
+ trace.max_entries = (kernel_stack_unwinding ? gator_backtrace_depth : 1);
+#endif
+ if (trace.max_entries < 1) {
+ trace.max_entries = 1;
+ }
+ else if (trace.max_entries > GATOR_KMOD_STACK_MAX_SIZE) {
+ trace.max_entries = GATOR_KMOD_STACK_MAX_SIZE;
+ }
+
+ save_stack_trace(&trace);
+
+ report_trace(cpu, &trace);
+}
+
+#endif /* GATOR_KERNEL_UNWINDING_USE_WALK_STACKFRAME */
+
+#else /* defined(__arm__) || defined(__aarch64__) */
+
+static void kernel_backtrace(int cpu, struct pt_regs *const regs)
+{
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1);
+}
+
+#endif /* defined(__arm__) || defined(__aarch64__) */
+
static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time)
{
bool in_kernel;