Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f66686f7 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle
Browse files

[MIPS] dump_stack() based on prologue code analysis



Instead of dump all possible address in the stack, unwind the stack frame
based on prologue code analysis, as like as get_wchan() does.  While the
code analysis might fail for some reason, there is a new kernel option
"raw_show_trace" to disable this feature.

Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 79495d87
Loading
Loading
Loading
Loading
+55 −11
Original line number Diff line number Diff line
@@ -281,7 +281,7 @@ static struct mips_frame_info {
} *schedule_frame, mfinfo[64];
static int mfinfo_num;

static int __init get_frame_info(struct mips_frame_info *info)
static int get_frame_info(struct mips_frame_info *info)
{
	int i;
	void *func = info->func;
@@ -329,14 +329,12 @@ static int __init get_frame_info(struct mips_frame_info *info)
				ip->i_format.simmediate / sizeof(long);
		}
	}
	if (info->pc_offset == -1 || info->frame_size == 0) {
		if (func == schedule)
			printk("Can't analyze prologue code at %p\n", func);
		info->pc_offset = -1;
		info->frame_size = 0;
	}

	if (info->frame_size && info->pc_offset >= 0) /* nested */
		return 0;
	if (info->pc_offset < 0) /* leaf */
		return 1;
	/* prologue seems boggus... */
	return -1;
}

static int __init frame_info_init(void)
@@ -367,8 +365,15 @@ static int __init frame_info_init(void)
	mfinfo[0].func = schedule;
	schedule_frame = &mfinfo[0];
#endif
	for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++)
		get_frame_info(&mfinfo[i]);
	for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) {
		struct mips_frame_info *info = &mfinfo[i];
		if (get_frame_info(info)) {
			/* leaf or unknown */
			if (info->func == schedule)
				printk("Can't analyze prologue code at %p\n",
				       info->func);
		}
	}

	mfinfo_num = i;
	return 0;
@@ -427,6 +432,8 @@ unsigned long get_wchan(struct task_struct *p)
		if (i < 0)
			break;

		if (mfinfo[i].pc_offset < 0)
			break;
		pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
		if (!mfinfo[i].frame_size)
			break;
@@ -437,3 +444,40 @@ unsigned long get_wchan(struct task_struct *p)
	return pc;
}

#ifdef CONFIG_KALLSYMS
/* used by show_frametrace() */
unsigned long unwind_stack(struct task_struct *task,
			   unsigned long **sp, unsigned long pc)
{
	unsigned long stack_page;
	struct mips_frame_info info;
	char *modname;
	char namebuf[KSYM_NAME_LEN + 1];
	unsigned long size, ofs;

	stack_page = (unsigned long)task_stack_page(task);
	if (!stack_page)
		return 0;

	if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
		return 0;
	if (ofs == 0)
		return 0;

	info.func = (void *)(pc - ofs);
	info.func_size = ofs;	/* analyze from start to ofs */
	if (get_frame_info(&info)) {
		/* leaf or unknown */
		*sp += info.frame_size / sizeof(long);
		return 0;
	}
	if ((unsigned long)*sp < stack_page ||
	    (unsigned long)*sp + info.frame_size / sizeof(long) >
	    stack_page + THREAD_SIZE - 32)
		return 0;

	pc = (*sp)[info.pc_offset];
	*sp += info.frame_size / sizeof(long);
	return pc;
}
#endif
+86 −12
Original line number Diff line number Diff line
@@ -98,24 +98,53 @@ static void show_trace(unsigned long *stack)
	printk("\n");
}

#ifdef CONFIG_KALLSYMS
static int raw_show_trace;
static int __init set_raw_show_trace(char *str)
{
	raw_show_trace = 1;
	return 1;
}
__setup("raw_show_trace", set_raw_show_trace);

extern unsigned long unwind_stack(struct task_struct *task,
				  unsigned long **sp, unsigned long pc);
static void show_frametrace(struct task_struct *task, struct pt_regs *regs)
{
	const int field = 2 * sizeof(unsigned long);
	unsigned long *stack = (long *)regs->regs[29];
	unsigned long pc = regs->cp0_epc;
	int top = 1;

	if (raw_show_trace || !__kernel_text_address(pc)) {
		show_trace(stack);
		return;
	}
	printk("Call Trace:\n");
	while (__kernel_text_address(pc)) {
		printk(" [<%0*lx>] ", field, pc);
		print_symbol("%s\n", pc);
		pc = unwind_stack(task, &stack, pc);
		if (top && pc == 0)
			pc = regs->regs[31];	/* leaf? */
		top = 0;
	}
	printk("\n");
}
#else
#define show_frametrace(task, r) show_trace((long *)(r)->regs[29]);
#endif

/*
 * This routine abuses get_user()/put_user() to reference pointers
 * with at least a bit of error checking ...
 */
void show_stack(struct task_struct *task, unsigned long *sp)
static void show_stacktrace(struct task_struct *task, struct pt_regs *regs)
{
	const int field = 2 * sizeof(unsigned long);
	long stackdata;
	int i;
	unsigned long *stack;

	if (!sp) {
		if (task && task != current)
			sp = (unsigned long *) task->thread.reg29;
		else
			sp = (unsigned long *) &sp;
	}
	stack = sp;
	unsigned long *sp = (unsigned long *)regs->regs[29];

	printk("Stack :");
	i = 0;
@@ -136,7 +165,44 @@ void show_stack(struct task_struct *task, unsigned long *sp)
		i++;
	}
	printk("\n");
	show_trace(stack);
	show_frametrace(task, regs);
}

static noinline void prepare_frametrace(struct pt_regs *regs)
{
	__asm__ __volatile__(
		"1: la $2, 1b\n\t"
#ifdef CONFIG_64BIT
		"sd $2, %0\n\t"
		"sd $29, %1\n\t"
		"sd $31, %2\n\t"
#else
		"sw $2, %0\n\t"
		"sw $29, %1\n\t"
		"sw $31, %2\n\t"
#endif
		: "=m" (regs->cp0_epc),
		"=m" (regs->regs[29]), "=m" (regs->regs[31])
		: : "memory");
}

void show_stack(struct task_struct *task, unsigned long *sp)
{
	struct pt_regs regs;
	if (sp) {
		regs.regs[29] = (unsigned long)sp;
		regs.regs[31] = 0;
		regs.cp0_epc = 0;
	} else {
		if (task && task != current) {
			regs.regs[29] = task->thread.reg29;
			regs.regs[31] = 0;
			regs.cp0_epc = task->thread.reg31;
		} else {
			prepare_frametrace(&regs);
		}
	}
	show_stacktrace(task, &regs);
}

/*
@@ -146,6 +212,14 @@ void dump_stack(void)
{
	unsigned long stack;

#ifdef CONFIG_KALLSYMS
	if (!raw_show_trace) {
		struct pt_regs regs;
		prepare_frametrace(&regs);
		show_frametrace(current, &regs);
		return;
	}
#endif
	show_trace(&stack);
}

@@ -265,7 +339,7 @@ void show_registers(struct pt_regs *regs)
	print_modules();
	printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
	        current->comm, current->pid, current_thread_info(), current);
	show_stack(current, (long *) regs->regs[29]);
	show_stacktrace(current, regs);
	show_code((unsigned int *) regs->cp0_epc);
	printk("\n");
}