当前位置: 首页 > news >正文

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/128966612icon-default.png?t=N176http://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富文本编辑器

官网&#xff1a;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增加用户自定义功能

备注&#xff1a;这里使用的AMI的开发环境MegaRAC进行AST2500软件开发&#xff0c;并非openlinux版本。1、添加上电后自动执行的任务在PDKAccess.c中列出了系统启动过程中的所有任务&#xff0c;若需要添加功能&#xff0c;在相应的任务中添加自定义线程。一般在两个任务里面添…...

用Python暴力求解德·梅齐里亚克的砝码问题

文章目录固定个数的砝码可称量重量砝码的组合方法40镑砝码的组合问 一个商人有一个40磅的砝码&#xff0c;由于跌落在地而碎成4块。后来&#xff0c;称得每块碎片的重量都是整磅数&#xff0c;而且可以用这4 块来称从1 至40 磅之间的任意整数磅的重物。问这4 块砝码片各重多少&…...

离散Hopfield神经网络的分类——高校科研能力评价

离散Hopfield网络离散Hopfield网络是一种经典的神经网络模型&#xff0c;它的基本原理是利用离散化的神经元和离散化的权值矩阵来实现模式识别和模式恢复的功能。它最初由美国物理学家John Hopfield在1982年提出&#xff0c;是一种单层的全连接神经网络&#xff0c;被广泛应用于…...

Retrofit核心源码分析(三)- Call逻辑分析和扩展机制

在前面的两篇文章中&#xff0c;我们已经对 Retrofit 的注解解析、动态代理、网络请求和响应处理机制有了一定的了解。在这篇文章中&#xff0c;我们将深入分析 Retrofit 的 Call 逻辑&#xff0c;并介绍 Retrofit 的扩展机制。 一、Call 逻辑分析 Call 是 Retrofit 中最基本…...

源码分析spring如和对@Component注解进行BeanDefinition注册的

Spring ioc主要职责为依赖进行处理&#xff08;依赖注入、依赖查找&#xff09;、容器以及托管的(java bean、资源配置、事件)资源声明周期管理&#xff1b;在ioc容器启动对元信息进行读取&#xff08;比如xml bean注解等&#xff09;、事件管理、国际化等处理&#xff1b;首先…...

C语言--字符串函数1

目录前言strlenstrlen的模拟实现strcpystrcatstrcat的模拟实现strcmpstrcmp的模拟实现strncpystrncatstrncmpstrstrstrchr和strrchrstrstr的模拟实现前言 本章我们将重点介绍处理字符和字符串的库函数的使用和注意事项。 strlen 我们先来看一个我们最熟悉的求字符串长度的库…...

Webstorm使用、nginx启动、FinalShell使用

文章目录 主题设置FinalShellFinalShell nginx 启动历史命令Nginx页面发布配置Webstorm的一些常用快捷键代码生成字体大小修改Webstorm - gitCode 代码拉取webstorm 汉化webstorm导致CPU占用率高方法一 【忽略node_modules】方法二 【设置 - 代码编辑 - 快速预览文档 - 关闭】主…...

源码分析Spring @Configuration注解如何巧夺天空,偷梁换柱。

前言 回想起五年前的一次面试&#xff0c;面试官问Configuration注解和Component注解有什么区别&#xff1f;记得当时的回答是&#xff1a; 相同点&#xff1a;Configuration注解继承于Component注解&#xff0c;都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean…...

vector的使用及模拟实现

目录 一.vector的介绍及使用 1.vector的介绍 2.vector的使用 1.vector的定义 2.vector iterator的使用 3. vector 空间增长问题 4.vector 增删查改 3.vector 迭代器失效问题&#xff08;重点&#xff09; 1. 会引起其底层空间改变的操作 2.指定位置元素的删除操作--erase 3. Li…...

“华为杯”研究生数学建模竞赛2007年-【华为杯】A题:基于自助法和核密度估计的膳食暴露评估模型(附获奖论文)

赛题描述 我国是一个拥有13亿人口的发展中国家,每天都在消费大量的各种食品,这批食品是由成千上万的食品加工厂、不可计数的小作坊、几亿农民生产出来的,并且经过较多的中间环节和长途运输后才为广大群众所消费,加之近年来我国经济发展迅速而环境治理没有能够完全跟上,以…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…...