summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2019-04-25 05:44:55 -0400
committerThomas Gleixner <tglx@linutronix.de>2019-04-29 06:37:46 -0400
commite9b98e162aa53cbea7c8b0d6c9d5dc6e0f822b9c (patch)
treee9371b3d5931ee441424286cc581d6393407d983
parent3d9a8072915366b5932beeed97f158f8d4955768 (diff)
stacktrace: Provide helpers for common stack trace operations
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
-rw-r--r--include/linux/stacktrace.h27
-rw-r--r--kernel/stacktrace.c170
2 files changed, 182 insertions, 15 deletions
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index ba29a0613e66..a24340b3e9e1 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -3,11 +3,26 @@
3#define __LINUX_STACKTRACE_H 3#define __LINUX_STACKTRACE_H
4 4
5#include <linux/types.h> 5#include <linux/types.h>
6#include <asm/errno.h>
6 7
7struct task_struct; 8struct task_struct;
8struct pt_regs; 9struct pt_regs;
9 10
10#ifdef CONFIG_STACKTRACE 11#ifdef CONFIG_STACKTRACE
12void stack_trace_print(unsigned long *trace, unsigned int nr_entries,
13 int spaces);
14int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
15 unsigned int nr_entries, int spaces);
16unsigned int stack_trace_save(unsigned long *store, unsigned int size,
17 unsigned int skipnr);
18unsigned int stack_trace_save_tsk(struct task_struct *task,
19 unsigned long *store, unsigned int size,
20 unsigned int skipnr);
21unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
22 unsigned int size, unsigned int skipnr);
23unsigned int stack_trace_save_user(unsigned long *store, unsigned int size);
24
25/* Internal interfaces. Do not use in generic code */
11struct stack_trace { 26struct stack_trace {
12 unsigned int nr_entries, max_entries; 27 unsigned int nr_entries, max_entries;
13 unsigned long *entries; 28 unsigned long *entries;
@@ -41,4 +56,16 @@ extern void save_stack_trace_user(struct stack_trace *trace);
41# define save_stack_trace_tsk_reliable(tsk, trace) ({ -ENOSYS; }) 56# define save_stack_trace_tsk_reliable(tsk, trace) ({ -ENOSYS; })
42#endif /* CONFIG_STACKTRACE */ 57#endif /* CONFIG_STACKTRACE */
43 58
59#if defined(CONFIG_STACKTRACE) && defined(CONFIG_HAVE_RELIABLE_STACKTRACE)
60int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
61 unsigned int size);
62#else
63static inline int stack_trace_save_tsk_reliable(struct task_struct *tsk,
64 unsigned long *store,
65 unsigned int size)
66{
67 return -ENOSYS;
68}
69#endif
70
44#endif /* __LINUX_STACKTRACE_H */ 71#endif /* __LINUX_STACKTRACE_H */
diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c
index f8edee9c792d..b38333b3bc18 100644
--- a/kernel/stacktrace.c
+++ b/kernel/stacktrace.c
@@ -11,35 +11,54 @@
11#include <linux/kallsyms.h> 11#include <linux/kallsyms.h>
12#include <linux/stacktrace.h> 12#include <linux/stacktrace.h>
13 13
14void print_stack_trace(struct stack_trace *trace, int spaces) 14/**
15 * stack_trace_print - Print the entries in the stack trace
16 * @entries: Pointer to storage array
17 * @nr_entries: Number of entries in the storage array
18 * @spaces: Number of leading spaces to print
19 */
20void stack_trace_print(unsigned long *entries, unsigned int nr_entries,
21 int spaces)
15{ 22{
16 int i; 23 unsigned int i;
17 24
18 if (WARN_ON(!trace->entries)) 25 if (WARN_ON(!entries))
19 return; 26 return;
20 27
21 for (i = 0; i < trace->nr_entries; i++) 28 for (i = 0; i < nr_entries; i++)
22 printk("%*c%pS\n", 1 + spaces, ' ', (void *)trace->entries[i]); 29 printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]);
30}
31EXPORT_SYMBOL_GPL(stack_trace_print);
32
33void print_stack_trace(struct stack_trace *trace, int spaces)
34{
35 stack_trace_print(trace->entries, trace->nr_entries, spaces);
23} 36}
24EXPORT_SYMBOL_GPL(print_stack_trace); 37EXPORT_SYMBOL_GPL(print_stack_trace);
25 38
26int snprint_stack_trace(char *buf, size_t size, 39/**
27 struct stack_trace *trace, int spaces) 40 * stack_trace_snprint - Print the entries in the stack trace into a buffer
41 * @buf: Pointer to the print buffer
42 * @size: Size of the print buffer
43 * @entries: Pointer to storage array
44 * @nr_entries: Number of entries in the storage array
45 * @spaces: Number of leading spaces to print
46 *
47 * Return: Number of bytes printed.
48 */
49int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
50 unsigned int nr_entries, int spaces)
28{ 51{
29 int i; 52 unsigned int generated, i, total = 0;
30 int generated;
31 int total = 0;
32 53
33 if (WARN_ON(!trace->entries)) 54 if (WARN_ON(!entries))
34 return 0; 55 return 0;
35 56
36 for (i = 0; i < trace->nr_entries; i++) { 57 for (i = 0; i < nr_entries && size; i++) {
37 generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ', 58 generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ',
38 (void *)trace->entries[i]); 59 (void *)entries[i]);
39 60
40 total += generated; 61 total += generated;
41
42 /* Assume that generated isn't a negative number */
43 if (generated >= size) { 62 if (generated >= size) {
44 buf += size; 63 buf += size;
45 size = 0; 64 size = 0;
@@ -51,6 +70,14 @@ int snprint_stack_trace(char *buf, size_t size,
51 70
52 return total; 71 return total;
53} 72}
73EXPORT_SYMBOL_GPL(stack_trace_snprint);
74
75int snprint_stack_trace(char *buf, size_t size,
76 struct stack_trace *trace, int spaces)
77{
78 return stack_trace_snprint(buf, size, trace->entries,
79 trace->nr_entries, spaces);
80}
54EXPORT_SYMBOL_GPL(snprint_stack_trace); 81EXPORT_SYMBOL_GPL(snprint_stack_trace);
55 82
56/* 83/*
@@ -77,3 +104,116 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
77 WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n"); 104 WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n");
78 return -ENOSYS; 105 return -ENOSYS;
79} 106}
107
108/**
109 * stack_trace_save - Save a stack trace into a storage array
110 * @store: Pointer to storage array
111 * @size: Size of the storage array
112 * @skipnr: Number of entries to skip at the start of the stack trace
113 *
114 * Return: Number of trace entries stored
115 */
116unsigned int stack_trace_save(unsigned long *store, unsigned int size,
117 unsigned int skipnr)
118{
119 struct stack_trace trace = {
120 .entries = store,
121 .max_entries = size,
122 .skip = skipnr + 1,
123 };
124
125 save_stack_trace(&trace);
126 return trace.nr_entries;
127}
128EXPORT_SYMBOL_GPL(stack_trace_save);
129
130/**
131 * stack_trace_save_tsk - Save a task stack trace into a storage array
132 * @task: The task to examine
133 * @store: Pointer to storage array
134 * @size: Size of the storage array
135 * @skipnr: Number of entries to skip at the start of the stack trace
136 *
137 * Return: Number of trace entries stored
138 */
139unsigned int stack_trace_save_tsk(struct task_struct *task,
140 unsigned long *store, unsigned int size,
141 unsigned int skipnr)
142{
143 struct stack_trace trace = {
144 .entries = store,
145 .max_entries = size,
146 .skip = skipnr + 1,
147 };
148
149 save_stack_trace_tsk(task, &trace);
150 return trace.nr_entries;
151}
152
153/**
154 * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
155 * @regs: Pointer to pt_regs to examine
156 * @store: Pointer to storage array
157 * @size: Size of the storage array
158 * @skipnr: Number of entries to skip at the start of the stack trace
159 *
160 * Return: Number of trace entries stored
161 */
162unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
163 unsigned int size, unsigned int skipnr)
164{
165 struct stack_trace trace = {
166 .entries = store,
167 .max_entries = size,
168 .skip = skipnr,
169 };
170
171 save_stack_trace_regs(regs, &trace);
172 return trace.nr_entries;
173}
174
175#ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
176/**
177 * stack_trace_save_tsk_reliable - Save task stack with verification
178 * @tsk: Pointer to the task to examine
179 * @store: Pointer to storage array
180 * @size: Size of the storage array
181 *
182 * Return: An error if it detects any unreliable features of the
183 * stack. Otherwise it guarantees that the stack trace is
184 * reliable and returns the number of entries stored.
185 *
186 * If the task is not 'current', the caller *must* ensure the task is inactive.
187 */
188int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
189 unsigned int size)
190{
191 struct stack_trace trace = {
192 .entries = store,
193 .max_entries = size,
194 };
195 int ret = save_stack_trace_tsk_reliable(tsk, &trace);
196
197 return ret ? ret : trace.nr_entries;
198}
199#endif
200
201#ifdef CONFIG_USER_STACKTRACE_SUPPORT
202/**
203 * stack_trace_save_user - Save a user space stack trace into a storage array
204 * @store: Pointer to storage array
205 * @size: Size of the storage array
206 *
207 * Return: Number of trace entries stored
208 */
209unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
210{
211 struct stack_trace trace = {
212 .entries = store,
213 .max_entries = size,
214 };
215
216 save_stack_trace_user(&trace);
217 return trace.nr_entries;
218}
219#endif /* CONFIG_USER_STACKTRACE_SUPPORT */