WebRTC开源库内部调用abort函数引发程序发生闪退问题的排查
目录
1、初始问题描述
2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来
3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题
4、更新库之后Debug程序启动时就发生异常,程序闪退
5、VS调试时看不到有效的函数调用堆栈,使用Windbg启动目标程序去查看异常时的函数调用堆栈
6、引入rtcmediacontrol音频处理插件的原因
7、分析引发WebRTC开源库内部调用C运行时函数abort强制结束进程的原因
7.1、初步分析
7.2、查看WebRTC开源库对应的源码,分析程序的走向
7.3、找到触发abort终止进程操作的最终原因
8、最后
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/125529931 最近在项目中遇到了一个比较典型的问题,由于调用WebRTC开源库的RegisterAudioCallBack接口的线程与创建ADM音频设备管理对象的线程不是同一线程,触发了WebRTC内部在Debug下的Check校验失败,触发了WebRTC内部调用abort接口强行将程序进程终止,导致程序发生闪退。本文讲解一下这一问题的完整排查过程。
1、初始问题描述
为了排查会议中的相关问题,在Visual Studio中对代码进行Debug下的调试,发现视频窗口无法显示对应与会终端的视频,与会终端的摄像头是开着的,测试同事电脑上安装的Release版本软件终端入会后是可以其他与会软件终端的视频图像的。于是,要排查一下我这边Debug版本软件为啥不显示其他与会终端的视频图像。
2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来
以前遇到过类似的问题,处理音视频业务的组件库rtcmpdll.dll是动态启动的,是不是这个库没有启动起来?
rtcmpdll.dll库是在初始化组件的模块时底层调用LoadLibrary或者LoadLibraryEx动态启动的。于是启动Process Explorer工具,找到Debug版本的程序进程,查看进程启动的dll库列表:、

rtcmpdll.dll确实没有启动起来(没有加载到进程空间中)。
3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题
rtcmpdll.dll之所以没有启动起来,基本是因为rtcmpdll.dll依赖的底层库有问题,一般有两种情况:
1)依赖的dll库,在系统中找不到。这个可能是打包安装程序时,没有将dll库打包到安装包中导致的。
2)调用了被依赖的库中的接口,但在当前系统中找到的该dll库中找不到接口或者接口的参数不一致。这一般是主dll库与被依赖的dll库版本不一致导致的。
可以使用Dependency Walker工具看一下。启动该工具,将rtcmpdll.dll库拖进工具中,发现其调用了其依赖的rtcmediacontrol.dll中的RegisterRtcLogCallBack接口:

但在rtcmediacontrol.dll库中找不到该接口,那应该是rtcmpdll.dll和rtcmediacontrol.dll库版本不一致导致的。
4、更新库之后Debug程序启动时就发生异常,程序闪退
于是和rtcmediacontrol库的开发同事确认了一下,他们最近确实发布了新版本的rtcmediacontrol库,于是取来最新Debug版本的rtcmediacontrol库,放到Debug路径下,重新启动VS调试,结果一启动就报错了:

打开Call stack函数调用堆栈页面,也看不到有效的函数调用堆栈:

以前遇到过调用IsBadReadPtr导致VS报错的,于是点击继续调试按钮,结果还是报错,查看报错时的函数调用堆栈,也看不到具体是哪个函数触发的,也看不到具体的函数调用堆栈。
对于在VS中调试启动程序报错时看不到有效的函数调用堆栈的问题,我们遇到很多次了,可以尝试使用Windbg启动Debug版本的exe主程序,Windbg能感知到程序启动时发生异常并中断下来,然后就可以看到发生异常时的函数调用堆栈。
这个问题有些奇怪,只有Debug版本程序在启动后会闪退,Release版本的程序是没有问题的!测试同事那边安装的最新Release版本,运行是没问题的,启动时不会报错!软件的日常开发和维护主要是在IDE Debug下进行调试的,而Debug下程序启动后有闪退,直接导致程序没法进行Debug调试,所以这个问题必须要排查解决!我们还需要搞清楚为啥Debug下有闪退、Release下没问题,要排查软件中可能存在的隐患!
因为Debug和Release下的不同代码控制或内存差异,可能会出现Debug和Release下运行的不同现象。比如Debug下运行没问题,Release下运行有异常,这在日常项目中比较常见。而本例中遇到的Release下运行正常、Debug下闪退的问题,是比较少见的!越是少见的问题,我们越要研究,要高清楚为什么会出现这样的问题!
5、VS调试时看不到有效的函数调用堆栈,使用Windbg启动目标程序去查看异常时的函数调用堆栈
于是启动Windbg,打开Debug版本的exe主程序,即通过Windbg启动目标程序,一上来就遇到了调用IsbadReadPtr引发的异常中断:

输入g命令跳过去即可,连续遇到三次这样的中断,所以连续g了三次。
结果又遇到了调用DebugBreak引发的中断:

DebugBreak是系统API函数,调用该函数是为了让当前正在调试的调试器中断下来,比如正在调试的IDE、正在调试的Windbg等。调试器中断下来后,就可以查看此时的函数调用堆栈,就知道当前发生什么问题了。
于是在DebugBreak触发Windbg中断下来时,输入kn命令查看此时的函数调,找来了相关模块的pdb文件,发现是rtcmediacontrol库调用了WebRTC开源库中的RegisterAudioCallBack接口触发的。
6、引入rtcmediacontrol音频处理插件的原因
我们在软件中要实现会议中扬声器的静音,最好的做法是,在收到平台服务器给过来的音频数据,不解码播放就可以了。但试了WebRTC的很多接口,不是达不到效果,就是多次频繁操作静音会引发崩溃。
如果按照理想的做法,在收到远端传过来的音频数据不解码播放,需要去修改WebRTC内部关于混音的代码,但这回牵涉到很多代码,比较复杂,不好修改。所以,中途引入了一个规避的方法,让上层去实现扬声器静音,不再依赖WebRTC库内部的实现。
具体的做法是,让UI层通过COM组件技术去将当前软件进程的声音关闭掉,这样就听不到会议中的声音了。关闭目标进程的声音的相关代码,可以参照下面的文章:
https://blog.csdn.net/chenlycly/article/details/128966612
http://VC++打开或关闭目标进程的声音(附源码)但这有个问题,整个进程的声音都没有了,这样进程中的其他声音都不播放了,比如IM子系统中收到消息的提示音都听不到了。所以,这种做法也不是很合适。
后来为了彻底解决这个扬声器静音的问题,引入了rtcmediacontrol库,把这个库作为WebRTC库引入的音频处理插件,在这个库去控制是否去解码播放音频数据。
7、分析引发WebRTC开源库内部调用C运行时函数abort强制结束进程的原因
7.1、初步分析
WebRTC库内部调用DebugBreak让调试器中断下来,紧接着应该就是abort将进程终止掉,如下:


在Windbg中输入g命令将DebugBreak引发的中断跳过去,紧接着就弹出了abort终止调试的提示框。
对于WebRTC内部先调用DebugBreak后调用abort将进程强行终止掉的场景,以前我们遇到过,当时使用malloc去申请一段内存,结果malloc返回NULL,内存申请失败,然后就触发了强行终止进程的操作。估计是WebRTC开源库认为,内存申请失败会导致相关数据没法处理,相关业务没法执行下去,进程没有活下去的必要了,所以就强行将进程终止掉。
在调用abort之前,调用DebugBreak函数,就是让调试器感知一下,可以查看函数调用堆栈,看看当前执行了什么操作。
从函数调用堆栈看,调用的webrtc::AudioDeviceBuffer::RegisterAudioCallBack函数怎么位于rtcmediacontrol.dll模块中呢?这是因为rtcmediacontrol.dll库引用了WebRTC开源库,引用的静态库,不是动态库,所以还归属于rtcmediacontrol.dll库。
7.2、查看WebRTC开源库对应的源码,分析程序的走向
根据调用Windbg中显示的函数调用堆栈中的函数AudioDeviceBuffer::RegisterAudioCallBack及行号,到WebRTC开源代码中找到对应的代码行,如下所示:

对应的代码行为82行,但82行对应的是一行打印日志的代码,应该不是这行代码引起的。函数调用堆栈中显示的行号,是当前函数调用被调用函数的返回地址那一行,所以应该是81行代码引发DebugBreak调用的。
81行代码是一个叫做RTC_DCHECK_RUN_ON的宏,根据名称大概猜测出来,当前这个宏是用来做Debug下Check的。所以这个Check应该是Debug下的Check,Release下不执行这个Check,是不是这个Check内部在检测到条件不满足时触发了DebugBreak和abort调用了呢?
这个Debug Check是不是导致Debug下有闪退、Release下没有闪退的原因呢?经后面研究得知,确实是这样的,正是这个Debug Check导致Debug和Release下不同表现的。
于是Go到RTC_DCHECK_RUN_ON宏的内部实现代码:


果然是不满足条件时,就会调用rtc_FatalMessage接口,rtc_FatalMessage接口会调用FatalLog接口,这个FatalLog接口中会先调用DebugBreak、后调用abort强制将进程关闭掉。
这个地方有一个控制变量RTC_DCHECK_IS_ON宏,应该是通过这个宏去感知当前是不是Debug版本的,GO到RTC_DCHECK_IS_ON的定义处:

果然是和NDEBUG相关的,如果当前是Debug版本,RTC_DCHECK_IS_ON宏就被定义为1;如果当前是Release版本,则宏会被定义为0。
7.3、找到触发abort终止进程操作的最终原因
GO到RTC_DCHECK_RUN_ON内部,看看为啥条件不满足Check。内部调用了RTC_DCHECK宏,该宏中的判断条件是(x)->IsCurrent(),如下:

在rtcmediacontrol库中从WebRTC的音频设备管理类类继承出一个子类,在这个子类中对音频进行控制。上述判断条件是(x)->IsCurrent(),估计是判断调用RegisterAudioCallBack接口时所在线程是不是和创建ADM音频设管理类的线程是不是同一个线程,WebRTC内部要求这两个线程必须在同一个线程中。
创建ADM音频设备管理类对象和对RegisterAudioCallBack接口的调用都是由组件层去做的,组件的同事查看代码得知,这两个操作确实不在同一个线程中执行的,一个是在singals线程,一个是在Worker线程中,所以不在一个线程中,所以调用RegisterAudioCallBack接口时触发Check失败,导致调用了rtc_FatalMessage接口,进而调用了DebugBreak和abort接口,所以导致程序启动时的闪退。
8、最后
本问题中,程序启动时会去调用RegisterAudioCallBack接口,会触发RTC_DCHECK校验不通过,然后触发DebugBreak和abort的调用,导致Debug版本程序闪退。但这个Check只在Debug下设置,Release下不会生效,所以Release下不会闪退。
之前音视频编解码组在对rtcmediacontrol自测时,主要进行的是Release下的自测。然后音视频编解码组将库发到组件那边,组件那边进行的也是Release下的联调,然后编译将新版本发布到我们产品流上,产品流上编译的Release安装包在测试机器上安装后运行也没问题,所以Debug下的闪退一直没暴露出来。直到我们产品这边需要更新底层库搭建最新的Debug运行环境时才暴露出来。
相关文章:
WebRTC开源库内部调用abort函数引发程序发生闪退问题的排查
目录 1、初始问题描述 2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来 3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题 4、更新库之后Debug程序启动时就发生异常,程序闪退 5、VS调试时看不到有效的函数调用堆…...
Golang并发编程
Golang并发编程 文章目录Golang并发编程1. 协程2. channel2.1 channel的创建2.2 使用waitGroup实现同步3. 并发编程3.1 并发编程之runtime包3.2 mutex互斥锁3.3 channel遍历3.3.1 for if遍历3.3.2 for range3.4 select switch3.5 Timer3.5.1 time.NewTimer()3.5.2 Stop、reset…...
windows+Anaconda环境下安装BERT成功安装方法及问题汇总
前言 在WindowsAnaconda环境下安装BERT,遇到各种问题,几经磨难,最终成功。接下来,先介绍成功的安装方法,再附上遇到的问题汇总 成功的安装方法 1、创建虚拟环境 注意:必须加上python3.7.12以创建环境&a…...
git - 简易指南
git - 简易指南 创建新仓库 创建新文件夹,打开,然后执行 git init 以创建新的 git 仓库。 检出仓库 执行如下命令以创建一个本地仓库的克隆版本: git clone /path/to/repository 如果是远端服务器上的仓库,你的命令会是这个样…...
[论文笔记]Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context
引言 我们知道Transformer很好用,但它设定的最长长度是512。像一篇文章超过512个token是很容易的,那么我们在处理这种长文本的情况下也想利用Transformer的强大表达能力需要怎么做呢? 本文就带来一种处理长文本的Transformer变种——Transf…...
华为OD机试题 - 找目标字符串(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:找目标字符串题目输入输出示例一输入输出说明Code解题思路版权说…...
C++面向对象编程之六:重载操作符(<<,>>,+,+=,==,!=,=)
重载操作符C允许我们重新定义操作符(例如:,-,*,/)等,使其对于我们自定义的类类型对象,也能像内置数据类型(例如:int,float,double&…...
JS_wangEditor富文本编辑器
官网:https://www.wangeditor.com/ 引入 CSS 定义样式 <link href"https://unpkg.com/wangeditor/editorlatest/dist/css/style.css" rel"stylesheet"> <style>#editor—wrapper {border: 1px solid #ccc;z-index: 100; /* 按需定…...
Django实践-06导出excel/pdf/echarts
文章目录Django实践-06导出excel/pdf/echartsDjango实践-06导出excel/pdf/echarts导出excel安装依赖库修改views.py添加excel导出函数修改urls.py添加excel/运行测试导出pdf安装依赖库修改views.py添加pdf导出函数修改urls.py添加pdf/生成前端统计图表修改views.py添加get_teac…...
java并发入门(一)共享模型—Synchronized、Wait/Notify、pack/unpack
一、共享模型—管程 1、共享存在的问题 1.1 共享变量案例 package com.yyds.juc.monitor;import lombok.extern.slf4j.Slf4j;Slf4j(topic "c.MTest1") public class MTest1 {static int counter 0;public static void main(String[] args) throws InterruptedEx…...
Ast2500增加用户自定义功能
备注:这里使用的AMI的开发环境MegaRAC进行AST2500软件开发,并非openlinux版本。1、添加上电后自动执行的任务在PDKAccess.c中列出了系统启动过程中的所有任务,若需要添加功能,在相应的任务中添加自定义线程。一般在两个任务里面添…...
用Python暴力求解德·梅齐里亚克的砝码问题
文章目录固定个数的砝码可称量重量砝码的组合方法40镑砝码的组合问 一个商人有一个40磅的砝码,由于跌落在地而碎成4块。后来,称得每块碎片的重量都是整磅数,而且可以用这4 块来称从1 至40 磅之间的任意整数磅的重物。问这4 块砝码片各重多少&…...
离散Hopfield神经网络的分类——高校科研能力评价
离散Hopfield网络离散Hopfield网络是一种经典的神经网络模型,它的基本原理是利用离散化的神经元和离散化的权值矩阵来实现模式识别和模式恢复的功能。它最初由美国物理学家John Hopfield在1982年提出,是一种单层的全连接神经网络,被广泛应用于…...
Retrofit核心源码分析(三)- Call逻辑分析和扩展机制
在前面的两篇文章中,我们已经对 Retrofit 的注解解析、动态代理、网络请求和响应处理机制有了一定的了解。在这篇文章中,我们将深入分析 Retrofit 的 Call 逻辑,并介绍 Retrofit 的扩展机制。 一、Call 逻辑分析 Call 是 Retrofit 中最基本…...
源码分析spring如和对@Component注解进行BeanDefinition注册的
Spring ioc主要职责为依赖进行处理(依赖注入、依赖查找)、容器以及托管的(java bean、资源配置、事件)资源声明周期管理;在ioc容器启动对元信息进行读取(比如xml bean注解等)、事件管理、国际化等处理;首先…...
C语言--字符串函数1
目录前言strlenstrlen的模拟实现strcpystrcatstrcat的模拟实现strcmpstrcmp的模拟实现strncpystrncatstrncmpstrstrstrchr和strrchrstrstr的模拟实现前言 本章我们将重点介绍处理字符和字符串的库函数的使用和注意事项。 strlen 我们先来看一个我们最熟悉的求字符串长度的库…...
Webstorm使用、nginx启动、FinalShell使用
文章目录 主题设置FinalShellFinalShell nginx 启动历史命令Nginx页面发布配置Webstorm的一些常用快捷键代码生成字体大小修改Webstorm - gitCode 代码拉取webstorm 汉化webstorm导致CPU占用率高方法一 【忽略node_modules】方法二 【设置 - 代码编辑 - 快速预览文档 - 关闭】主…...
源码分析Spring @Configuration注解如何巧夺天空,偷梁换柱。
前言 回想起五年前的一次面试,面试官问Configuration注解和Component注解有什么区别?记得当时的回答是: 相同点:Configuration注解继承于Component注解,都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean…...
vector的使用及模拟实现
目录 一.vector的介绍及使用 1.vector的介绍 2.vector的使用 1.vector的定义 2.vector iterator的使用 3. vector 空间增长问题 4.vector 增删查改 3.vector 迭代器失效问题(重点) 1. 会引起其底层空间改变的操作 2.指定位置元素的删除操作--erase 3. Li…...
“华为杯”研究生数学建模竞赛2007年-【华为杯】A题:基于自助法和核密度估计的膳食暴露评估模型(附获奖论文)
赛题描述 我国是一个拥有13亿人口的发展中国家,每天都在消费大量的各种食品,这批食品是由成千上万的食品加工厂、不可计数的小作坊、几亿农民生产出来的,并且经过较多的中间环节和长途运输后才为广大群众所消费,加之近年来我国经济发展迅速而环境治理没有能够完全跟上,以…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
uni-app学习笔记二十七--设置底部菜单TabBar的样式
官方文档地址:uni.setTabBarItem(OBJECT) | uni-app官网 uni.setTabBarItem(OBJECT) 动态设置 tabBar 某一项的内容,通常写在项目的App.vue的onLaunch方法中,用于项目启动时立即执行 重要参数: indexnumber是tabBar 的哪一项&…...
dvwa11——XSS(Reflected)
LOW 分析源码:无过滤 和上一关一样,这一关在输入框内输入,成功回显 <script>alert(relee);</script> MEDIUM 分析源码,是把<script>替换成了空格,但没有禁用大写 改大写即可,注意函数…...
如何优雅地绕过限制调用海外AI-API?反向代理与API中转技术详解
阅读时长 | 8分钟 适用读者 | 需要跨境调用OpenAI等AI服务的开发者/企业 一、问题背景:为什么需要代理? 最近在技术社区看到这样的求助: "公司服务器在国内,但业务需要调用OpenAI接口,直接访…...
