SIGPIPE(Signal?13,?Code?0)?異常排查及處理
問(wèn)題現(xiàn)象
最近一個(gè)版本 APP
更新之后,sentry
大量異常數(shù)據(jù)上報(bào),影響用戶的數(shù)量非??鋸?10w +
,具體報(bào)錯(cuò)如下
排查過(guò)程
首先查看 SIGPIPE 的報(bào)錯(cuò)原因, 在官網(wǎng)搜索到了相關(guān)信息
大意是 Socket
連接關(guān)閉后,如果客戶端仍在發(fā)送數(shù)據(jù),這個(gè)時(shí)候就會(huì)產(chǎn)生 SIGPIPE
信號(hào),如果信號(hào)沒(méi)有被處理就會(huì)產(chǎn)生崩潰,這里截取了部分關(guān)鍵信息。
文檔上說(shuō)可以使用 signal(SIGPIPE, SIG_IGN)
全局忽略,確認(rèn)客戶端添加了該邏輯,但是異常還是上報(bào)到了 sentry
。signal
這個(gè)函數(shù)是給信號(hào)關(guān)聯(lián)一個(gè) handler
,收到這個(gè)信號(hào)的時(shí)候去執(zhí)行。 SIG_IGN
是系統(tǒng)提供的忽略信號(hào)的處理方式,定義如下:
#define SIG_IGN (void (*)(int))1
嘗試手動(dòng)觸發(fā) SIGPIPE
, 運(yùn)行后可以正常輸出。
void signalHandler(int signal) { printf("bingo"); } int main(int argc, char * argv[]) { signal(SIGPIPE, signalHandler); kill(getpid(), SIGPIPE); }
多次添加 handler
繼續(xù)嘗試, 控制臺(tái)輸出 333
, 也就是說(shuō)只有最后添加的 handler
會(huì)執(zhí)行到,比較容易理解一個(gè)信號(hào)只能關(guān)聯(lián)一個(gè) handler
。
void signalHandler(int signal) { printf("111"); } void signalHandler2(int signal) { printf("222"); } void signalHandler3(int signal) { printf("333"); } int main(int argc, char * argv[]) { signal(SIGPIPE, signalHandler); signal(SIGPIPE, signalHandler2); signal(SIGPIPE, signalHandler3); kill(getpid(), SIGPIPE); }
現(xiàn)狀是 sentry
可以捕獲并處理這個(gè)異常,所以此時(shí)懷疑是 sentry
把客戶端的處理給覆蓋了。
查看 sentry
里面的邏輯,sentry
使用了 sigaction
函數(shù)關(guān)聯(lián) handler
,這個(gè)函數(shù)與 signal
函數(shù)一樣,可以設(shè)置與信號(hào) sig
關(guān)聯(lián)的動(dòng)作,而 oact
如果不是空指針的話,就用它來(lái)保存原先對(duì)該信號(hào)的動(dòng)作的位置,act
則用于設(shè)置指定信號(hào)的動(dòng)作。sentry
關(guān)聯(lián)了自己的處理 handleSignal
并且會(huì)把之前的handler
存儲(chǔ)到數(shù)組 g_previousSignalHandlers
里面。
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); // sentry 關(guān)聯(lián)的 action 為 handleSignal sigaction(fatalSignals[i], &action, &g_previousSignalHandlers[i])
sentry
在 handleSignal
里面上報(bào)異常并且執(zhí)行了了 sentrycrashcm_handleException
,然后使用 raise
重新拋出這個(gè)信號(hào)。
static void handleSignal(int sigNum, siginfo_t *signalInfo, void *userContext) { SentryCrashLOG_DEBUG("Trapped signal %d", sigNum); if (g_isEnabled) { // 這里省略上報(bào)邏輯 sentrycrashcm_handleException(); } SentryCrashLOG_DEBUG("Re-raising signal for regular handlers to catch."); // This is technically not allowed, but it works in OSX and iOS. raise(sigNum); }
查看 handleException
簡(jiǎn)化后的調(diào)用棧:
void sentrycrashcm_handleException(**struct** SentryCrash_MonitorContext *context) { sentrycrashcm_setActiveMonitors(SentryCrashMonitorTypeNone); } void sentrycrashcm_setActiveMonitors(SentryCrashMonitorType monitorTypes) { // isEnabled = false setMonitorEnabled(monitor, isEnabled); } static inline void setMonitorEnabled(Monitor *monitor, bool isEnabled) { uninstallSignalHandler(); } static void uninstallSignalHandler(void) { sigaction(fatalSignals[i], &g_previousSignalHandlers[i], **NULL**); }
可以看到 handleException
這個(gè)函數(shù)最終會(huì)重新關(guān)聯(lián)保存在 g_previousSignalHandlers
里面的 handler
,也就是客戶端設(shè)置的 SIG_IGN
默認(rèn)忽略。sentry 關(guān)聯(lián)的函數(shù) handleSignal
會(huì)在處理完會(huì)重新拋出信號(hào),這個(gè)信號(hào)會(huì)觸發(fā) SIG_IGN
,所以這里并不存在覆蓋關(guān)系,sentry
不會(huì)影響到客戶端默認(rèn)忽略的邏輯。
綜上客戶端設(shè)置的 SIG_IGN
是會(huì)生效的,sentry
只是上報(bào)了異常,并沒(méi)有崩潰產(chǎn)生。在 APP
里面手動(dòng)觸發(fā) SIGPIPE
,Charles
抓包可以看到 sentry
上報(bào),APP
未出現(xiàn)崩潰。
原因與處理
和多個(gè)業(yè)務(wù)方確認(rèn)這個(gè)版本并沒(méi)有 socket
相關(guān)的改動(dòng),那為什么在這個(gè)版本之后突然有大量異常上報(bào)呢?
后面 diff
代碼發(fā)現(xiàn)是改動(dòng)了 sentry
的初始時(shí)機(jī)造成的。之前的邏輯是 sentry
初始化,客戶端調(diào)用 signal
關(guān)聯(lián) SIG_IGN
,這個(gè)時(shí)候 SIG_IGN
覆蓋了 sentry
的 signalHandler
,并且沒(méi)有保存和恢復(fù)之前 handler
的邏輯,sentry
捕獲不到信號(hào)不會(huì)上報(bào),當(dāng)前版本的改動(dòng)使這個(gè)順序顛倒了,導(dǎo)致了大量異常數(shù)據(jù)上報(bào)。后續(xù)嘗試去定位具體的 socket
無(wú)果,重新修改了順序 SIG_IGN
在 sentry
初始化之后關(guān)聯(lián),之后的版本不再有異常數(shù)據(jù)上報(bào)。
以上就是SIGPIPE(Signal 13, Code 0) 異常排查及處理的詳細(xì)內(nèi)容,更多關(guān)于SIGPIPE異常排查的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS開(kāi)發(fā)之TableView實(shí)現(xiàn)完整的分割線詳解
在iOS開(kāi)發(fā)中, tableView是我們最常用的UI控件之一。所以這篇文章主要給大家詳細(xì)介紹了關(guān)于iOS中的TableView分割線,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-12-12iOS App開(kāi)發(fā)中使用及自定義UITableViewCell的教程
這篇文章主要介紹了iOS App開(kāi)發(fā)中使用及自定義UITableViewCell的教程,自定義TableViewCell文中使用Objective-C演示而非ib,需要的朋友可以參考下2016-04-04iOS WKWebView無(wú)法處理URL Scheme和App Store鏈接的問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于iOS WKWebView無(wú)法處理URL Scheme和App Store鏈接的問(wèn)題解決的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03iOS中Label實(shí)現(xiàn)顯示不同顏色與字體的方法
這篇文章主要給大家介紹了關(guān)于在iOS中Label實(shí)現(xiàn)顯示不同顏色與字體的相關(guān)資料,文中分別介紹了利用range或者文字兩種實(shí)現(xiàn)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-11-11iOS應(yīng)用開(kāi)發(fā)中SQLite的初步配置指南
這篇文章主要介紹了iOS應(yīng)用開(kāi)發(fā)中SQLite的初步配置指南,SQLite是一個(gè)極輕量級(jí)可作嵌入式的數(shù)據(jù)庫(kù),非常適合入門開(kāi)發(fā)者使用,需要的朋友可以參考下2015-12-12iOS開(kāi)發(fā)之App主題切換解決方案完整版(Swift版)
這篇文章主要為大家詳細(xì)介紹了iOS開(kāi)發(fā)之App主題切換完整解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02ios開(kāi)發(fā)加載webview顯示進(jìn)度條實(shí)例
本篇文章主要介紹了ios開(kāi)發(fā)加載webview顯示進(jìn)度條實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05