WebRTC开源库内部调用abort函数引发程序发生闪退问题的排查

news2024/11/26 6:21:17

目录

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运行环境时才暴露出来。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

hashmap存储方式 hash碰撞及其解决方式

1.Map的存储特点 在Map这个结构中,数据是以键值对(key-value)的形式进行存储的,每一个存储进map的数据都是一一对应的。 创建一个Map结构可以使用new HashMap()以及new TreeMap()两种方式,两者之间的区别是&#xff1a…

TVS和稳压管的相同点和不同点

大家好,我是记得诚。 文章目录 介绍相同点不同点介绍 TVS和稳压管都是电路中很常用的电子元器件,都是二极管的一个种类。 TVS二极管全称是Transient voltage suppression diode,也叫瞬态电压抑制二极管。 稳压二极管英文名字Zener diode,又叫齐纳二极管。 关于稳压二极…

Jenkins从下载到部署项目的流程

Jenkins安装配置1.1 Jenkins介绍Jenkins 是一款流行的开源持续集成(Continuous Integration)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。官网: http://jenkins-ci.org/。Jenkins的特征:开源…

谷粒学院开发(三):统一日志、异常及前端准备工作

特定异常处理 ControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class) // 指定出现什么异常会被处理ResponseBody // 为了能够返回数据public R error(Exception e) {e.printStackTrace();return R.error().message("执行了全局异常…

Linux--磁盘存储管理 分区工具 fdisk 分区实操 详解~

上一篇文章介绍了 fdisk 的各个菜单功能,这篇,我们直接实操 管理磁盘 fdisk :分区 : 我们上一篇文章里讲过,上篇文章的 磁盘 /dev/nvme0n1 空间已经满了因此 , 又重新添加了一块儿硬盘~!!>&g…

传统图像处理之颜色特征

博主简介 博主是一名大二学生,主攻人工智能研究。感谢让我们在CSDN相遇,博主致力于在这里分享关于人工智能,c,Python,爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主,博主会继续更新的&#xff0c…

UML时序图速查——架构设计必备技能

目录 一、时序图概述 二、时序图元素 1. Actor(角色)& Object(对象) 2. Lifeline(生命线) 3. Message(消息) 4. Combined Fragment(组合片段) 5. …

【Linux】多线程---线程控制

进程在前面已经讲过了,所以这次我们来讨论一下多线程。前言:线程的背景进程是Linux中资源及事物管理的基本单位,是系统进行资源分配和调度的一个独立单位。但是实现进程间通信需要借助操作系统中专门的通信机制,但是只这些机制将占…

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…

如何科学管理技术团队的研发交付速率?

每当提及「研发效能」,我们都在谈论什么? 研发效能管理要在保证质量的前提下,思考如何更快地向客户交付价值。在管理实践中,效能度量涉及三大维度:交付速率、交付质量、交付价值。 技术团队对内如何优化开发流程&…

STM32实战项目-基本定时器

前言: 通过基本定时器TIM6,让三个LED灯每间隔1s闪烁一次。 目录 1.基本定时器参数配置 1.1框图分析 1.2参数配置 2.软件程序 2.1整体框架 2.2定时器结构体 2.3定时器回调函数 1.基本定时器参数配置 1.1框图分析 TIM6作为基本定时器 它是挂载…

【Linux】-- 线程池

目录 铺垫 内存 线程的角度 线程池 基本代码结构 对于线程池的生产消费的完善 初步实现线程池生产消费 结合日志完善线程池 铺垫 内存 (以STL处理方式,引入提供效率的一种思想) 通过进行C语言与C语言的学习中,平时我们使…

C语言 深度剖析数据在内存中的存储(2)

本次博客是继上次博客,继续向下剖析数据在内存当中的存储。练习浮点型在内存中的存储练习代码1:int main() {char a -1;signed char b-1;unsigned char c-1;printf("a%d,b%d,c%d",a,b,c);return 0; }1.在本题中首先我们要知道的是%d打印的是有…

【数据结构之树】——什么是树,树的特点,树的相关概念和表示方法以及在实际的应用。

文章目录一、1.树是什么?2.树的特点二、树的相关概念三、树的表示方法1.常规方法表示树2.使用左孩子右兄弟表示法3. 使用顺序表来存储父亲节点的下标三、树在实际的应用总结一、1.树是什么? 树是一种非线性的数据结构,它是由n(n&…

MatCap模拟光照效果实现

大家好,我是阿赵 之前介绍过各种光照模型的实现方法。那些光照模型的实现虽然有算法上的不同,但基本上都是灯光方向和法线方向的计算得出的明暗结果。 下面介绍一种叫做MatCap的模拟光照效果,这种方式计算非常简单,脱离灯光的计算…

javaWeb核心05-FilterListenerAjax(Axios)json

文章目录Filter&Listener&Ajax1,Filter1.1 Filter概述1.2 Filter快速入门1.2.1 开发步骤1.2.2 代码演示1.3 Filter执行流程1.4 Filter拦截路径配置1.5 过滤器链1.5.1 概述1.5.2 代码演示1.5.3 问题1.6 案例1.6.1 需求1.6.2 分析1.6.3 代码实现1.6.3.1 创建F…

Linux 安装 nginx 详细教程

文章目录Linux 安装 nginx 详细步骤①安装依赖包②下载并解压安装包③安装 nginx④启动 nginx 服务⑤配置 nginx.conf提示:以下是本篇文章正文内容,Linux 系列学习将会持续更新 Linux 安装 nginx 详细步骤 ①安装依赖包 下载模块依赖性 Nginx 需要依赖…

resp无法连接Redis服务的解决方法

在保证Windows主机和Linux虚拟机能够相互ping通的前提下,resp仍无法连接到Linux上的redis服务,那么需要考虑以下原因: Linux防火墙问题,Linux未关闭防火墙,或防火墙未放通6379/tcp端口;redis配置问题&#…

Project ERROR: Unknown module(s) in QT: webenginewidgets

Qt系列文章目录 文章目录Qt系列文章目录前言一、问题定位二、解决方法1.引入WebEngine库2.重新打开工程3. 解决办法:运行结果前言 最近项目中需要用到:Qt中使用cesium三维引擎库,涉及到Qt和和JavaScript之间通信,工程源码报错&am…

202109-3 CCF 脉冲神经网络 66分题解 + 解题思路 + 解题过程

解题思路 根据题意&#xff0c;脉冲源的阈值大于随机数时&#xff0c;会向其所有出点发送脉冲 神经元当v>30时&#xff0c;会向其所有出点发送脉冲&#xff0c;unordered_map <int, vector > ne; //存储神经元/脉冲源的所有出点集合vector 所有脉冲会有一定的延迟&am…