um: Add support for CONFIG_STACKTRACE
Add stacktrace support for User Mode Linux Signed-off-by: Daniel Walter <dwalter@google.com> Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
		
					parent
					
						
							
								2a2361228c
							
						
					
				
			
			
				commit
				
					
						970e51fead
					
				
			
		
					 5 changed files with 144 additions and 55 deletions
				
			
		|  | @ -39,7 +39,8 @@ config LOCKDEP_SUPPORT | ||||||
| 
 | 
 | ||||||
| config STACKTRACE_SUPPORT | config STACKTRACE_SUPPORT | ||||||
| 	bool | 	bool | ||||||
| 	default n | 	default y | ||||||
|  | 	select STACKTRACE | ||||||
| 
 | 
 | ||||||
| config GENERIC_CALIBRATE_DELAY | config GENERIC_CALIBRATE_DELAY | ||||||
| 	bool | 	bool | ||||||
|  |  | ||||||
							
								
								
									
										42
									
								
								arch/um/include/asm/stacktrace.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								arch/um/include/asm/stacktrace.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | #ifndef _ASM_UML_STACKTRACE_H | ||||||
|  | #define _ASM_UML_STACKTRACE_H | ||||||
|  | 
 | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include <linux/ptrace.h> | ||||||
|  | 
 | ||||||
|  | struct stack_frame { | ||||||
|  | 	struct stack_frame *next_frame; | ||||||
|  | 	unsigned long return_address; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct stacktrace_ops { | ||||||
|  | 	void (*address)(void *data, unsigned long address, int reliable); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FRAME_POINTER | ||||||
|  | static inline unsigned long | ||||||
|  | get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||||||
|  | { | ||||||
|  | 	if (!task || task == current) | ||||||
|  | 		return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); | ||||||
|  | 	return KSTK_EBP(task); | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static inline unsigned long | ||||||
|  | get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static inline unsigned long | ||||||
|  | *get_stack_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||||||
|  | { | ||||||
|  | 	if (!task || task == current) | ||||||
|  | 		return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); | ||||||
|  | 	return (unsigned long *)KSTK_ESP(task); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dump_trace(struct task_struct *tsk, const struct stacktrace_ops *ops, void *data); | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_UML_STACKTRACE_H */ | ||||||
|  | @ -19,6 +19,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o | ||||||
| obj-$(CONFIG_GPROF)	+= gprof_syms.o | obj-$(CONFIG_GPROF)	+= gprof_syms.o | ||||||
| obj-$(CONFIG_GCOV)	+= gmon_syms.o | obj-$(CONFIG_GCOV)	+= gmon_syms.o | ||||||
| obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | ||||||
|  | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||||||
| 
 | 
 | ||||||
| USER_OBJS := config.o | USER_OBJS := config.o | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										80
									
								
								arch/um/kernel/stacktrace.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								arch/um/kernel/stacktrace.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | ||||||
|  |  * Copyright (C) 2013 Richard Weinberger <richard@nod.at> | ||||||
|  |  * Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com> | ||||||
|  |  * | ||||||
|  |  * 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/kallsyms.h> | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/stacktrace.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include <asm/stacktrace.h> | ||||||
|  | 
 | ||||||
|  | void dump_trace(struct task_struct *tsk, | ||||||
|  | 		const struct stacktrace_ops *ops, | ||||||
|  | 		void *data) | ||||||
|  | { | ||||||
|  | 	int reliable = 0; | ||||||
|  | 	unsigned long *sp, bp, addr; | ||||||
|  | 	struct pt_regs *segv_regs = tsk->thread.segv_regs; | ||||||
|  | 	struct stack_frame *frame; | ||||||
|  | 
 | ||||||
|  | 	bp = get_frame_pointer(tsk, segv_regs); | ||||||
|  | 	sp = get_stack_pointer(tsk, segv_regs); | ||||||
|  | 
 | ||||||
|  | 	frame = (struct stack_frame *)bp; | ||||||
|  | 	while (((long) sp & (THREAD_SIZE-1)) != 0) { | ||||||
|  | 		addr = *sp; | ||||||
|  | 		if (__kernel_text_address(addr)) { | ||||||
|  | 			reliable = 0; | ||||||
|  | 			if ((unsigned long) sp == bp + sizeof(long)) { | ||||||
|  | 				frame = frame ? frame->next_frame : NULL; | ||||||
|  | 				bp = (unsigned long)frame; | ||||||
|  | 				reliable = 1; | ||||||
|  | 			} | ||||||
|  | 			ops->address(data, addr, reliable); | ||||||
|  | 		} | ||||||
|  | 		sp++; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void save_addr(void *data, unsigned long address, int reliable) | ||||||
|  | { | ||||||
|  | 	struct stack_trace *trace = data; | ||||||
|  | 
 | ||||||
|  | 	if (!reliable) | ||||||
|  | 		return; | ||||||
|  | 	if (trace->nr_entries >= trace->max_entries) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	trace->entries[trace->nr_entries++] = address; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct stacktrace_ops dump_ops = { | ||||||
|  | 	.address = save_addr | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace) | ||||||
|  | { | ||||||
|  | 	dump_trace(tsk, &dump_ops, trace); | ||||||
|  | 	if (trace->nr_entries < trace->max_entries) | ||||||
|  | 		trace->entries[trace->nr_entries++] = ULONG_MAX; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void save_stack_trace(struct stack_trace *trace) | ||||||
|  | { | ||||||
|  | 	__save_stack_trace(current, trace); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(save_stack_trace); | ||||||
|  | 
 | ||||||
|  | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||||||
|  | { | ||||||
|  | 	__save_stack_trace(tsk, trace); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | ||||||
|  | @ -12,58 +12,21 @@ | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/sched.h> | #include <linux/sched.h> | ||||||
| #include <asm/sysrq.h> | #include <asm/sysrq.h> | ||||||
|  | #include <asm/stacktrace.h> | ||||||
| #include <os.h> | #include <os.h> | ||||||
| 
 | 
 | ||||||
| struct stack_frame { | static void _print_addr(void *data, unsigned long address, int reliable) | ||||||
| 	struct stack_frame *next_frame; | { | ||||||
| 	unsigned long return_address; | 	pr_info(" [<%08lx>]", address); | ||||||
|  | 	pr_cont(" %s", reliable ? "" : "? "); | ||||||
|  | 	print_symbol("%s", address); | ||||||
|  | 	pr_cont("\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct stacktrace_ops stackops = { | ||||||
|  | 	.address = _print_addr | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void do_stack_trace(unsigned long *sp, unsigned long bp) |  | ||||||
| { |  | ||||||
| 	int reliable; |  | ||||||
| 	unsigned long addr; |  | ||||||
| 	struct stack_frame *frame = (struct stack_frame *)bp; |  | ||||||
| 
 |  | ||||||
| 	printk(KERN_INFO "Call Trace:\n"); |  | ||||||
| 	while (((long) sp & (THREAD_SIZE-1)) != 0) { |  | ||||||
| 		addr = *sp; |  | ||||||
| 		if (__kernel_text_address(addr)) { |  | ||||||
| 			reliable = 0; |  | ||||||
| 			if ((unsigned long) sp == bp + sizeof(long)) { |  | ||||||
| 				frame = frame ? frame->next_frame : NULL; |  | ||||||
| 				bp = (unsigned long)frame; |  | ||||||
| 				reliable = 1; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			printk(KERN_INFO " [<%08lx>]", addr); |  | ||||||
| 			printk(KERN_CONT " %s", reliable ? "" : "? "); |  | ||||||
| 			print_symbol(KERN_CONT "%s", addr); |  | ||||||
| 			printk(KERN_CONT "\n"); |  | ||||||
| 		} |  | ||||||
| 		sp++; |  | ||||||
| 	} |  | ||||||
| 	printk(KERN_INFO "\n"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static unsigned long get_frame_pointer(struct task_struct *task, |  | ||||||
| 				       struct pt_regs *segv_regs) |  | ||||||
| { |  | ||||||
| 	if (!task || task == current) |  | ||||||
| 		return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); |  | ||||||
| 	else |  | ||||||
| 		return KSTK_EBP(task); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static unsigned long *get_stack_pointer(struct task_struct *task, |  | ||||||
| 					struct pt_regs *segv_regs) |  | ||||||
| { |  | ||||||
| 	if (!task || task == current) |  | ||||||
| 		return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); |  | ||||||
| 	else |  | ||||||
| 		return (unsigned long *)KSTK_ESP(task); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void show_stack(struct task_struct *task, unsigned long *stack) | void show_stack(struct task_struct *task, unsigned long *stack) | ||||||
| { | { | ||||||
| 	unsigned long *sp = stack, bp = 0; | 	unsigned long *sp = stack, bp = 0; | ||||||
|  | @ -71,7 +34,7 @@ void show_stack(struct task_struct *task, unsigned long *stack) | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
| 	if (!segv_regs && os_is_signal_stack()) { | 	if (!segv_regs && os_is_signal_stack()) { | ||||||
| 		printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler," | 		pr_err("Received SIGSEGV in SIGSEGV handler," | ||||||
| 				" aborting stack trace!\n"); | 				" aborting stack trace!\n"); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  | @ -83,16 +46,18 @@ void show_stack(struct task_struct *task, unsigned long *stack) | ||||||
| 	if (!stack) | 	if (!stack) | ||||||
| 		sp = get_stack_pointer(task, segv_regs); | 		sp = get_stack_pointer(task, segv_regs); | ||||||
| 
 | 
 | ||||||
| 	printk(KERN_INFO "Stack:\n"); | 	pr_info("Stack:\n"); | ||||||
| 	stack = sp; | 	stack = sp; | ||||||
| 	for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { | 	for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { | ||||||
| 		if (kstack_end(stack)) | 		if (kstack_end(stack)) | ||||||
| 			break; | 			break; | ||||||
| 		if (i && ((i % STACKSLOTS_PER_LINE) == 0)) | 		if (i && ((i % STACKSLOTS_PER_LINE) == 0)) | ||||||
| 			printk(KERN_CONT "\n"); | 			pr_cont("\n"); | ||||||
| 		printk(KERN_CONT " %08lx", *stack++); | 		pr_cont(" %08lx", *stack++); | ||||||
| 	} | 	} | ||||||
| 	printk(KERN_CONT "\n"); | 	pr_cont("\n"); | ||||||
| 
 | 
 | ||||||
| 	do_stack_trace(sp, bp); | 	pr_info("Call Trace:\n"); | ||||||
|  | 	dump_trace(current, &stackops, NULL); | ||||||
|  | 	pr_info("\n"); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Daniel Walter
				Daniel Walter