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调用栈部分有删减。