【C++软件调试技术】C++软件开发维护过程中典型调试问题的解答与总结

目录

1、引发C++软件异常的常见原因有哪些?

2、排查C++软件异常的常用方法有哪些?

3、为什么要熟悉常见的异常内存地址?

4、调试时遇到调用IsBadReadPtr或者IsBadWritePtr引发的异常,该如何处理?

5、如何排查GDI对象泄露问题?

6、如何排查内存泄露问题?

7、如何排查死循环(高CPU占用)问题?

8、如何排查数据格式化时的崩溃问题?

9、如何排查堆内存被破坏问题?

10、如何排查线程堵塞或卡死问题?

11、如何排查程序中的资源泄露问题?

12、如何排查第三方库注入引发的异常问题?

13、如何排查库与库之间不匹配问题?

14、如何排查程序启动异常(启动报错、崩溃、卡死)问题?

15、使用Windbg分析软件异常有哪些方式?

16、为什么有的异常捕获不到?

17、常用的开源异常捕获库有哪些?

18、异常捕获库捕获不到异常时,该怎么办?

19、生成dump文件的方式有哪些?

20、为什么要加载pdb文件?为什么要加载系统库的pdb文件?


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html
       最近在C++软件调试交流群中,很多人问了开发维护过程中遇到的多个调试问题,这两天正好有时间,对这些常见的问题进行一个梳理和解答。本文以问答的方式进行展开,罗列了C++软件日常开发和维护中遇到的多个软件调试问题及有代表性的场景,给出详细的处置思路和处理办法,以供大家借鉴和参考!希望大家在了解这些内容以后,能从容地应对开发维护过程中遇到的各种问题。

1、引发C++软件异常的常见原因有哪些?

       引发C++软件异常的常见原因有:访问空指针与野指针、内存越界(栈内存越界、堆内存越界和全局内存越界)、内存访问违例、线程栈溢出、堆内存被破坏、内存泄露、死循环、多线程死锁、待格式化参数与格式化符号不匹配、库与库之间的不匹配等。

       我之前根据多年的C++软件异常排查实践,对这些引发C++软件异常的常见原因进行了详细总结,可查看文章:

引发C++软件异常的常见原因分析与总结(实战经验分享)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124996473了解这些常见原因之后,我们在排查问题时可能会快速找到可疑点和排查方向,能有限地提高我们排查问题的效率!

2、排查C++软件异常的常用方法有哪些?

       了解引发C++软件异常的常见原因之后,我们还要掌握排查C++软件异常的常用手段与方法。

       排查问题的常用方法有:使用常用工具去辅助分析(比如程序启动失败、启动报错、库加载失败、死循环与高CPU占用问题等)、使用IDE开发工具调试(Debug调试、Release调试和附加到进程调试)、添加打印日志、分块注释代码、数据断点监测内存、历史版本比对法、Windbg分析(静态分析dump文件、动态调试目标进程)、使用反汇编工具IDA查看汇编代码辅助分析等。

       我之前根据多年的C++软件异常排查实践,对这些排查手段和方法进行了详细的总结,可查看文章:

排查C++软件异常的常见思路与方法(实战经验总结)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/120629327掌握这些排查软件异常的常用手段与方法之后,在遇到新的问题时我们的排查思路会更开阔、更高效,必要时需要将多种排查方法结合起来使用。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量已达到420多个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


3、为什么要熟悉常见的异常内存地址?

       一般我们只需要了解0xcccccccc、0xcdcdcdcd、0xfeeefeee和0xdddddddd这4个内存地址异常值:

当我们调试代码时遇到这些内存地址异常值后,可以大概知道是什么原因引起的,以这个为线索,就可以迅速地分析并定位问题。

        对于0xcccccccc和0xcdcdcdcd,在 debug 模式下,Visual Studio会把未初始化的栈内存全部填充成0xcccccccc,当成字符串看就是“烫烫烫烫……”;Visual Studio会把未初始化的堆内存全部填充成 0xcdcdcdcd,当成字符串看就是 “屯屯屯屯……”。这两类特殊的字符串,很多人应该都见到过。所以在debug调试时遇到有变量的值为0xcccccccc 或0xcdcdcdcd,一般是变量没有初始化引起的。

        对于0xfeeefeee,是Debug下用来标记堆上已经释放掉的内存,即已经释放的堆内存中会被填充成0xfeeefeee。注意,如果指针指向的内存被释放了,指针变量本身的地址是没做改变的,还是其之前指向的内存的地址,只是其指向的堆内存中被填充成0xfeeefeee。

        对于0xdddddddd,是Debug下用来标记堆上已经释放掉的内存,即已经释放的堆内存会被填充成0xdddddddd。0xfeeefeee也是Debug下用来填充已经释放的堆内存,但0xdddddddd和0xfeeefeee的使用场景应该是有区别的,具体区别我也不太清楚。

之前在项目问题中看到的基本都是0xfeeefeee,没见到过0xdddddddd,但前段时间在Debug下调试代码时遇到了,代码中访问了已经释放的内存,内存中都被置为0xdddddddd,这还是第一次遇到0xdddddddd异常值!正是通过0xdddddddd的说明,得知这个0xdddddddd是用来填充已经释放的堆内存,以这个为线索,快速地定位了问题!

        关于上述异常地址的详细说明,可以参见文章:

C++ 中常见异常内存地址的说明(0xcccccccc、0xcdcdcdcd、0xfeeefeee 和 0xdddddddd 等)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/128285918

4、调试时遇到调用IsBadReadPtr或者IsBadWritePtr引发的异常,该如何处理?

       IsBadReadPtr和IsBadWritePtr最开始是为了判断内存地址是否是可读可写的,但这两个API函数已经被官方废弃了,已经不再具有最开始那种判断作用了,强烈建议大家不要再使用这两个API函数!

       一些老的代码中可能还会调用IsBadReadPtr和IsBadWritePtr,比如我们项目中调用了一个第三方的dll库,这个库是好几年前的版本。我是怎么知道第三方库中调用了IsBadReadPtr和IsBadWritePtr函数的呢?看了我接下来说的内容就知道了!

       当代码执行到IsBadReadPtr和IsBadWritePtr函数调用的地方会产生一个异常,如果当前正在调试代码,则会让调试器中断下来。比如我们在用Visual Studio调试代码或者正在使用Windbg动态调试程序,会让调试器Visual Studio或Windbg中断下来。

        程序产生异常,如果在调试状态下,都会让调试器中断下来,怎么知道是调用IsBadReadPtr和IsBadWritePtr的呢?其实很简单,调试器中断下来后直接查看此时的函数调用堆栈就知道了,堆栈中能看到这两个函数的调用。

        当我们使用Visual Studio或者Windbg调试时,代码执行到IsBadReadPtr和IsBadWritePtr调用处,会抛出一个异常,调试器中断下来,没有经验的人以为程序产生崩溃了,其实不然,这个异常是非致命性的,可以跳过去,程序还可以继续运行。

异常根据严重程度可分为致命性异常和非致命性异常。致命性异常是不可恢复的,会导致程序发生崩溃闪退。非致命性的异常,则是可以跳过去的,程序可以继续运行,不会导致程序的崩溃和闪退。

       如果在用Visual Studio调试代码遇到这个异常中断,按下F5可以将异常中断跳过去。如果是在用Windbg调试目标进程,则输入命令g就可以将异常中断跳过去。注意,如果代码中多次调用了IsBadReadPtr和IsBadWritePtr,则需要多次跳过去。

       之前我写过关于IsBadReadPtr和IsBadWritePtr相关案例的文章,需要的话,可以去查看:

使用Windbg排查C++程序调用IsBadReadPtr或IsBadWritePtr引发内存访问违例问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/129892952

5、如何排查GDI对象泄露问题?

       所谓的GDI对象泄露,就是GDI对象使用完成后没有释放,导致泄露。在Windows系统中,一个进程的GDI对象总数为1万个,当进程的GDI对象总数接近或达到这个上限,则会导致GDI绘图失败,甚至引发程序崩溃。

       GDI对象泄露在UI程序编程中经常遇到,如果存在GDI对象泄露,在Windows任务管理器中可以看到GDI对象总数在持续的上涨。但任务管理器中看不到具体是哪一种GDI对象在不断增长。可以使用GDIView工具,可以看到给类GDI对象的数目,这样就能看到具体是哪个对象在增长,这样结合代码的修改记录,就能很快定位泄露的地方了。

       之前写过使用GDIView排查GDI对象泄露的分析实例,可以去查看文章:

使用GDIView工具排查GDI对象泄漏导致程序UI界面绘制异常的问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/128625868

6、如何排查内存泄露问题?

       所谓的内存泄露就是动态申请的内存,在使用完后没有去释放,导致泄露。内存泄露在C++程序中比较常见。

       对于大型项目,代码量比较大,业务复杂,内存泄露排查起来会比较费劲!有多个工具和方法去排查,比如可以使用Windbg中的umdh. exe工具、使用Windbg中的堆内存信息查看命令、使用Visual Leak Detector工具、甚至使用Visual Studio 2019开始集成的google出品的专用内存分析工具AddressSanitizer。此外像BounderCheck这种老的工具,若干年前就已经停止维护了,已经不支持新版本的Visual Studio了,也就没法使用了。

       有时有的内存泄露问题,很难排查,用一个工具和方法分析不出来,可以多尝试几个工具和方法,直到分析出来为止!

       使用Visual Leak Detector(简称VLD)排查内存泄露的案例文章:

​ 使用Visual Leak Detector排查内存泄漏 icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135472681 ​       使用其他工具分析内存泄露的案例,可以查看内存泄露专栏中的文章:

​ C++内存泄漏排查专题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12370029.html ​

7、如何排查死循环(高CPU占用)问题?

       代码中发生了死循环,一直在执行循环体中的代码,会有一个典型的现象,进程的CPU占用会比较高。

       为什么死循环会导致CPU占用高的问题呢?其实很好理解,因为代码一直在不停歇地执行循环体中代码,一直在占用CPU时间片,所以导致CPU占用高。其实是表现为进程中的某个线程占用的CPU高,因为死循环的代码是执行在某个线程中的,所以这个线程占用的CPU高。

        这种死循环问题排查起来很简单,先用Process Explorer查看哪个线程占用CPU高。然后将Windbg附加到进程上,然后通过占用CPU高的那个线程的id,在windbg中切换到该线程中,输入kn命令查看该线程当前的函数调用堆栈,然后对照着代码就能确定发生死循环的代码位置了。在Process Explorer中也可以查看函数调用堆栈,但看的不准确,并且有时要看相关变量的值去确定为什么发生死循环,这些只能在Windbg中查看,在Process Explorer中是查看不到的。

       我之前写过使用Process Explorer和Windbg排查死循环的案例,可以查看文章:

使用Process Explorer/Process Hacker和Windbg高效排查软件高CPU占用问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135822428

8、如何排查数据格式化时的崩溃问题?

        一般我们在打印日志时需要将相关变量的值格式化到字符串中,然后将字符串打印出来。在格式化时,时常会用到遇到异常崩溃问题。

        一种最简单的场景是,待格式化参数与对应的格式化符不匹配导致了异常。比如一个int类型的参数,却使用了%s的格式化符,比如:

int i = 1;
char szBuf[256] = { 0 };
sprintf( szBuf, "%s", i );

格式化函数内部会去解析出设置的格式化符,然后到栈上传过来的对应位置的待格式化参数。格式化函数解析到%s,到栈上拿到对应的参数,然后把参数值与%s对应,即把参数值当成字符串首地址来处理,在此示例代码中,把参数i的值1,当成字符串首地址,即到 0x00000001地址的内存中把字符串读出来去格式化,而这个0x00000001地址值很小,属于64KB的小地址内存区(空指针地址内存区),这个区域的内存是禁止访问的,所以产生了内存违例,引发崩溃。

        这种场景下问题排查很简单,只要看看格式化符与待格式化参数是否匹配即可发现问题。一般这类问题,可能是写代码时手误,讲格式化符号用错了。

       还有一类场景隐藏的比较深,本质上也是格式化符与待格式化参数类型不匹配的问题。比如一个int64的参数,却使用了32位整型对应的%d,导致格式化函数内部再从栈上取对应位置的数据(传入被调用函数的参数是压到栈上传递过来的)时取的数据长度有问题,导致格式化符取数据错位问题,可能会引发崩溃。这类问题一般很难一眼看出来,之前我就帮同事排查过一个类似的问题,详细分析排查过程可以见文章:

UINT64整型数据在格式化时使用了不匹配的格式化符%d导致其他参数无法打印的问题排查icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/132549186

9、如何排查堆内存被破坏问题?

       堆内存被越界破坏,是最难查的一类内存问题。当堆内存被破坏时,程序会到处“乱崩溃”,一会崩溃在这里,一会崩溃在那里,有时崩溃在new一个对象或一段内存地方,有时崩溃在delete对象或内存地方。

       为啥会出现胡乱崩溃的问题呢?因为越界将堆内存头部或尾部区域给篡改了,而系统管理堆内存就是通过头部和尾部的信息来管理的,一旦被篡改,导致堆内存管理出问题,导致后续申请或释放堆内存产生异常。

       堆内存被破坏,排查起来比较困难,Windows平台上没有专用的内存检测工具,只能通过注释代码逐步缩小排查范围去查。而Linux平台上则有专用的内存检测工具Valgrind和AddressSanitizer。好在Visual Studio 2019 19.6以后的版本引入了google的AddressSanitizer,大家后面遇到问题可以试用一下。因为我们项目组从上到下统一使用的是Visual Studio 2017,所以不能使用。

       以前项目中遇到一个内存越界篡改堆内存头信息导致delete去释放堆内存时产生了异常,相关文章如下:

C++堆内存错误:C运行时库检测到向堆内存头部写入了内容icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/121800292

10、如何排查线程堵塞或卡死问题?

       一般线程堵塞或卡死可能是两个原因导致的,一个是多线程之间发生了死锁,一个是线程中发生了死循环。一旦线程发生堵塞,线程相关的业务没法顺利执行,导致软件业务出现异常。

       对于死循环问题的排查,上面已经讲过了。对于多线程死锁,如果是临界区死锁,也可以在Windbg中查看发生死锁的临界区锁信息,可以快速地定位。如果是非临界区锁,排查起来比较麻烦,可以将所有线程的函数调用堆栈都打印出来,然后结合代码,判断出哪个线程发生了死锁。一般添加打印日志,也能辅助定位死锁发生的位置。

       对于使用Windbg分析临界区死锁问题,可以查看我以前写的文章:

使用Windbg分析多线程临界区死锁问题分享icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/128532743

11、如何排查程序中的资源泄露问题?

        程序中的GDI对象泄露、内存泄露和句柄泄露,都属于资源泄露。GDI对象泄露和内存泄露比较常见,我上面已经详细的讲解了。对于句柄泄露,我们在项目中遇到过,代码中频繁创建线程,任务很快执行完线程退出,但没有关闭线程句柄,导致后续创建线程失败,业务出现异常。

        对于资源泄露,之前写了专题文章,可以查看:

深入探究 C++ 程序中的资源泄漏问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/133631728

12、如何排查第三方库注入引发的异常问题?

       有些第三方程序(比如输入法和安全软件)会有模块注入到我们的程序进程中,可能会导致我们的程序出现异常。我们在项目中遇到过几次。

       对于输入法注入,输入法之所以能支持在所有程序中输入文字,是因为他有专门的注入模块,会注入到所有的进程中。到这种注入可能会引发异常,比如会导致软件时不时的卡顿,导致软件发生死锁,我们在项目中遇到过。对于死锁,我们在Windbg显示的发生死锁的那个线程的函数调用堆栈中看到了输入法的模块,正因为输入法接口调用触发的!

        对于第三方安全软件注入,第三方安全软件就是通过注入到进程中对进程进行监控的。如果注入模块发生内存泄露或者崩溃,会直接影响到我们的软件进程,因为它位于我们的进程空间中!这个我们在项目中都遇到过,相关文章可以查看:

第三方模块远程注入到软件中引发软件异常的若干实战案例分享icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/134545223

13、如何排查库与库之间不匹配问题?

        库与库之间的不匹配,一般是两个库的版本不一致。可以从头文件的角度去看(底层发布库到我们的软件中),比如只更新了API头文件但库没更新库文件,再比如只更新了库文件却没有更新API头文件。API头文件中可能有相关结构体的定义,比如新版本中修改了结构体成员(删除或新增了成员),库的版本不匹配也可能会出问题。

        比如A库依赖B库,A库中调用了B库的接口,A库编译时用到B库的API头文件b. h。编译A库时,因为调用 B库的接口,所以要依赖B库的API头文件b. h。而B库本身编译时,一会依赖其API头文件b. h。如果A库使用的B库与B库的头文件b. h版本不一致,也可能会出问题,编译可能没问题,但运行时可能会出问题。

       当软件出现崩溃时,分析函数调用堆栈时也看不出问题,这时可能是库与库不匹配导致的。根据函数调用堆栈中模块,查看这些模块及头文件在svn上的发布记录(假设库是底层模块开发组打不过来的),并与模块的维护人员确认是否是库的版本问题。

14、如何排查程序启动异常(启动报错、崩溃、卡死)问题?

        程序启动时,系统会先把exe程序依赖的所有dll库加载到程序的进程空间中,最后才会进入exe主程序的main函数,程序才能运行起来。

       如果依赖的库,在电脑系统中找不到,则程序启动时会报缺少库的提示,一般是因为程序打包(生成安装包)忘记把缺少的库打包进安装包导致的。把缺少的库打包进去即可。

       如果程序调用的接口在对应的库找不到,程序启动时会报在某个库找不到接口(可能接口名称边了,也可能是接口参数变了),此时可以用Dependency Walker打开exe主程序文件,看看具体是哪个模块调用了这个接口,确认一下是主调模块有问题,还是被调模块有问题。

       如果是启动过程中发生崩溃,可能是加载某个库时库的初始化代码有问题引发的,程序发生崩溃闪退。这时没有机会将Windbg附加到进程上去分析,因为程序已经崩溃闪退了。此时可以使用Windbg将程序启动起来,一旦程序发生崩溃,Windbg就会感知到,就会中断下来,就可以去分析了。

      《C++软件调试与异常排查从入门到精通》专栏中有专门的章节讲述程序启动异常排查的专题,感兴趣的朋友可以去查看:

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

15、使用Windbg分析软件异常有哪些方式?

       Windbg排查软件异常主要有两种方式,即静态分析dump和动态调试目标进程。当我们有dump文件时,则使用静态分析dump文件的方式。如果没有生成dump文件(可能没捕获到异常),或者从现有的dump文件中分析不出问题时,可以将Windbg附加到目标进程上进行动态调试。

       使用Windbg静态分析dump文件的一般步骤,见文章:

使用Windbg静态分析dump文件的一般步骤详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135484682       使用Windbg动态调试目标进程的一般步骤,见文章:

使用Windbg动态调试目标进程的一般步骤详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135484906

16、为什么有的异常捕获不到?

       应该是程序中只是简单使用API函数SetUnhandleExceptFilter设置异常回调函数去捕获异常,但这种做法是与线程关联的,需要每个线程都设置回调。只在上层模块中调用设置,只作用所在的线程中的,一般无法捕获其他线程中的异常。而进程中包含了多个模块多个线程,所以其他线程中的异常捕获不到。所以会遇到很多时候捕获不到异常的问题。

       只是简单地调用SetUnhandleExceptFilter设置异常回调去捕获异常,是远远不够的!一般我们使用开源的异常捕获库去捕获异常,比如CrashRpt、BreadPad和CrashPad。

17、常用的开源异常捕获库有哪些?

       常见的开源捕获库CrashRpt、BreadPad和CrashPad。其中CrashRpt是Google早期开源的,但通过项目实战发现该库有些时捕获不到发生的异常,才看代码研究其实现机制发现,她是通过将加载模块导入表中创建线程的CreateThread函数hook成自定义的函数,在该函数中给当前线程设置异常回调函数去实现该线程的异常捕获。但只对在异常捕获模块之前加载的模块进行hook,后加载到进程空间中的模块没有hook,所以后加载模块如果发生异常,是捕获不到的。

       后来我们使用了微软开源的detours项目对CrashRpt进行改进,有效地提高了捕获异常的几率。BreadPad是Google开源的Chrome浏览器中的异常捕获模块,最新版本浏览器中已经改为CrashPad。CrashPad默认是支持Windows的,如果用在linux中,则需要使用Linux版本。

18、异常捕获库捕获不到异常时,该怎么办?

       大家在实际项目中会发现,即使使用了开源的异常捕获库(或者经过升级改造的捕获库),有少部分异常还是捕获不到的

       对于捕获不到的异常,则只能将Windbg附加到目标进程上,和目标进程一起跑,一单进程发生异常,会投递给正在附加调试的Windbg,Windbg就会感知到异常并中断下来,就可以直接在Windbg中分析了。如果一时半会看不出来,可以使用. dump命令将异常上下文导出到dump文件中,事后再去静态分析。

       在正在调试的Windbg中使用命令导出的dump文件,属于全dump文件,文件大小接近进程的虚拟内存的大小。Windbg动态调试相对于静态分析,可以多次go多次动态查看函数调用堆栈,还可以添加断点进行断点调试。

19、生成dump文件的方式有哪些?

       生成dump文件的方式主要有三种。

       最常用的方式是通过异常捕获库去自动生成,即异常捕获库捕获到了软件中发生的异常,自动调用系统API函数MiniDumpWriteDump将异常上下文信息保存到dump文件中。

       也可以从Windows任务管理器中导出进程的dump文件。它用在什么场合下呢?比如程序发生异常弹出保存提示框,此时进程还在的,可以导出dump文件。再比如程序发生死循环或死锁时,进程还在的,也可以从任务管理器中导出dump文件。

       还可以从正在调试的Windbg中使用. dump命令导出dump文件。比如我们在使用Windbg动态调试目标进程时,如果一时半会分析不出问题或者出问题的电脑别人还要用(我们不能长时间占用别人的电脑去分析问题),可以导出dump文件,然后事后去分析dump文件即可。

       关于生成dump文件方式的详细说明,可以参见文章:

dump文件类型与dump文件生成方法详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/127991002

20、为什么要加载pdb文件?为什么要加载系统库的pdb文件?

       pdb文件中包含函数及变量的符号信息,加载pdb后在函数调用堆栈中才能看到具体的函数名和代码行号。有时我们需要在Windbg中查看函数中相关变量的值,变量的值可能是关键线索,有pdb中的变量符号才能识别变量,才能看到变量的值。

       pdb文件分软件业务模块的pdb文件和Windows系统库pdb文件。业务模块的pdb需要加载,有时为了搞清楚问题,也要加载系统库pdb的,加载后可以看到系统库中具体调用了哪些接口,可能能找到更为直接的线索。加载pdb后,可能能看到更多行的函数调用,更便于分析问题。

       对于Windows系统库的pdb文件,不需要下载,微软提供了一个在线pdb符号服务器,只需要将这个在线地址设置给Windbg,Windbg在需要时会自动去下载。

       此外,不仅仅Windbg会用到pdb文件,有些分析工具也会用到。比如Process Explorer/Process Hacker、Process Monitor,使用这些工具分析问题时有时需要看函数调用堆栈,需要pdb中的函数符号。再比如使用反汇编工具IDA查看二进制文件的汇编代码,也需要使用到二进制文件的pdb文件中的函数与变量符号。

       关于pdb符号文件的详细说明,可以参见文章:

pdb符号文件详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125508858

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openstack安装dashboard后登录网页显示404错误

1. 2.进入该目录vim /etc/httpd/conf.d/openstack-dashboard.conf 增加这一行 WSGIApplicationGroup %{GLOBAL} 重启httpd后就可以访问了

如何在Windows使用固定公网地址SSH远程访问本地Archcraft系统

文章目录 1. 本地SSH连接测试2. Archcraft安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接小结 5. 固定SSH公网地址6. SSH固定地址连接 Archcraft是一个基于Arch Linux的Linux发行版,它使用最简主义的窗口管理器而不是功能齐全的桌面环境来提供图形化用户界面。 C…

使用SpeechRecognition和vosk处理ASR

SpeechRecognition可以支持多种模型语音转文字,感觉vosk还不错,使用起来也简单一些;百度也有PaddleSpeech,但是安装起来太麻烦,不是这个库版本不对就是那个库有问题,用起来不方便; 安装SpeechR…

【Git教程】(十)版本库之间的依赖 —— 项目与子模块之间的依赖、与子树之间的依赖 ~

Git教程 版本库之间的依赖 1️⃣ 与子模块之间的依赖2️⃣ 与子树之间的依赖🌾 总结 在 Git 中,版本库是发行单位,代表的是一个版本,而分支或标签则只能被创建在版本库这个整体中。如果一个项目中包含了若干个子项目,…

12.文件浏览器

子程序参数的使用 1.可空的用法;表示这个参数不写也行。 2.如何使用递归 3.需要注意的事 递归的子程序必须有个退出的条件 注意区分递归和循环,不要混用 流程: 1.插入按钮,输入输出调试文本(“按钮被单击”&…

2024年主流的java混淆工具有哪些

2024年,主流的Java混淆工具可能会包括: ProGuard:ProGuard 是一个免费的开源 Java 混淆工具,可用于压缩、优化和混淆 Java 字节码。它是Android开发者的首选混淆工具之一,并且在Java应用程序中也得到了广泛应用。 Dex…

Windows本地部署Ollama+qwen本地大语言模型Web交互界面并实现公网访问

文章目录 前言1. 运行Ollama2. 安装Open WebUI2.1 在Windows系统安装Docker2.2 使用Docker部署Open WebUI 3. 安装内网穿透工具4. 创建固定公网地址 前言 本文主要介绍如何在Windows系统快速部署Ollama开源大语言模型运行工具,并安装Open WebUI结合cpolar内网穿透软…

OpenCV4.9图像金字塔

目标 在本教程中,您将学习如何: 使用 OpenCV 函数 pyrUp()和 pyrDown()对给定图像进行下采样或上采样。 理论 注意 下面的解释属于 Bradski 和 Kaehler 的 Learning OpenCV 一书。 通常,我们需要将图像转换为与原始图像不同的大小。为此…

Linux 1.文件编程(dup、dup2)

重定向 重定向是什么?dupdup2 重定向是什么? 进程在最开始运行的时候,首先打开了三个文件,分别是标准输入流、标准输出流、标准错误输出流。证明的时候我是把标准输出留给关闭了,然后紧接着创建的文件就会占用已关闭的…

JavaScript-2.对话框、函数、数组、Date、DOM

对话框 window对象封装了三个对话框用于与用户交互 提示框:alert(title);确认框:confirm(title);输入框:prompt(title); 确认框 包含两个按钮“确认”/“取消”,点击确定时,返回值为true // 确认框 var bool con…

Linux系统编程---文件系统

一、文件存储 一个文件主要由两部分组成,dentry(目录项)和inode inode本质是结构体,存储文件的属性信息,如:权限、类型、大小、时间、用户、盘块位置… 也叫做文件属性管理结构,大多数的inode都存储在磁盘上。 少量…

XWX-SX三箱社交箱

简单介绍: 动物行为学是一门研究动物行为的科学,它包括观察动物在自然环境中的行为,以及在控制环境中的实验行为。三箱社交实验是其中一种常见的实验方法,用于评估动物的社交行为和决策制定能力。这种实验在许多领域都有应用&…

bugku-web-需要管理员

页面源码 <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <title>404 Not Found</title> </head> <body> <div idmain><i> <h2>Something error:</h2…

QT、ffmpeg视频监控分屏

1、支持分屏&#xff08;4&#xff0c;6&#xff0c;8&#xff0c;9&#xff0c;13&#xff0c;16&#xff0c;25&#xff0c;32&#xff0c;64&#xff09;切换 2、支持拖拽效果 3、支持播放mp4&#xff0c;rtmp等 4、本人亲测支持播放32路&#xff0c;64路没做测试 5、支持读…

【C++成长记】C++入门 | 类和对象(中) |类的6个默认成员函数、构造函数、析构函数

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、类的6个默认成员函数 二、构造函数 1、概念 2、特性 三、析构函数 1、概念 2、特性 一、…

R语言计算:t分布及t检验

t分布理论基础 t分布也称Student’s t-distribution&#xff0c;主要出现在小样本统计推断中&#xff0c;特别是当样本量较小且总体标准差未知时&#xff0c;用于估计正态分布的均值。其定义基于正态分布和 X 2 X^{2} X2分布&#xff08;卡方分布&#xff09;。如果随机变量X服…

springCloudAlibaba集成seata实战(分布式事物详解)

一、分布式事务 1. 事务介绍 1.1 基础概念 事务&#xff1a;保证我们多个数据库操作的原子性&#xff0c;多个操作要么都成功要么都不成功 事务ACID原则 A&#xff08;Atomic&#xff09;原子性&#xff1a;构成事务的所有操作&#xff0c;要么都执行完成&#xff0c;要么全部…

三次握手与四次挥手到底是怎么回事?

三次握手和四次挥手是TCP/IP协议中建立和断开连接的关键步骤&#xff0c;它们是保证可靠通信的重要机制。这里将探讨这两个概念&#xff0c;并解释它们背后的原理。 三次握手 三次握手用于建立TCP连接&#xff0c;它由客户端和服务器之间发送的三个报文组成&#xff1a; 第一次…

市场份额第一:SmartX 领跑 23全年中国超融合软件市场

日前&#xff0c;IDC 发布《中国软件定义存储&#xff08;SDS&#xff09;及超融合存储系统&#xff08;HCI&#xff09;市场季度跟踪报告&#xff0c;2023 年第四季度》&#xff0c;详解中国区超融合发展趋势、市场份额规模以及厂商占比。 IDC 数据显示&#xff0c;2023 年全…

rust使用print控制台打印输出五颜六色的彩色红色字体

想要在控制台打印输出彩色的字体&#xff0c;可以使用一些已经封装好的依赖库&#xff0c;比如ansi_term这个依赖库&#xff0c;官方依赖库地址&#xff1a;https://crates.io/crates/ansi_term 安装依赖&#xff1a; cargo add ansi_term 或者在Cargo.toml文件中加入&#…