1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28637c099SIngo Molnar /*
38637c099SIngo Molnar * kernel/stacktrace.c
48637c099SIngo Molnar *
58637c099SIngo Molnar * Stack trace management functions
68637c099SIngo Molnar *
78637c099SIngo Molnar * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <[email protected]>
88637c099SIngo Molnar */
9214d8ca6SThomas Gleixner #include <linux/sched/task_stack.h>
10214d8ca6SThomas Gleixner #include <linux/sched/debug.h>
118637c099SIngo Molnar #include <linux/sched.h>
129212ddb5SIngo Molnar #include <linux/kernel.h>
139984de1aSPaul Gortmaker #include <linux/export.h>
148637c099SIngo Molnar #include <linux/kallsyms.h>
158637c099SIngo Molnar #include <linux/stacktrace.h>
16f39f21b3SMarco Elver #include <linux/interrupt.h>
178637c099SIngo Molnar
18e9b98e16SThomas Gleixner /**
19e9b98e16SThomas Gleixner * stack_trace_print - Print the entries in the stack trace
20e9b98e16SThomas Gleixner * @entries: Pointer to storage array
21e9b98e16SThomas Gleixner * @nr_entries: Number of entries in the storage array
22e9b98e16SThomas Gleixner * @spaces: Number of leading spaces to print
23e9b98e16SThomas Gleixner */
stack_trace_print(const unsigned long * entries,unsigned int nr_entries,int spaces)24a2970421SBart Van Assche void stack_trace_print(const unsigned long *entries, unsigned int nr_entries,
25e9b98e16SThomas Gleixner int spaces)
268637c099SIngo Molnar {
27e9b98e16SThomas Gleixner unsigned int i;
288637c099SIngo Molnar
29e9b98e16SThomas Gleixner if (WARN_ON(!entries))
30bfeeeeb9SJohannes Berg return;
31bfeeeeb9SJohannes Berg
32e9b98e16SThomas Gleixner for (i = 0; i < nr_entries; i++)
33e9b98e16SThomas Gleixner printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]);
34e9b98e16SThomas Gleixner }
35e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_print);
36e9b98e16SThomas Gleixner
37e9b98e16SThomas Gleixner /**
38e9b98e16SThomas Gleixner * stack_trace_snprint - Print the entries in the stack trace into a buffer
39e9b98e16SThomas Gleixner * @buf: Pointer to the print buffer
40e9b98e16SThomas Gleixner * @size: Size of the print buffer
41e9b98e16SThomas Gleixner * @entries: Pointer to storage array
42e9b98e16SThomas Gleixner * @nr_entries: Number of entries in the storage array
43e9b98e16SThomas Gleixner * @spaces: Number of leading spaces to print
44e9b98e16SThomas Gleixner *
45e9b98e16SThomas Gleixner * Return: Number of bytes printed.
46e9b98e16SThomas Gleixner */
stack_trace_snprint(char * buf,size_t size,const unsigned long * entries,unsigned int nr_entries,int spaces)47a2970421SBart Van Assche int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries,
48e9b98e16SThomas Gleixner unsigned int nr_entries, int spaces)
499a92a6ceSJoonsoo Kim {
50e9b98e16SThomas Gleixner unsigned int generated, i, total = 0;
519a92a6ceSJoonsoo Kim
52e9b98e16SThomas Gleixner if (WARN_ON(!entries))
539a92a6ceSJoonsoo Kim return 0;
549a92a6ceSJoonsoo Kim
55e9b98e16SThomas Gleixner for (i = 0; i < nr_entries && size; i++) {
56bfeda41dSOmar Sandoval generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ',
57e9b98e16SThomas Gleixner (void *)entries[i]);
589a92a6ceSJoonsoo Kim
599a92a6ceSJoonsoo Kim total += generated;
609a92a6ceSJoonsoo Kim if (generated >= size) {
619a92a6ceSJoonsoo Kim buf += size;
629a92a6ceSJoonsoo Kim size = 0;
639a92a6ceSJoonsoo Kim } else {
649a92a6ceSJoonsoo Kim buf += generated;
659a92a6ceSJoonsoo Kim size -= generated;
669a92a6ceSJoonsoo Kim }
679a92a6ceSJoonsoo Kim }
689a92a6ceSJoonsoo Kim
699a92a6ceSJoonsoo Kim return total;
709a92a6ceSJoonsoo Kim }
71e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_snprint);
72e9b98e16SThomas Gleixner
73214d8ca6SThomas Gleixner #ifdef CONFIG_ARCH_STACKWALK
74214d8ca6SThomas Gleixner
75214d8ca6SThomas Gleixner struct stacktrace_cookie {
76214d8ca6SThomas Gleixner unsigned long *store;
77214d8ca6SThomas Gleixner unsigned int size;
78214d8ca6SThomas Gleixner unsigned int skip;
79214d8ca6SThomas Gleixner unsigned int len;
80214d8ca6SThomas Gleixner };
81214d8ca6SThomas Gleixner
stack_trace_consume_entry(void * cookie,unsigned long addr)82264c03a2SMark Brown static bool stack_trace_consume_entry(void *cookie, unsigned long addr)
83214d8ca6SThomas Gleixner {
84214d8ca6SThomas Gleixner struct stacktrace_cookie *c = cookie;
85214d8ca6SThomas Gleixner
86214d8ca6SThomas Gleixner if (c->len >= c->size)
87214d8ca6SThomas Gleixner return false;
88214d8ca6SThomas Gleixner
89214d8ca6SThomas Gleixner if (c->skip > 0) {
90214d8ca6SThomas Gleixner c->skip--;
91214d8ca6SThomas Gleixner return true;
92214d8ca6SThomas Gleixner }
93214d8ca6SThomas Gleixner c->store[c->len++] = addr;
94214d8ca6SThomas Gleixner return c->len < c->size;
95214d8ca6SThomas Gleixner }
96214d8ca6SThomas Gleixner
stack_trace_consume_entry_nosched(void * cookie,unsigned long addr)97264c03a2SMark Brown static bool stack_trace_consume_entry_nosched(void *cookie, unsigned long addr)
98214d8ca6SThomas Gleixner {
99214d8ca6SThomas Gleixner if (in_sched_functions(addr))
100214d8ca6SThomas Gleixner return true;
101264c03a2SMark Brown return stack_trace_consume_entry(cookie, addr);
102214d8ca6SThomas Gleixner }
103214d8ca6SThomas Gleixner
104214d8ca6SThomas Gleixner /**
105214d8ca6SThomas Gleixner * stack_trace_save - Save a stack trace into a storage array
106214d8ca6SThomas Gleixner * @store: Pointer to storage array
107214d8ca6SThomas Gleixner * @size: Size of the storage array
108214d8ca6SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
109214d8ca6SThomas Gleixner *
110214d8ca6SThomas Gleixner * Return: Number of trace entries stored.
111214d8ca6SThomas Gleixner */
stack_trace_save(unsigned long * store,unsigned int size,unsigned int skipnr)112214d8ca6SThomas Gleixner unsigned int stack_trace_save(unsigned long *store, unsigned int size,
113214d8ca6SThomas Gleixner unsigned int skipnr)
114214d8ca6SThomas Gleixner {
115214d8ca6SThomas Gleixner stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
116214d8ca6SThomas Gleixner struct stacktrace_cookie c = {
117214d8ca6SThomas Gleixner .store = store,
118214d8ca6SThomas Gleixner .size = size,
119214d8ca6SThomas Gleixner .skip = skipnr + 1,
120214d8ca6SThomas Gleixner };
121214d8ca6SThomas Gleixner
122214d8ca6SThomas Gleixner arch_stack_walk(consume_entry, &c, current, NULL);
123214d8ca6SThomas Gleixner return c.len;
124214d8ca6SThomas Gleixner }
125214d8ca6SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_save);
126214d8ca6SThomas Gleixner
127214d8ca6SThomas Gleixner /**
128214d8ca6SThomas Gleixner * stack_trace_save_tsk - Save a task stack trace into a storage array
129*5f981878SRandy Dunlap * @tsk: The task to examine
130214d8ca6SThomas Gleixner * @store: Pointer to storage array
131214d8ca6SThomas Gleixner * @size: Size of the storage array
132214d8ca6SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
133214d8ca6SThomas Gleixner *
134214d8ca6SThomas Gleixner * Return: Number of trace entries stored.
135214d8ca6SThomas Gleixner */
stack_trace_save_tsk(struct task_struct * tsk,unsigned long * store,unsigned int size,unsigned int skipnr)136214d8ca6SThomas Gleixner unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store,
137214d8ca6SThomas Gleixner unsigned int size, unsigned int skipnr)
138214d8ca6SThomas Gleixner {
139214d8ca6SThomas Gleixner stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;
140214d8ca6SThomas Gleixner struct stacktrace_cookie c = {
141214d8ca6SThomas Gleixner .store = store,
142214d8ca6SThomas Gleixner .size = size,
143b0c51f15SJiri Slaby /* skip this function if they are tracing us */
1444b48512cSJiri Slaby .skip = skipnr + (current == tsk),
145214d8ca6SThomas Gleixner };
146214d8ca6SThomas Gleixner
147214d8ca6SThomas Gleixner if (!try_get_task_stack(tsk))
148214d8ca6SThomas Gleixner return 0;
149214d8ca6SThomas Gleixner
150214d8ca6SThomas Gleixner arch_stack_walk(consume_entry, &c, tsk, NULL);
151214d8ca6SThomas Gleixner put_task_stack(tsk);
152214d8ca6SThomas Gleixner return c.len;
153214d8ca6SThomas Gleixner }
1547d672f40SChristopher James Halse Rogers EXPORT_SYMBOL_GPL(stack_trace_save_tsk);
155214d8ca6SThomas Gleixner
156214d8ca6SThomas Gleixner /**
157214d8ca6SThomas Gleixner * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
158214d8ca6SThomas Gleixner * @regs: Pointer to pt_regs to examine
159214d8ca6SThomas Gleixner * @store: Pointer to storage array
160214d8ca6SThomas Gleixner * @size: Size of the storage array
161214d8ca6SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
162214d8ca6SThomas Gleixner *
163214d8ca6SThomas Gleixner * Return: Number of trace entries stored.
164214d8ca6SThomas Gleixner */
stack_trace_save_regs(struct pt_regs * regs,unsigned long * store,unsigned int size,unsigned int skipnr)165214d8ca6SThomas Gleixner unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
166214d8ca6SThomas Gleixner unsigned int size, unsigned int skipnr)
167214d8ca6SThomas Gleixner {
168214d8ca6SThomas Gleixner stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
169214d8ca6SThomas Gleixner struct stacktrace_cookie c = {
170214d8ca6SThomas Gleixner .store = store,
171214d8ca6SThomas Gleixner .size = size,
172214d8ca6SThomas Gleixner .skip = skipnr,
173214d8ca6SThomas Gleixner };
174214d8ca6SThomas Gleixner
175214d8ca6SThomas Gleixner arch_stack_walk(consume_entry, &c, current, regs);
176214d8ca6SThomas Gleixner return c.len;
177214d8ca6SThomas Gleixner }
178214d8ca6SThomas Gleixner
179214d8ca6SThomas Gleixner #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
180214d8ca6SThomas Gleixner /**
181214d8ca6SThomas Gleixner * stack_trace_save_tsk_reliable - Save task stack with verification
182214d8ca6SThomas Gleixner * @tsk: Pointer to the task to examine
183214d8ca6SThomas Gleixner * @store: Pointer to storage array
184214d8ca6SThomas Gleixner * @size: Size of the storage array
185214d8ca6SThomas Gleixner *
186214d8ca6SThomas Gleixner * Return: An error if it detects any unreliable features of the
187214d8ca6SThomas Gleixner * stack. Otherwise it guarantees that the stack trace is
188214d8ca6SThomas Gleixner * reliable and returns the number of entries stored.
189214d8ca6SThomas Gleixner *
190214d8ca6SThomas Gleixner * If the task is not 'current', the caller *must* ensure the task is inactive.
191214d8ca6SThomas Gleixner */
stack_trace_save_tsk_reliable(struct task_struct * tsk,unsigned long * store,unsigned int size)192214d8ca6SThomas Gleixner int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
193214d8ca6SThomas Gleixner unsigned int size)
194214d8ca6SThomas Gleixner {
195214d8ca6SThomas Gleixner stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
196214d8ca6SThomas Gleixner struct stacktrace_cookie c = {
197214d8ca6SThomas Gleixner .store = store,
198214d8ca6SThomas Gleixner .size = size,
199214d8ca6SThomas Gleixner };
200214d8ca6SThomas Gleixner int ret;
201214d8ca6SThomas Gleixner
202214d8ca6SThomas Gleixner /*
203214d8ca6SThomas Gleixner * If the task doesn't have a stack (e.g., a zombie), the stack is
204214d8ca6SThomas Gleixner * "reliably" empty.
205214d8ca6SThomas Gleixner */
206214d8ca6SThomas Gleixner if (!try_get_task_stack(tsk))
207214d8ca6SThomas Gleixner return 0;
208214d8ca6SThomas Gleixner
209214d8ca6SThomas Gleixner ret = arch_stack_walk_reliable(consume_entry, &c, tsk);
210214d8ca6SThomas Gleixner put_task_stack(tsk);
2117eaf51a2SJoe Lawrence return ret ? ret : c.len;
212214d8ca6SThomas Gleixner }
213214d8ca6SThomas Gleixner #endif
214214d8ca6SThomas Gleixner
215214d8ca6SThomas Gleixner #ifdef CONFIG_USER_STACKTRACE_SUPPORT
216214d8ca6SThomas Gleixner /**
217214d8ca6SThomas Gleixner * stack_trace_save_user - Save a user space stack trace into a storage array
218214d8ca6SThomas Gleixner * @store: Pointer to storage array
219214d8ca6SThomas Gleixner * @size: Size of the storage array
220214d8ca6SThomas Gleixner *
221214d8ca6SThomas Gleixner * Return: Number of trace entries stored.
222214d8ca6SThomas Gleixner */
stack_trace_save_user(unsigned long * store,unsigned int size)223214d8ca6SThomas Gleixner unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
224214d8ca6SThomas Gleixner {
225214d8ca6SThomas Gleixner stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
226214d8ca6SThomas Gleixner struct stacktrace_cookie c = {
227214d8ca6SThomas Gleixner .store = store,
228214d8ca6SThomas Gleixner .size = size,
229214d8ca6SThomas Gleixner };
230214d8ca6SThomas Gleixner
231214d8ca6SThomas Gleixner /* Trace user stack if not a kernel thread */
2327e8e6816SThomas Gleixner if (current->flags & PF_KTHREAD)
233214d8ca6SThomas Gleixner return 0;
234214d8ca6SThomas Gleixner
235214d8ca6SThomas Gleixner arch_stack_walk_user(consume_entry, &c, task_pt_regs(current));
236cac9b9a4SPeter Zijlstra
237214d8ca6SThomas Gleixner return c.len;
238214d8ca6SThomas Gleixner }
239214d8ca6SThomas Gleixner #endif
240214d8ca6SThomas Gleixner
241214d8ca6SThomas Gleixner #else /* CONFIG_ARCH_STACKWALK */
242214d8ca6SThomas Gleixner
2439212ddb5SIngo Molnar /*
244af085d90SJosh Poimboeuf * Architectures that do not implement save_stack_trace_*()
245af085d90SJosh Poimboeuf * get these weak aliases and once-per-bootup warnings
246c624d33fSMasami Hiramatsu * (whenever this facility is utilized - for example by procfs):
2479212ddb5SIngo Molnar */
2489212ddb5SIngo Molnar __weak void
save_stack_trace_tsk(struct task_struct * tsk,struct stack_trace * trace)2499212ddb5SIngo Molnar save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
2509212ddb5SIngo Molnar {
2519212ddb5SIngo Molnar WARN_ONCE(1, KERN_INFO "save_stack_trace_tsk() not implemented yet.\n");
2529212ddb5SIngo Molnar }
253c624d33fSMasami Hiramatsu
254c624d33fSMasami Hiramatsu __weak void
save_stack_trace_regs(struct pt_regs * regs,struct stack_trace * trace)255c624d33fSMasami Hiramatsu save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
256c624d33fSMasami Hiramatsu {
257c624d33fSMasami Hiramatsu WARN_ONCE(1, KERN_INFO "save_stack_trace_regs() not implemented yet.\n");
258c624d33fSMasami Hiramatsu }
259af085d90SJosh Poimboeuf
260e9b98e16SThomas Gleixner /**
261e9b98e16SThomas Gleixner * stack_trace_save - Save a stack trace into a storage array
262e9b98e16SThomas Gleixner * @store: Pointer to storage array
263e9b98e16SThomas Gleixner * @size: Size of the storage array
264e9b98e16SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
265e9b98e16SThomas Gleixner *
266e9b98e16SThomas Gleixner * Return: Number of trace entries stored
267e9b98e16SThomas Gleixner */
stack_trace_save(unsigned long * store,unsigned int size,unsigned int skipnr)268e9b98e16SThomas Gleixner unsigned int stack_trace_save(unsigned long *store, unsigned int size,
269e9b98e16SThomas Gleixner unsigned int skipnr)
270e9b98e16SThomas Gleixner {
271e9b98e16SThomas Gleixner struct stack_trace trace = {
272e9b98e16SThomas Gleixner .entries = store,
273e9b98e16SThomas Gleixner .max_entries = size,
274e9b98e16SThomas Gleixner .skip = skipnr + 1,
275e9b98e16SThomas Gleixner };
276e9b98e16SThomas Gleixner
277e9b98e16SThomas Gleixner save_stack_trace(&trace);
278e9b98e16SThomas Gleixner return trace.nr_entries;
279e9b98e16SThomas Gleixner }
280e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_save);
281e9b98e16SThomas Gleixner
282e9b98e16SThomas Gleixner /**
283e9b98e16SThomas Gleixner * stack_trace_save_tsk - Save a task stack trace into a storage array
284e9b98e16SThomas Gleixner * @task: The task to examine
285e9b98e16SThomas Gleixner * @store: Pointer to storage array
286e9b98e16SThomas Gleixner * @size: Size of the storage array
287e9b98e16SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
288e9b98e16SThomas Gleixner *
289e9b98e16SThomas Gleixner * Return: Number of trace entries stored
290e9b98e16SThomas Gleixner */
stack_trace_save_tsk(struct task_struct * task,unsigned long * store,unsigned int size,unsigned int skipnr)291e9b98e16SThomas Gleixner unsigned int stack_trace_save_tsk(struct task_struct *task,
292e9b98e16SThomas Gleixner unsigned long *store, unsigned int size,
293e9b98e16SThomas Gleixner unsigned int skipnr)
294e9b98e16SThomas Gleixner {
295e9b98e16SThomas Gleixner struct stack_trace trace = {
296e9b98e16SThomas Gleixner .entries = store,
297e9b98e16SThomas Gleixner .max_entries = size,
298b0c51f15SJiri Slaby /* skip this function if they are tracing us */
2994b48512cSJiri Slaby .skip = skipnr + (current == task),
300e9b98e16SThomas Gleixner };
301e9b98e16SThomas Gleixner
302e9b98e16SThomas Gleixner save_stack_trace_tsk(task, &trace);
303e9b98e16SThomas Gleixner return trace.nr_entries;
304e9b98e16SThomas Gleixner }
3057d672f40SChristopher James Halse Rogers EXPORT_SYMBOL_GPL(stack_trace_save_tsk);
306e9b98e16SThomas Gleixner
307e9b98e16SThomas Gleixner /**
308e9b98e16SThomas Gleixner * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
309e9b98e16SThomas Gleixner * @regs: Pointer to pt_regs to examine
310e9b98e16SThomas Gleixner * @store: Pointer to storage array
311e9b98e16SThomas Gleixner * @size: Size of the storage array
312e9b98e16SThomas Gleixner * @skipnr: Number of entries to skip at the start of the stack trace
313e9b98e16SThomas Gleixner *
314e9b98e16SThomas Gleixner * Return: Number of trace entries stored
315e9b98e16SThomas Gleixner */
stack_trace_save_regs(struct pt_regs * regs,unsigned long * store,unsigned int size,unsigned int skipnr)316e9b98e16SThomas Gleixner unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
317e9b98e16SThomas Gleixner unsigned int size, unsigned int skipnr)
318e9b98e16SThomas Gleixner {
319e9b98e16SThomas Gleixner struct stack_trace trace = {
320e9b98e16SThomas Gleixner .entries = store,
321e9b98e16SThomas Gleixner .max_entries = size,
322e9b98e16SThomas Gleixner .skip = skipnr,
323e9b98e16SThomas Gleixner };
324e9b98e16SThomas Gleixner
325e9b98e16SThomas Gleixner save_stack_trace_regs(regs, &trace);
326e9b98e16SThomas Gleixner return trace.nr_entries;
327e9b98e16SThomas Gleixner }
328e9b98e16SThomas Gleixner
329e9b98e16SThomas Gleixner #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
330e9b98e16SThomas Gleixner /**
331e9b98e16SThomas Gleixner * stack_trace_save_tsk_reliable - Save task stack with verification
332e9b98e16SThomas Gleixner * @tsk: Pointer to the task to examine
333e9b98e16SThomas Gleixner * @store: Pointer to storage array
334e9b98e16SThomas Gleixner * @size: Size of the storage array
335e9b98e16SThomas Gleixner *
336e9b98e16SThomas Gleixner * Return: An error if it detects any unreliable features of the
337e9b98e16SThomas Gleixner * stack. Otherwise it guarantees that the stack trace is
338e9b98e16SThomas Gleixner * reliable and returns the number of entries stored.
339e9b98e16SThomas Gleixner *
340e9b98e16SThomas Gleixner * If the task is not 'current', the caller *must* ensure the task is inactive.
341e9b98e16SThomas Gleixner */
stack_trace_save_tsk_reliable(struct task_struct * tsk,unsigned long * store,unsigned int size)342e9b98e16SThomas Gleixner int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
343e9b98e16SThomas Gleixner unsigned int size)
344e9b98e16SThomas Gleixner {
345e9b98e16SThomas Gleixner struct stack_trace trace = {
346e9b98e16SThomas Gleixner .entries = store,
347e9b98e16SThomas Gleixner .max_entries = size,
348e9b98e16SThomas Gleixner };
349e9b98e16SThomas Gleixner int ret = save_stack_trace_tsk_reliable(tsk, &trace);
350e9b98e16SThomas Gleixner
351e9b98e16SThomas Gleixner return ret ? ret : trace.nr_entries;
352e9b98e16SThomas Gleixner }
353e9b98e16SThomas Gleixner #endif
354e9b98e16SThomas Gleixner
355e9b98e16SThomas Gleixner #ifdef CONFIG_USER_STACKTRACE_SUPPORT
356e9b98e16SThomas Gleixner /**
357e9b98e16SThomas Gleixner * stack_trace_save_user - Save a user space stack trace into a storage array
358e9b98e16SThomas Gleixner * @store: Pointer to storage array
359e9b98e16SThomas Gleixner * @size: Size of the storage array
360e9b98e16SThomas Gleixner *
361e9b98e16SThomas Gleixner * Return: Number of trace entries stored
362e9b98e16SThomas Gleixner */
stack_trace_save_user(unsigned long * store,unsigned int size)363e9b98e16SThomas Gleixner unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
364e9b98e16SThomas Gleixner {
365e9b98e16SThomas Gleixner struct stack_trace trace = {
366e9b98e16SThomas Gleixner .entries = store,
367e9b98e16SThomas Gleixner .max_entries = size,
368e9b98e16SThomas Gleixner };
369e9b98e16SThomas Gleixner
370e9b98e16SThomas Gleixner save_stack_trace_user(&trace);
371e9b98e16SThomas Gleixner return trace.nr_entries;
372e9b98e16SThomas Gleixner }
373e9b98e16SThomas Gleixner #endif /* CONFIG_USER_STACKTRACE_SUPPORT */
374214d8ca6SThomas Gleixner
375214d8ca6SThomas Gleixner #endif /* !CONFIG_ARCH_STACKWALK */
376f39f21b3SMarco Elver
in_irqentry_text(unsigned long ptr)377f39f21b3SMarco Elver static inline bool in_irqentry_text(unsigned long ptr)
378f39f21b3SMarco Elver {
379f39f21b3SMarco Elver return (ptr >= (unsigned long)&__irqentry_text_start &&
380f39f21b3SMarco Elver ptr < (unsigned long)&__irqentry_text_end) ||
381f39f21b3SMarco Elver (ptr >= (unsigned long)&__softirqentry_text_start &&
382f39f21b3SMarco Elver ptr < (unsigned long)&__softirqentry_text_end);
383f39f21b3SMarco Elver }
384f39f21b3SMarco Elver
385f39f21b3SMarco Elver /**
386f39f21b3SMarco Elver * filter_irq_stacks - Find first IRQ stack entry in trace
387f39f21b3SMarco Elver * @entries: Pointer to stack trace array
388f39f21b3SMarco Elver * @nr_entries: Number of entries in the storage array
389f39f21b3SMarco Elver *
390f39f21b3SMarco Elver * Return: Number of trace entries until IRQ stack starts.
391f39f21b3SMarco Elver */
filter_irq_stacks(unsigned long * entries,unsigned int nr_entries)392f39f21b3SMarco Elver unsigned int filter_irq_stacks(unsigned long *entries, unsigned int nr_entries)
393f39f21b3SMarco Elver {
394f39f21b3SMarco Elver unsigned int i;
395f39f21b3SMarco Elver
396f39f21b3SMarco Elver for (i = 0; i < nr_entries; i++) {
397f39f21b3SMarco Elver if (in_irqentry_text(entries[i])) {
398f39f21b3SMarco Elver /* Include the irqentry function into the stack. */
399f39f21b3SMarco Elver return i + 1;
400f39f21b3SMarco Elver }
401f39f21b3SMarco Elver }
402f39f21b3SMarco Elver return nr_entries;
403f39f21b3SMarco Elver }
404f39f21b3SMarco Elver EXPORT_SYMBOL_GPL(filter_irq_stacks);
405