kernel上我们可能经常会遇到kernel oops或kernel pannic错误,
通常我们通过串口LOG应该能得到类似下面的debug信息:
----------------------------------------------------------------------------------------------------
[ 3.256221] [<c00e4624>] (__queue_work+0x2dc/0x444) from [<c00e47ec>] (queue_work_on+0x38/0x40)
[ 3.264889] [<c00e47ec>] (queue_work_on+0x38/0x40) from [<c00e4834>] (queue_work+0x2c/0x54)
[ 3.273223] [<c00e4834>] (queue_work+0x2c/0x54) from [<c03a1018>] (switch_key_irq+0x24/0x38)
[ 3.281648] [<c03a1018>] (switch_key_irq+0x24/0x38) from [<c010dc48>] (handle_irq_event_percpu+0xe4/0x2d0)
[ 3.291281] [<c010dc48>] (handle_irq_event_percpu+0xe4/0x2d0) from [<c010de70>] (handle_irq_event+0x3c/0x5c)
[ 3.301086] [<c010de70>] (handle_irq_event+0x3c/0x5c) from [<c0110c3c>] (handle_edge_irq+0x130/0x16c)
[ 3.310289] [<c0110c3c>] (handle_edge_irq+0x130/0x16c) from [<c010d6ec>] (generic_handle_irq+0x28/0x3c)
[ 3.319669] [<c010d6ec>] (generic_handle_irq+0x28/0x3c) from [<c00b6b8c>] (msm_gpio_irq_handler+0x80/0xcc)
[ 3.329301] [<c00b6b8c>] (msm_gpio_irq_handler+0x80/0xcc) from [<c010d6ec>] (generic_handle_irq+0x28/0x3c)
[ 3.338936] [<c010d6ec>] (generic_handle_irq+0x28/0x3c) from [<c0042fc0>] (handle_IRQ+0x7c/0xc0)
[ 3.347703] [<c0042fc0>] (handle_IRQ+0x7c/0xc0) from [<c003c348>] (gic_handle_irq+0xa8/0x100)
[ 3.356211] [<c003c348>] (gic_handle_irq+0xa8/0x100) from [<c05a84d4>] (__irq_svc+0x54/0x80)
----------------------------------------------------------------------------------------------------
下面介绍一个通过stack表得知代码出错位置的文件名以及行号的方法:
1. 找到kernel编译时生成的map文件:System.map,此文件位于 out/target/product/<项目名>/obj/KERNEL_OBJ 目录下面
2. 执行命令:
cat System.map | grep __queue_work
其中:__queue_work 就是上面debug信息中call stack的第一行,这实际上就是PC指针所在的位置。
命令返回如下类似的结果:
c00e4348 t __queue_work
其中,c00e4348就是符号__queue_work的地址值。
3. 使用交叉编译链的gdb工具加载vmlinux文件
vmlinux文件与System.map通常是在一个文件夹下,位于 out/target/product/<项目名>/obj/KERNEL_OBJ 目录下面。
交叉编译链的gdb工具通常名叫:arm-eabi-gdb 此命令在执行 choosecomboext 命令选择项目后即可执行。
执行命令如下:
arm-eabi-gdb vmlinux
命令执行完毕后你就处于了gdb调试环境中。
此时,计算代码所在位置,就是符号地址 + 偏移量,本例中就是 c00e4348 + 0x2dc 结果是:c00e4624
在gdb环境中输入以下命令:
b * 0xC00E4624
此例中输出了如下结果:
Breakpoint 1 at 0xc00e4624: file /home/diao/workspace2/ics8x25/kernel/kernel/workqueue.c, line 1037.
这就是我们想要的答案了,找到代码所在地,本例中,workqueue.c, line 1037 处的代码如下:
1037 BUG_ON(!list_empty(&work->entry));
这样我们就知道了代码出错在哪一行。
当然,可能这里还不是答案,你可能需要继续在call stack中向前查找,但这对于我们定位问题确实有帮助。
最后,退出gdb环境的命令是:q
通常我们通过串口LOG应该能得到类似下面的debug信息:
----------------------------------------------------------------------------------------------------
[ 3.256221] [<c00e4624>] (__queue_work+0x2dc/0x444) from [<c00e47ec>] (queue_work_on+0x38/0x40)
[ 3.264889] [<c00e47ec>] (queue_work_on+0x38/0x40) from [<c00e4834>] (queue_work+0x2c/0x54)
[ 3.273223] [<c00e4834>] (queue_work+0x2c/0x54) from [<c03a1018>] (switch_key_irq+0x24/0x38)
[ 3.281648] [<c03a1018>] (switch_key_irq+0x24/0x38) from [<c010dc48>] (handle_irq_event_percpu+0xe4/0x2d0)
[ 3.291281] [<c010dc48>] (handle_irq_event_percpu+0xe4/0x2d0) from [<c010de70>] (handle_irq_event+0x3c/0x5c)
[ 3.301086] [<c010de70>] (handle_irq_event+0x3c/0x5c) from [<c0110c3c>] (handle_edge_irq+0x130/0x16c)
[ 3.310289] [<c0110c3c>] (handle_edge_irq+0x130/0x16c) from [<c010d6ec>] (generic_handle_irq+0x28/0x3c)
[ 3.319669] [<c010d6ec>] (generic_handle_irq+0x28/0x3c) from [<c00b6b8c>] (msm_gpio_irq_handler+0x80/0xcc)
[ 3.329301] [<c00b6b8c>] (msm_gpio_irq_handler+0x80/0xcc) from [<c010d6ec>] (generic_handle_irq+0x28/0x3c)
[ 3.338936] [<c010d6ec>] (generic_handle_irq+0x28/0x3c) from [<c0042fc0>] (handle_IRQ+0x7c/0xc0)
[ 3.347703] [<c0042fc0>] (handle_IRQ+0x7c/0xc0) from [<c003c348>] (gic_handle_irq+0xa8/0x100)
[ 3.356211] [<c003c348>] (gic_handle_irq+0xa8/0x100) from [<c05a84d4>] (__irq_svc+0x54/0x80)
----------------------------------------------------------------------------------------------------
下面介绍一个通过stack表得知代码出错位置的文件名以及行号的方法:
1. 找到kernel编译时生成的map文件:System.map,此文件位于 out/target/product/<项目名>/obj/KERNEL_OBJ 目录下面
2. 执行命令:
cat System.map | grep __queue_work
其中:__queue_work 就是上面debug信息中call stack的第一行,这实际上就是PC指针所在的位置。
命令返回如下类似的结果:
c00e4348 t __queue_work
其中,c00e4348就是符号__queue_work的地址值。
3. 使用交叉编译链的gdb工具加载vmlinux文件
vmlinux文件与System.map通常是在一个文件夹下,位于 out/target/product/<项目名>/obj/KERNEL_OBJ 目录下面。
交叉编译链的gdb工具通常名叫:arm-eabi-gdb 此命令在执行 choosecomboext 命令选择项目后即可执行。
执行命令如下:
arm-eabi-gdb vmlinux
命令执行完毕后你就处于了gdb调试环境中。
此时,计算代码所在位置,就是符号地址 + 偏移量,本例中就是 c00e4348 + 0x2dc 结果是:c00e4624
在gdb环境中输入以下命令:
b * 0xC00E4624
此例中输出了如下结果:
Breakpoint 1 at 0xc00e4624: file /home/diao/workspace2/ics8x25/kernel/kernel/workqueue.c, line 1037.
这就是我们想要的答案了,找到代码所在地,本例中,workqueue.c, line 1037 处的代码如下:
1037 BUG_ON(!list_empty(&work->entry));
这样我们就知道了代码出错在哪一行。
当然,可能这里还不是答案,你可能需要继续在call stack中向前查找,但这对于我们定位问题确实有帮助。
最后,退出gdb环境的命令是:q