失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > dll domodal运行时异常_软件运行异常时的多种排查思路与方法

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

时间:2020-02-24 03:26:58

相关推荐

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

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

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运行时异常_软件运行异常时的多种排查思路与方法》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。