首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何调试SEGV_ACCERR

如何调试SEGV_ACCERR
EN

Stack Overflow用户
提问于 2017-01-24 10:06:50
回答 2查看 7.4K关注 0票数 14

我有一个用踢腿ButterflyTV libRTMP播放视频的应用程序

现在,99%的应用程序正常工作,但有时我会遇到无法调试的本地分段错误,因为消息太神秘了:

代码语言:javascript
复制
01-24 10:52:25.576 199-199/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-24 10:52:25.576 199-199/? A/DEBUG: Build fingerprint: 'google/hammerhead/hammerhead:6.0.1/M4B30Z/3437181:user/release-keys'
01-24 10:52:25.576 199-199/? A/DEBUG: Revision: '11'
01-24 10:52:25.576 199-199/? A/DEBUG: ABI: 'arm'
01-24 10:52:25.576 199-199/? A/DEBUG: pid: 14302, tid: 14382, name: MuxerThread  >>> tv.myapp.broadcast.dev <<<
01-24 10:52:25.576 199-199/? A/DEBUG: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x9fef1000
01-24 10:52:25.636 199-199/? A/DEBUG: Abort message: 'Setting to ready!'
01-24 10:52:25.636 199-199/? A/DEBUG:     r0 9c6f9500  r1 9c6f94fc  r2 9fee900c  r3 00007ff4
01-24 10:52:25.636 199-199/? A/DEBUG:     r4 9fee9010  r5 9fef0ffd  r6 00007ff1  r7 9fef0d88
01-24 10:52:25.636 199-199/? A/DEBUG:     r8 cfe40980  r9 9e0a6900  sl 00007ff4  fp 9c6f94fc
01-24 10:52:25.636 199-199/? A/DEBUG:     ip 9c6f9058  sp 9c6f94dc  lr 000000e9  pc b3a33cb6  cpsr 800f0030
01-24 10:52:25.650 199-199/? A/DEBUG: backtrace:
01-24 10:52:25.651 199-199/? A/DEBUG:     #00 pc 00004cb6  /data/app/tv.myapp.broadcast.dev-2/lib/arm/librtmp-jni.so
01-24 10:52:25.651 199-199/? A/DEBUG:     #01 pc 00005189  /data/app/tv.myapp.broadcast.dev-2/lib/arm/librtmp-jni.so (rtmp_sender_write_video_frame+28)
01-24 10:52:25.651 199-199/? A/DEBUG:     #02 pc 00005599  /data/app/tv.myapp.broadcast.dev-2/lib/arm/librtmp-jni.so (Java_net_butterflytv_rtmp_1client_RTMPMuxer_writeVideo+60)
01-24 10:52:25.651 199-199/? A/DEBUG:     #03 pc 014e84e7  /data/app/tv.myapp.broadcast.dev-2/oat/arm/base.odex (offset 0xa66000) (int net.butterflytv.rtmp_client.RTMPMuxer.writeVideo(byte[], int, int, int)+122)
01-24 10:52:25.651 199-199/? A/DEBUG:     #04 pc 014dbd55  /data/app/tv.myapp.broadcast.dev-2/oat/arm/base.odex (offset 0xa66000) (void io.kickflip.sdk.av.muxer.RtmpMuxerMix.writeThread()+2240)
01-24 10:52:25.651 199-199/? A/DEBUG:     #05 pc 014d8c41  /data/app/tv.myapp.broadcast.dev-2/oat/arm/base.odex (offset 0xa66000) (void io.kickflip.sdk.av.muxer.RtmpMuxerMix.access$000(io.kickflip.sdk.av.muxer.RtmpMuxerMix)+60)
01-24 10:52:25.651 199-199/? A/DEBUG:     #06 pc 014d819f  /data/app/tv.myapp.broadcast.dev-2/oat/arm/base.odex (offset 0xa66000) (void io.kickflip.sdk.av.muxer.RtmpMuxerMix$1.run()+98)
01-24 10:52:25.651 199-199/? A/DEBUG:     #07 pc 721e78d1  /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x1ed6000)

同样,在2小时的流中,这种情况可能永远不会发生,也可能发生在流中的10分钟内。调试是非常困难的,因为我不能强迫错误发生。

有什么方法可以改善我得到的调试信息吗?SEGV_ACCER到底是什么意思?我读到这“意味着你试图访问一个你没有权限访问的地址。”但我不确定这意味着什么,因为我可以连续几个小时不发生错误。

有什么办法能捕捉到信号然后继续吗?

编辑:要添加更多信息,这是本机库中应用程序崩溃的部分(使用ndk-堆栈找到):

代码语言:javascript
复制
JNIEXPORT jint JNICALL
Java_net_butterflytv_rtmp_1client_RTMPMuxer_writeVideo(JNIEnv *env, jobject instance,
                                                       jbyteArray data_, jint offset, jint length,
                                                       jint timestamp) {
    jbyte *data = (*env)->GetByteArrayElements(env, data_, NULL);
    jint result = rtmp_sender_write_video_frame(data, length, timestamp, 0, 0);
    (*env)->ReleaseByteArrayElements(env, data_, data, 0);

    return result;
}


int rtmp_sender_write_video_frame(uint8_t *data,
                                  int size,
                                  uint64_t dts_us,
                                  int key,
                                  uint32_t abs_ts)
{


    uint8_t * buf;
    uint8_t * buf_offset;
    int val = 0;
    int total;
    uint32_t ts;
    uint32_t nal_len;
    uint32_t nal_len_n;
    uint8_t *nal;
    uint8_t *nal_n;
    char *output ;
    uint32_t offset = 0;
    uint32_t body_len;
    uint32_t output_len;

    buf = data;
    buf_offset = data;
    total = size;
    ts = (uint32_t)dts_us;

    //ts = RTMP_GetTime() - start_time;
    offset = 0;

    nal = get_nal(&nal_len, &buf_offset, buf, total);

(...)


}



static uint8_t * get_nal(uint32_t *len, uint8_t **offset, uint8_t *start, uint32_t total)
{
    uint32_t info;
    uint8_t *q ;
    uint8_t *p  =  *offset;
    *len = 0;




    if ((p - start) >= total)
        return NULL;

    while(1) {
        info =  find_start_code(p, 3);

        if (info == 1)
            break;
        p++;
        if ((p - start) >= total)
            return NULL;
    }
    q = p + 4;
    p = q;

    while(1) {
        info =  find_start_code(p, 3);

        if (info == 1)
            break;
        p++;
        if ((p - start) >= total)
            //return NULL;
            break;
    }


    *len = (p - q);
    *offset = p;
    return q;
}


static uint32_t find_start_code(uint8_t *buf, uint32_t zeros_in_startcode)
{
    uint32_t info;
    uint32_t i;

    info = 1;
    if ((info = (buf[zeros_in_startcode] != 1)? 0: 1) == 0)
        return 0;

    for (i = 0; i < zeros_in_startcode; i++)
        if (buf[i] != 0)
        {
            info = 0;
            break;
        };

    return info;
}

事故发生在buf[zeros_in_startcode] in find_start_code。我也删除了一些android_log行(认为这不重要吗?)

据我理解,这个缓冲区应该是可访问的,它只在“有时”崩溃是没有意义的。

PS。这就是我从Java调用本机代码的地方:

代码语言:javascript
复制
private void writeThread() {

       while (true) {

           Frame frame = null;
           synchronized (mBufferLock) {
              if (!mConfigBuffer.isEmpty()) {
                   frame = mConfigBuffer.peek();
               } else if (!mBuffer.isEmpty()) {
                   frame = mBuffer.remove();
               }
               if (frame == null) {
                   try {
                       mBufferLock.wait();
                   } catch (InterruptedException e) {
                   }
               }
           }

           if (frame == null) {
               continue;
           } else if (frame instanceof Sentinel) {
               break;
           }


           int writeResult = 0;

           synchronized (mWriteFence) {
               if (!mConnected) {
                   debug(WARN, "Skipping frame due to disconnection");
                   continue;
               }

               if (frame.getFrameType() == Frame.VIDEO_FRAME) {              
                   writeResult = mRTMPMuxer.writeVideo(frame.getData(), frame.getOffset(), frame.getSize(), frame.getTime());
               } else if (frame.getFrameType() == Frame.AUDIO_FRAME) {
                   writeResult = mRTMPMuxer.writeAudio(frame.getData(), frame.getOffset(), frame.getSize(), frame.getTime());

               }

               if (writeResult < 0) {
                       mRtmpListener.onDisconnected();
                       mConnected = false;
               } else {
                   //Now we remove the config frame, only if sending was successful!
                   if (frame.isConfig()) {
                       synchronized (mBufferLock) {
                           mConfigBuffer.remove();
                       }
                   }
               }
           }

       }

   }

请注意,即使我根本没有发送音频,崩溃也会发生。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-01-27 01:34:43

“您可以将数据存储在byte[]中。这允许从托管代码中进行非常快速的访问。然而,在本机方面,您不能保证不需要复制就可以访问数据。”

请参阅https://developer.android.com/training/articles/perf-jni.html

分析

一些思考和尝试的事情:

  • 它掉下来的代码非常通用,所以那里可能没有bug。
  • 它必须是frame数据已被删除/损坏/锁定/移动
  • Java垃圾收集器是否已删除或重新定位了数据?
  • 您可以将详细的调试写入文件,在每个框架上覆盖它,因此您只有一个包含最后调试信息的小日志。
  • frame变量信息的本地副本(使用ByteBuffer)发送到mRTMPMuxer.writeVideo 与常规的byte缓冲区不同,在ByteBuffer中,没有在托管heap上分配存储,总是可以从本机代码直接访问

实现

//从本机堆分配内存数据= ByteBuffer.allocateDirect(frame.getData().length);data.clear();//System.gc();//复制数据data.get (frame.getData(),0,frame.getData().length);//data =(frame.getData() == null)?空: frame.getData().clone();int偏移量= frame.getOffset();int size = frame.getSize();int time = frame.getTime();writeResult =mRTMPMuxer.writeVideo(数据、偏移量、大小、时间);JNIEXPORT JNICALL frame.getOffset JNIEnv *env、jobject实例、jobject data_、//NOT jbyteArray data_、jint偏移量、jint长度、jint时间戳){j字节*data = env->GetDirectBufferAddress(env,data);//GetDirectBufferAddress NOT GetByteArrayElements jint result =rtmp_sender_write_video_frame(数据、长度、时间戳,0,0);//(*env)->ReleaseByteArrayElements(env,data_,data,0);//??返回结果;} 调试

来自因此,捕获本机代码引发的异常。的一些代码

代码语言:javascript
复制
    static uint32_t find_start_code(uint8_t *buf, uint32_t zeros_in_startcode){
    //...
    try {
        if ((info = (buf[zeros_in_startcode] != 1)? 0: 1) == 0) return 0;//your code
    }
    // You can catch std::exception for more generic error handling
    catch (std::exception e){
        throwJavaException (env, e.what());//see method below
    }
    //...

然后是一种新的方法:

代码语言:javascript
复制
    void throwJavaException(JNIEnv *env, const char *msg)
    {
     // You can put your own exception here
     jclass c = env->FindClass("java/lang/RuntimeException");
     if (NULL == c)
     {
         //B plan: null pointer ...
         c = env->FindClass("java/lang/NullPointerException");
     }
     env->ThrowNew(c, msg);
    }
}

不要太挂在SEGV_ACCERR上,您有一个分段错误,SIGSEGV (由于程序试图读取或写入非法的内存位置,在您的情况下读取)。

来自西金福赫:

SEGV_MAPERR意味着您试图访问一个不映射到任何东西的地址。SEGV_ACCERR意味着您试图访问您没有访问权限的地址。

其他

这可能令人感兴趣:

问:我注意到有RTMP的支持。但是一个删除RTMP的补丁已经合并了。 问:你能告诉我为什么吗? 答:我们认为RTMP不像HLS那样服务于移动广播用例, 答:所以我们不想把有限的资源花在支持它上。

请参阅: https://github.com/Kickflip/kickflip-android-sdk/issues/33

我建议你向以下机构提出一个问题:

https://github.com/Kickflip/kickflip-android-sdk/issues

https://github.com/ButterflyTV/LibRtmp-Client-for-Android/issues

票数 6
EN

Stack Overflow用户

发布于 2017-01-30 07:27:17

通过对问题的症状/描述,您的程序很可能遇到某种无效的内存访问/损坏,这与多线程争用条件场景有某种关系。根据我过去的经验,调试内存损坏本身非常困难,如果将其链接到多线程环境,则非常困难。我以前的一些文章可能会有所帮助,并提供一些关于这些主题的一般性指导。请注意,这些帖子与Windows/Linux有关,而不是Android平台。

cpp -瓦磨-8码的无效读数

在网络URL上调用函数cvCreateFileCapture时,有时会发生分段错误

在进一步阅读有关类似问题和代码sinppet的文章时,我看到了下面提到的一篇文章:

意思是?

应用程序的客户端代码片段

代码语言:javascript
复制
synchronized (mWriteFence) {
                if (!mConnected) {
                    continue;
                }
                if (frame.getFrameType() == Frame.VIDEO_FRAME) {
                    writeResult = mRTMPMuxer.writeVideo(frame.getData(), frame.getOffset(), frame.getSize(), frame.getTime());
                    calcVideoFpsAndBitrate(frame.getSize());

                } else if (frame.getFrameType() == Frame.AUDIO_FRAME) {
                    writeResult = mRTMPMuxer.writeAudio(frame.getData(), frame.getOffset(), frame.getSize(), frame.getTime());
                    calcAudioBitrate(frame.getSize());
                }

}

从上面的代码中,在我看来,如果您的应用程序按一定的顺序接收Frame.VIDEO_FRAME & Frame.AUDIO_FRAME,可能会导致某种争用条件(可能是异步模型实现),同时在RtmpMuxerMix.writeThread模块中使用frame变量。

结束这些问题:

  • 我们应该尝试阅读有关库及其最佳实践的文档,并让您的代码得到审查。有时它有助于找出我们的逻辑中明显的问题。
  • 在动态工具下运行应用程序时,我们应该尝试重现这个问题。我不知道Android平台上有这样的工具。请不要认为,一旦我们开始在dynamics工具下运行应用程序,执行的顺序就会改变,在此之后,我们有可能经常地再现这些问题,或者几乎无法再现它。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41825305

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档