0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux编程时遇到Oops提示该如何排查?

西西 作者:致远电子 2018-11-28 16:52 次阅读

各位工程师Linux下开发程序时,有没有遇到由于系统中存在某些小故障而跳出了“Oops”提示的情况,此时你是如何排查故障?一行行的查看代码吗?其实不用那么复杂,本文将为你介绍一种高效的Linux编程的故障排除方法。

在分析Oops之前,我们先来看以下这么一个例子,使用GPIO的中断做掉电检测,参考《嵌入式Linux开发教程下册》的驱动框架,设计如下程序框图:

Linux编程时遇到Oops提示该如何排查?

这个框架设计之初的理想流程为:应用启动-》程序初始化-》应用open设备-》等待中断事件,但实际项目开发时,往往发生许许多多不可预测的事情。如小王正在调Qt应用,发现老王的进程老在打印,那就不让老王的进程开机自启动,调了两三天后,不定时地提示个Oops提示,小王按照“以前代码不出现,新加的出现,那么起因绝对在新代码内”的惯性思维,认为是新加的Qt导致的,然后小王就不断测试,不断查找bug中。..。..。这样就过去了十年。

但原因其实是小王没有open设备,即驱动层没有初始化定时器队列,那么中断处理函数中50ms触发的队列就为一个空值,空指针时Linux内核当然“哎呦”一下提醒你了,而不定时地提示其实就是因为电源不定时地松动,gpio检测到掉电了所以触发了中断。

实际上,这样的案例十分常见,原本想A-》B-》C,实际使用是A-》D-》C,又或者驱动中有某个变量忘记初始化等等,这时分析Oops就可以十分快速地解决问题。那接下来我们就用Linux中标准驱动去触发一个Oops,对的你没看错,Linux内核标准源码也存在这样的异常,而且我们也可以去修复这样的问题。

使用我司的EasyARM-iMX283开发板,内核源码为光盘内的Linux-2.6.35.3.tar.bz2,编译方法请参考光盘资料,我们需要把lcd的背光驱动修改为ko模式。

Linux编程时遇到Oops提示该如何排查?

烧录完新内核,加载新编译出来的drivers/video/backlight/mxs_bl.ko文件就会提示以下Oops信息

Linux编程时遇到Oops提示该如何排查?

乍看之下,这段信息跟乱码差不多,但只要你一层层地分析,你就会发现,这些信息已经告诉了我们错误的原因。接下来就开始我们的Oops分析之旅。

1、主要错误信息

Linux编程时遇到Oops提示该如何排查?

用于提示错误的类型,这里表示使用空指针。

2、操作入口

Linux编程时遇到Oops提示该如何排查?

用于提示错误的操作,这里表示加载mxs_bl模块时出错,对应于加载操作insmod mxs_bl.ko。

3、PC指针

Linux编程时遇到Oops提示该如何排查?

用于提示出错时的PC指针位置,PC指针即当前程序运行点的地址,这里提示表示错误函数为regulator_set_current_limit,偏移地址为0xc。

4、LR指针

Linux编程时遇到Oops提示该如何排查?

用于提示出错时的LR指针位置,LR指针即调用子函数的上一个函数名以及入口偏移量,这里表示上一个函数为set_bl_intensity,偏移地址为0xd8。即set_bl_intensity调用regulator_set_current_limit时出错。

5、寄存器

Linux编程时遇到Oops提示该如何排查?

用于记录出错时各个寄存器的值,对于汇编比较熟悉的同志们可以研究一下这段信息。

6、出错进程信息

Linux编程时遇到Oops提示该如何排查?

用于提示出错的进程id号与进程名称。出错进程为insmod, PID号2261,对于多任务系统中,可能存在多个PID调用同一个接口的情况。

7、出错时的堆栈信息

Linux编程时遇到Oops提示该如何排查?

用于提示出错时堆栈内保存的寄存器信息,当程序由于中断发生或子程序调用时,会执行压栈操作,即将运行环境保存到堆栈内,保证退出中断或跳出子程序后,运行环境不发生改变。

而此处的堆栈信息即记录了程序运行时的环境信息。从中我们可以找到许多LR地址,从而分析出函数调用关系,与下一段的信息有类似作用。

8、函数执行的回溯关系

Linux编程时遇到Oops提示该如何排查?

用于表示函数的调用关系,通过这段信息我们可以知道,函数的整个执行流程,知道它的函数调用关系,最后整理出来的函数执行流程如下:

Linux编程时遇到Oops提示该如何排查?

从中我们看到了熟悉的init函数、probe函数、以及清楚probe函数下执行的操作过程是到哪一步出错的。现在我们知道了代码的执行流程,出错的PC指针的位置,但还是看不到代码,出错指针处我们只看到了一串数字,那么接下来我们就操作一下,把pc指针的数据变为有意义的代码。

第一步,分辨出错误代码在什么位置

这次实验涉及的二进制文件有内核的烧录固件以及驱动的ko文件,所以第一步分析就需要确定出错代码是在内核固件里还是ko文件里。

首先得到内核代码的范围,用以下命令将内核反汇编。

Linux编程时遇到Oops提示该如何排查?

查看这个文件的格式如是:

Linux编程时遇到Oops提示该如何排查?

第一列行数,第二列运行地址,第三列二进制码,第四列汇编代码,既然第二列为运行地址,即等同于程序运行到这行时,pc指针的值等于这个数值。这样只要翻看这个文件的头部以及尾部,就能知道内核代码的PC指针范围为:c0008000~c0562338。

根据前面第5步寄存器值,出错时PC指针为c02f1878,即在内核源码范围内。

第二步,分析出错函数的出错语句

那么根据第3步PC指针,得到regulator_set_current_limit的汇编代码,如下:

Linux编程时遇到Oops提示该如何排查?

函数入口地址为c02f186c 《regulator_set_current_limit》。

在第3步PC指针指出偏移地址为“PC is at regulator_set_current_limit+0xc”。

PC = 0xc02f1878 = 0xc02f186c + 0xc,符合汇编代码地址。

第三步,找到出错函数的C语言代码

这步可以说是最困难的,因为内核代码层次多,同名函数也可能存在许多份,可能几份编译进内核(static声明的局部函数),也可能没编译进内核,如何从众多的代码中分析出具体哪段呢。

本人就使用了一些小手段,首先给每个同名函数的入口加段乱码,让编译器筛选出编译进内核的文件(因为乱码,所以编译会报错),然后给剩下的函数加打印语句,通常经过第一步之后,可选的目标就两三个,通过打印进一步确认代码即可。

以下为筛选出来的C语言代码。

Linux编程时遇到Oops提示该如何排查?

看到这好像是定位了函数,但对于不熟悉汇编的人来说,C与汇编还是没有关联起来,好像进入了死胡同,但先别气馁,从上面的汇编代码中我们知道,函数名即为函数的首地址,那么调用子函数即需要让CPU知道子函数名,那么汇编如何调用子函数呢?使用bl指令, bl+子函数名。既然汇编有这么一个特性,那么我们看汇编代码。

上面582734行为“bl c0493104 《mutex_lock》”这句调用了子函数,再看C中调用此函数的语句。

Linux编程时遇到Oops提示该如何排查?

那么结果显而易见,不可能定义个变量都报错吧,所以唯一可能错误的语句就是struct regulator_dev *rdev = regulator-》rdev,同理,这句的前半部也只是定义一个rdev的变量,再结合内核给出来的提示——空指针,所以错误就是regulator-》rdev是一个空指针。

最终的问题就归结于,为什么regulatar-》rdev为空指针。这部分的查阅代码以及推理需要更深层次地挖掘,工作量也非本文能说清的,故作者在这里就大胆地推测与上面的A-》B-》C模型类似。所以我们就需要在这个资源存在的时刻,调用它之前给它赋值。

这时侯,我们就需要拿出第8步函数执行的回溯关系图,既然知道这个图中最后的函数的输入参数regulator的rdev为空,那么我们就关心regulator结构体以及它的意义。从结构体的意义我们才能知道如何给它赋值。

Linux编程时遇到Oops提示该如何排查?

在相关的代码文件中搜索关键字”regulator”或”regulator =”(建议搜这个,因为这种才是赋值语句)得到如下代码。

Linux编程时遇到Oops提示该如何排查?

分析这个函数可知,regulator实际是pdata的一个成员,他需要data来初始化,那么接下来的事情就简单了,在回溯关系中找一个位置把data的数据塞入pdata中,刚好这段函数就是初始化的regulator的,那就直接拿去用吧。

把这段添加到probe函数内的这个位置,实现了在mxsbl_probe和mxsbl_do_probe之间赋值此变量。

Linux编程时遇到Oops提示该如何排查?

这样重新编译后即可正常加载ko文件。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10942

    浏览量

    206544
  • 编程
    +关注

    关注

    88

    文章

    3431

    浏览量

    92218
  • Oops
    +关注

    关注

    0

    文章

    4

    浏览量

    3274
收藏 人收藏

    评论

    相关推荐

    linux运维怎么排查

    linux运维排查常用命令(开发专享)
    发表于 11-11 06:34

    内核oops的根本原因是什么?我们如何调试内核oops

    Broadcom 交换机设备连接到我们主板上带有 PCIe 的 T1042。开关工作在 EP 模式。linux内核版本是4.14。在检测到 pcie 错误的现场卡中引发内核 oops。但是这个
    发表于 04-20 06:19

    LINUX网络编程

    linux开发编程教程资料——LINUX网络编程,感兴趣的小伙伴们可以看一看。
    发表于 08-23 16:23 0次下载

    Linux下的网络编程总结

    linux开发编程教程资料——Linux下的网络编程总结,感兴趣的小伙伴们可以看一看。
    发表于 08-23 16:23 0次下载

    Linux网络编程

    linux开发编程教程资料——Linux网络编程,感兴趣的小伙伴们可以看一看。
    发表于 08-23 16:23 0次下载

    Linux__C网络编程

    linux开发编程教程资料——Linux__C网络编程,感兴趣的小伙伴们可以看一看。
    发表于 08-23 16:23 0次下载

    linux-网络编程-socket编程

    linux开发编程教程资料——linux-网络编程-socket编程,感兴趣的小伙伴们可以看一看。
    发表于 08-23 16:23 0次下载

    linux 下C编程

    linux 下C编程
    发表于 10-25 08:54 9次下载
    <b class='flag-5'>linux</b> 下C<b class='flag-5'>编程</b>

    LINUX系统教程之如何在Linux系统下进行编程

    本文档的主要内容详细介绍的是LINUX系统教程之如何在Linux系统下进行编程主要内容包括了:程序开发过程 ,Linux编程环境和开发工具
    发表于 12-18 19:09 9次下载

    linux内核中的Oops

    gdb来分析问题。 开发内核驱动的过程中可能遇到的问题是千奇百怪的,调试的方法也是多种多样,OopsLinux内核给我们的提示,我们要用好它。
    发表于 04-02 14:31 536次阅读

    当STM32遇到Linux = STM32MP1

    当STM32遇到Linux = STM32MP1
    的头像 发表于 03-01 13:37 1866次阅读

    内核oops错误原因及处理方法

    最近在调试设备时,遇到了一个偶发的开机死机问题。通过查看输出日志,发现内核报告了oops错误,如下所示(中间省略了部分日志,以......代替)。
    的头像 发表于 05-12 16:20 7463次阅读

    怎么解读内核的oops

    Oops的错误代码根据错误的原因会有不同的定义,如果发现自己遇到Oops和下面无法对应的话,最好去内核代码里查找
    的头像 发表于 02-17 16:08 644次阅读

    Linux服务器常见的网络故障排查方法

    日常工作中我们有时会遇到服务器网络不通问题,导致服务器无法正常运行。要想解决服务器网络故障问题,通常要先进行网络故障排查,这里以Linux服务器为例来看下常用的网络故障排查方法介绍。
    的头像 发表于 04-14 15:47 2158次阅读

    常用linux网络排查命令

    今天浩道跟大家分享linux网络运维中常见的命令,掌握好这些命令,在排查故障时将会游刃有余!
    发表于 06-25 10:49 486次阅读