• 问题描述

内核调试中最常见的一个问题是:内核Panic后,如何快速定位到出错的代码行?

就是这样一个常见的问题,面试过的大部分同学都未能很好地回答,这里希望能够做很彻底地解答。

  • 问题分析

内核Panic时,一般会打印回调,并打印出当前出错的地址:

kernel/panic.c:panic():

#ifdef CONFIG_DEBUG_BUGVERBOSE
/*
* Avoid nested stack-dumping if a panic occurs during oops processing
*/
if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
dump_stack();
#endif

而dump_stack()调用关系如下:

dump_stack() --> __dump_stack() --> show_stack() --> dump_backtrace()

dump_backtrace()会打印整个回调,例如:

[<001360ac>] (unwind_backtrace+0x0/0xf8) from [<00147b7c>] (warn_slowpath_common+0x50/0x60)
[<00147b7c>] (warn_slowpath_common+0x50/0x60) from [<00147c40>] (warn_slowpath_null+0x1c/0x24)
[<00147c40>] (warn_slowpath_null+0x1c/0x24) from [<0014de44>] (local_bh_enable_ip+0xa0/0xac)
[<0014de44>] (local_bh_enable_ip+0xa0/0xac) from [<0019594c>] (bdi_register+0xec/0x150)

通常,上面的回调会打印出出错的地址。

  • 解决方案

情况一

在代码编译连接时,每个函数都有起始地址和长度,这个地址是程序运行时的地址,而函数内部,每条指令相对于函数开始地址会有偏移。那么有了地址以后,就可以定位到该地址落在哪个函数的区间内,然后找到该函数,进而通过计算偏移,定位到代码行。

情况二

但是,如果拿到的日志文件所在的系统版本跟当前的代码版本不一致,那么编译后的地址就会有差异。那么简单地直接通过地址就可能找不到原来的位置,这个就可能需要回调里头的函数名信息。先通过函数名定位到所在函数,然后通过偏移定位到代码行。

相应的工具有addr2line, gdb, objdump等,这几个工具在How to read a Linux kernel panic?都有介绍,我们将针对上面的实例做更具体的分析。

需要提到的是,代码的实际运行是不需要符号的,只需要地址就行。所以如果要调试代码,必须确保调试符号已经编译到内核中,不然,回调里头打印的是一堆地址,根本看不到符号,那么对于上面提到的情况二而言,将无法准确定位问题。

如果要获取到足够多的调试信息,请根据需要打开如下选项:

CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_STACKTRACE=y

下面分别介绍各种用法。

  • addr2line

如果出错的内核跟当前需要调试的内核一致,而且编译器等都一致,那么可以通过addr2line直接获取到出错的代码行,假设出错地址为0019594c:

$ addr2line -e vmlinux_with_debug_info 0x0019594c
mm/backing-dev.c:335

然后用vim就可以直接找到代码出错的位置:

$ vim mm/backing-dev.c +335

如果是情况二,可以先通过nm获取到当前的vmlinux中bdi_register函数的真实位置。

$ nm vmlinux | grep bdi_register
0x00195860 T bdi_register

然后,加上0xec的偏移,即可算出真实地址:

$ echo "obase=16;ibase=10;$((0x00195860+0xec))" | bc -l
19594C
  • gdb

这个也适用情况二,因为可以直接用 符号+偏移 的方式,因此,即使其他地方有改动,这个相对的位置是不变的。

$ gdb vmlinux_with_debug_info
$ list *(bdi_register+0xec)
0x0019594c is in bdi_register (/path/to/mm/backing-dev.c:335).
330 bdi->dev = dev;
331
332 bdi_debug_register(bdi, dev_name(dev));
333 set_bit(BDI_registered, &bdi->state);
334
335 spin_lock_bh(&bdi_lock);
336 list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
337 spin_unlock_bh(&bdi_lock);
338
339 trace_writeback_bdi_register(bdi);

如果是情况一,则可以直接用地址:list *0x0019594c。

  • objdump

如果是情况一,直接用地址dump出来。咱们回头看一下Backtrace信息:bdi_register+0xec/0x150,这里的0xec是偏移,而0x150是该函数的大小。用objdump默认可以获取整个vmlinux的代码,但是咱们其实只获取一部分,这个可以通过--start-address和--stop-address来指定。另外-d可以反汇编代码,-S则可以并入源代码,-l显示源代码文件和行号。

$ objdump -dlS vmlinux_with_debug_info --start-address=0x0019594c --stop-address=$((0x0019594c+0x150))

如果是情况二,也可以跟addr2line一样先算出真实地址,然后再通过上面的方法导出。

总地来看,gdb还是来得简单方便,无论是情况一还是情况二都适用,而且很快捷地就显示出了出错的代码位置,并且能够显示代码的内容。

对于用户态来说,分析的方式类似。如果要在应用中获取Backtrace,可以参考Generating backtraces。其例子如下:

#include <execinfo.h>
#define BACKTRACE_SIZ 64 void show_backtrace (void)
{
void *array[BACKTRACE_SIZ];
size_t size, i;
char **strings; size = backtrace(array, BACKTRACE_SIZ);
strings = backtrace_symbols(array, size); for (i = 0; i < size; i++) {
printf("%p : %s\n", array[i], strings[i]);
} free(strings); // malloced by backtrace_symbols
}

编译代码时需要加上:-funwind-tables,-g和-rdynamic。

如何快速定位 Linux Panic 出错的代码行的更多相关文章

  1. 【未解决】对于使用Windows的IDEA进行编译的文件,但无法在Linux系统中统计代码行数的疑问

    在我学习使用Windows的IDEA的过程中,将代码文件转移到Linux虚拟机当中,但无法在Linux系统中统计代码行数. 注意:拷贝进虚拟机的文件均能编译运行. 具体过程如下: root@yogil ...

  2. linux设备驱动第四篇:从如何定位oops的代码行谈驱动调试方法

    上一篇我们大概聊了如何写一个简单的字符设备驱动,我们不是神,写代码肯定会出现问题,我们需要在编写代码的过程中不断调试.在普通的c应用程序中,我们经常使用printf来输出信息,或者使用gdb来调试程序 ...

  3. VC6.0 通过崩溃地址中找到异常代码行

    来源:http://blog.csdn.net/mydeardingxiaoli/article/details/20371585 这是从“VC编程经验总结7”中转出来的借花献佛——如何通过崩溃地址找 ...

  4. linux下编译make文件报错“/bin/bash^M: 坏的解释器,使用grep快速定位代码位置

    一.linux下编译make文件报错“/bin/bash^M: 坏的解释器 参考文章:http://blog.csdn.net/liuqiyao_01/article/details/41542101 ...

  5. 使用MAP文件快速定位程序崩溃代码行 (转)

    使用MAP文件快速定位程序崩溃代码行 =========================================================== 作者: lzmfeng(http://lz ...

  6. js分析 快速定位 js 代码, 还原被混淆压缩的 js 代码

    -1.目录 0.参考 1.页面表现 2. 慢镜头观察:低速网络请求 3. 从头到尾调试:Fiddler 拦截 index.html 并添加 debugger; 4. 快速定位 js 代码 5. 还原被 ...

  7. linux磁盘空间占满问题快速定位并解决

    经常会遇到这样的场景:测试环境磁盘跑满了,导致系统不能正常运行!此时就需要查看是哪个目录或者文件占用了空间.常使用如下几个命令进行排查:df, lsof,du. 通常的解决步骤如下:1. df -h ...

  8. Linux性能优化从入门到实战:06 CPU篇:快速定位CPU瓶颈

    CPU性能指标      (1)CPU使用率:1) 用户态CPU使用率(包括用户态 user 和低优先级用户态 nice).2) 系统CPU使用率.3) 等待 I/O 的CPU使用率.4) 软中断和硬 ...

  9. 32位汇编第四讲,干货分享,汇编注入的实现,以及快速定位调用API的数量(OD查看)

    32位汇编第四讲,干货分享,汇编注入的实现,以及快速定位调用API的数量(OD查看) 昨天,大家可能都看了代码了,不知道昨天有没有在汇编代码的基础上,实现注入计算器. 如果没有,今天则会讲解,不过建议 ...

  10. linux io的cfq代码理解

    内核版本: 3.10内核. CFQ,即Completely Fair Queueing绝对公平调度器,原理是基于时间片的角度去保证公平,其实如果一台设备既有单队列,又有多队列,既有快速的NVME,又有 ...

随机推荐

  1. java实现二维码登录功能

    本文采用Springboot工程进行开发,使用Google的zxing生成二维码,直接放代码: <?xml version="1.0" encoding="UTF- ...

  2. openeuler linux内核4.19安装(centos 同理)

    linux内核安装: 安装内核步骤 下载相应内核版本[我这里用的是linux-4.19.90.tar.gz] 下载网址:https://mirrors.edge.kernel.org/pub/linu ...

  3. 【MySQL】全库调整表大小写语句

    统一修改字段成小写+下划线的命名规则: V1上线后,重新看SQL调整的较可行的写法: # = = = = = = = = = = = = = = = 统一更改全库所有字段大小写脚本SQL(会删除字段原 ...

  4. 【Java】Collection 集合框架概述

    Collection 集合框架概述 1.集合.数组都是为了存储数据而产生的 2.为什么需要集合?为了更灵活方便的存储数据,且集合能存储的容量比数组更大 3.存储的概念还停留在内存活动范围内,也只是短暂 ...

  5. 【RabbitMQ】14 集群搭建

    多服务器单实例 -- 参考博客: https://www.cnblogs.com/lixioayi/articles/9993658.html 首先要找到cookie文件,所有实例要保持cookie一 ...

  6. 【RabbitMQ】09 深入部分P2 消费限流 & TTL

    1.消费限流设置 就是设置项的2个调整,当然还有前面的手动确认的监听改动处理 https://www.bilibili.com/video/BV15k4y1k7Ep?p=26 2.消息过时设置 TTL ...

  7. 分段树(segment tree)的实现 —— 强化学习中 "优先级回放机制" 的重要组成部分

    分段树(segment tree)是强化学习中 "优先级回放机制" 的重要组成部分.本文针对分段树(segment tree)的一个开源版本的实现来进行分析,代码地址: https ...

  8. 深度学习框架 MindSpore —— 华为出品的AI计算框架, docker 安装

    深度学习框架  MindSpore  --   华为出品的AI计算框架 官网地址: https://www.mindspore.cn/ 源代码地址: https://gitee.com/devilma ...

  9. 圆方树学习笔记 & 最短路 题解

    前言 圆方树学习笔记,从一道例题讲起. 题目链接:Hydro & bzoj. 题意简述 仙人掌上求两点距离. 题目分析 为了把仙人掌的性质发挥出来,考虑将其变成一棵树.圆方树就是这样转换的工具 ...

  10. games101 作业1及作业2分析及解决

    games101 作业1及作业2分析及解决 去年的时候把games101的课程以及作业完成,但是整个过程比较粗略,也借助了不少外界的力量(doge),于是最近准备抽几天集中再把作业(1-7)过一遍,常 ...