2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-07-03 11:24:38 +04:00
/*
* kernel / stacktrace . c
*
* Stack trace management functions
*
* Copyright ( C ) 2006 Red Hat , Inc . , Ingo Molnar < mingo @ redhat . com >
*/
2019-04-25 12:45:21 +03:00
# include <linux/sched/task_stack.h>
# include <linux/sched/debug.h>
2006-07-03 11:24:38 +04:00
# include <linux/sched.h>
2008-12-25 13:21:20 +03:00
# include <linux/kernel.h>
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2006-07-03 11:24:38 +04:00
# include <linux/kallsyms.h>
# include <linux/stacktrace.h>
2021-11-05 23:45:25 +03:00
# include <linux/interrupt.h>
2006-07-03 11:24:38 +04:00
2019-04-25 12:44:55 +03:00
/**
* 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
*/
2019-07-22 21:24:41 +03:00
void stack_trace_print ( const unsigned long * entries , unsigned int nr_entries ,
2019-04-25 12:44:55 +03:00
int spaces )
2006-07-03 11:24:38 +04:00
{
2019-04-25 12:44:55 +03:00
unsigned int i ;
2006-07-03 11:24:38 +04:00
2019-04-25 12:44:55 +03:00
if ( WARN_ON ( ! entries ) )
2008-05-12 23:21:14 +04:00
return ;
2019-04-25 12:44:55 +03:00
for ( i = 0 ; i < nr_entries ; i + + )
printk ( " %*c%pS \n " , 1 + spaces , ' ' , ( void * ) entries [ i ] ) ;
}
EXPORT_SYMBOL_GPL ( stack_trace_print ) ;
/**
* 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 .
*/
2019-07-22 21:24:41 +03:00
int stack_trace_snprint ( char * buf , size_t size , const unsigned long * entries ,
2019-04-25 12:44:55 +03:00
unsigned int nr_entries , int spaces )
2014-12-13 03:55:58 +03:00
{
2019-04-25 12:44:55 +03:00
unsigned int generated , i , total = 0 ;
2014-12-13 03:55:58 +03:00
2019-04-25 12:44:55 +03:00
if ( WARN_ON ( ! entries ) )
2014-12-13 03:55:58 +03:00
return 0 ;
2019-04-25 12:44:55 +03:00
for ( i = 0 ; i < nr_entries & & size ; i + + ) {
2017-02-08 02:33:20 +03:00
generated = snprintf ( buf , size , " %*c%pS \n " , 1 + spaces , ' ' ,
2019-04-25 12:44:55 +03:00
( void * ) entries [ i ] ) ;
2014-12-13 03:55:58 +03:00
total + = generated ;
if ( generated > = size ) {
buf + = size ;
size = 0 ;
} else {
buf + = generated ;
size - = generated ;
}
}
return total ;
}
2019-04-25 12:44:55 +03:00
EXPORT_SYMBOL_GPL ( stack_trace_snprint ) ;
2019-04-25 12:45:21 +03:00
# ifdef CONFIG_ARCH_STACKWALK
struct stacktrace_cookie {
unsigned long * store ;
unsigned int size ;
unsigned int skip ;
unsigned int len ;
} ;
2020-09-14 18:34:07 +03:00
static bool stack_trace_consume_entry ( void * cookie , unsigned long addr )
2019-04-25 12:45:21 +03:00
{
struct stacktrace_cookie * c = cookie ;
if ( c - > len > = c - > size )
return false ;
if ( c - > skip > 0 ) {
c - > skip - - ;
return true ;
}
c - > store [ c - > len + + ] = addr ;
return c - > len < c - > size ;
}
2020-09-14 18:34:07 +03:00
static bool stack_trace_consume_entry_nosched ( void * cookie , unsigned long addr )
2019-04-25 12:45:21 +03:00
{
if ( in_sched_functions ( addr ) )
return true ;
2020-09-14 18:34:07 +03:00
return stack_trace_consume_entry ( cookie , addr ) ;
2019-04-25 12:45:21 +03:00
}
/**
* 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 )
{
stack_trace_consume_fn consume_entry = stack_trace_consume_entry ;
struct stacktrace_cookie c = {
. store = store ,
. size = size ,
. skip = skipnr + 1 ,
} ;
arch_stack_walk ( consume_entry , & c , current , NULL ) ;
return c . len ;
}
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 * tsk , unsigned long * store ,
unsigned int size , unsigned int skipnr )
{
stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched ;
struct stacktrace_cookie c = {
. store = store ,
. size = size ,
2019-10-30 10:25:45 +03:00
/* skip this function if they are tracing us */
2019-11-11 12:26:47 +03:00
. skip = skipnr + ( current = = tsk ) ,
2019-04-25 12:45:21 +03:00
} ;
if ( ! try_get_task_stack ( tsk ) )
return 0 ;
arch_stack_walk ( consume_entry , & c , tsk , NULL ) ;
put_task_stack ( tsk ) ;
return c . len ;
}
/**
* 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 )
{
stack_trace_consume_fn consume_entry = stack_trace_consume_entry ;
struct stacktrace_cookie c = {
. store = store ,
. size = size ,
. skip = skipnr ,
} ;
arch_stack_walk ( consume_entry , & c , current , regs ) ;
return c . len ;
}
# 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 )
{
stack_trace_consume_fn consume_entry = stack_trace_consume_entry ;
struct stacktrace_cookie c = {
. store = store ,
. size = size ,
} ;
int ret ;
/*
* If the task doesn ' t have a stack ( e . g . , a zombie ) , the stack is
* " reliably " empty .
*/
if ( ! try_get_task_stack ( tsk ) )
return 0 ;
ret = arch_stack_walk_reliable ( consume_entry , & c , tsk ) ;
put_task_stack ( tsk ) ;
2019-05-17 21:51:17 +03:00
return ret ? ret : c . len ;
2019-04-25 12:45:21 +03:00
}
# 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 )
{
stack_trace_consume_fn consume_entry = stack_trace_consume_entry ;
struct stacktrace_cookie c = {
. store = store ,
. size = size ,
} ;
/* Trace user stack if not a kernel thread */
2019-07-02 18:53:35 +03:00
if ( current - > flags & PF_KTHREAD )
2019-04-25 12:45:21 +03:00
return 0 ;
arch_stack_walk_user ( consume_entry , & c , task_pt_regs ( current ) ) ;
2019-07-18 11:47:47 +03:00
2019-04-25 12:45:21 +03:00
return c . len ;
}
# endif
# else /* CONFIG_ARCH_STACKWALK */
2008-12-25 13:21:20 +03:00
/*
2017-02-14 04:42:28 +03:00
* Architectures that do not implement save_stack_trace_ * ( )
* get these weak aliases and once - per - bootup warnings
2011-06-08 11:09:27 +04:00
* ( whenever this facility is utilized - for example by procfs ) :
2008-12-25 13:21:20 +03:00
*/
__weak void
save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
{
WARN_ONCE ( 1 , KERN_INFO " save_stack_trace_tsk() not implemented yet. \n " ) ;
}
2011-06-08 11:09:27 +04:00
__weak void
save_stack_trace_regs ( struct pt_regs * regs , struct stack_trace * trace )
{
WARN_ONCE ( 1 , KERN_INFO " save_stack_trace_regs() not implemented yet. \n " ) ;
}
2017-02-14 04:42:28 +03:00
2019-04-25 12:44:55 +03:00
/**
* 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 ,
2019-10-30 10:25:45 +03:00
/* skip this function if they are tracing us */
2019-11-11 12:26:47 +03:00
. skip = skipnr + ( current = = task ) ,
2019-04-25 12:44:55 +03:00
} ;
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 */
2019-04-25 12:45:21 +03:00
# endif /* !CONFIG_ARCH_STACKWALK */
2021-11-05 23:45:25 +03:00
static inline bool in_irqentry_text ( unsigned long ptr )
{
return ( ptr > = ( unsigned long ) & __irqentry_text_start & &
ptr < ( unsigned long ) & __irqentry_text_end ) | |
( ptr > = ( unsigned long ) & __softirqentry_text_start & &
ptr < ( unsigned long ) & __softirqentry_text_end ) ;
}
/**
* filter_irq_stacks - Find first IRQ stack entry in trace
* @ entries : Pointer to stack trace array
* @ nr_entries : Number of entries in the storage array
*
* Return : Number of trace entries until IRQ stack starts .
*/
unsigned int filter_irq_stacks ( unsigned long * entries , unsigned int nr_entries )
{
unsigned int i ;
for ( i = 0 ; i < nr_entries ; i + + ) {
if ( in_irqentry_text ( entries [ i ] ) ) {
/* Include the irqentry function into the stack. */
return i + 1 ;
}
}
return nr_entries ;
}
EXPORT_SYMBOL_GPL ( filter_irq_stacks ) ;