=============================================================
内核异常分析方法
=============================================================
-------------------------------------------------------------
1 总体方法
-------------------------------------------------------------
1.1 确定出错函数地址
1.2 确定出错地址在函数中的偏移
-------------------------------------------------------------
2 oops信息(由die函数产生)
-------------------------------------------------------------
/* This is gone through when something in the kernel
* has done something bad and is about to be terminated.
*/
void die(const char * str, struct pt_regs * regs, long err)
{
static struct {
spinlock_t lock;
u32 lock_owner;
int lock_owner_depth;
} die = {
.lock = SPIN_LOCK_UNLOCKED,
.lock_owner = -1,
.lock_owner_depth = 0
};
static int die_counter;
unsigned long flags;
oops_enter();
if (die.lock_owner != raw_smp_processor_id()) {
console_verbose();
spin_lock_irqsave(&die.lock, flags);
die.lock_owner = smp_processor_id();
die.lock_owner_depth = 0;
bust_spinlocks(1);
}
else
local_save_flags(flags);
if (++die.lock_owner_depth < 3) {
int nl = 0;
unsigned long esp;
unsigned short ss;
handle_BUG(regs); //对应oops信息的第一二行,指出出错位置所在的文件,以及行号,有了这个信息,就可以直接找到c源代码
printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);//这里的str对应invalid opcode,是在调用die时传入的值,表示错误类型,err表示这类型具体的错误号,die_counter表示调用die的次数
#ifdef CONFIG_PREEMPT
printk(KERN_EMERG "PREEMPT "); //抢占版本内核
nl = 1;
#endif
#ifdef CONFIG_SMP
if (!nl)
printk(KERN_EMERG);
printk("SMP "); //多核版本内核
nl = 1;
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
if (!nl)
printk(KERN_EMERG);
printk("DEBUG_PAGEALLOC");
nl = 1;
#endif
if (nl)
printk("\n");
#ifdef CONFIG_SYSFS
printk(KERN_ALERT "last sysfs file: %s\n", last_sysfs_file);
#endif
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) !=
NOTIFY_STOP) {
show_registers(regs); //打印了图中的大部分信息
/* Executive summary in case the oops scrolled away */
esp = (unsigned long) (®s->esp);
savesegment(ss, ss);
if (user_mode(regs)) {
esp = regs->esp;
ss = regs->xss & 0xffff;
}
printk(KERN_EMERG "EIP: [] ", regs->eip);
print_symbol("%s", regs->eip);
printk(" SS:ESP %04x:%08lx\n", ss, esp); //这三行打印最后一行
}
else
regs = NULL;
} else
printk(KERN_EMERG "Recursive die() failure, output suppressed\n");
bust_spinlocks(0);
die.lock_owner = -1;
spin_unlock_irqrestore(&die.lock, flags);
if (!regs)
return;
if (kexec_should_crash(current))
crash_kexec(regs);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
oops_exit();
do_exit(SIGSEGV);
}
void show_registers(struct pt_regs *regs