目录
2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来
3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题
5、VS调试时看不到有效的函数调用堆栈,使用Windbg启动目标程序去查看异常时的函数调用堆栈
7、分析引发WebRTC开源库内部调用C运行时函数abort强制结束进程的原因
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接口强行将程序进程终止,导致程序发生闪退。本文讲述一下这一问题的完整排查过程。
为了排查会议中的相关问题,在Visual Studio中对代码进行Debug下的调试,发现视频窗口无法显示对应与会终端的视频,与会终端的摄像头是开着的,测试同事电脑上安装的Release版本软件终端入会后是可以看到其他与会终端的视频图像的。于是,要排查一下我这边Debug版本软件为啥不显示其他与会终端的视频图像。
以前遇到过类似的问题,处理音视频业务的组件库rtcmpdll.dll是动态启动的,是不是这个库没有启动起来?
rtcmpdll.dll库是在初始化组件的模块时底层调用LoadLibrary或者LoadLibraryEx动态启动的。于是启动Process Explorer工具,找到Debug版本的程序进程,查看进程启动的dll库列表:

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库版本不一致导致的。
于是和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下闪退的问题,是比较少见的!越是少见的问题,我们越要研究,要高清楚为什么会出现这样的问题!
于是启动Windbg,打开Debug版本的exe主程序,即通过Windbg启动目标程序,一上来就遇到了调用IsbadReadPtr引发的异常中断:

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

DebugBreak是系统API函数,调用该函数是为了让当前正在调试的调试器中断下来,比如正在调试的IDE、正在调试的Windbg等。调试器中断下来后,就可以查看此时的函数调用堆栈,就知道当前发生什么问题了。
于是在DebugBreak触发Windbg中断下来时,输入kn命令查看此时的函数调,找来了相关模块的pdb文件,发现是rtcmediacontrol库调用了WebRTC开源库中的RegisterAudioCallBack接口触发的。
我们在软件中要实现会议中扬声器的静音,最好的做法是,在收到平台服务器给过来的音频数据,不解码播放就可以了。但试了WebRTC的很多接口,不是达不到效果,就是多次频繁操作静音会引发崩溃。
如果按照理想的做法,在收到远端传过来的音频数据不解码播放,需要去修改WebRTC内部关于混音的代码,但这回牵涉到很多代码,比较复杂,不好修改。所以,中途引入了一个规避的方法,让上层去实现扬声器静音,不再依赖WebRTC库内部的实现。
具体的做法是,让UI层通过COM组件技术去将当前软件进程的声音关闭掉,这样就听不到会议中的声音了。关闭目标进程的声音的相关代码,可以参照下面的文章:
VC++打开或关闭目标进程的声音(附源码)
https://blog.csdn.net/chenlycly/article/details/128966612但这有个问题,整个进程的声音都没有了,这样进程中的其他声音都不播放了,比如IM子系统中收到消息的提示音都听不到了。所以,这种做法也不是很合适。
后来为了彻底解决这个扬声器静音的问题,引入了rtcmediacontrol库,把这个库作为WebRTC库引入的音频处理插件,在这个库去控制是否去解码播放音频数据。
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库。
根据调用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。
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接口,所以导致程序启动时的闪退。
本问题中,程序启动时会去调用RegisterAudioCallBack接口,会触发RTC_DCHECK校验不通过,然后触发DebugBreak和abort的调用,导致Debug版本程序闪退。但这个Check只在Debug下设置,Release下不会生效,所以Release下不会闪退。
之前音视频编解码组在对rtcmediacontrol自测时,主要进行的是Release下的自测。然后音视频编解码组将库发到组件那边,组件那边进行的也是Release下的联调,然后编译将新版本发布到我们产品流上,产品流上编译的Release安装包在测试机器上安装后运行也没问题,所以Debug下的闪退一直没暴露出来。直到我们产品这边需要更新底层库搭建最新的Debug运行环境时才暴露出来。
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file