软件发生异常,排查起来毫无头绪和思路时,该怎么办呢?结合多年的开发经验,我来告诉你们几个常用的方法,不妨用这些方法去试一试!希望能帮到你们。

1、通过安装软件不同时间的版本对比一下

这个方法有点笨,但有时却很有效果。假设我们的软件出问题了,但实在是找不到线索,而我们的软件每天都会编译,或者代码有改动时会自动编译(通过脚本控制的),我们可以安装不同时间的版本,看看这个问题是从哪天开始出现的,然后查看这个时间点提交的代码,问题很可能就出在提交的代码中。

举个之前遇到的例子。测试同事发现最近我们一个软件在运行时窗口老是很卡,也没做什么频繁的操作,负责维护这个软件的同事,也很困惑,最近代码没有做太大的改动,前一段时间运行还好好的。查找了最近代码的修改记录,也没找到问题。于是他使用了这个方法,让测试同事多安装几个时间点的版本,看看是哪个时间点的版本开始出问题的。找到了这个时间点,开发同事查找了前一天提交的代码,详细看了一下,在实现一个功能时从网上拷贝了一段参考代码,在代码中启动了一个UI界面的定时器,在定时器处理函数中竟然有sleep操作,这个是运行在UI界面线程中的,当执行到sleep操作时,系统会将UI线程挂起,这就直接导致UI窗口卡住了,虽然sleep时间很短,但是定时器频繁执行,所以就出现了界面频繁卡顿的问题了。

其实这样的例子不少,上周遇到的一个问题也是用这个办法定位的。

2、注释代码

如果软件的问题是必现或很好复现的,开发人员可以直接在自己的机器上调试,复现问题后调试会中断下来,查看中断点附近的或相关的代码,然后采取多处注释代码的方式。如果将某一行或者某些行的代码注释掉后,软件不再有问题了,那基本上可以确定问题就出在被注释的代码上了。分析这些被注释的代码就能找到问题所在了。

上周五有个问题就是这么查出来的,当然这只是找到线索的方法,后面还需要按照这个线索去详细排查。

3、加打印

很多时候根据出问题时现象,很难判断出是哪里出的问题,可以考虑将可能出现问题的地方都添加上打印,将运行到的点以及相关的内存中数据打印到文件中,然后根据运行时产生的日志去找线索。这对频繁执行、不方便直接调试的代码块,比较实用。

4、数据断点

这个方法,对于软件运行中有变量的值被篡改的场景很有用。比如某个变量,我在最开始时给变量初始化一个值,并且代码中没有再人为的去修改这个变量的值,但是再用到这个变量作为目标函数的传入参数去调用目标函数时,这个变量的值竟然发生了变化,明明代码中没有修改过这个变量的值,这个值为什么就变化了呢?

这个很有可能是在进行memcpy时有内存越界,直接越界到这个出问题的变量内存上了,将这个变量值篡改了。此时可以在给这个变量加一个数据断点,监测这个变量的内存是否被修改。如果有修改,调试时会中断停下来,此时查看函数的调用堆栈就能确认是哪个模块及哪个函数篡改了变量的内存了。

有种情况比较隐蔽,比如有两个模块:dll-A.dll和dll-B.dll,dll-B.dll模块的导出头文件中定义了Strucht1结构体,dll-A.dll模块中的函数Fun-M中定义了dll-B.dll模块的导出头文件中定义的Strucht1结构体对象,函数Fun-M调用dll-B.dll模块中的函数Func-N,并且将Strucht1结构体对象地址传给函数Func-N,在函数Func-N中会将所在模块中相关内存中数据拷贝到该结构体对象地址指向的内存中。假设在编译dll-A.dll时,引用的dll-B库的头文件是旧的(头文件在发布过来时漏发了),dll-B库的dll库文件是新的(头文件和库文件版本不一致),并且新版本的dll-B库中的定义的Strucht1结构体新增了一些字段,即结构变大了。这样dll-A.dll模块的函数Fun-M在调用dll-B库函数Fun-N时,在函数Fun-N中会拷贝信息到传进来的Strucht1结构体对象地址指向的内存中,这时就会发生内存拷贝越界,由于操作的内存地址是主调函数Fun-M中的变量地址,所以越界是越到了主调函数Fun-M栈内存上,可能会越界到主调函数Fun-M中定义的其他局部变量的内存上,即将主调函数中其他变量的值给篡改了。

这个场景比较隐蔽的,甚至一开始排查时是很难发现的。我们之前就遇到过这样的问题。

5、windbg静态分析dump文件

建议在代码中安装捕获软件异常的代码,比如常用的是google开源的CrashReport异常捕获库(很多软件比如QQ、钉钉都使用类似的库的),在软件发生崩溃时能实时捕获到异常信息,生成dump文件。然后测试人员将dump文件发送给开发人员,开发人员使用windbg打开dump文件,使用.ecxr命令切换到异常发生时的上下文,使用kn命令查看异常发生时的函数调用堆栈,然后去详细排查崩溃问题了。这种情况属于windbg的静态分析,不是动态调试。

6、windbg动态调试

对于代码发生死循环的场景,可以先用Process Explorer先查看一下目标进程的哪个线程占用CPU比较高,记录下该线程id,然后将windbg将附加到目标进程中,然后使用windbg命令~*命令打印出所有线程,按之前记录的目标线程id,在windbg打印出的所有线程信息中找到该目标线程的序号,使用~n(n为线程序号)命令,切换到目标线程中,然后使用kn命令打印出目标线程中当前时刻的函数调用堆栈。可以使用bp命令在windbg中设置断点,可以分辨出到底发生在哪个函数中了。

最好理解的死循环,就是for或while循环中的死循环。但有的死循环可能是消息触发的死循环,比如函数A调用函数B,函数B调用函数C,函数C有调用了函数A,形成闭环了,所以导致了函数调用上的死循环。

将windbg附加到目标进程中进行动态调试,能获取到完整的内存信息,能看到当前查看函数中的局部变量的值和所在类的成员变量的值,这样可能更有利于排查问题。某些程序崩溃闪退,CrashReport捕获不到,或者捕获到了,但在生成dump文件时产生了崩溃(二次崩溃),生成了空的无效的dump文件,这种情况下可以尝试着将windbg附加到到目标进程上,复现那个崩溃,可能windbg能抓到有效的信息。

上述多种方法有时可能要结合在一起使用。

最后希望以上分享的内容能对你有所帮助,也欢迎大家留言、评论,也可以和我在线交流。

dll domodal运行时异常_软件运行异常时的多种排查思路与方法相关推荐

  1. trycatch 不能捕获运行时异常_软件运行异常时的多种排查思路与方法

    软件发生异常,排查起来毫无头绪和思路时,该怎么办呢?结合多年的开发经验,我来告诉你们几个常用的方法,不妨用这些方法去试一试!希望能帮到你们. 1.通过安装软件不同时间的版本对比一下 这个方法有点笨,但 ...

  2. python怎么运行ipynb文件_如何运行.ipynb文件的图文讲解

    如何运行.ipynb文件的图文讲解 首先cmd下面输入: pip install jupyter notebook,安装慢的改下pip的源为国内的源 然后cmd中输入: jupyter noteboo ...

  3. classcastexception异常_内部类、异常以及 LeetCode 每日一题

    1 内部类 内部类的作用: 内部类提供了更好的封装,可以把内部类隐藏于外部类之内,不允许同一个包中的其他类访问该类.(例如给"牛"这个类组合一个"牛腿",则可以 ...

  4. classnotfoundexception是什么异常_大佬说“异常信息”是优秀程序员编写代码的宝贵财富,这是真的吗...

    嗯嗯.......大佬给我看看我的代码呢,到底错哪里了?大佬走过来,一波骚操作,安排得巴巴适适的.走时撂下一句:哥仔建议你看一下控制台,那么简单的问题,记住异常信息就是你宝贵的财富. try 用来指定 ...

  5. numberformatexception是什么异常_处理Java异常的9个最佳实践

    Java中的异常处理不是一个简单的主题.初学者发现很难理解,甚至有经验的开发人员也可以花几个小时讨论如何以及应该抛出或处理哪些异常. 这就是为什么大多数开发团队都有自己的如何使用它们的规则.如果你是一 ...

  6. 新能源汽车保养vr仿真教学软件为职业培训带来新的思路和方法

    电动车电池更换VR虚拟体验是一种利用VR虚拟现实技术实现对电动车电池更换进行模拟仿真演示和实操训练的虚拟仿真实验教学课件,相比传统教学模式,有效提高学生的实践能力和技能水平. 通过VR技术模拟现场,使 ...

  7. uat测试用例和sit测试用例_软件测试用例设计时的颗粒度

    很多工作了好几年的测试工程师初次听到"用例的颗粒度"的时候会感觉很惊讶,这是个什么东西?我们工作里用到过?其实在实际的工作当中已经有意无意的涉及到了"颗粒度". ...

  8. java编译异常和运行时异常_浅谈异常结构图、编译期异常和运行期异常的区别...

    异常处理一般有2种方式,要么捕获异常try-catch,要么抛出异常throws 如果一个方法后面抛出一个运行时期异常(throws RuntimeException),调用者无须处理 如果一个方法后 ...

  9. Hadoop运行wordcount实例任务卡在job running的多种情况及解决方法

    第一种:配置问题 这是别人的图片,据楼主排查解决是因为hosts配置问题- 现象:各种无法运行.启动 解决办法: 1.修改日志级别 export HADOOP_ROOT_LOGGER=DEBUG,co ...

最新文章

  1. 《构建之法》阅读笔记07
  2. 创纪录!Oracle关键补丁更新修复关键漏洞曝光
  3. 006_FastDFS文件上传
  4. 2018年最好用的20个Bootstrap网站模板
  5. LeetCode 268. 缺失数字
  6. 索引-jquery-第二版-pyhui
  7. 少儿是先学计算机 还是学机器人,机器人学习和少儿编程有什么区别?
  8. python 3教程_Python 3 教程
  9. xpath提取目录下所有标签内的内容,递归 //text()
  10. java计算-5%3_JAVA基础教程day03--运算符
  11. 一款,整合百度翻译api跟有道翻译api的翻译君
  12. 4针串口线接法图_​RS232串口线常见接法与引脚定义
  13. 英式和美式的单词拼写差异详细对照表
  14. UVALive3713-Astronauts 2-SAT
  15. 怎樣制作线段动画_教程:如何制作一个绘制线条动画
  16. CSPS-S 模拟47
  17. 使用R语言进行时间序列(arima,指数平滑)分析
  18. 接近开关 NPN PNP
  19. c语言x20是什么意思,为什么vivo X20坚持不用TYPE-C插口?
  20. 笔记本电脑里的微信文件数据误删了 如何恢复?

热门文章

  1. 实现线程安全的单例模式的四种方式
  2. 爬取校园网新闻首页的新闻 使用正则表达式,函数抽离
  3. 22.C++- 继承与组合,protected访问级别
  4. Redis 数据库、键过期的实现
  5. 两个月新手的几点 storyboard 心得
  6. ExcelHandle
  7. C++再议构造函数及复制构造函数深度复制
  8. OC开发_Storyboard——绘制和视图
  9. 示例化讲解RIP路由更新机制
  10. 陶哲轩实分析定理11.9.1:微积分第一基本定理(二)