如何调试ARM Linux内核(msleep())锁定?

Posted

技术标签:

【中文标题】如何调试ARM Linux内核(msleep())锁定?【英文标题】:How to debug ARM Linux kernel (msleep()) lock up? 【发布时间】:2012-03-27 19:57:20 【问题描述】:

我首先在寻找调试技巧。如果有人可以指出要更改的一行代码或要设置的一个外围配置位来解决问题,那就太好了。但这不是我所希望的。我正在寻找更多关于如何调试它。

谷歌搜索“msleep hang linux kernel site:***.com”得到 13 个答案,但没有一个是重点,所以我想我可以放心提问。

我为嵌入式 TI AM1808 ARM 处理器(Sitara/DaVinci?)重建了 ARM Linux 内核。我看到所有启动日志到登录:提示从串口出来,但是尝试登录没有响应,甚至没有回显我输入的内容。

经过大量调试后,我到达了内核并在第 828 行和第 830 行之间添加了调试代码(是的,内核版本是 2.6.37)。这是在调用 'sbin/init' 之前的内核模式:

http://lxr.linux.no/linux+v2.6.37/init/main.c#L815

就在第 830 行之前,我添加了一个永久循环 printk,我看到了结果。我让它运行了大约几个小时,它计数到大约 200 万。采样线:

dbg:init/main.c:1202: 2088430

所以它吐出 6000 万字节没有问题。

但是,如果我在循环中添加 msleep(1000),它只会打印一次,即 msleep() 不会返回。

详情: 在调度程序的第 4073 行添加条件 printk,条件是在上述永久测试循环开始时设置的标志,表明 schedule() 挂起时不再调用:

http://lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4064

.config/'Device Drivers' 下的唯一选择是: 块设备 I2C 支持 SPI 支持

使用 uboot/TFTP 加载内核及其 ramdisk。 我不相信它会尝试使用以太网。 由于所有这些都发生在 '/sbin/init' 之前,因此应该很少发生。

更多详情: 我有一块非常相似的主板,它的 CPU 相同。我可以运行相同的 uImage 和相同的 ramdisk,它在那里工作正常。我可以登录并做通常的事情。

我已经运行内存测试(总共 64 MB,将内核限制为 32M 并测试其他 32M;它是单芯片 DDR2),没有发现任何问题。 一块板子使用 UART0,另一块板子使用 UART2,但启动日志来自两者,所以应该不是问题。

非常感谢任何调试技巧。 我没有合适的 JTAG,所以我不能使用它。

【问题讨论】:

会不会是调度器依赖于某个硬件定时器?哪个可能坏了?还是使用不同的 io 地址? 据我所知,一切都应该在芯片上(我想它值得仔细检查)所以他们应该看到除了串行端口之外的相同环境(所有 3 个都应该处于活动状态,只需选择哪个处于活动状态.) 我想我会寻找时间滴答 IRQ 并在那里添加一个 printk(如果我能找到它:) 【参考方案1】:

如果msleep 没有返回或没有到达schedule,那么为了调试我们可以跟踪调用堆栈。

msleep 调用schedule_timeout_uninterruptible(timeout),后者调用schedule_timeout(timeout),如果传递给它的 jiffies 中的超时时间

如果timeout 为正,则调用setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);,然后调用__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);,然后再调用schedule

如果我们没有到达schedule,那么setup_timer_on_stack__mod_timer 中一定发生了一些事情。

setup_timer_on_stack 的调用跟踪是 setup_timer_on_stack 调用 setup_timer_on_stack_key 调用 init_timer_on_stack_key 如果启用了 CONFIG_DEBUG_OBJECTS_TIMERS 或者调用 init_timer_key(timer, name, key); 调用是外部的 debug_init 后跟__init_timer(timer, name, key)

__mod_timer 首先调用timer_stats_timer_set_start_info(timer);,然后是一大堆其他函数调用。

我建议先在 schedule_timeout 中放置一个或两个 printk,可能是 setup_timer_on_stack 调用的任一侧或 __mod_timer 调用的任一侧。

【讨论】:

这对我来说太难了。谢谢,会回来报告的。 在'schedule();'周围添加了“之前”和“之后”:lxr.linux.no/linux+v2.6.37/kernel/timer.c#L1477也在schedule()的入口和出口处打印k:lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4073lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4152 在两块板上都看到了“之前”。在坏板上两次看到 schedule() 进入/退出对,在好板上看到三次。另请参阅:lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4133 上下文切换已经从我们下面翻转了堆栈,所以我猜 schedule() 返回到其他地方,只有第三次它返回到我的 msleep 调用。所以我想问题是为什么调用 msleep() 的任务还没有准备好再次调度,或者 CPU 已经崩溃(我认为至少会有一个恐慌消息)。 你以前看过多少? 2代表坏板,3代表好?您可能希望使用其他信息更新您的问题,而不是将其放在 cmets 中。【参考方案2】:

这个问题已经解决了。

通过大量使用 prink 可以确定 schedule() 确实切换到另一个任务,即空闲任务。在这种情况下,作为嵌入式 Linux,我复制的原始代码库安装了一个空闲任务。那个空闲任务似乎不适合我的主板,并且锁定了 CPU,从而导致了崩溃。注释掉对空闲任务的调用

http://lxr.linux.no/linux+v2.6.37/arch/arm/mach-davinci/cpuidle.c#L93

解决问题。

【讨论】:

我也遇到了和你一样的问题,我的董事会挂在 msleep 电话上,但我没有得到你的修复....你在“schedule_timeout”下注释掉了 schedule() 吗? 正如我所说,我注释掉了对空闲任务的调用。 Linux 浏览器有一个新 URL:lxr.free-electrons.com/source/arch/arm/mach-davinci/…,而我注释掉的行是:cpu_do_idle();

以上是关于如何调试ARM Linux内核(msleep())锁定?的主要内容,如果未能解决你的问题,请参考以下文章

JLinkGDBServer调试ARM linux内核

内核调试 arm-none-linux-gnueabi-addr2line 工具使用

如何去掉ARM-LINUX启动时输出到串口的调试信息

树莓派4b linux内核调试(jtagkgdb)

树莓派4b linux内核调试(jtagkgdb)

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()