gpt4 book ai didi

android - 如何调试 SEGV_ACCERR

转载 作者:塔克拉玛干 更新时间:2023-11-01 21:26:20 32 4
gpt4 key购买 nike

我有一个使用 Kickflip 流式传输视频的应用程序和 ButterflyTV libRTMP

现在应用程序在 99% 的时间里工作正常,但有时我会遇到无法调试的 native 段错误,因为消息太隐晦了:

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 到底是什么意思?我读到这“意味着您试图访问您无权访问的地址。”但我不确定这意味着什么,因为我可以流式传输数小时而不会发生错误。

有什么方法可以捕捉到信号并继续吗?

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

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;
}

崩溃发生在 find_start_code 中的 buf[zeros_in_startcode] 处。我也删除了一些 android_log 行(不认为这很重要?)。

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

附言。这是我从 Java 调用 native 代码的地方:

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();
}
}
}
}

}

}

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

最佳答案

"You can store the data in a byte[]. This allows very fast access from managed code. On the native side, however, you're not guaranteed to be able to access the data without having to copy it."

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

分析

一些思考和尝试:

  • 失败的代码非常通用,所以那里可能没有错误
  • 一定是frame数据被移除/损坏/锁定/移动
  • Java 垃圾收集器是否已删除或重新定位数据?
  • 你可以把详细的调试写到一个文件中,在每个文件上覆盖它帧,因此您只有一个包含最后调试信息的小日志。
  • frame 变量信息的本地副本(使用 ByteBuffer)发送到 mRTMPMuxer.writeVideo
    与常规的 byte 缓冲区不同,在 ByteBuffer 中,存储未分配在托管上,并且可以始终访问直接来自 native 代码。

实现

//allocates memory from the native heap
ByteBuffer data = ByteBuffer.allocateDirect(frame.getData().length);
data.clear();
//System.gc();
//copy data
data.get(frame.getData(), 0, frame.getData().length);
//data = (frame.getData() == null) ? null : frame.getData().clone();
int offset = frame.getOffset();
int size = frame.getSize();
int time = frame.getTime();
writeResult = mRTMPMuxer.writeVideo(data , offset, size, time);

JNIEXPORT jint JNICALL
Java_net_butterflytv_rtmp_1client_RTMPMuxer_writeVideo(
JNIEnv *env,
jobject instance,
jobject data_, //NOT jbyteArray data_,
jint offset,
jint length,
jint timestamp)
{
jbyte *data = env->GetDirectBufferAddress(env, data);//GetDirectBufferAddress NOT GetByteArrayElements
jint result = rtmp_sender_write_video_frame(data, length, timestamp, 0, 0);
//(*env)->ReleaseByteArrayElements(env, data_, data, 0);//????
return result;
}

Debugging

一些代码来自 SO Catching exceptions thrown from native code :

    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
}
//...

然后是一个新的方法:

    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(由试图读取或写入非法内存位置的程序引起,读取在你的情况下)。
来自 siginfo.h:

SEGV_MAPERR 表示您试图访问一个没有映射到任何内容的地址。SEGV_ACCERR 表示您试图访问您无权访问的地址。

其他

这可能是有趣的:

Q: I noticed that there was RTMP support. But a patch which remove RTMP had been merged.
Q: Could you tell me why ?
A: We don't think RTMP serves the mobile broadcasting use case as well as HLS,
A: and so we don't want to dedicate our limited resources towards supporting it.

see: 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

关于android - 如何调试 SEGV_ACCERR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41825305/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com