做者:字節移動技術——陳奕前端
去年 9 月份開始,許多用戶升級到 iOS 14 以後,線上出現不少 ImageIO 相關堆棧的 Crash 問題,並且公司內幾乎全部的 APP 上都有出現,在部分 APP上甚至達到了 Top 3 Crash。算法
得益於 APM 平臺精準數據採集機制和豐富的異常信息現場,咱們經過收集到詳細的 Crash 日誌信息進行分析解決。markdown
從堆棧信息看,是在 ImageIO 解析圖片信息的時候 Crash ,而且最後調用的方法都是看起來都是和 INameSpacePrefixMap
相關,推測 Crash 應該是和這個方法 CGImageSourceCopyPropertiesAtIndex
的實現有關。多線程
機型集中在 iOS14 以上的版本,同時是在後臺出現函數
從 CrashLog 作一個初步分析oop
從堆棧信息看,這段代碼是圖片庫在子線程經過 CGImageSourceCopyPropertiesAtIndex
解析 imageSource
中的圖片相關信息,而後發生了野指針的 Crash。性能
CGImageSourceCopyPropertiesAtIndex
的輸入只有一個 imageSource
,imageSource
由圖片的 data 生成,調用棧並無多線程操做,能夠排除是多線程操做 imageSource
、data 致使的 Crash。測試
看堆棧是在解析 PNG 圖片,經過將下發的圖片格式換成 JPG 格式,發現量級並無下降。推測 Crash 不是某種特定圖片格式引發的。spa
Navigate => Go To File Offset 2555072線程
0000000181b09cc0 ldr x8, [x8, #0x10]
,能夠看到應該是訪問 [x8, #0x10]
指向的內存出錯了far: 0x000021a1ee2fa271
,並且 x8 寄存器已是一個錯誤的值 0x000021a1ee2fa261
0000000181b09cbc ldr x8, [x20]
x8 是存在 x20 指向的內存中(即 x8 = *x20
)
0000000181b09c98 ldr x20, [x21, #0x8]
x20 又存在 [x21, #0x8]
指向的內存中
0000000181b09c8c adrp x21, #0x1da0ed000
,0000000181b09c90 add x21, x21, #0xe10
x21 指向的是一個 data 段,推測 x21 應該是一個全局變量,因此,多是這個全局變量野了,或者是這個全局變量引用的某些內存(x20)野了
ImageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
複製代碼
sDefaultNameSpacePrefixMap
只在AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)
這個函數中調用。可能會在多線程下調用這個函數,而致使這個全局變量的出現 data race 致使了野指針。
__ZZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEbE26sDefaultNameSpacePrefixMap: // AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
00000001da0ede10 dq 0x0000000000000000 ; DATA XREF=__ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+44, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+120, __ZN16AdobeXMPCore_IntL31ManageDefaultNameSpacePrefixMapEb+392
複製代碼
AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)
會在多個方法中調用(而且調用時都加了鎖,不太可能會出現 data race):
AdobeXMPCore_Int::INameSpacePrefixMap_I::CreateDefaultNameSpacePrefixMap()
AdobeXMPCore_Int::INameSpacePrefixMap_I::InsertInDefaultNameSpacePrefixMap(char const*, unsigned long long, char const*, unsigned long long)
AdobeXMPCore_Int::INameSpacePrefixMap_I::DestroyDefaultNameSapcePrefixMap()
sDefaultNameSpacePrefixMap
時 Crash,推測多是用戶手動殺進程後,全局變量在主線程已經被析構,後臺線程還會繼續訪問這個全局變量,從而出現野指針訪問異常。發現 Crash 日誌的主線程堆棧也出現 _exit 的調用,能夠肯定是全局變量析構致使。在用戶手動殺進程後,主線程將這個全局變量析構了,這時候子線程再訪問這個全局變量就出現了野指針。
嘗試在子線程不斷調用 CFDictionaryRef CGImageSourceCopyPropertiesAtIndex(CGImageSourceRef isrc, size_t index, CFDictionaryRef options);
,而且手動殺掉進程觸發這個 crash
能夠證實上述的推理是正確的。
CFDictionaryRef CGImageSourceCopyPropertiesAtIndex(CGImageSourceRef isrc, size_t index, CFDictionaryRef options);
這個方法在解析部分圖片的時候最終會訪問全局變量
ImageIO`AdobeXMPCore_Int::ManageDefaultNameSpacePrefixMap(bool)::sDefaultNameSpacePrefixMap
複製代碼
在用戶手動殺進程後,這個sDefaultNameSpacePrefixMap
被析構,若是這時候在子線程再被訪問就可能出現野指針的問題
由於sDefaultNameSpacePrefixMap
是在系統庫內部的全局變量,沒辦法對其進行修改,只能避免在子線程調用 CGImageSourceCopyPropertiesAtIndex
方法
方法一: CGImageSourceCopyPropertiesAtIndex
是用來獲取圖片的寬高、imageOrientation
、動圖幀等信息,選擇用其餘方法來替換,e.g. 寬高用 CGImageRef
來獲取
方法二:將 CGImageSourceCopyPropertiesAtIndex
被調用的線程收斂起來,調用atexit函數來註冊一個進程結束回調函數,進程結束的時候將終止線程
字節跳動移動平臺團隊(Client Infrastructure)是大前端基礎技術行業領軍者,負責整個字節跳動的中國區大前端基礎設施建設,提高公司全產品線的性能、穩定性和工程效率,支持的產品包括但不限於抖音、今日頭條、西瓜視頻、火山小視頻等,在移動端、Web、Desktop等各終端都有深刻研究。
就是如今!客戶端/前端/服務端/端智能算法/測試開發 面向全球範圍招聘!一塊兒來用技術改變世界,感興趣能夠聯繫郵箱 chenxuwei.cxw@bytedance.com,郵件主題 簡歷-姓名-求職意向-電話。