C++程序某動態(tài)庫的日志配置文件被修改問題定位

首先介紹下,整套程序包括fdTestC、fdTestS、Server。
fdTestC主要有三部分組成:主程序fdTestC,調用庫libR.so、libthird.so.

  • 業(yè)務功能關系: fdTestC通過libR.so與fdTestS交互; fdTestC通過libthird.so與Server交互。
  • libthird.so是一個殼,會下載真正的客戶端庫(客戶端庫需要一個日志配置文件,只有第一次初始化會創(chuàng)建,后邊只讀,然后傳到zlog模塊中,里邊有文件鎖),然后和對應的服務端交互。
  • fdTestC和fdTestS其實是一個程序,只是工作模式不同。

問題現(xiàn)象:程序運行過程中,libthird的日志配置文件被修改了(內(nèi)容是fdTest的業(yè)務日志),進而導致所有與libthird相關業(yè)務失敗。

問題定位過程:
從問題現(xiàn)象上分析,有兩個猜測:fdTestC截取了libthird的日志文件句柄,或者是 libthird模塊讀取了fdTestC的buffer。
第一階段:看日志及代碼排查分析。
1、首先排查日志模塊,日志是一個單例(和程序生命周期相同),里邊一把鎖,所有文件操作時上鎖,切換文件時傳過去。不太可能去拿其他句柄;
2、寫入配置文件的日志,在fdTestC的日志文件中可以找到相同內(nèi)容,出現(xiàn)的兩次都是在日志文件中間,因此猜測1不太可能。(事后一段時間復盤,應該是最大懷疑點
3、分析libthird代碼,業(yè)務代碼第一次生成是寫入固定內(nèi)容,文件存在則只打開,沒有寫操作,然后就傳入zlog。zlog作為開源庫,可信度還是比較高的而且,里邊有文件鎖。 猜測2感覺也不現(xiàn)實。

第二階段:hook跟蹤
4、踩內(nèi)存,用asan編譯主程序跑了一把,未抓到有效內(nèi)容(事后分析原因:asan原理是編譯時插入樁代碼,重實現(xiàn)malloc和free,因此可以排除主程序問題);
5、用hook勾住fwrite、write、puts、put,拷機復現(xiàn)后,看未抓取到有效堆棧(事后分析,事故函數(shù)用的printf,因此給漏了);
6、猜測可能是libthird的頻繁初始化導致,重構優(yōu)化fdTestC中關于libthird的初始化方法,一個server只init一次,問題不再出現(xiàn);以為規(guī)避解決。
https://blog.csdn.net/whbing1471/article/details/112394403?spm=1035.2023.3001.6557&utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-2defaultOPENSEARCHRate-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-2defaultOPENSEARCHRate-1.nonecase

然而,fdTestS再次跑出配置文件被修改問題。需要繼續(xù)定位,汗。。。。
第三階段:寫demo自測(事后發(fā)現(xiàn),作用不大)
7、由于時隔很久,問題不太記得了,再次分析了一波代碼,再次分析兩個猜測可行性不大;
8、fdTestS加入測試程序,模擬出問題場景,拷機測試10天未復現(xiàn)。(期間放假)

各種扯皮,有規(guī)避手段,但是實施計劃受限,需要繼續(xù)定位。
第四階段:回退程序,聯(lián)手加日志定位排查
9、回退fdTestC到問題版本,fdTestC的日志中加入fd打印,libthird中配置文件的開關操作也加入打印,拷機復現(xiàn),發(fā)現(xiàn)配置文件被修改的時候,libthird中配置文件被打開的fd是1. 沒錯,是標準輸出;
10、從而理清了事故發(fā)生現(xiàn)場原因,fdTestC日志模塊會寫日志文件,同時寫控制臺,后臺模式運行時,會寫入fd 1(指向/dev/null)。如果fd 1被修改指向配置文件,那么配置文件就被修改了。再次運行程序,拷機觀察fd,句柄1確實經(jīng)常被改動。離問題真相貌似不遠了。(事后一段時間復盤,應該可以中止了,1就是printf,當時意識到
11、再次,hook勾住fclose和close,拷機復現(xiàn)。(期間因為LD_PRELOAD加的位置不對,導致不生效,定位了一段時間《hook對第三方庫也是生效的》)。復現(xiàn)后,分析堆棧是在libthird的epoll中,想不通。

第五階段:懷疑踩內(nèi)存
12、主程序和所有庫(libthird的庫未編譯)都用asan編譯,拷機復現(xiàn)踩內(nèi)存問題,未復現(xiàn);(事后一段時間復盤,不需要
13、結合堆棧分析存儲日志和代碼,懷疑是存儲庫代碼導致。
原因:存儲庫機制,事件線程專門處理各種事件,然后標記fd;有專門的檢測線程專門去清理異常fd及相關資源。存儲庫destroy、或者存儲庫socket檢測到服務端異常時,會去標記fd,這時檢測線程把fd及資源給關了,但是事件線程不知道,還有處理,導致異常。
14、修改為:異常時,先處理完,再標記,驗證ok;
15、問題解決。

問題回顧:
1、問題出現(xiàn)后,應該把被修改文件的地方都加上fd打印,然后拷機復現(xiàn)(直接掛hook可能抓不全,printf這種想不到);
2、asan踩內(nèi)存應該所有相關模塊都編譯,然后拷機運行檢測;
3、hook會對程序和所有庫都生效,不過位置要加對。
4、__thread是GCC內(nèi)置的線程局部存儲設施。_thread變量每一個線程有一份獨立實體,各個線程的值互不干擾??梢杂脕硇揎椖切в腥中郧抑悼赡茏儯怯植恢档糜萌肿兞勘Wo的變量。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容