目录
1Linux驱动程序调试方法
Linux驱动程序调试主要有以下几种方法
- 打印:pirintk,自制proc文件
- 自制工具
- 根据内核打印的段错误信息分析
- 修改内核
2printk原理
2.1 内核处理uboot传入的参数
我们首先启动开发板,进入uboot,然后在uboot命令行print打印出uboot的环境变量,
然后我们改一下参数,console不写了,
然后boot启动,发现启动后内核没有打印任何信息,只要设置了console之后,内核才会打印出信息来。
我们在代码中搜索 sonsole= 然后找到
我们看一下console_setup这个函数
然后调用了add_preferred_console函数。
这里的console_cmdline是在别的地方定义的一个全局的静态变量。
2.2 硬件驱动的入口函数里注册控制台参数
然后我们看一下console_cmdline还在哪个地方被使用,然后发现了一个register_console函数
/*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
void register_console(struct console *console)
{
int i;
unsigned long flags;
struct console *bootconsole = NULL;
if (console_drivers) {
if (console->flags & CON_BOOT)
return;
if (console_drivers->flags & CON_BOOT)
bootconsole = console_drivers;
}
if (preferred_console < 0 || bootconsole || !console_drivers)
preferred_console = selected_console;
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
* that registers here.
*/
if (preferred_console < 0) {
if (console->index < 0)
console->index = 0;
if (console->setup == NULL ||
console->setup(console, NULL) == 0) {
console->flags |= CON_ENABLED | CON_CONSDEV;
preferred_console = 0;
}
}
/*
* See if this console matches one we selected on
* the command line.
*/
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
i++) {
if (strcmp(console_cmdline[i].name, console->name) != 0)
continue;
if (console->index >= 0 &&
console->index != console_cmdline[i].index)
continue;
if (console->index < 0)
console->index = console_cmdline[i].index;
if (console->setup &&
console->setup(console, console_cmdline[i].options) != 0)
break;
console->flags |= CON_ENABLED;
console->index = console_cmdline[i].index;
if (i == selected_console) {
console->flags |= CON_CONSDEV;
preferred_console = selected_console;
}
break;
}
if (!(console->flags & CON_ENABLED))
return;
if (bootconsole) {
printk(KERN_INFO "console handover: boot [%s%d] -> real [%s%d]\n",
bootconsole->name, bootconsole->index,
console->name, console->index);
unregister_console(bootconsole);
console->flags &= ~CON_PRINTBUFFER;
}
/*
* Put this console in the list - keep the
* preferred driver at the head of the list.
*/
acquire_console_sem();
if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
console->next = console_drivers;
console_drivers = console;
if (console->next)
console->next->flags &= ~CON_CONSDEV;
} else {
console->next = console_drivers->next;
console_drivers->next = console;
}
if (console->flags & CON_PRINTBUFFER) {
/*
* release_console_sem() will print out the buffered messages
* for us.
*/
spin_lock_irqsave(&logbuf_lock, flags);
con_start = log_start;
spin_unlock_irqrestore(&logbuf_lock, flags);
}
release_console_sem();
}
这个register_console是在 drivers/serial/s3c2410.c里面被调用的,register_console函数是注册struct console这样一个结构体,
这个结构体里面有名字,有write函数,
2.3 printk函数实现
代码中搜索printk,
然后继续看vprintk
asmlinkage int vprintk(const char *fmt, va_list args)
{
unsigned long flags;
int printed_len;
char *p;
static char printk_buf[1024];
static int log_level_unknown = 1;
preempt_disable();
if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
/* If a crash is occurring during printk() on this CPU,
* make sure we can't deadlock */
zap_locks();
/* This stops the holder of console_sem just where we want him */
raw_local_irq_save(flags);
lockdep_off();
spin_lock(&logbuf_lock);
printk_cpu = smp_processor_id();
/* Emit the output into the temporary buffer */
printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
*/
for (p = printk_buf; *p; p++) {
if (log_level_unknown) {
/* log_level_unknown signals the start of a new line */
if (printk_time) {
int loglev_char;
char tbuf[50], *tp;
unsigned tlen;
unsigned long long t;
unsigned long nanosec_rem;
/*
* force the log level token to be
* before the time output.
*/
if (p[0] == '<' && p[1] >='0' &&
p[1] <= '7' && p[2] == '>') {
loglev_char = p[1];
p += 3;
printed_len -= 3;
} else {
loglev_char = default_message_loglevel
+ '0';
}
t = printk_clock();
nanosec_rem = do_div(t, 1000000000);
tlen = sprintf(tbuf,
"<%c>[%5lu.%06lu] ",
loglev_char,
(unsigned long)t,
nanosec_rem/1000);
for (tp = tbuf; tp < tbuf + tlen; tp++)
emit_log_char(*tp);
printed_len += tlen;
} else {
if (p[0] != '<' || p[1] < '0' ||
p[1] > '7' || p[2] != '>') {
emit_log_char('<');
emit_log_char(default_message_loglevel
+ '0');
emit_log_char('>');
printed_len += 3;
}
}
log_level_unknown = 0;
if (!*p)
break;
}
emit_log_char(*p);
if (*p == '\n')
log_level_unknown = 1;
}
if (!down_trylock(&console_sem)) {
/*
* We own the drivers. We can drop the spinlock and
* let release_console_sem() print the text, maybe ...
*/
console_locked = 1;
printk_cpu = UINT_MAX;
spin_unlock(&logbuf_lock);
/*
* Console drivers may assume that per-cpu resources have
* been allocated. So unless they're explicitly marked as
* being able to cope (CON_ANYTIME) don't call them until
* this CPU is officially up.
*/
if (cpu_online(smp_processor_id()) || have_callable_console()) {
console_may_schedule = 0;
release_console_sem();
} else {
/* Release by hand to avoid flushing the buffer. */
console_locked = 0;
up(&console_sem);
}
lockdep_on();
raw_local_irq_restore(flags);
} else {
/*
* Someone else owns the drivers. We drop the spinlock, which
* allows the semaphore holder to proceed and to call the
* console drivers with the output which we just produced.
*/
printk_cpu = UINT_MAX;
spin_unlock(&logbuf_lock);
lockdep_on();
raw_local_irq_restore(flags);
}
preempt_enable();
return printed_len;
}