【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++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_2276111.html 最近在C++软件调试交流群中,很多人问了开发维护过程中遇到的多个调试问题,这两天正好有时间,对这些常见的问题进行一个梳理和解答。本文以问答的方式进行展开,罗列了C++软件日常开发和维护中遇到的多个软件调试问题及有代表性的场景,给出详细的处置思路和处理办法,以供大家借鉴和参考!希望大家在了解这些内容以后,能从容地应对开发维护过程中遇到的各种问题。
考察一个开发人员的水平,一是要看其编码与设计能力,而是要看其软件调试能力(排查软件异常)!
为什么要学习C++软件调试技术?学习调试技术有哪些好处?,可以参看我之前的写的文章:
为什么要学习C++软件调试技术?掌握调试技术都有哪些好处?https://blog.csdn.net/chenlycly/article/details/130856385学习C++软件调试技术的学习路线路线,可以参见我的文章:
C++软件调试与异常排查技术从入门到精通学习路线分享https://blog.csdn.net/chenlycly/article/details/135048954
1、引发C++软件异常的常见原因有哪些?
引发C++软件异常的常见原因有:访问空指针与野指针、内存越界(栈内存越界、堆内存越界和全局内存越界)、内存访问违例、线程栈溢出、堆内存被破坏、内存泄露、死循环、多线程死锁、待格式化参数与格式化符号不匹配、库与库之间的不匹配等。
我之前根据多年的C++软件异常排查实践,对这些引发C++软件异常的常见原因进行了详细总结,可查看文章:
引发C++软件异常的常见原因分析与总结(实战经验分享)https://blog.csdn.net/chenlycly/article/details/124996473了解这些常见原因之后,我们在排查问题时可能会快速找到可疑点和排查方向,能有限地提高我们排查问题的效率!
2、排查C++软件异常的常用方法有哪些?
了解引发C++软件异常的常见原因之后,我们还要掌握排查C++软件异常的常用手段与方法。
排查问题的常用方法有:使用常用工具去辅助分析(比如程序启动失败、启动报错、库加载失败、死循环与高CPU占用问题等)、使用IDE开发工具调试(Debug调试、Release调试和附加到进程调试)、添加打印日志、分块注释代码、数据断点监测内存、历史版本比对法、Windbg分析(静态分析dump文件、动态调试目标进程)、使用反汇编工具IDA查看汇编代码辅助分析等。
我之前根据多年的C++软件异常排查实践,对这些排查手段和方法进行了详细的总结,可查看文章:
排查C++软件异常的常见思路与方法(实战经验总结)https://blog.csdn.net/chenlycly/article/details/120629327掌握这些排查软件异常的常用手段与方法之后,在遇到新的问题时我们的排查思路会更开阔、更高效,必要时需要将多种排查方法结合起来使用。
在这里,给大家重点推荐一下我的几个热门畅销专栏:
专栏1:(该专栏订阅量已达到420多个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:
C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!
专栏3:
开源组件及数据库技术https://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 等)https://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引发内存访问违例问题https://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界面绘制异常的问题https://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排查内存泄漏 https://blog.csdn.net/chenlycly/article/details/135472681%20%20%E2%80%8B 使用其他工具分析内存泄露的案例,可以查看内存泄露专栏中的文章:
C++内存泄漏排查专题https://blog.csdn.net/chenlycly/category_12370029.html%20%20%E2%80%8B
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占用问题https://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导致其他参数无法打印的问题排查https://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运行时库检测到向堆内存头部写入了内容https://blog.csdn.net/chenlycly/article/details/121800292
10、如何排查线程堵塞或卡死问题?
一般线程堵塞或卡死可能是两个原因导致的,一个是多线程之间发生了死锁,一个是线程中发生了死循环。一旦线程发生堵塞,线程相关的业务没法顺利执行,导致软件业务出现异常。
对于死循环问题的排查,上面已经讲过了。对于多线程死锁,如果是临界区死锁,也可以在Windbg中查看发生死锁的临界区锁信息,可以快速地定位。如果是非临界区锁,排查起来比较麻烦,可以将所有线程的函数调用堆栈都打印出来,然后结合代码,判断出哪个线程发生了死锁。一般添加打印日志,也能辅助定位死锁发生的位置。
对于使用Windbg分析临界区死锁问题,可以查看我以前写的文章:
使用Windbg分析多线程临界区死锁问题分享https://blog.csdn.net/chenlycly/article/details/128532743
11、如何排查程序中的资源泄露问题?
程序中的GDI对象泄露、内存泄露和句柄泄露,都属于资源泄露。GDI对象泄露和内存泄露比较常见,我上面已经详细的讲解了。对于句柄泄露,我们在项目中遇到过,代码中频繁创建线程,任务很快执行完线程退出,但没有关闭线程句柄,导致后续创建线程失败,业务出现异常。
对于资源泄露,之前写了专题文章,可以查看:
深入探究 C++ 程序中的资源泄漏问题https://blog.csdn.net/chenlycly/article/details/133631728
12、如何排查第三方库注入引发的异常问题?
有些第三方程序(比如输入法和安全软件)会有模块注入到我们的程序进程中,可能会导致我们的程序出现异常。我们在项目中遇到过几次。
对于输入法注入,输入法之所以能支持在所有程序中输入文字,是因为他有专门的注入模块,会注入到所有的进程中。到这种注入可能会引发异常,比如会导致软件时不时的卡顿,导致软件发生死锁,我们在项目中遇到过。对于死锁,我们在Windbg显示的发生死锁的那个线程的函数调用堆栈中看到了输入法的模块,正因为输入法接口调用触发的!
对于第三方安全软件注入,第三方安全软件就是通过注入到进程中对进程进行监控的。如果注入模块发生内存泄露或者崩溃,会直接影响到我们的软件进程,因为它位于我们的进程空间中!这个我们在项目中都遇到过,相关文章可以查看:
第三方模块远程注入到软件中引发软件异常的若干实战案例分享https://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++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
15、使用Windbg分析软件异常有哪些方式?
Windbg排查软件异常主要有两种方式,即静态分析dump和动态调试目标进程。当我们有dump文件时,则使用静态分析dump文件的方式。如果没有生成dump文件(可能没捕获到异常),或者从现有的dump文件中分析不出问题时,可以将Windbg附加到目标进程上进行动态调试。
使用Windbg静态分析dump文件的一般步骤,见文章:
使用Windbg静态分析dump文件的一般步骤详解https://blog.csdn.net/chenlycly/article/details/135484682 使用Windbg动态调试目标进程的一般步骤,见文章:
使用Windbg动态调试目标进程的一般步骤详解https://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文件生成方法详解https://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符号文件详解https://blog.csdn.net/chenlycly/article/details/125508858
相关文章:
【C++软件调试技术】C++软件开发维护过程中典型调试问题的解答与总结
目录 1、引发C软件异常的常见原因有哪些? 2、排查C软件异常的常用方法有哪些? 3、为什么要熟悉常见的异常内存地址? 4、调试时遇到调用IsBadReadPtr或者IsBadWritePtr引发的异常,该如何处理? 5、如何排查GDI对象泄…...
Pygame经典游戏:贪吃蛇
------------★Pygame系列教程★------------ Pygame经典游戏:贪吃蛇 Pygame教程01:初识pygame游戏模块 Pygame教程02:图片的加载缩放旋转显示操作 Pygame教程03:文本显示字体加载transform方法 Pygame教程04:dra…...
推荐一个免费使用Claude 3, GPT4和Gemini 1.5 Pro的网站
在探索人工智能的广阔天地时,我偶然间发现了You AI这一平台,它不仅更新了大量的模型,还慷慨地提供了免费的使用机会。兴奋之余,我迅速开始尝试这些新功能,并决定将我的体验分享给大家。以下是我试用的流程: 打开网站:点击左下角的Sign in蓝色框 https://you.comhttps://…...
An Investigation of Geographic Mapping Techniques for Internet Hosts(2001年)第二部分
下载地址:An investigation of geographic mapping techniques for internet hosts | Proceedings of the 2001 conference on Applications, technologies, architectures, and protocols for computer communications 被引次数:766 Padmanabhan V N, Subramanian L. An i…...
解锁生成式 AI 的力量:a16z 提供的 16 个企业指南
企业构建和采购生成式AI方面的16项改变 生成式 AI 领域趋势洞察:企业构建和采购生成式 AI 的方式正在发生重大转变,具体表现在:* 专注于可信度和安全性:75% 的企业将信任和安全性视为关键因素。* 优先考虑可扩展性和灵活性&#x…...
Kylin使用心得
Kylin是一个开源的分布式分析引擎,基于Apache Hadoop构建,专为处理大规模数据集而设计。以下是一些使用Kylin的心得体会: 快速查询 Kylin的OLAP引擎能够对大规模数据集进行高效的多维分析查询。通过预计算和存储多维度的聚合数据࿰…...
CentOS7使用Docker搭建Joplin Server并实现多端同步与公网使用本地笔记
文章目录 1. 安装Docker2. 自建Joplin服务器3. 搭建Joplin Sever4. 安装cpolar内网穿透5. 创建远程连接的固定公网地址 Joplin 是一个开源的笔记工具,拥有 Windows/macOS/Linux/iOS/Android/Terminal 版本的客户端。多端同步功能是笔记工具最重要的功能,…...
C语言100道练习题打卡(1)
1 有1,2,3,4四个数字,能组成多少个互不相同且不重复的三位数,都是多少 #include<stdio.h> //有1,2,3,4四个数字,能组成多少个互不相同且不重复的三位数ÿ…...
5G-A有何能耐?5G-A三载波聚合技术介绍
2024年被称作5G-A元年。5G-A作为5G下一阶段的演进技术,到底有何能耐呢? 三载波聚合(3CC)被认为是首个大规模商用的5G-A技术,将带来手机网速的大幅提升。 █ 什么是3CC 3CC,全称叫3 Component Carriers…...
理解Go语言中上下文
开发人员有时会误解context.Context类型,尽管它是Go语言的关键概念之一,也是Go中并发代码的基础之一。接下来让我们看看这个概念,并确保我们理解为什么乃如何有效地使用它。 根据官方文档: 上下文(context)携带最后期限、取消信号和其他跨API边界的值。 下面让我们来看下这…...
[MySQL]数据库原理8——喵喵期末不挂科
希望你开心,希望你健康,希望你幸福,希望你点赞! 最后的最后,关注喵,关注喵,关注喵,大大会看到更多有趣的博客哦!!! 喵喵喵,你对我真的…...
【算法基础】插入排序与二分查找、升级二分查找
文章目录 1. 插入排序1.1 插入排序的思想1.2 插入排序的实现 2. 普通二分查找2.1 普通二分查找的思想2.2 普通二分查找的实现 3. 升级二分查找3.1 升级二分查找思想3.2 升级二分查找实现 1. 插入排序 1.1 插入排序的思想 插入排序很类似于已有一副有序的扑克牌,不断…...
在Vue3中如何使用H.265视频流媒体播放器EasyPlayer.js?
H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器,可支持多种流媒体协议播放,可支持H.264与H.265编码格式,性能稳定、播放流畅,能支持WebSocket-FLV、HTTP-FLV,HLS(m3u8&#…...
基于51单片机的PM2.5监测系统设计—环境监测仪
基于51单片机的PM2.5监测系统 (仿真+程序+原理图+PCB+设计报告) 功能介绍 具体功能: 1.PM2.5传感器模块检测信息给单片机处理; 2.LCD1602实时显示PM2.5浓度和PM2.5报警阈值&#x…...
【C语言】指针篇-初识指针(1/5)
🌈个人主页:是店小二呀 🌈C语言笔记专栏:C语言笔记 🌈C笔记专栏: C笔记 🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 文章目录 **内存和地址(知识铺垫(了解即可))**如何理解编址**指针变量*…...
【御控物联】物联网平台设备接入-JSON数据格式转化(场景案例四)
文章目录 一、背景二、解决方案三、在线转换工具四、技术资料 一、背景 物联网平台是一种实现设备接入、设备监控、设备管理、数据存储、消息多源转发和数据分析等能力的一体化平台。南向支持连接海量异构(协议多样)设备,实现设备数据云端存…...
stack和queue模拟实现
前言 上一期我们介绍了stack和queue的使用,本期我们来模拟实现一下他们! 本期内容介绍 容器适配器 deque介绍 为什么stack和queue的底层选择deque为默认容器? stack 模拟现实 queue 模拟实现 什么是容器适配器? 适配器是一种设…...
docker操作
1、容器生命周期管理命令 docker run docker run --name tomcat8 -d -p 28080:8080 tomcat:8.5.38 docker run -i --name hausf --network bridge --ip 172.17.0.10 ubuntu:20.04 /bin/bash docker run -d --name hausf --net host ubuntu:20.04 /bin/bash docker run…...
分布式锁介绍
引言 分布式锁是一种用于协调不同进程或线程对共享资源的访问控制的机制。在分布式系统中,由于多个节点可能同时访问或修改同一资源,因此需要一个中心化的协调机制来确保资源的访问是有序的,避免数据不一致的问题。 分布式锁的特性…...
Unity 获取RenderTexture像素颜色值
拿来吧你~ 🦪功能介绍🌭Demo 🦪功能介绍 💡不通过Texture2D 而是通过ComputerShader 提取到RenderTexture的像素值,效率有提升哦! 💡通过扩展方法调用,方便快捷:xxxRT.G…...
Tomcat以服务方式启动,无法访问网络共享目录问题
关于“Tomcat以服务方式启动,无法访问网络共享目录问题”解决方式如下: 1、通过doc命令【services.msc】打开本地服务找到,找到tomcat服务所在位置 2、右键打开Tomcat服务的属性 3、选择 登陆选项卡 4、选择“此账户”选项,并…...
SVN的介绍
首先SVN是什么: Apache下的一个开源的项目Subversion,通常缩写为 SVN,是一个版本控制系统。 版本控制系统是一个软件,它可以伴随我们软件开发人员一起工作,让我们编写代码的完整的历史保存下来。 目前它的各个版本的…...
ZYNQ-700呼吸灯
参考野火例程 实现呼吸灯即要调整led亮的占比时间,完成视觉上看起来由灭到亮或者由亮到灭的过程。 如果主频为50MHz,理论上一秒钟我们可以控制50_000_000次led的亮和灭,肉眼不可能分辨出来每一次亮灭,如果这50M我们设定为间隔一…...
UE5学习日记——制作多语言版本游戏,同时初步学习UI制作、多语言化、控制器配置、独立进程测试、打包配置和快速批量翻译等
所有的文本类,无论变量还是控件等都能实现本地化,以此实现不同语言版本。 在这里先将重点注意标注一下: 所有文本类的变量、控件等都可以多语言;本地化控制板中收集、编译时,别忘了编译这一步;支持批量复制…...
电脑重启后word文档空白或打不开,word无法自动修复,如何拯救
最近编辑word文档,写了好几个星期的内容随着电脑重启的一瞬间,灰飞烟灭,让我简直痛不欲生! 好在,天无绝人之路,以下两个方法拯救了地球 第一,普通的文档word自动修复不好使的时候,…...
MVC和MVVM这两种设计模式的区别
一、MVC和MVVM是什么? MVC是Model-View-Controller的简写,Model就是模型,对应后端数据,View就是视图对应用户界面,Controller就是控制器,对应页面的业务逻辑。 MVC的工作机制原理就是,用户操作…...
淘宝app端商品详情数据采集(商品价格,商品库存,商品销量,商品优惠券)
在淘宝App端采集商品详情数据,包括商品价格、库存、销量以及优惠券信息,可以通过多种方式实现。以下是几种常见的方法: 使用淘宝开放平台API: 淘宝开放平台提供了一系列API接口,这些接口允许开发者获取淘宝商品的详细…...
第42篇:随机存取存储器(RAM)模块<一>
Q:本期开始我们分期介绍随机存取存储器(RAM)模块及其设计实现方法。 A:随机存储器RAM,即工作时可以随时从一个指定地址读出数据,也可以随时将数据写入一个指定的存储单元。 DE2-115开发板上的Cyclone IV …...
在Java中实现记录1000万用户连续7天登录的功能,可以使用Redis的Bitmap来跟踪每个用户的登录状态
在Java中实现记录1000万用户连续7天登录的功能,可以使用Redis的Bitmap来跟踪每个用户的登录状态。以下是一个简化的Java示例,使用了Jedis库作为Redis的Java客户端。 首先,确保你已经在项目中添加了Jedis的依赖。如果你使用Maven,…...
深入探讨VIVE OpenXR:为Unity开发者的全面指南
随着虚拟现实(VR)和增强现实(AR)技术的迅速发展,开发者们对于能够简化和优化沉浸式应用开发的工具需求日益增长。HTC Vive 作为行业内的领先品牌,其最新推出的 VIVE OpenXR 插件为Unity开发者提供了一个强大…...
导航网址网站怎么做/沈阳关键词seo排名
一、Eureka特性1.当注册中心挂了,客户端之间依然可以通过原有的注册表进行调用;注册中心重启后,客户端会继续注册进来 2.当服务提供者挂了,在关闭自我保护的情况下,注册中心在规定时间(默认是60s࿰…...
永川建网站/东莞网站seo公司
这里用到的ide是netbeans ruby环境安装好后,下载netbeans,然后进行安装 netbeans ruby plugins download URL:http://jruby.org.s3.amazonaws.com/downloads/community-ruby/community-ruby_7_1_preview1.zip 解压 打开netbeans,工…...
网站建设销售怎么样/淘宝推广费用一般多少
作为一个程序员来说,简历可谓是面试的时候一个加分砝码,往往有的面试者只在意自己的技术,其实这是非常不对的。简历对于一个面试者来说非常重要,他是第一个入口,也是面试官认识你的第一个途径。不管是对于刚毕业或者即…...
金融企业网站建设公司/网页模板网站
日前,欧洲云计算托管服务提供商Interxion公司正计划在欧洲的马赛、巴黎、维也纳、杜塞尔多夫扩建其数据中心。 总体来说,该公司预计将新增3900平方米的数据中心空间和5MW的电力容量,这些项目将在2017年中期投入运营。 “这四个数据中心的扩建…...
网站建设人员岗位要求/网络推广的基本方法
还是17年本科时制作的毕业设计,基于solidworks 2014所制作。 文件里包含了制作的整个移动机器人的SolidWorks原文件。 是设计完各个零件然后组合在一起的,所以适用性很强,可以根据你的需求将绘制的零件加到你的机器人solidwroks三维中&…...
万户网络网站管理系统/宁波网站推广公司价格
WordPress模板制作中,function.php是一个特殊的文件,可以用来自定义模板中需要用到的函数,添加钩子函数等。为了为不同的模板添加不同的钩子函数,需要使用WordPress的条件标签(Conditional Tag)。WordPress常用条件标签如下&#…...