stacktrace: Provide helpers for common stack trace operations
authorThomas Gleixner <tglx@linutronix.de>
Thu, 25 Apr 2019 09:44:55 +0000 (11:44 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Mon, 29 Apr 2019 10:37:46 +0000 (12:37 +0200)
All operations with stack traces are based on struct stack_trace. That's a
horrible construct as the struct is a kitchen sink for input and
output. Quite some usage sites embed it into their own data structures
which creates weird indirections.

There is absolutely no point in doing so. For all use cases a storage array
and the number of valid stack trace entries in the array is sufficient.

Provide helper functions which avoid the struct stack_trace indirection so
the usage sites can be cleaned up.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Alexander Potapenko <glider@google.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: linux-mm@kvack.org
Cc: David Rientjes <rientjes@google.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: kasan-dev@googlegroups.com
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: iommu@lists.linux-foundation.org
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
Cc: David Sterba <dsterba@suse.com>
Cc: Chris Mason <clm@fb.com>
Cc: Josef Bacik <josef@toxicpanda.com>
Cc: linux-btrfs@vger.kernel.org
Cc: dm-devel@redhat.com
Cc: Mike Snitzer <snitzer@redhat.com>
Cc: Alasdair Kergon <agk@redhat.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: intel-gfx@lists.freedesktop.org
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: dri-devel@lists.freedesktop.org
Cc: David Airlie <airlied@linux.ie>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Tom Zanussi <tom.zanussi@linux.intel.com>
Cc: Miroslav Benes <mbenes@suse.cz>
Cc: linux-arch@vger.kernel.org
Link: https://lkml.kernel.org/r/20190425094801.324810708@linutronix.de
include/linux/stacktrace.h
kernel/stacktrace.c

index ba29a0613e66ffa83162692f05e367e7dd129d82..a24340b3e9e142766fe65cb03394132a73cd3d7f 100644 (file)
@@ -3,11 +3,26 @@
 #define __LINUX_STACKTRACE_H
 
 #include <linux/types.h>
+#include <asm/errno.h>
 
 struct task_struct;
 struct pt_regs;
 
 #ifdef CONFIG_STACKTRACE
+void stack_trace_print(unsigned long *trace, unsigned int nr_entries,
+                      int spaces);
+int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
+                       unsigned int nr_entries, int spaces);
+unsigned int stack_trace_save(unsigned long *store, unsigned int size,
+                             unsigned int skipnr);
+unsigned int stack_trace_save_tsk(struct task_struct *task,
+                                 unsigned long *store, unsigned int size,
+                                 unsigned int skipnr);
+unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
+                                  unsigned int size, unsigned int skipnr);
+unsigned int stack_trace_save_user(unsigned long *store, unsigned int size);
+
+/* Internal interfaces. Do not use in generic code */
 struct stack_trace {
        unsigned int nr_entries, max_entries;
        unsigned long *entries;
@@ -41,4 +56,16 @@ extern void save_stack_trace_user(struct stack_trace *trace);
 # define save_stack_trace_tsk_reliable(tsk, trace)     ({ -ENOSYS; })
 #endif /* CONFIG_STACKTRACE */
 
+#if defined(CONFIG_STACKTRACE) && defined(CONFIG_HAVE_RELIABLE_STACKTRACE)
+int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
+                                 unsigned int size);
+#else
+static inline int stack_trace_save_tsk_reliable(struct task_struct *tsk,
+                                               unsigned long *store,
+                                               unsigned int size)
+{
+       return -ENOSYS;
+}
+#endif
+
 #endif /* __LINUX_STACKTRACE_H */
index f8edee9c792de527cfb968664cc1ae91ea062d1f..b38333b3bc181f2d33db80b2c1c75fa92550ca95 100644 (file)
 #include <linux/kallsyms.h>
 #include <linux/stacktrace.h>
 
-void print_stack_trace(struct stack_trace *trace, int spaces)
+/**
+ * stack_trace_print - Print the entries in the stack trace
+ * @entries:   Pointer to storage array
+ * @nr_entries:        Number of entries in the storage array
+ * @spaces:    Number of leading spaces to print
+ */
+void stack_trace_print(unsigned long *entries, unsigned int nr_entries,
+                      int spaces)
 {
-       int i;
+       unsigned int i;
 
-       if (WARN_ON(!trace->entries))
+       if (WARN_ON(!entries))
                return;
 
-       for (i = 0; i < trace->nr_entries; i++)
-               printk("%*c%pS\n", 1 + spaces, ' ', (void *)trace->entries[i]);
+       for (i = 0; i < nr_entries; i++)
+               printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]);
+}
+EXPORT_SYMBOL_GPL(stack_trace_print);
+
+void print_stack_trace(struct stack_trace *trace, int spaces)
+{
+       stack_trace_print(trace->entries, trace->nr_entries, spaces);
 }
 EXPORT_SYMBOL_GPL(print_stack_trace);
 
-int snprint_stack_trace(char *buf, size_t size,
-                       struct stack_trace *trace, int spaces)
+/**
+ * stack_trace_snprint - Print the entries in the stack trace into a buffer
+ * @buf:       Pointer to the print buffer
+ * @size:      Size of the print buffer
+ * @entries:   Pointer to storage array
+ * @nr_entries:        Number of entries in the storage array
+ * @spaces:    Number of leading spaces to print
+ *
+ * Return: Number of bytes printed.
+ */
+int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
+                       unsigned int nr_entries, int spaces)
 {
-       int i;
-       int generated;
-       int total = 0;
+       unsigned int generated, i, total = 0;
 
-       if (WARN_ON(!trace->entries))
+       if (WARN_ON(!entries))
                return 0;
 
-       for (i = 0; i < trace->nr_entries; i++) {
+       for (i = 0; i < nr_entries && size; i++) {
                generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ',
-                                    (void *)trace->entries[i]);
+                                    (void *)entries[i]);
 
                total += generated;
-
-               /* Assume that generated isn't a negative number */
                if (generated >= size) {
                        buf += size;
                        size = 0;
@@ -51,6 +70,14 @@ int snprint_stack_trace(char *buf, size_t size,
 
        return total;
 }
+EXPORT_SYMBOL_GPL(stack_trace_snprint);
+
+int snprint_stack_trace(char *buf, size_t size,
+                       struct stack_trace *trace, int spaces)
+{
+       return stack_trace_snprint(buf, size, trace->entries,
+                                  trace->nr_entries, spaces);
+}
 EXPORT_SYMBOL_GPL(snprint_stack_trace);
 
 /*
@@ -77,3 +104,116 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
        WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n");
        return -ENOSYS;
 }
+
+/**
+ * stack_trace_save - Save a stack trace into a storage array
+ * @store:     Pointer to storage array
+ * @size:      Size of the storage array
+ * @skipnr:    Number of entries to skip at the start of the stack trace
+ *
+ * Return: Number of trace entries stored
+ */
+unsigned int stack_trace_save(unsigned long *store, unsigned int size,
+                             unsigned int skipnr)
+{
+       struct stack_trace trace = {
+               .entries        = store,
+               .max_entries    = size,
+               .skip           = skipnr + 1,
+       };
+
+       save_stack_trace(&trace);
+       return trace.nr_entries;
+}
+EXPORT_SYMBOL_GPL(stack_trace_save);
+
+/**
+ * stack_trace_save_tsk - Save a task stack trace into a storage array
+ * @task:      The task to examine
+ * @store:     Pointer to storage array
+ * @size:      Size of the storage array
+ * @skipnr:    Number of entries to skip at the start of the stack trace
+ *
+ * Return: Number of trace entries stored
+ */
+unsigned int stack_trace_save_tsk(struct task_struct *task,
+                                 unsigned long *store, unsigned int size,
+                                 unsigned int skipnr)
+{
+       struct stack_trace trace = {
+               .entries        = store,
+               .max_entries    = size,
+               .skip           = skipnr + 1,
+       };
+
+       save_stack_trace_tsk(task, &trace);
+       return trace.nr_entries;
+}
+
+/**
+ * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
+ * @regs:      Pointer to pt_regs to examine
+ * @store:     Pointer to storage array
+ * @size:      Size of the storage array
+ * @skipnr:    Number of entries to skip at the start of the stack trace
+ *
+ * Return: Number of trace entries stored
+ */
+unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
+                                  unsigned int size, unsigned int skipnr)
+{
+       struct stack_trace trace = {
+               .entries        = store,
+               .max_entries    = size,
+               .skip           = skipnr,
+       };
+
+       save_stack_trace_regs(regs, &trace);
+       return trace.nr_entries;
+}
+
+#ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
+/**
+ * stack_trace_save_tsk_reliable - Save task stack with verification
+ * @tsk:       Pointer to the task to examine
+ * @store:     Pointer to storage array
+ * @size:      Size of the storage array
+ *
+ * Return:     An error if it detects any unreliable features of the
+ *             stack. Otherwise it guarantees that the stack trace is
+ *             reliable and returns the number of entries stored.
+ *
+ * If the task is not 'current', the caller *must* ensure the task is inactive.
+ */
+int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
+                                 unsigned int size)
+{
+       struct stack_trace trace = {
+               .entries        = store,
+               .max_entries    = size,
+       };
+       int ret = save_stack_trace_tsk_reliable(tsk, &trace);
+
+       return ret ? ret : trace.nr_entries;
+}
+#endif
+
+#ifdef CONFIG_USER_STACKTRACE_SUPPORT
+/**
+ * stack_trace_save_user - Save a user space stack trace into a storage array
+ * @store:     Pointer to storage array
+ * @size:      Size of the storage array
+ *
+ * Return: Number of trace entries stored
+ */
+unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
+{
+       struct stack_trace trace = {
+               .entries        = store,
+               .max_entries    = size,
+       };
+
+       save_stack_trace_user(&trace);
+       return trace.nr_entries;
+}
+#endif /* CONFIG_USER_STACKTRACE_SUPPORT */