xref: /linux-6.15/kernel/stacktrace.c (revision 457c8996)
1*457c8996SThomas 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>
168637c099SIngo Molnar 
17e9b98e16SThomas Gleixner /**
18e9b98e16SThomas Gleixner  * stack_trace_print - Print the entries in the stack trace
19e9b98e16SThomas Gleixner  * @entries:	Pointer to storage array
20e9b98e16SThomas Gleixner  * @nr_entries:	Number of entries in the storage array
21e9b98e16SThomas Gleixner  * @spaces:	Number of leading spaces to print
22e9b98e16SThomas Gleixner  */
23e9b98e16SThomas Gleixner void stack_trace_print(unsigned long *entries, unsigned int nr_entries,
24e9b98e16SThomas Gleixner 		       int spaces)
258637c099SIngo Molnar {
26e9b98e16SThomas Gleixner 	unsigned int i;
278637c099SIngo Molnar 
28e9b98e16SThomas Gleixner 	if (WARN_ON(!entries))
29bfeeeeb9SJohannes Berg 		return;
30bfeeeeb9SJohannes Berg 
31e9b98e16SThomas Gleixner 	for (i = 0; i < nr_entries; i++)
32e9b98e16SThomas Gleixner 		printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]);
33e9b98e16SThomas Gleixner }
34e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_print);
35e9b98e16SThomas Gleixner 
36e9b98e16SThomas Gleixner /**
37e9b98e16SThomas Gleixner  * stack_trace_snprint - Print the entries in the stack trace into a buffer
38e9b98e16SThomas Gleixner  * @buf:	Pointer to the print buffer
39e9b98e16SThomas Gleixner  * @size:	Size of the print buffer
40e9b98e16SThomas Gleixner  * @entries:	Pointer to storage array
41e9b98e16SThomas Gleixner  * @nr_entries:	Number of entries in the storage array
42e9b98e16SThomas Gleixner  * @spaces:	Number of leading spaces to print
43e9b98e16SThomas Gleixner  *
44e9b98e16SThomas Gleixner  * Return: Number of bytes printed.
45e9b98e16SThomas Gleixner  */
46e9b98e16SThomas Gleixner int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
47e9b98e16SThomas Gleixner 			unsigned int nr_entries, int spaces)
489a92a6ceSJoonsoo Kim {
49e9b98e16SThomas Gleixner 	unsigned int generated, i, total = 0;
509a92a6ceSJoonsoo Kim 
51e9b98e16SThomas Gleixner 	if (WARN_ON(!entries))
529a92a6ceSJoonsoo Kim 		return 0;
539a92a6ceSJoonsoo Kim 
54e9b98e16SThomas Gleixner 	for (i = 0; i < nr_entries && size; i++) {
55bfeda41dSOmar Sandoval 		generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ',
56e9b98e16SThomas Gleixner 				     (void *)entries[i]);
579a92a6ceSJoonsoo Kim 
589a92a6ceSJoonsoo Kim 		total += generated;
599a92a6ceSJoonsoo Kim 		if (generated >= size) {
609a92a6ceSJoonsoo Kim 			buf += size;
619a92a6ceSJoonsoo Kim 			size = 0;
629a92a6ceSJoonsoo Kim 		} else {
639a92a6ceSJoonsoo Kim 			buf += generated;
649a92a6ceSJoonsoo Kim 			size -= generated;
659a92a6ceSJoonsoo Kim 		}
669a92a6ceSJoonsoo Kim 	}
679a92a6ceSJoonsoo Kim 
689a92a6ceSJoonsoo Kim 	return total;
699a92a6ceSJoonsoo Kim }
70e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_snprint);
71e9b98e16SThomas Gleixner 
72214d8ca6SThomas Gleixner #ifdef CONFIG_ARCH_STACKWALK
73214d8ca6SThomas Gleixner 
74214d8ca6SThomas Gleixner struct stacktrace_cookie {
75214d8ca6SThomas Gleixner 	unsigned long	*store;
76214d8ca6SThomas Gleixner 	unsigned int	size;
77214d8ca6SThomas Gleixner 	unsigned int	skip;
78214d8ca6SThomas Gleixner 	unsigned int	len;
79214d8ca6SThomas Gleixner };
80214d8ca6SThomas Gleixner 
81214d8ca6SThomas Gleixner static bool stack_trace_consume_entry(void *cookie, unsigned long addr,
82214d8ca6SThomas Gleixner 				      bool reliable)
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 
97214d8ca6SThomas Gleixner static bool stack_trace_consume_entry_nosched(void *cookie, unsigned long addr,
98214d8ca6SThomas Gleixner 					      bool reliable)
99214d8ca6SThomas Gleixner {
100214d8ca6SThomas Gleixner 	if (in_sched_functions(addr))
101214d8ca6SThomas Gleixner 		return true;
102214d8ca6SThomas Gleixner 	return stack_trace_consume_entry(cookie, addr, reliable);
103214d8ca6SThomas Gleixner }
104214d8ca6SThomas Gleixner 
105214d8ca6SThomas Gleixner /**
106214d8ca6SThomas Gleixner  * stack_trace_save - Save a stack trace into a storage array
107214d8ca6SThomas Gleixner  * @store:	Pointer to storage array
108214d8ca6SThomas Gleixner  * @size:	Size of the storage array
109214d8ca6SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
110214d8ca6SThomas Gleixner  *
111214d8ca6SThomas Gleixner  * Return: Number of trace entries stored.
112214d8ca6SThomas Gleixner  */
113214d8ca6SThomas Gleixner unsigned int stack_trace_save(unsigned long *store, unsigned int size,
114214d8ca6SThomas Gleixner 			      unsigned int skipnr)
115214d8ca6SThomas Gleixner {
116214d8ca6SThomas Gleixner 	stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
117214d8ca6SThomas Gleixner 	struct stacktrace_cookie c = {
118214d8ca6SThomas Gleixner 		.store	= store,
119214d8ca6SThomas Gleixner 		.size	= size,
120214d8ca6SThomas Gleixner 		.skip	= skipnr + 1,
121214d8ca6SThomas Gleixner 	};
122214d8ca6SThomas Gleixner 
123214d8ca6SThomas Gleixner 	arch_stack_walk(consume_entry, &c, current, NULL);
124214d8ca6SThomas Gleixner 	return c.len;
125214d8ca6SThomas Gleixner }
126214d8ca6SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_save);
127214d8ca6SThomas Gleixner 
128214d8ca6SThomas Gleixner /**
129214d8ca6SThomas Gleixner  * stack_trace_save_tsk - Save a task stack trace into a storage array
130214d8ca6SThomas Gleixner  * @task:	The task to examine
131214d8ca6SThomas Gleixner  * @store:	Pointer to storage array
132214d8ca6SThomas Gleixner  * @size:	Size of the storage array
133214d8ca6SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
134214d8ca6SThomas Gleixner  *
135214d8ca6SThomas Gleixner  * Return: Number of trace entries stored.
136214d8ca6SThomas Gleixner  */
137214d8ca6SThomas Gleixner unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store,
138214d8ca6SThomas Gleixner 				  unsigned int size, unsigned int skipnr)
139214d8ca6SThomas Gleixner {
140214d8ca6SThomas Gleixner 	stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;
141214d8ca6SThomas Gleixner 	struct stacktrace_cookie c = {
142214d8ca6SThomas Gleixner 		.store	= store,
143214d8ca6SThomas Gleixner 		.size	= size,
144214d8ca6SThomas Gleixner 		.skip	= skipnr + 1,
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 }
154214d8ca6SThomas Gleixner 
155214d8ca6SThomas Gleixner /**
156214d8ca6SThomas Gleixner  * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
157214d8ca6SThomas Gleixner  * @regs:	Pointer to pt_regs to examine
158214d8ca6SThomas Gleixner  * @store:	Pointer to storage array
159214d8ca6SThomas Gleixner  * @size:	Size of the storage array
160214d8ca6SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
161214d8ca6SThomas Gleixner  *
162214d8ca6SThomas Gleixner  * Return: Number of trace entries stored.
163214d8ca6SThomas Gleixner  */
164214d8ca6SThomas Gleixner unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
165214d8ca6SThomas Gleixner 				   unsigned int size, unsigned int skipnr)
166214d8ca6SThomas Gleixner {
167214d8ca6SThomas Gleixner 	stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
168214d8ca6SThomas Gleixner 	struct stacktrace_cookie c = {
169214d8ca6SThomas Gleixner 		.store	= store,
170214d8ca6SThomas Gleixner 		.size	= size,
171214d8ca6SThomas Gleixner 		.skip	= skipnr,
172214d8ca6SThomas Gleixner 	};
173214d8ca6SThomas Gleixner 
174214d8ca6SThomas Gleixner 	arch_stack_walk(consume_entry, &c, current, regs);
175214d8ca6SThomas Gleixner 	return c.len;
176214d8ca6SThomas Gleixner }
177214d8ca6SThomas Gleixner 
178214d8ca6SThomas Gleixner #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
179214d8ca6SThomas Gleixner /**
180214d8ca6SThomas Gleixner  * stack_trace_save_tsk_reliable - Save task stack with verification
181214d8ca6SThomas Gleixner  * @tsk:	Pointer to the task to examine
182214d8ca6SThomas Gleixner  * @store:	Pointer to storage array
183214d8ca6SThomas Gleixner  * @size:	Size of the storage array
184214d8ca6SThomas Gleixner  *
185214d8ca6SThomas Gleixner  * Return:	An error if it detects any unreliable features of the
186214d8ca6SThomas Gleixner  *		stack. Otherwise it guarantees that the stack trace is
187214d8ca6SThomas Gleixner  *		reliable and returns the number of entries stored.
188214d8ca6SThomas Gleixner  *
189214d8ca6SThomas Gleixner  * If the task is not 'current', the caller *must* ensure the task is inactive.
190214d8ca6SThomas Gleixner  */
191214d8ca6SThomas Gleixner int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
192214d8ca6SThomas Gleixner 				  unsigned int size)
193214d8ca6SThomas Gleixner {
194214d8ca6SThomas Gleixner 	stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
195214d8ca6SThomas Gleixner 	struct stacktrace_cookie c = {
196214d8ca6SThomas Gleixner 		.store	= store,
197214d8ca6SThomas Gleixner 		.size	= size,
198214d8ca6SThomas Gleixner 	};
199214d8ca6SThomas Gleixner 	int ret;
200214d8ca6SThomas Gleixner 
201214d8ca6SThomas Gleixner 	/*
202214d8ca6SThomas Gleixner 	 * If the task doesn't have a stack (e.g., a zombie), the stack is
203214d8ca6SThomas Gleixner 	 * "reliably" empty.
204214d8ca6SThomas Gleixner 	 */
205214d8ca6SThomas Gleixner 	if (!try_get_task_stack(tsk))
206214d8ca6SThomas Gleixner 		return 0;
207214d8ca6SThomas Gleixner 
208214d8ca6SThomas Gleixner 	ret = arch_stack_walk_reliable(consume_entry, &c, tsk);
209214d8ca6SThomas Gleixner 	put_task_stack(tsk);
210214d8ca6SThomas Gleixner 	return ret;
211214d8ca6SThomas Gleixner }
212214d8ca6SThomas Gleixner #endif
213214d8ca6SThomas Gleixner 
214214d8ca6SThomas Gleixner #ifdef CONFIG_USER_STACKTRACE_SUPPORT
215214d8ca6SThomas Gleixner /**
216214d8ca6SThomas Gleixner  * stack_trace_save_user - Save a user space stack trace into a storage array
217214d8ca6SThomas Gleixner  * @store:	Pointer to storage array
218214d8ca6SThomas Gleixner  * @size:	Size of the storage array
219214d8ca6SThomas Gleixner  *
220214d8ca6SThomas Gleixner  * Return: Number of trace entries stored.
221214d8ca6SThomas Gleixner  */
222214d8ca6SThomas Gleixner unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
223214d8ca6SThomas Gleixner {
224214d8ca6SThomas Gleixner 	stack_trace_consume_fn consume_entry = stack_trace_consume_entry;
225214d8ca6SThomas Gleixner 	struct stacktrace_cookie c = {
226214d8ca6SThomas Gleixner 		.store	= store,
227214d8ca6SThomas Gleixner 		.size	= size,
228214d8ca6SThomas Gleixner 	};
229214d8ca6SThomas Gleixner 
230214d8ca6SThomas Gleixner 	/* Trace user stack if not a kernel thread */
231214d8ca6SThomas Gleixner 	if (!current->mm)
232214d8ca6SThomas Gleixner 		return 0;
233214d8ca6SThomas Gleixner 
234214d8ca6SThomas Gleixner 	arch_stack_walk_user(consume_entry, &c, task_pt_regs(current));
235214d8ca6SThomas Gleixner 	return c.len;
236214d8ca6SThomas Gleixner }
237214d8ca6SThomas Gleixner #endif
238214d8ca6SThomas Gleixner 
239214d8ca6SThomas Gleixner #else /* CONFIG_ARCH_STACKWALK */
240214d8ca6SThomas Gleixner 
2419212ddb5SIngo Molnar /*
242af085d90SJosh Poimboeuf  * Architectures that do not implement save_stack_trace_*()
243af085d90SJosh Poimboeuf  * get these weak aliases and once-per-bootup warnings
244c624d33fSMasami Hiramatsu  * (whenever this facility is utilized - for example by procfs):
2459212ddb5SIngo Molnar  */
2469212ddb5SIngo Molnar __weak void
2479212ddb5SIngo Molnar save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
2489212ddb5SIngo Molnar {
2499212ddb5SIngo Molnar 	WARN_ONCE(1, KERN_INFO "save_stack_trace_tsk() not implemented yet.\n");
2509212ddb5SIngo Molnar }
251c624d33fSMasami Hiramatsu 
252c624d33fSMasami Hiramatsu __weak void
253c624d33fSMasami Hiramatsu save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
254c624d33fSMasami Hiramatsu {
255c624d33fSMasami Hiramatsu 	WARN_ONCE(1, KERN_INFO "save_stack_trace_regs() not implemented yet.\n");
256c624d33fSMasami Hiramatsu }
257af085d90SJosh Poimboeuf 
258af085d90SJosh Poimboeuf __weak int
259af085d90SJosh Poimboeuf save_stack_trace_tsk_reliable(struct task_struct *tsk,
260af085d90SJosh Poimboeuf 			      struct stack_trace *trace)
261af085d90SJosh Poimboeuf {
262af085d90SJosh Poimboeuf 	WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n");
263af085d90SJosh Poimboeuf 	return -ENOSYS;
264af085d90SJosh Poimboeuf }
265e9b98e16SThomas Gleixner 
266e9b98e16SThomas Gleixner /**
267e9b98e16SThomas Gleixner  * stack_trace_save - Save a stack trace into a storage array
268e9b98e16SThomas Gleixner  * @store:	Pointer to storage array
269e9b98e16SThomas Gleixner  * @size:	Size of the storage array
270e9b98e16SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
271e9b98e16SThomas Gleixner  *
272e9b98e16SThomas Gleixner  * Return: Number of trace entries stored
273e9b98e16SThomas Gleixner  */
274e9b98e16SThomas Gleixner unsigned int stack_trace_save(unsigned long *store, unsigned int size,
275e9b98e16SThomas Gleixner 			      unsigned int skipnr)
276e9b98e16SThomas Gleixner {
277e9b98e16SThomas Gleixner 	struct stack_trace trace = {
278e9b98e16SThomas Gleixner 		.entries	= store,
279e9b98e16SThomas Gleixner 		.max_entries	= size,
280e9b98e16SThomas Gleixner 		.skip		= skipnr + 1,
281e9b98e16SThomas Gleixner 	};
282e9b98e16SThomas Gleixner 
283e9b98e16SThomas Gleixner 	save_stack_trace(&trace);
284e9b98e16SThomas Gleixner 	return trace.nr_entries;
285e9b98e16SThomas Gleixner }
286e9b98e16SThomas Gleixner EXPORT_SYMBOL_GPL(stack_trace_save);
287e9b98e16SThomas Gleixner 
288e9b98e16SThomas Gleixner /**
289e9b98e16SThomas Gleixner  * stack_trace_save_tsk - Save a task stack trace into a storage array
290e9b98e16SThomas Gleixner  * @task:	The task to examine
291e9b98e16SThomas Gleixner  * @store:	Pointer to storage array
292e9b98e16SThomas Gleixner  * @size:	Size of the storage array
293e9b98e16SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
294e9b98e16SThomas Gleixner  *
295e9b98e16SThomas Gleixner  * Return: Number of trace entries stored
296e9b98e16SThomas Gleixner  */
297e9b98e16SThomas Gleixner unsigned int stack_trace_save_tsk(struct task_struct *task,
298e9b98e16SThomas Gleixner 				  unsigned long *store, unsigned int size,
299e9b98e16SThomas Gleixner 				  unsigned int skipnr)
300e9b98e16SThomas Gleixner {
301e9b98e16SThomas Gleixner 	struct stack_trace trace = {
302e9b98e16SThomas Gleixner 		.entries	= store,
303e9b98e16SThomas Gleixner 		.max_entries	= size,
304e9b98e16SThomas Gleixner 		.skip		= skipnr + 1,
305e9b98e16SThomas Gleixner 	};
306e9b98e16SThomas Gleixner 
307e9b98e16SThomas Gleixner 	save_stack_trace_tsk(task, &trace);
308e9b98e16SThomas Gleixner 	return trace.nr_entries;
309e9b98e16SThomas Gleixner }
310e9b98e16SThomas Gleixner 
311e9b98e16SThomas Gleixner /**
312e9b98e16SThomas Gleixner  * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array
313e9b98e16SThomas Gleixner  * @regs:	Pointer to pt_regs to examine
314e9b98e16SThomas Gleixner  * @store:	Pointer to storage array
315e9b98e16SThomas Gleixner  * @size:	Size of the storage array
316e9b98e16SThomas Gleixner  * @skipnr:	Number of entries to skip at the start of the stack trace
317e9b98e16SThomas Gleixner  *
318e9b98e16SThomas Gleixner  * Return: Number of trace entries stored
319e9b98e16SThomas Gleixner  */
320e9b98e16SThomas Gleixner unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store,
321e9b98e16SThomas Gleixner 				   unsigned int size, unsigned int skipnr)
322e9b98e16SThomas Gleixner {
323e9b98e16SThomas Gleixner 	struct stack_trace trace = {
324e9b98e16SThomas Gleixner 		.entries	= store,
325e9b98e16SThomas Gleixner 		.max_entries	= size,
326e9b98e16SThomas Gleixner 		.skip		= skipnr,
327e9b98e16SThomas Gleixner 	};
328e9b98e16SThomas Gleixner 
329e9b98e16SThomas Gleixner 	save_stack_trace_regs(regs, &trace);
330e9b98e16SThomas Gleixner 	return trace.nr_entries;
331e9b98e16SThomas Gleixner }
332e9b98e16SThomas Gleixner 
333e9b98e16SThomas Gleixner #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
334e9b98e16SThomas Gleixner /**
335e9b98e16SThomas Gleixner  * stack_trace_save_tsk_reliable - Save task stack with verification
336e9b98e16SThomas Gleixner  * @tsk:	Pointer to the task to examine
337e9b98e16SThomas Gleixner  * @store:	Pointer to storage array
338e9b98e16SThomas Gleixner  * @size:	Size of the storage array
339e9b98e16SThomas Gleixner  *
340e9b98e16SThomas Gleixner  * Return:	An error if it detects any unreliable features of the
341e9b98e16SThomas Gleixner  *		stack. Otherwise it guarantees that the stack trace is
342e9b98e16SThomas Gleixner  *		reliable and returns the number of entries stored.
343e9b98e16SThomas Gleixner  *
344e9b98e16SThomas Gleixner  * If the task is not 'current', the caller *must* ensure the task is inactive.
345e9b98e16SThomas Gleixner  */
346e9b98e16SThomas Gleixner int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store,
347e9b98e16SThomas Gleixner 				  unsigned int size)
348e9b98e16SThomas Gleixner {
349e9b98e16SThomas Gleixner 	struct stack_trace trace = {
350e9b98e16SThomas Gleixner 		.entries	= store,
351e9b98e16SThomas Gleixner 		.max_entries	= size,
352e9b98e16SThomas Gleixner 	};
353e9b98e16SThomas Gleixner 	int ret = save_stack_trace_tsk_reliable(tsk, &trace);
354e9b98e16SThomas Gleixner 
355e9b98e16SThomas Gleixner 	return ret ? ret : trace.nr_entries;
356e9b98e16SThomas Gleixner }
357e9b98e16SThomas Gleixner #endif
358e9b98e16SThomas Gleixner 
359e9b98e16SThomas Gleixner #ifdef CONFIG_USER_STACKTRACE_SUPPORT
360e9b98e16SThomas Gleixner /**
361e9b98e16SThomas Gleixner  * stack_trace_save_user - Save a user space stack trace into a storage array
362e9b98e16SThomas Gleixner  * @store:	Pointer to storage array
363e9b98e16SThomas Gleixner  * @size:	Size of the storage array
364e9b98e16SThomas Gleixner  *
365e9b98e16SThomas Gleixner  * Return: Number of trace entries stored
366e9b98e16SThomas Gleixner  */
367e9b98e16SThomas Gleixner unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
368e9b98e16SThomas Gleixner {
369e9b98e16SThomas Gleixner 	struct stack_trace trace = {
370e9b98e16SThomas Gleixner 		.entries	= store,
371e9b98e16SThomas Gleixner 		.max_entries	= size,
372e9b98e16SThomas Gleixner 	};
373e9b98e16SThomas Gleixner 
374e9b98e16SThomas Gleixner 	save_stack_trace_user(&trace);
375e9b98e16SThomas Gleixner 	return trace.nr_entries;
376e9b98e16SThomas Gleixner }
377e9b98e16SThomas Gleixner #endif /* CONFIG_USER_STACKTRACE_SUPPORT */
378214d8ca6SThomas Gleixner 
379214d8ca6SThomas Gleixner #endif /* !CONFIG_ARCH_STACKWALK */
380