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

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

这个框架设计之初的理想流程为:应用启动->程序初始化->应用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模式。

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

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

1、主要错误信息

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

2、操作入口

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

3、PC指针

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

4、LR指针

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

5、寄存器值

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

6、出错进程信息

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

7、出错时的堆栈信息

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

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

8、函数执行的回溯关系

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

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

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

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

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

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

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

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

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

函数入口地址为c02f186c

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

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

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

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

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

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

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

上面582734行为“bl  c0493104

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

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

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

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

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

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

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

Linux运行项目部分空指针,技术文章—Linux编程时遇到Oops提示该如何排查?相关推荐

  1. linux内核oops消息分析,Linux 编程时遇到 Oops 提示该如何排查?

    原标题:Linux 编程时遇到 Oops 提示该如何排查? 各位工程师在Linux下开发程序时,有没有遇到由于系统中存在某些小故障而跳出了"Oops"提示的情况,此时你是如何排查故 ...

  2. 嵌入式linux内核oops,Linux编程时遇到Oops提示该如何排查?

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

  3. c程序兼容多个linux,个人项目-WC(C/C++ 兼容Linux和Windows)

    一.Github地址 实现的功能: 支持 -c 支持 -w 支持 -l 支持 -s 支持 -a 图形界面 多参数(文件名或通配符表达式) 跨系统平台 多种编程语言 支持中文 二.PSP表格 PSP2. ...

  4. 基于linux运行指的什么意思,基于Linux的Windows非常有意义

    原标题:基于Linux的Windows非常有意义 开源创始人之一的Eric S. Raymond认为,Windows的未来是一层关于Linux内核的.几天前,开发者兼作家Eric S. Raymond ...

  5. linux运行并行计算cuda,并行化计算与CUDA编程

    原标题:并行化计算与CUDA编程 近年来,显卡的更新换代也比较快,NVIDIA今年的发布会也宣布了RTX3080TI即将到来.显卡的运算能力也越来越强. 很多人对显卡的了解可能源于游戏,但是你的显卡不 ...

  6. linux 运行msi文件是什么意思,linux msi文件怎么安装

    因为Python是跨平台的,它可以运行在Windows.Mac和各种Linux/Unix系统上.在Windows上写Python程序,放到Linux上也是能够运行的. 要开始学习Python编程,首先 ...

  7. linux查看usb设备文件,技术|在 Linux 系统里识别 USB 设备名字的 4 种方法

    对于初学者来说,在 Linux 系统里你必须掌握的技术之一就是识别出插入系统里的各种设备.这也许是你的系统硬盘.外部的存储设备或者是可移动设备,比如 USB 设备或 SD 闪存卡等. 现如今,使用 U ...

  8. linux磁盘使用情况脚本,技术|用 Linux Shell 脚本来监控磁盘使用情况并发送邮件...

    市场上有很多用来监控 Linux 系统的监控工具,当系统到达阀值后它将发送一封邮件.它监控所有的东西例如 CPU 利用率.内存利用率.交换空间利用率.磁盘空间利用率等等.然而,它更适合小环境和大环境. ...

  9. linux运行炉石传说,使用Lutris在Linux上玩炉石传说

    目的 安装Lutris并播放炉石传说. 发行版 这将适用于大多数当前发行版,但主要针对Ubuntu. 要求 具有更新的图形驱动程序和root权限的有效Linux安装(最好是Ubuntu). 困难 简单 ...

最新文章

  1. JavaScript初学者编程题(2)
  2. pytorch默认初始化_小白学PyTorch | 9 tensor数据结构与存储结构
  3. toch_geometric 笔记:message passing GCNConv
  4. python点操作符语法_最基础的python语法
  5. C语言学习之一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如,6的因子为1,2,3,而6=1+2+3,因此6是“完数”。编程序找出1000之内的所有完数。
  6. python闯关_99题--Python闯关
  7. 鸿蒙系统年底发布?华为人员看到这个山寨网站笑了
  8. 史上最优秀的转换工具类网站推荐[唯一保存历史转换记录]
  9. 自定义View/ViewGroup的步骤和实现
  10. CS230+deeplearning.ai专项课程笔记及作业目录
  11. Linux常用编程工具
  12. android vlc m3u8,Exoplayer播放m3u8文件Android
  13. s5p6818 Smart6818 nanopi3开发板,uboot启动linux,配置nfs文件系统,设置bootargs
  14. WinSAT 系统评估工具使用
  15. 利用手机app语音控制arduino
  16. 【Photoshop技巧】两步将黑色图片改变颜色
  17. OpenFlow Tutorial
  18. lara with tp
  19. html管理是什么意思,质效管理是什么意思?
  20. python中numpy-choice函数

热门文章

  1. 用神经网络做分子模型是不是扯淡,f2,cl2,br2分子模型
  2. 权重可以当做概率幅吗?---用神经网络的收敛模拟机械波的波动
  3. java中获取特定时间段_获取某一时间段特定星期几的所有日期(Java实现)
  4. pythontxt文件怎么读_python怎么读txt文件
  5. 【控制】二阶 UGV 的无穷时间状态输入性能最优解算
  6. 3.5 将 Batch 拟合进神经网络-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  7. 计算机网络·数据链路层.三个基本问题
  8. 给定数组的子集 Subsets
  9. 高速掌握Lua 5.3 —— 字符串库 (2)
  10. 以“不变”方能应“万变”,以“有心”必能筑“无界”