82 lines
		
	
	
	
		
			2 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			82 lines
		
	
	
	
		
			2 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); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 |