Skip to content
Snippets Groups Projects
Select Git revision
  • ec2ad9ccf12dc965cad2d367a4063f68d6561a6b
  • master default
  • android-container
  • nanopc-t4
  • for-kernelci
  • WIP-syscall
  • v4.16-rc5
  • v4.16-rc4
  • v4.16-rc3
  • v4.16-rc2
  • v4.16-rc1
  • v4.15
  • v4.15-rc9
  • v4.15-rc8
  • v4.15-rc7
  • v4.15-rc6
  • v4.15-rc5
  • v4.15-rc4
  • v4.15-rc3
  • v4.15-rc2
  • v4.15-rc1
  • v4.14
  • v4.14-rc8
  • v4.14-rc7
  • v4.14-rc6
  • v4.14-rc5
26 results

backtrace.c

Blame
  • backtrace.c 2.58 KiB
    /**
     * @file backtrace.c
     *
     * @remark Copyright 2002 OProfile authors
     * @remark Read the file COPYING
     *
     * @author John Levon
     * @author David Smith
     */
    
    #include <linux/oprofile.h>
    #include <linux/sched.h>
    #include <linux/mm.h>
    #include <linux/compat.h>
    #include <linux/uaccess.h>
    
    #include <asm/ptrace.h>
    #include <asm/stacktrace.h>
    #include <asm/unwind.h>
    
    #ifdef CONFIG_COMPAT
    static struct stack_frame_ia32 *
    dump_user_backtrace_32(struct stack_frame_ia32 *head)
    {
    	/* Also check accessibility of one struct frame_head beyond: */
    	struct stack_frame_ia32 bufhead[2];
    	struct stack_frame_ia32 *fp;
    	unsigned long bytes;
    
    	bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
    	if (bytes != 0)
    		return NULL;
    
    	fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame);
    
    	oprofile_add_trace(bufhead[0].return_address);
    
    	/* frame pointers should strictly progress back up the stack
    	* (towards higher addresses) */
    	if (head >= fp)
    		return NULL;
    
    	return fp;
    }
    
    static inline int
    x86_backtrace_32(struct pt_regs * const regs, unsigned int depth)
    {
    	struct stack_frame_ia32 *head;
    
    	/* User process is IA32 */
    	if (!current || !test_thread_flag(TIF_IA32))
    		return 0;
    
    	head = (struct stack_frame_ia32 *) regs->bp;
    	while (depth-- && head)
    		head = dump_user_backtrace_32(head);
    
    	return 1;
    }
    
    #else
    static inline int
    x86_backtrace_32(struct pt_regs * const regs, unsigned int depth)
    {
    	return 0;
    }
    #endif /* CONFIG_COMPAT */
    
    static struct stack_frame *dump_user_backtrace(struct stack_frame *head)
    {
    	/* Also check accessibility of one struct frame_head beyond: */
    	struct stack_frame bufhead[2];
    	unsigned long bytes;
    
    	bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
    	if (bytes != 0)
    		return NULL;
    
    	oprofile_add_trace(bufhead[0].return_address);
    
    	/* frame pointers should strictly progress back up the stack
    	 * (towards higher addresses) */
    	if (head >= bufhead[0].next_frame)
    		return NULL;
    
    	return bufhead[0].next_frame;
    }
    
    void
    x86_backtrace(struct pt_regs * const regs, unsigned int depth)
    {
    	struct stack_frame *head = (struct stack_frame *)frame_pointer(regs);
    
    	if (!user_mode(regs)) {
    		struct unwind_state state;
    		unsigned long addr;
    
    		if (!depth)
    			return;
    
    		oprofile_add_trace(regs->ip);
    
    		if (!--depth)
    			return;
    
    		for (unwind_start(&state, current, regs, NULL);
    		     !unwind_done(&state); unwind_next_frame(&state)) {
    			addr = unwind_get_return_address(&state);
    			if (!addr)
    				break;
    
    			oprofile_add_trace(addr);
    
    			if (!--depth)
    				break;
    		}
    
    		return;
    	}
    
    	if (x86_backtrace_32(regs, depth))
    		return;
    
    	while (depth-- && head)
    		head = dump_user_backtrace(head);
    }