82 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			82 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * AVR32 specific backtracing code for oprofile
 | |
|  *
 | |
|  * Copyright 2008 Weinmann GmbH
 | |
|  *
 | |
|  * Author: Nikolaus Voss <n.voss@weinmann.de>
 | |
|  *
 | |
|  * Based on i386 oprofile backtrace code by John Levon and David Smith
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License version 2 as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/oprofile.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/uaccess.h>
 | |
| 
 | |
| /* The first two words of each frame on the stack look like this if we have
 | |
|  * frame pointers */
 | |
| struct frame_head {
 | |
| 	unsigned long lr;
 | |
| 	struct frame_head *fp;
 | |
| };
 | |
| 
 | |
| /* copied from arch/avr32/kernel/process.c */
 | |
| static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
 | |
| {
 | |
| 	return (p > (unsigned long)tinfo)
 | |
| 		&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
 | |
| }
 | |
| 
 | |
| /* copied from arch/x86/oprofile/backtrace.c */
 | |
| static struct frame_head *dump_user_backtrace(struct frame_head *head)
 | |
| {
 | |
| 	struct frame_head bufhead[2];
 | |
| 
 | |
| 	/* Also check accessibility of one struct frame_head beyond */
 | |
| 	if (!access_ok(VERIFY_READ, head, sizeof(bufhead)))
 | |
| 		return NULL;
 | |
| 	if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead)))
 | |
| 		return NULL;
 | |
| 
 | |
| 	oprofile_add_trace(bufhead[0].lr);
 | |
| 
 | |
| 	/* frame pointers should strictly progress back up the stack
 | |
| 	 * (towards higher addresses) */
 | |
| 	if (bufhead[0].fp <= head)
 | |
| 		return NULL;
 | |
| 
 | |
| 	return bufhead[0].fp;
 | |
| }
 | |
| 
 | |
| void avr32_backtrace(struct pt_regs * const regs, unsigned int depth)
 | |
| {
 | |
| 	/* Get first frame pointer */
 | |
| 	struct frame_head *head = (struct frame_head *)(regs->r7);
 | |
| 
 | |
| 	if (!user_mode(regs)) {
 | |
| #ifdef CONFIG_FRAME_POINTER
 | |
| 		/*
 | |
| 		 * Traverse the kernel stack from frame to frame up to
 | |
| 		 * "depth" steps.
 | |
| 		 */
 | |
| 		while (depth-- && valid_stack_ptr(task_thread_info(current),
 | |
| 						  (unsigned long)head)) {
 | |
| 			oprofile_add_trace(head->lr);
 | |
| 			if (head->fp <= head)
 | |
| 				break;
 | |
| 			head = head->fp;
 | |
| 		}
 | |
| #endif
 | |
| 	} else {
 | |
| 		/* Assume we have frame pointers in user mode process */
 | |
| 		while (depth-- && head)
 | |
| 			head = dump_user_backtrace(head);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 |