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

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能抓到有效的信息。

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

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

图片和内容源自网络分享,若有侵权,请联系删除!

上海艾磊科技有限公司专门为企业提供IT咨询,IT外包,系统集成,以及各类IT增值服务。其中增值服务包括OFFICE 365云服务,鼎捷企业ERP管理软件,云备份,企业邮箱,无线覆盖,上网行为管理,VPN架设,网络安全服务,INTERNET接入,设备租赁, IP电话服务

trycatch 不能捕获运行时异常_软件运行异常时的多种排查思路与方法相关推荐

  1. dll domodal运行时异常_软件运行异常时的多种排查思路与方法

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

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

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

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

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

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

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

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

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

  6. illegalargumentexception是什么异常_线程出现异常!应该如何处理?

    点击上方 一个优秀的废人,选择 设为星标 优质文章,及时送达 juejin.im/post/6844903997388636174 之前使用线程执行任务的时候,总是忽略了线程异常的处理,直到最近看书~ ...

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

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

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

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

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

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

最新文章

  1. 人脸检测--FaceBoxes: A CPU Real-time Face Detector with High Accuracy
  2. CC1101、SI4432、SI4463 相互通信的可能性与得失探讨
  3. (转)C# 把我所积累的类库全部分享给博友(附件已经上传)
  4. 携程运维自动化平台,上万服务器变更也可以很轻松
  5. 微信小程序中相机api_微信小程序 Image API实例详解
  6. 托管项目到github
  7. 【原创】大叔案例分享(4)定位分析--见证scala的强大
  8. c# 实现模糊PID控制算法
  9. python安装 文件或目录损坏_文件或目录损坏且无法读取怎么办?
  10. 转:TED高赞演讲:我们的认知,正在被这3种偏见毁掉
  11. 看完比尔盖茨30年的56条思考,我才理解他为什么能17年斩获世界首富!
  12. vue脚手架 编译速度慢95% emitting CopyPlugin
  13. Java期末大作业-工资系统平台(实验报告内附代码)
  14. 英寸和厘米的交互python_Python课 #04号作业
  15. 批量缩小图片大小的方法
  16. 手撕HashMap(原理)
  17. 2019java学习路线图
  18. 关于IBM的X3200M3型IMM和UEFI的问题解决方案汇总
  19. 华为手机下拉菜单变大_华为手机下拉菜单变少了 华为下拉通知栏变白色
  20. Chatbot项目的剖析

热门文章

  1. SQL格式化流水号位数
  2. DAN疼之后上些基础知识---自定义HttpModule和httpHandler
  3. 如何提高WEB程序的效率
  4. deleter mysql,如何通过env文件传递docker中的环境变量?
  5. python定义数据框大小_python – 如何设置框架的最小和最大高度或宽度?
  6. Camera ISP与DSP的区别(二十五)
  7. wireshark最新版本for Ubuntu18.04(六)
  8. C++创建对象new与不new区别
  9. Android多媒体:AudioTrack
  10. 嵌入式系统下对GPIO的简单操作