gstreamer中由gstbuffer unref引起的crash分析

在調試字幕插件的時候,碰到了一個問題: 在切換文本字幕和圖片字幕的過程中,會概率性的發生crash,串口顯示signal 11,通過分析生成的coredump文件,得到了如下信息:

(敏感信息都以×代替,不影響分析問題)

(gdb) where
#0  0xf56317d0 in magazine_chain_pop_head () from /××/symbols/system/vendor/mmlib/libglib-2.0.so
#1  0xf56319c4 in magazine_chain_prepare_fields () from / ××//symbols/system/vendor/mmlib/libglib-2.0.so
#2  0xf5631c2c in magazine_cache_push_magazine () from / ××/symbols/system/vendor/mmlib/libglib-2.0.so
#3  0xf5632258 in thread_memory_magazine2_unload () from /××/symbols/system/vendor/mmlib/libglib-2.0.so
#4  0xf563275c in g_slice_free1 () from /××/symbols/system/vendor/mmlib/libglib-2.0.so
#5  0xf4f1b5bc in _gst_buffer_free (buffer=0xac2a4cb8) at gstbuffer.c:756
#6  0xf4f7205c in gst_mini_object_unref (mini_object=0xac2a4cb8) at gstminiobject.c:454
#7  0xdbadafc0 in gst_buffer_unref (buf=0xac2a4cb8)  at /××/gst-out/usr/local/include/gstreamer-1.0/gst/gstbuffer.h:384
#8  0xdbadb1c4 in _gst_video_codec_frame_free (frame=0xac724c98) at gstvideoutils.c:43
#9  0xdbadb518 in gst_video_codec_frame_unref (frame=0xac724c98) at gstvideoutils.c:133
#10 0xdbacbfc8 in gst_video_decoder_release_frame (dec=0xab5461a8, frame=0xac724c98) at gstvideodecoder.c:2819
#11 0xdbaccc74 in gst_video_decoder_finish_frame (decoder=0xab5461a8, frame=0xac724c98) at ×.c:3041
#12 0xda3f83d6 in *function (self=0xab5461a8) at /××c/×.c:1753
#13 0xf4fcf814 in gst_task_func (task=0xab564e10) at gsttask.c:334
#14 0xf4fd0efc in default_func (tdata=0xab4347c8, pool=0xab101c58) at gsttaskpool.c:68
#15 0xf5645090 in g_thread_pool_thread_proxy () from /××/symbols/system/vendor/mmlib/libglib-2.0.so
#16 0xf56447e4 in g_thread_proxy () from /××/symbols/system/vendor/mmlib/libglib-2.0.so
#17 0xf6b905fc in __pthread_start (arg=0xd7119930, arg@entry=<error reading variable: value has been optimized out>) at bionic/libc/bionic/pthread_create.cpp:199
#18 0xf6b68236 in __start_thread (fn=<optimized out>, arg=<optimized out>) at bionic/libc/bionic/clone.cpp:41
#19 0x00000000 in ?? ()
(gdb) 

對應的log文件也明確的顯示,crash的確是發生在上面trace所顯示的地方。

通過上面的信息,基本可以判斷是釋放gstbuffer的時候,內存操作時發生了空指針錯誤,但是在trace顯示的代碼裏仔細檢查了很久,確定這部分沒有使用上的錯誤。

關鍵是測試中,問題發生沒有明顯的規律,所以很是撓頭。

在該部分功能初步調試時,曾發生了一件比較奇怪的事情,就是我們緩存起來的gstbuffer有時候從緩存中去出來的時候,發現其pst發生了變化。由於該gstbuffer的指針會被傳到後面的很多插件中,所以很難確定是誰對此做了修改,當時沒有深究,只是從緩存中取出來的時候,做了一下判斷,並採取了一些措施,使其不會影響正常的功能。

就是問題陷入僵局的時候,又發現了一個問題:有時候從queue中取出的audio數據會莫名其妙的變成了video數據,確定在queue中不會有人去故意touch數據,所以我們開始懷疑是該部分內存數據被覆蓋了,也就是說,有人錯誤的提前釋放了這塊內存,被系統衝行進行了分配。

結合前面的第二個問題,進一步印證了我們的想法;

再回到crash的問題,應該也是應該被操作釋放的內存,已經被人錯誤的釋放過了,所以纔會導致空指針的錯誤。

但是這些問題的線索裏,涉及到了字幕,音頻和視頻數據,所以從不同的插件中進行詳細的排查。

最後的結果與分析的相符,有一個地方,會對圖片字幕的gstbuffer進行延後釋放,但是該buffer會被即時的push到後面的插件中,並在使用完畢後進行unref; 問題就是在延後釋放的時候也會進行unref,這個unref非常危險,因爲由於延後的時間不確定,這個時候unref的buffer可能已經被系統重新分配了(甚至好多次了),不再是當時的buffer了。

這個分析很好的解釋了上面出現的幾個問題,並且修改後問題都的到了解決。

由於在gstreamer中,buffer會經過很多了插件,所以這種問題犯錯的地方跟出錯的地方可能相隔十萬八千里,通過coredump的trace很難直接定位問題,需要仔細分析整個流程中buffer的處理過程,確保不會多或者少unref任何一個buffer。

知道原因後回推總是比較簡單,正向分析中,有幾個點需要注意:

1)如果buffer會被通過指針傳向多個插件,需要特別注意ref和unref的計算。

2)如果自己buffer出現了比較奇怪的修改,首先確定是否有插件會做類似的修改,如果沒有,就要考慮該buffer可能已經被系統回收再分配了,也就是說,有人偷偷的給你釋放過了,這時候雖然你的buffer指針不爲空,但是裏面的數據已經發生了變化。

3)由於gstreamer中的內存管理採用了glib的slice機制,最後釋放的內存會最可能被再次分配,甚至連尺寸都不變,只是裏面的數據發生了變化,一旦被錯誤的釋放,很容易在短時間內被再次分配,你手裏拿的指針不爲空,也就無法通過NULL Pointer check的方式檢查出來,但是,這時候對其進行操作,後果很難預料。
4)另外,調試時有個比較笨的辦法,在ref和unref的地方,不妨將buffer的指針打印出來,這樣就可以跟蹤這塊內存被分配和釋放的過程,有利於分析問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章