从业务层的代码出发,去排查通用框架代码崩溃的问题
目录
1、问题说明
1.1、Release下崩溃,Debug下很难复现
1.2、用Windbg打开dump文件,发现崩溃在通用的框架代码中
2、进一步分析
2.1、使用IDA查看汇编代码尝试寻找崩溃的线索
2.2、在Windbg中查看相关变量的值
2.3、查看最近代码的修改记录,找到了引发问题的点
2.4、该问题中需要关注的点
3、问题总结
4、最后
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_11931267.html 有时程序可能会崩溃在通用的框架代码中,但一般不是框架代码的问题,基本都是上层业务代码有问题导致的,框架代码中可能会引用业务层管理的业务类对象,如果引用的业务类对象有问题,则可能导致框架代码出异常。今天我们就来讲一个崩溃在通用框架代码中的实例,这个问题虽然比较简单,但有很多值得思考的细节,这个实例很有价值。下面详细讲述一下该问题实例的完整分析过程,以及有哪些值得思考的点与细节。

1、问题说明
测试同事在测试新版本软件(Release版本)时,发现在执行某个常用的操作时会发生崩溃,且问题在测试PC机上是必现的,这个功能在前几天的版本中是没问题的。
1.1、Release下崩溃,Debug下很难复现
最近,我们的开发人员在这个功能点上做了一些修改,新增了一些需求点,但开发人员在Debug下没有复现这个崩溃,一直查不出具体的原因。
Debug下运行没问题,Release下运行有问题,这种现象我们之前也时不时的遇到。究其原因,可能是因为Debug下和Release下的内存管理分配机制是不一样的,Debug下会额外多分配一些内存存放调试信息。也有可能是变量未初始化引起的,Debug下会对变量自动进行初始化,比如:
* 0xcccccccc : Used by Microsoft's C++ debugging runtime library to mark uninitialised stack memory
* 0xcdcdcdcd : Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory
Release下不会对变量内存进行初始化,变量的值会是分配内存时内存中残留的随机值。这些是引发程序在Debug下和Release下运行不同表现现象的常见原因。此外,也有可能测试场景有所不同,开发同事这边Debug下的场景和测试同事那边的Release下的场景有差异,所以表现出不同的现象。
1.2、用Windbg打开dump文件,发现崩溃在通用的框架代码中
程序在发生崩溃时,程序中安装的异常捕获模块捕获到异常并自动生成了包含异常上下文信息的dump文件,测试同事让我帮忙分析一下这个问题。取来dump文件,用Windbg打开,使用.ecxr命令切换到发生异常的那个线程,然后使用kn命令查看崩溃时的函数调用堆栈,如下所示:

首先查看崩溃的那条汇编指令,汇编指令中访问了很小的内存地址,所以触发了内存访问违例,引发崩溃。像是空指针引发的,但有待于进一步确定。
然后查看崩溃时的函数调用堆栈。和以往大部分崩溃有所不同,大部分时候函数调用堆栈中显示都是与崩溃有直接关系的业务层代码的函数调用,但这次堆栈中显示的是duilib开源库中的框架代码,和具体的业务层代码没有直接的关系,所以这个问题有着一定的隐蔽性。
所以这个问题没法快速定位出来,并且我这边也有开发任务,没有多余的时间和精力去详细研究这个问题,于是只能让相关开发人员自行排查,想办法复现问题,并详细查看最近修改的代码,看看能否找到引发问题的点。
2、进一步分析
开发同事排查了,但始终找不到引发问题的代码。出问题的功能点是软件的主要功能点,一操作就崩溃,导致后续的很多功能点测试没法展开了。所以这个崩溃需要尽快解决,于是又把我拉过来,希望尽快将这个问题解决掉。
2.1、使用IDA查看汇编代码尝试寻找崩溃的线索
函数调用堆栈都是框架中的代码,没法直接找到引发崩溃的线索,于是尝试使用IDA查看汇编代码上下文,看看为什么会产生崩溃。用IDA打开函数调用的堆栈中的模块文件,将汇编代码与C++源码对照起来看:

但看下来并没有找到有效的线索。
2.2、在Windbg中查看相关变量的值
于是又在Windbg中尝试查看函数中相关变量的值,有时相关变量的值是排查问题的关键线索。查看CWindowWnd::__WndProc函数中相关变量的值,发现收到消息id为0xf,如下所示:

对应的消息为WM_PAINT消息(#define WM_PAINT 0x000f),为啥收到该消息会产生崩溃呢?
2.3、查看最近代码的修改记录,找到了引发问题的点
当前出问题的这个功能点,不是我这边开发的,对相关的细节不清楚,于是让同事看svn上的修改记录,和他一起看都修改了哪些代码。
当看到某个cpp的修改记录时,一眼看出了问题,相关代码片段如下所示:

程序在收到底层的某个消息时,会弹出一个提示框,这个提示框之前是模态的,后来应测试要求(模态框体验不好,要改成非模态的)将之改成非模态的。之前将模态框换成了非模态框,即本来是调用ShowModal显示模态框,后来改成调用ShowWindow接口显示非模态框。调用ShowModal接口时,窗口关闭时ShowModal才会返回,对应的函数中的局部变量窗口类对象CMicStateTipWnd dlg的生命周期和ShowModal接口一致,不会有问题。
但改成ShowWindow后,窗口显示出来后,ShowWindow接口立即返回,这样就退出了当前函数,这样局部变量CMicStateTipWnd dlg的内存就自动释放了,但窗口已经创建出来了,窗口需要绘制,会产生WM_PAINT消息,就会进入到CWindowWnd::__WndProc静态函数中处理,就需要调用对应的窗口类CMicStateTipWnd对象,调用窗口类CMicStateTipWnd::HandleMessage去进行窗口的绘制刷新。但窗口类CMicStateTipWnd对象是局部变量,对象已经析构,内存已经释放引用已经释放内存的对象地址,所以就产生了崩溃。
2.4、该问题中需要关注的点
这个地方有一点需要注意一下,特别是新人会容易混淆。

窗口类CMicStateTipWnd对象在函数退出时自动析构销毁,但使用该类创建的窗口是不会跟着自动销毁的!窗口类对象的销毁是业务代码管理和控制的,而窗口创建成功后是操作系统管理的,窗口的销毁需要调用DestroyWindow去销毁,当然这是业务层去控制类对象与窗口的同步的!在这个问题中虽然窗口类CMicStateTipWnd对象析构了, 但窗口还在的,窗口会产生一些消息(比如本例中的WM_PAINT消息),这些消息会走到CWindowWnd::__WndProc静态函数中进行处理,当引用到CMicStateTipWnd对象,但对象已经析构了,所以导致内存访问违例,产生了崩溃!
3、问题总结
本例中,程序崩溃在duilib界面库的框架代码中,这个通用框架用了很多年了,基本可以确定不是框架代码的问题,应该是上层业务代码的问题。在排查这类框架代码崩溃问题时,应该从上面的业务代码入手,要从业务层代码维护的窗口类对象入手,因为框架代码中会引用到业务层的类对象,如果业务层将类对象内存释放了,框架代码还访问该业务类对象,则会引发内存访问违例,引发崩溃。
虽然本例中涉及到的是UI界面框架,但项目代码中有使用了很多其他框架,这些框架代码可能也会出现类似的异常崩溃场景,所以对这个实例的排查思路及排查方法有参考意义。
总之,如果程序崩溃在通用的框架代码中,一般不是框架的问题,基本都是上层业务代码的问题,应该从业务层代码入手,从框架中引用的业务层类对象入手(引用的业务层类对象是业务层维护和管理的)。比如,程序崩溃在QT库中,一般不是QT库有问题,基本都是上层业务代码的问题。再比如,崩溃在系统库或者C/C++运行时库中,不是系统库或运行时库有问题,一般都是上层业务代码有问题。可能是传入了不合法的参数,传入了不可访问的内存地址。
4、最后
在编写代码时,要尽量考虑的全面一些,考虑尽可能多的场景,尽量将代码写的严谨缜密一点。遇到问题、排查问题时,一定要多关注细节,事后要进行积极的思考与总结。要搞清楚为什么会出问题以及问题是怎么解决的,对出问题的代码进行进一步或深入研究,要彻底搞清楚根源,这样下次再遇到类似问题代码时能快速做出反应!
对于复杂的问题,事后要主动进行复盘,要搞清楚问题的来龙去脉(为什么会出这个问题?这个问题是如何解决的?),即便问题点所在的模块不是自己负责的,也要尽量去接触去了解。事后要积极地思考和总结,可以将以往遇到的一些问题给串联起来,将相关的知识点进行归纳,比如归纳出引发问题的常见原因积极常用的排查方法。
细节出真知!要多关注细节,多提炼出一些值得关注和思考的点,甚至可以从一些看似简单的实例中找到一些有价值的点进行展开和总结。思考的多了,了解的技术细节和知识点就多了,可以总结的东西就越多,对一些编程问题与细节点理解的就更深刻了。在遇到新的问题时,就能在已取得的认知和积累的基础上做出快速反应,有更多更开阔的思路去排查问题。如果之前遇到的问题,后面又遇到了,或者遇到了类似的问题,依然是没有思路、没有头绪,不知从何查起,这就不应该了!
对相关细节和编程点了解的更透彻后,能让我们在编写新的代码时能考虑的更加全面,在最开始编码时就能想到可能存在的潜在问题。
相关文章:
从业务层的代码出发,去排查通用框架代码崩溃的问题
目录 1、问题说明 1.1、Release下崩溃,Debug下很难复现 1.2、用Windbg打开dump文件,发现崩溃在通用的框架代码中 2、进一步分析 2.1、使用IDA查看汇编代码尝试寻找崩溃的线索 2.2、在Windbg中查看相关变量的值 2.3、查看最近代码的修改记录&#…...
LLM预训练大型语言模型Pre-training large language models
在上一个视频中,您被介绍到了生成性AI项目的生命周期。 如您所见,在您开始启动您的生成性AI应用的有趣部分之前,有几个步骤需要完成。一旦您确定了您的用例范围,并确定了您需要LLM在您的应用程序中的工作方式,您的下…...
[Machine Learning] 损失函数和优化过程
文章目录 机器学习算法的目的是找到一个假设来拟合数据。这通过一个优化过程来实现,该过程从预定义的 hypothesis class(假设类)中选择一个假设来最小化目标函数。具体地说,我们想找到 arg min h ∈ H 1 n ∑ i 1 n ℓ ( X i…...
serialVersionUID 有何用途?如果没定义会有什么问题?
序列化是将对象的状态信息转换为可存储或传输的形式的过程。我们都知道,Java 对象是保持在 JVM 的堆内存中的,也就是说,如果 JVM 堆不存在了,那么对象也就跟着消失了。 而序列化提供了一种方案,可以让你在即使 JVM 停机…...
C# OpenCvSharp DNN 二维码增强 超分辨率
效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp; using OpenCvSharp.Dnn; using OpenCvSh…...
this.$refs使用方法
深入理解和使用this.$refs——Vue.js的利器 Vue.js是一个流行的JavaScript框架,用于构建交互性强大的用户界面。在Vue.js中,this.$refs是一个强大的特性,允许你直接访问组件中的DOM元素或子组件实例。本教程将带你深入了解this.$refs的使用方…...
Ohio主题 - 创意组合和代理机构WordPress主题
Ohio主题是一个精心制作的多用途、简约、华丽、多功能的组合和创意展示主题,具有敏锐的用户体验,您需要构建一个现代且实用的网站,并开始销售您的产品和服务。它配备了最流行的WordPress页面构建器 WPBakery Page Builder(以前称为…...
mysql 、sql server trigger 触发器
sql server mySQL create trigger 触发器名称 { before | after } [ insert | update | delete ] on 表名 for each row 触发器执行的语句块## 表名: 表示触发器监控的对象 ## before | after : 表示触发的时间,before : 表示在事件之前触发&am…...
自然语言处理从入门到应用——LangChain:索引(Indexes)-[检索器(Retrievers)]
分类目录:《自然语言处理从入门到应用》总目录 检索器(Retrievers)是一个通用的接口,方便地将文档与语言模型结合在一起。该接口公开了一个get_relevant_documents方法,接受一个查询(字符串)并返…...
春秋云境:CVE-2022-0543(Redis 沙盒逃逸漏洞)
目录 一、i春秋题目 二、CVE-2022-0543:(redis沙盒逃逸) 漏洞介绍: 漏洞复现: 一、i春秋题目 靶标介绍: Redis 存在代码注入漏洞,攻击者可利用该漏洞远程执行代码。 进入题目:…...
关于uniapp组件的坑
关于uniapp组件的坑 我有一个组件写的没什么问题,但是报下面这个错误 is not found in path “components/xxx/xxxx” (using by “components/yyy/yyy”) 最后经过排除发现命名需要驼峰命名法 我原本组件命名: 文件夹名 test_tttt 文件名 test_tttt.vue 不行 最后改成文件…...
AIGC与软件测试的融合
一、ChatGPT与AIGC 生成式人工智能——AIGC(Artificial Intelligence Generated Content),是指基于生成对抗网络、大型预训练模型等人工智能的技术方法,通过已有数据的学习和识别,以适当的泛化能力生成相关内容的技术。…...
滑动验证码-elementui实现
使用elementui框架实现 html代码 <div class"button-center"><el-popoverplacement"top":width"imgWidth"title"安全验证"trigger"manual"v-model"popoverVisible"hide"popoverHide"show&quo…...
ubuntu 20.04 安装 高版本cuda 11.7 和 cudnn最新版
一、安装显卡驱动 参考另一篇文章:Ubuntu20.04安装Nvidia显卡驱动教程_ytusdc的博客-CSDN博客 二、安装CUDA 英伟达官网(最新版):CUDA Toolkit 12.2 Update 1 Downloads | NVIDIA Developer CUDA历史版本下载地址:C…...
svg图片如何渲染到页面,以及svg文件的上传
svg图片渲染到页面的几种方式 背景🟡require.context获取目录下的所有文件🟡方式1: 直接在html中渲染🟡方式: 发起ajax请求,获取SVG文件 背景 需要实现从本地目录下去获取所有的svg图标进行预览,将选中的图片显示在另…...
GPT-LLM-Trainer:如何使用自己的数据轻松快速地微调和训练LLM
一、前言 想要轻松快速地使用您自己的数据微调和培训大型语言模型(LLM)?我们知道训练大型语言模型具有挑战性并需要耗费大量计算资源,包括收集和优化数据集、确定合适的模型及编写训练代码等。今天我们将介绍一种实验性新方法&am…...
深入理解ForkJoin
任务类型 线程池执行的任务可以分为两种:CPU密集型任务和IO密集型任务。在实际的业务场景中,我们需要根据任务的类型来选择对应的策略,最终达到充分并合理地使用CPU和内存等资源,最大限度地提高程序性能的目的。 CPU密集型任务 …...
Spring5学习笔记—AOP编程
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Spring专栏 ✨特色专栏: M…...
适用于 Docker 用户的 kubectl
适用于 Docker 用户的 kubectl 你可以使用 Kubernetes 命令行工具 kubectl 与 API 服务器进行交互。如果你熟悉 Docker 命令行工具, 则使用 kubectl 非常简单。但是,Docker 命令和 kubectl 命令之间有一些区别。以下显示了 Docker 子命令, 并…...
网络安全设备篇——加密机
加密机是一种专门用于数据加密和解密的网络安全设备。它通过使用密码学算法对数据进行加密,从而保护数据的机密性和完整性。加密机通常被用于保护敏感数据,如金融信息、个人身份信息等。 加密机的主要功能包括: 数据加密:加密机使…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
