Android上不可思议的MetaData::findInt32相关crash

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012188065/article/details/84978519

Android上经常可以碰到一些不可思议的crash,初看没有什么思路,如果从持锁竞争角度来看,两个线程同时操作同一个变量,便会有不可思议的crash
如:

/system/lib/libc.so (abort+63)
    /system/lib/liblog.so (__android_log_assert+154)
    /system/lib/libstagefright_foundation.so (android::MetaData::findInt32(unsigned int, int*)+144)
    /system/lib/libstagefright.so (android::convertMetaDataToMessage(android::sp<android::MetaData> const&, android::sp<android::AMessage>*)+582)
    /system/lib/libmediaplayerservice.so (android::NuPlayer::HTTPLiveSource::getFormat(bool)+54)
    /system/lib/libmediaplayerservice.so (android::NuPlayer::onMessageReceived(android::sp<android::AMessage> const&)+3240)
    /system/lib/libstagefright_foundation.so (android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+24)
    /system/lib/libstagefright_foundation.so (android::AMessage::deliver()+60)
    /system/lib/libstagefright_foundation.so (android::ALooper::loop()+470)
    /system/lib/libutils.so (android::Thread::_threadLoop(void*)+270)
    /system/lib/libc.so (__pthread_start(void*)+22)
    /system/lib/libc.so (__start_thread+32)

初看 MetaData的findInt32方法是android很基础的一个方法,不可能会出现crash啊
先使用stack反汇编看一下crash的位置

Stack Trace:
  RELADDR   FUNCTION                                                                                                       FILE:LINE
  v------>  inline_tgkill(int, int, int)                                                                                   bionic/libc/bionic/abort.cpp:?
  0001aa0c  abort+64                                                                                                       bionic/libc/bionic/abort.cpp:68
  0000665f  __android_log_assert+154                                                                                       system/core/liblog/logger_write.c:533
  00019745  android::MetaData::findInt32(unsigned int, int*)+144                                                           frameworks/av/media/libstagefright/foundation/MetaData.cpp:117
  000fde93  android::convertMetaDataToMessage(android::sp<android::MetaData> const&, android::sp<android::AMessage>*)+582  frameworks/av/media/libstagefright/Utils.cpp:796
  0007104f  android::NuPlayer::HTTPLiveSource::getFormat(bool)+54                                                          frameworks/av/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp:176
  000517d9  android::NuPlayer::onMessageReceived(android::sp<android::AMessage> const&)+3240                               frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp:832
  0000fdc9  android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+24                                    frameworks/av/media/libstagefright/foundation/AHandler.cpp:27
  000122bd  android::AMessage::deliver()+60                                                                                frameworks/av/media/libstagefright/foundation/AMessage.cpp:401
  00010a2f  android::ALooper::loop()+470                                                                                   frameworks/av/media/libstagefright/foundation/ALooper.cpp:220
  0000d343  android::Thread::_threadLoop(void*)+270                                                                        system/core/libutils/Threads.cpp:747
  000482e3  __pthread_start(void*)+22                                                                                      bionic/libc/bionic/pthread_create.cpp:226
  0001b5e7  __start_thread+32                                                                                              bionic/libc/bionic/clone.cpp:47

可以看出,crash的位置发生在 convertMetaDataToMessage方法中的
meta->findInt32(kKeyMaxInputSize, &maxInputSize) 语句上,
根据MetaData类中方法分析,一定是别的地方设置了此meta的 KeyMaxInputSize。

因为在setInt32中,会有clear操作,会把原有设置的value清除掉,再写入新的value,两个线程竞争,
导致findInt32函数中, 在校验时,发现kKeyMaxInputSize此key有值,在真正取值时,发现为Null,引发crash。

搜索 setInt32(KeyMaxInputSize 关键字,如下:

libstagefright/AudioSource.cpp:203:    meta->setInt32(kKeyMaxInputSize, mMaxBufferSize);
libstagefright/ItemTable.cpp:1421:    meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
libstagefright/ItemTable.cpp:1451:        meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
libstagefright/MPEG4Extractor.cpp:1656:                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
libstagefright/MPEG4Extractor.cpp:1690:                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size);
libstagefright/JPEGSource.cpp:105:    meta->setInt32(kKeyMaxInputSize, mSize);
libstagefright/WAVExtractor.cpp:369:    mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize);
libstagefright/matroska/MatroskaExtractor.cpp:1081:    meta->setInt32(kKeyMaxInputSize, maxInputSize);
libstagefright/AVIExtractor.cpp:915:        track->mMeta->setInt32(kKeyMaxInputSize, track->mMaxSampleSize);
libstagefright/httplive/LiveSession.cpp:480:        (*meta)->setInt32(kKeyMaxInputSize, 32 * 1024);
libstagefright/Utils.cpp:1459:        meta->setInt32(kKeyMaxInputSize, maxInputSize);

由于crash发生在 HTTPLiveSource 中, 此句嫌疑最大:

libstagefright/httplive/LiveSession.cpp:480:        (*meta)->setInt32(kKeyMaxInputSize, 32 * 1024);

查看之,
在这里插入图片描述
恩,getStreamFormatMeta函数会去设置,而android::NuPlayer::HTTPLiveSource::getFormat(bool)函数中,
调用getStreamFormatMeta完成后,采取调用convertMetaDataToMessage,所以不会发生冲突,如下:
在这里插入图片描述

一定是别的地方也调用了
getStreamFormatMeta函数,搜索之,发现HTTPLiveSource中还有另外一方法会调用此函数,如下:
在这里插入图片描述

至此很明白,NuPlayer中一个线程调用了getFormat的同时,别的线程又调用了getStreamFormatMeta函数,
导致两个线程同时操作meta变量, 一个set, 一个find,就会出问题。
那就加锁解决吧。

感想: 如果遇到类似不可思议的crash,纯代码角度来看,怎么都不可能发生crash的情况,
可以尝试从多线程并发角度来看待问题,便会发现此类不可能变为了可能, 多线程可以将各种不可能转换成可能,android源码中的这类crash还挺多,大家快去试一下吧。

注:crash调用栈部分有删减。

猜你喜欢

转载自blog.csdn.net/u012188065/article/details/84978519