软件崩溃时VS中看不到有效的调用堆栈,使用Windbg动态调试去分析定位

目录

1、问题说明

2、使用Windbg查看崩溃时详细的函数调用堆栈

3、将Windbg中显示的函数调用堆栈对照着C++源码进一步分析

4、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       最近在使用VS调试代码的过程中发生了异常崩溃,在VS中看不到详细的函数调用堆栈,使用Windbg去动态调试目标进程则可以看到详细的函数调用堆栈,进而可以去详细分析崩溃问题。这个问题有一定的代表性,本文详细讲述一下这个崩溃问题的分析过程。

1、问题说明

       在调试软件的新功能时,刚加入一个正在发送桌面共享的会议,软件就发生了崩溃: 

且问题是必现的。此时,切换到函数调用堆栈窗口中查看函数调用堆栈,如下所示:

从堆栈中只能看到崩溃在mediasdk.dll模块中,但看不到详细的函数调用堆栈。这种情形下,使用VS排查很不方便,还是使用Windbg排查崩溃问题要顺手很多。


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

专栏1:(该专栏订阅量接近350个,有很强的实战参考价值,广受好评!)

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

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


2、使用Windbg查看崩溃时详细的函数调用堆栈

       从崩溃时报错的提示框来看:

程序中访问了一个很小的内存地址0x00000003,产生了内存访问违例,第一感觉是不是访问了空指针导致的,但这也只是最开始时的感觉,还要结合后续分析才能找出最终问题的。

       当前这个问题是在Debug下调试出现的,且是必现的,所以先直接启动Debug版本的程序,然后将Windbg附加到程序进程上,然后加入一个正在发送桌面共享的会议中,然后复现崩溃,Windbg中断下来,如下所示:

然后输入kn命令查看此时的函数调用堆栈:

因为没有加载相关模块的pdb文件,所以堆栈中看不到具体的函数名及代码的行号。然后根据堆栈中涉及到的模块,使用lm命令查看模块的时间戳,然后通过时间戳到文件服务器上找到模块对应的pdb文件。这个地方有两个细节需要注意一下:

1)不需要找出堆栈中所有模块的pdb文件,离崩溃点最近的1-2个模块即可,当需要更上层模块的详细函数信息时,再去获取对应模块的pdb文件。
2)加载pdb文件时,会严格校验二进制文件与pdb文件的时间戳,要完全一致才能加载成功,否则会加载失败。比如昨天编译的库和今天编译的库,即使代码没修改,二进制文件和pdb文件都不能交叉配对使用,因为严格校验时间戳。

       拿来pdb文件后,将pdb文件的路径设置到Windbg中,然后重新输入kn命令查看加载pdb符号后的函数调用堆栈:

从函数调用堆栈中可以看到,业务库中调用C函数vsnprintf_s去格式化字符串,最终引发了异常。但调用堆栈最上面的几行是运行时库ucrtbased.dll中,显示的偏移地址较大,看不到系统库中具体的函数名,但有时可能需要查看系统库中具体接口,对分析问题可能很关键!那如何找到系统库的pdb符号文件呢?其实很简单!对于Windows系统库,只需要设置微软在线pdb下载地址就可以了,如下所示:

srv*f:\mss0616*http://msdl.microsoft.com/download/symbols

其中,http://msdl.microsoft.com/download/symbols是微软在线pdb下载地址,f:\mss0616是下载pdb文件时在本机上的缓存路径。按照这个格式来写就可以了。当然程序业务库的pdb路径也要存在,增加新的pdb路径时,直接在现有的路径下加上英文分号,然后将新的路径追加到末尾即可,比如:

C:\Users\admin \Desktop\pdbdir;srv*f:\mss0616*http://msdl.microsoft.com/download/symbols

设置微软在线pdb下载地址后,就可以看到系统库的接口了,如下所示:

其实在本例中,不需要查看系统库中的具体接口就能定位问题了,此处我之所以加到加载系统库pdb文件,是为了说明有时可能会比较关键!

       关于如何查看二进制文件的时间戳以及如何将pdb文件所在的路径设置到Windbg中,已经多次讲过,此处就不详细展开了,如果需要查看,可以参看我之前写的文章:
使用Windbg静态分析dump文件的一般步骤及要点详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130873143

3、将Windbg中显示的函数调用堆栈对照着C++源码进一步分析

       当我看到函数堆栈中因为调用vsnprintf_s接口去格式化字符串产生了崩溃,我第一反应可能是待格式化的参数与使用的格式化符不一致导致的,这是格式化字符串产生崩溃的一种最常见的原因。比如整型变量错误地是用字符串格式化符%s。还有一种隐蔽性较强的场景,带格式化的参数类型长度与格式化符的长度不一致,比如64位整型参数(占用8字节)使用对应四字节的%d去格式化,这种场景有较强的隐蔽性,不熟悉的可能很难看出问题,即使能看出来,可能也搞不清楚引发异常的根本原因。对于这种隐蔽的场景,我前段时间在项目中遇到过,也发表了对应的博客文章:(深度分析了因为格式化符与带格式化参数的类型不匹配引发崩溃的根本原因)
UINT64整型数据在格式化时使用了不匹配的格式化符%d导致其他参数无法打印的问题排查icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/132549186感兴趣的可以去看看。

       于是根据函数调用堆栈中显示的cpp名称和代码的行号,到C++源码中找到调用vsnprintf_s格式化接口的函数,此处为啥会有多行记录呢?因为我们对相关接口做了多层封装。我们只要找添加打印的那个函数即可,如下所示:

因为我最开始就怀疑可能是带格式化的参数类型与格式化符不一致导致的,所以一看到源码,我一眼看出了问题,一个枚举类型的整型值居然使用了字符串格式化符%s,这就是问题所在了。

       经查看,这个枚举值就是3,格式化函数内部在解析格式化串时,当看到%s格式化符时,会把传入的对应参数值3,当成一个字符串的首地址,即会到0x00000003地址的内存中去读取字符串,而这个0x00000003地址,是很小的内存地址,小于64KB地址值的内存区域是禁止访问的,所以产生了内存访问违例,产生了崩溃。这个问题修改起来很简单,将%s换成%d就好了。

      关于格式化函数内部时如何解析格式化符,以及如何根据格式化符找到对应的带格式化参数的可以参见我的这篇文章:

UINT64整型数据在格式化时使用了不匹配的格式化符%d导致其他参数无法打印的问题排查icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/132549186       至于为什么会产生这个问题,应该是开发人员手误导致的,也有可能打印日志的这行代码是从其他地方直接拷贝过来的,没有检查格式化符是否合适,就直接提交代码了。写代码时还是要严谨一点比较好!

4、最后

       大家在日常工作中要去主动排查问题,排查的问题多了,见过的问题场景就多了,经验就更丰富了,在后面遇到问题时思路就会更多,排查的更迅速了!特别是一些“难缠”问题,更锻炼人,能学到的更多!在问题中学习,在问题中成长,在问题中积累经验!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Java学习日记]多线程练习、线程池

目录 一.案例:五个人抢红包 二.案例:两个抽奖池抽奖 三.案例:两个抽奖池抽奖:获取线程运行的结果 四.线程池:用来存放线程,避免多次重复创建线程 五.自定义线程池 六.最大并行数与线程池大小 一.案例&…

【Android】Glide的简单使用(上)

文章目录 引入Glide的优点:缺点: 使用常用方法:从网络加载图片从文件加载图片加载resource资源加载URI地址设置占位图出错时的图片占位图图片过渡的Transitions自定义过渡动画图片大小调整缩放图片播放gifasGif()把Gif当作Bitmap播放显示本地视频缩略图 引入 Glide是Google员工…

IntelliJ IDEA 2023.2新特性详解第三弹!Docker、Kubernetes等支持!

9 Docker 在 Docker 镜像层内预览文件 现在可以在 Services(服务)工具窗口中轻松访问和预览 Docker 镜像层的内容。 从列表选择镜像,选择 Show layers(显示层),然后点击 Analyze image for more informati…

平价开放式耳机怎么选?盘点几款好用的平价开放式耳机!

在这个充满音频奇迹的年代,选一副好耳机就像是挑选人生伴侣一样重要,而开放式耳机,由于佩戴无需入耳带来了不错的舒适度,就此受到了许多人的喜爱。 可问题是,市面上平价开放式耳机太多,让人眼花缭乱&#…

医院运维 告警闪现后的故障排查

长期以来,医院信息化运维中存在着科室复杂、应用场景多、终端运维工作量大、软件系统兼容需求强等诸多痛点,且对技术设备的稳定性、连续性要求极高,在日常运维中,需要应对和解决这些问题来保障业务稳定、健康运行。 1、数据孤岛 …

【离散数学】——期末刷题题库(二元关系作业一(运算性质闭包))

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…

揭秘MQTT:为何它是物联网的首选协议?

文章目录 MQTT 协议简介概览MQTT 与其他协议对比MQTT vs HTTPMQTT vs XMPP 为什么 MQTT 是适用于物联网的最佳协议?轻量高效,节省带宽可靠的消息传递海量连接支持安全的双向通信在线状态感知 MQTT 5.0 与 3.1.1MQTT 服务器MQTT 客户端 MQTT 协议简介 概…

acwing1209.带分数暴力与优化(java版)

//n a b / c n是确定的,只需找到其中两个。判断剩下一个数是否满足条件即可 //由题目条件可知,每个数不能重复使用,需要一个st全局数组判断每个数是否使用过 //递归实现排列型枚举,cn ac b //对于枚举出来的每一个a,再去枚举每一个c,再在c的枚举里判断b是否满足条件 //…

第四期丨酷雷曼无人机技能培训

第4期无人机技能培训 2023年10月25日,酷雷曼无人机技能培训及执照考试第四期成功举办,自7月份首期开办以来,已按照每月一期的惯例连续举办四期,取得了极为热烈的反响。 随着无人机培训的重要性及影响力逐渐扩大,参加培…

算法-贪心思想

贪心的思想非常不好解释,而且越使用权威的语言解释越难懂。而且做题的时候根据自己的理解可能直接做出来,但是非要解释一下怎么使用的贪心的话,就懵圈了。一般来说,贪心的题目没有固定的套路,一题一样,不过…

分享67个节日PPT,总有一款适合您

分享67个节日PPT,总有一款适合您 67个节日PPT下载链接:https://pan.baidu.com/s/1oU-UUCV_69e8Gp5Y6zrzVA?pwd6666 提取码:6666 Python采集代码下载链接:采集代码.zip - 蓝奏云 学习知识费力气,收集整理更不易…

Spark---Spark on Hive

1、Spark On Hive的配置 1&#xff09;、在Spark客户端配置Hive On Spark 在Spark客户端安装包下spark-2.3.1/conf中创建文件hive-site.xml&#xff1a; 配置hive的metastore路径 <configuration><property><name>hive.metastore.uris</name><v…

关于对ArrayBlockingQueue 的AQS探究

1、介绍 条件队列是 AQS 中最容易被忽视的一个细节。大部分时候&#xff0c;我们都用不上条件队列&#xff0c;但是这并不说明条件队列就没有用处了&#xff0c;它反而是我们学习生产者-消费者模式的最佳教材。条件队列是指一个阻塞队列&#xff0c;其中的元素是等待某个条件成…

每日一题:LeetCode-75. 颜色分类

每日一题系列&#xff08;day 12&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

ROS 元功能包

ROS元功能包&#xff08;Metapackage&#xff09;是一种特殊的软件包&#xff0c;它本身并不包含任何可执行代码或数据文件。在ROS 1中&#xff0c;可以通过catkin_create_pkg命令创建元功能包。 相反&#xff0c;它的主要目的是作为一组相关功能包的集合或者依赖关系列表。使…

蓝桥杯每日一题2023.12.5

题目描述 1.一步之遥 - 蓝桥云课 (lanqiao.cn) 题目分析 对于本题遵循多了就减少了就加的原则&#xff0c;用while进行计算即可 #include<bits/stdc.h> using namespace std; int x, ans; int main() {while(x ! 1){if(x < 1)x 97;else x - 127;ans ;}cout <&…

vue-cli创建项目运行报错this[kHandle] = new _Hash(algorithm, xofLen);(完美解决)

1&#xff1a;问题出现的原因 出现这个问题是node.js 的版本问题&#xff0c;因为 node.js V17开始版本中发布的是OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制&#xff0c;可能会对生态系统造成一些影响。故此以前的项目在使用 nodejs V17以上版本后会报错。…

使用VBA快速统计词组(单词组合)词频

实例需求&#xff1a;产品清单如A列所示&#xff0c;现在如下统计词组词频。想必各位小伙伴都指定如何使用字典对象实现去重&#xff0c;进而实现单个单词的词频统计。 但是统计词组词频就没有那么简单了&#xff0c;为了便于演示&#xff0c;此处的词组只限于两个单词的组合。…

阿里云Arthas使用——在日志没有输出异常情况下,如何进行线上bug定位 stack命令 和 trace命令

前言 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c;类…

深入理解:指针变量的解引用 与 加法运算

前言 指针变量的解引用和加法运算是非常高频的考点&#xff0c;也是难点&#xff0c;因为对初学者的不友好&#xff0c;这就导致了各大考试都很喜欢在这里出题&#xff0c;通常会伴随着强制类型转换、二维数组、数组指针等一起考查大家对指针的理解。但是不要怕&#xff0c;也许…