Mam aplikacja, która strumieni wideo za pomocą Kickflip i ButterflyTV libRTMPJak debugować SEGV_ACCERR
Teraz do 99% procent czasu aplikacja pracuje ok, ale od czasu do czasu dostaję natywną winy segmentacji, że jestem nie jest w stanie do debugowania, ponieważ wiadomości są zbyt tajemnicze:
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/[email protected]@boot.oat (offset 0x1ed6000)
Ponownie, w strumieniu 2 godziny to nie może się wydarzyć lub może się zdarzyć w 10 minucie strumienia. Bardzo ciężko jest debugować, ponieważ nie mogę wymusić błędu.
Czy istnieje sposób na poprawę informacji o debugowaniu, które otrzymuję? Co dokładnie oznacza SEGV_ACCER? Czytałem, że to "oznacza, że próbujesz uzyskać dostęp do adresu, do którego nie masz uprawnień dostępu." ale nie jestem pewien, co to oznacza, ponieważ mogę przesyłać strumieniowo wiele godzin bez wystąpienia błędu.
Czy istnieje sposób, aby złapać sygnał i po prostu kontynuować?
EDIT: aby dodać więcej informacji, jest to część rodzimej biblioteki gdzie awarie aplikacji (znaleziono przy użyciu 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;
}
Zawieszanie się dzieje w buf[zeros_in_startcode]
w find_start_code
. Usunąłem także kilka linii android_log (nie sądzę, że to ma znaczenie?).
Według mojego zrozumienia, ten bufor powinien być dostępny, nie ma sensu, że ulega awarii tylko "czasami".
PS. tutaj nazywam kod natywny z Javy:
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();
}
}
}
}
}
}
Pamiętaj, że awarie występują nawet wtedy, gdy w ogóle nie wysyłam dźwięku.
Zgaduję, że przekraczasz swoją tablicę. Czy masz pewność, że podajesz odpowiednią liczbę elementów tablicy? Czy 'length' odpowiada liczbie elementów zwróconych przez' GetArrayLength() '? Jaka jest rzeczywista wartość "danych"? Czy możesz zmodyfikować swój kod JNI, aby jakoś rejestrować lub w inny sposób pokazywać te informacje? –
@AndrewHenle wszystko to jest bardzo trudne do wykrycia, ponieważ dzieje się to 30 razy na sekundę, a zdarzenie dzieje się tylko losowo (i może minąć wiele godzin), mogę spróbować wydrukować te informacje dla każdej ramki, aby znaleźć wartości, gdy nastąpi awaria, ale kod jest wykonywany mniej razy (z powodu spowolnienia logowania), a zatem może upłynąć wiele godzin ... W każdym razie dane nigdy nie są mniejsze niż 3 bajty, do których indeks jest dostępny w newralgicznym punkcie. –
* wszystko to jest bardzo trudne do wymyślenia ... * Tak, bardzo trudno jest wykryć problemy z uszkodzeniem pamięci. Najprostszym rozwiązaniem jest prawdopodobnie napisanie prostego sterownika C dla biblioteki JNI i wykonywanie wywołań funkcji 'rtmp_sender_write_video_frame()' z prostego programu C. Następnie możesz łatwo wykonać takie czynności, jak przeniesienie kodu do systemu Linux i uruchamianie na przykład w Valgrind. Zauważ, że nawet zrobienie tego może nie wykryć błędu uszkodzenia pamięci - JNI jest * bardzo * bezlitosny, a uszkodzenie pamięci w dowolnym miejscu może spowodować awarie w pozornie całkowicie niepowiązanym kodzie. –