查询瓦解标题流程
- 拿到瓦解日志
- 检察瓦解线程、瓦解缘故原由
- 检察瓦解函数堆栈
- 确定瓦解调用参数
- 根据控制台日志来详细分析标题
例子1:
如图,瓦解线程是线程5,瓦解范例是EXC_BREAKPOINT(SIGTRAP),下表是常见的瓦解非常,可以看到EXC_BREAKPOINT(SIGTRAP)是一种调试器相干的,跟踪/断点捕捉,多见于非常抛出。
UNIX 信号表明SIGSEGV访问无效的内存地点。地点存在,但是应用步调无法访问。SIGABRT步调瓦解。由 C函数 abort() 初始化。通常意味着体系检测到某些变乱堕落,比方 assert() 大概 NSAssert() 校验失败。SIGBUS访问无效的内存地点。地点不存在,或对齐无效。(The address does not exist, or the alignment is invalid.)SIGTRAP调试器相干SIGILL实验实验非法的、有缺陷、未知的大概必要权限的指令。Mach 非常形貌表明EXC_BAD_ACCESS错误内存访问访问“错误”内存地点。“错误”大概指“地点不存在”大概“应用没有权限访问”。因此通常与 SIGBUS 及 SIGSEGV 相干联。EXC_CRASH非常跳出通常与 SIGABRT 相干联,意思是由于检测到代码抛出的未捕捉非常而使应用步调非常退出。EXC_BREAKPOINT跟踪/断点捕捉通用与 SIGTRAP 相干联。可以由你自己的代码大概 NSExceptions 抛出时触发。EXC_GUARD违反了受保护资源的防护(Violated Guarded Resource Protection)由违反受保护资源防护触发,比方‘某些文件形貌符’。EXC_BAD_INSTRUCTION非法指令通常与特定非法或未界说指令/操纵数相干。EXC_RESOURCE资源限定应用由于到达资源消耗限定而退出。00000020十六进制非常范例非 'OS Kernel' 非常。
如图所示,我们末了瓦解在libobjc.A.dylib的objc_opt_respondsToSelector+48的地方,现实上,这是objc是否相应selector的地方,我们可以检察objc的源码,以下选自objc4-838
// Calls [obj respondsToSelector]BOOLobjc_opt_respondsToSelector(id obj, SEL sel){#if __OBJC2__ if (slowpath(!obj)) return NO; Class cls = obj->getIsa(); if (fastpath(!cls->hasCustomCore())) { return class_respondsToSelector_inst(obj, sel, cls); }#endif return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(obj, @selector(respondsToSelector , sel);}为了弄清楚毕竟崩在哪一行,我们必要把它转成汇编
留意,我们末了走到的是+48,这并不代表我们是实验完+48所对应的代码才瓦解的,恰恰是实验上一句代码瓦解,而上一句代码转成汇编后的返回地点是+48,而上一句对应的是
if (slowpath(!obj)) return NO;也就是说此时objc不存在,团结前面的DDLog打印函数,我们根本可以确定我们打印的对象已经被开释了,但是指针还没有清空,即指针所指向的内存已开释,而指针自己的地点不为null,以是它指向了一块不可访问的内存。我们回到第5行,ResetVTSession,来确定打印的是个啥
- 确定瓦解参数,还记得我们前面说的吗,瓦解的偏移号不是代表我们实验完这一句才瓦解,而是上一句,之以是表现+112偏移地点是由于上一句实验完毕的返回地点是这个,可以很清楚的看到汇编实在已经给我们表明出来了,是"ResetVtSession = %@"调用出现的标题,我们转成正常代码
如今我们确定了引发瓦解的参数 vtSession.
- 如今我们来详细分析一下这个函数的毕竟有什么标题,实在我们都不必要详细分析自己的日志就能看出来。
标题出在这里,vtSession = NULL,这是一句没什么作用的代码,反而很有迷惑性,为什么呢?我们来分析一下这个方法想干什么,先逼迫编完剩下的帧VTCompressionSessionCompleteFrames,相称于快速处置处罚完还没处置处罚的内容,然后VTCompressionSessionInvalidate(vtSession)和CFRelease(vtSession),这两步是烧毁session,并开释内存,末了再把vtSession置空,看起来perfect,但是不要忘了我们的参数vtSession是值传递!换句话说我们在函数内部的vtSession只是外部调用的值拷贝,就算我们把它置为空,也不影响外部的指针不为空,下次如果有其他线程重新调进来,就会引发瓦解。以是办理方案有两种,一是改为址传递,改为
void MHH264VideoSource::ResetVtSession(VTCompressionSessionRef& vtSession)二是仍旧是值传递,不外外貌手动把调用指针置空
// before: ResetVtSession(this->m_portrait_vtSession); ResetVtSession(this->m_landscape_vtSession);// after: ResetVtSession(this->m_portrait_vtSession); this->m_portrait_vtSession = nullptr; ResetVtSession(this->m_landscape_vtSession); this->m_landscape_vtSession = nullptr;总结
我们先通过瓦解日志确定瓦解范例和瓦解缘故原由,然后根据瓦解堆栈来详细锁定诱发瓦解的缘故原由,然后再回到SDK层去检察详细引发瓦解的代码和变量,末了我们再根据自己的代码来详细排查为什么会如许。
后话:固然厥后复盘的时间第5步我并没有写根据日志来排查,那是由于末了看了一圈日志末了又查返来到这个函数,发现是这里的标题,我一开始实在没看出来这里的标题,复盘的时间想写简朴点,毕竟业务上的设计各家各有差别,但是最根本的步调bug却是雷同的。
跋文
反面再分享一下我排查的详细操纵吧,总体绕了一圈弯路
- 起首检察调用ResetVtSession的地方是ResetVtSession(this->m_landscape_vtSession)时瓦解,表明横屏编码器不存在,但是竖屏编码器能正常开释
- 接着检察调用开释的地方是verifyProcess(),这是一个嗅探机制,旨在检察当前的码流是否正常发送中,如果不正常,就重新创建编码器或革新关键帧,根据日志判断,其时检测到竖屏码流不能正常发送中,于是重新创建了一个竖屏的编码器,但是横屏的没有创建,以是这一步可以得到一个信息:横屏的编码器压根儿没有(但是不能确定是已经开释了还是根本没创建)
- 接着看上面的日志,发现走到了创建流程,但是到加锁创建的那一步,直接被return掉了,这里可以确定,vtSession并不为空,阐明上一次开释并没有把指针置空
- 接着就回到了上面,发现是开释函数的标题。那么为什么之前不停没出过标题呢?由于之前是启动扩展进程,每次都是新创建一个进程,以是everything is new,上一个扩展进程反正已经没了,没置空也不影响,以是不停没啥标题,但是这一次咱们是用主进程采集流并发送的,导致整一个videoSource压根儿已经创建过就不消再创建了,以是这里vt_Session指针没置空就很伤害了,不光会导致下一次创建的时间创建不了,而且一旦走到析构就直接咖喱给给了。
|