0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

高并發(fā)性能調(diào)試一些經(jīng)驗(yàn)和工具分享

C語言專家集中營 ? 來源:未知 ? 作者:李倩 ? 2018-06-04 17:06 ? 次閱讀

引文

4月份的時(shí)候看到一道面試題,據(jù)說是騰訊校招面試官提的:在多線程和高并發(fā)環(huán)境下,如果有一個(gè)平均運(yùn)行一百萬次才出現(xiàn)一次的bug,你如何調(diào)試這個(gè)bug?知乎原貼地址如下:騰訊實(shí)習(xí)生面試,這兩道題目該怎么回答? - 編程 .遺憾的是知乎很多答案在抨擊這道題本身的正確性,雖然我不是這次的面試官,但我認(rèn)為這是一道非常好的面試題。當(dāng)然,只是道加分題,答不上,不扣分。答得不錯(cuò),說明解決問題的思路和能力要超過應(yīng)屆生平均水平。 之所以寫上面這段,是因?yàn)槲矣X得大部分后臺(tái)服務(wù)端開發(fā)都有可能遇到這樣的BUG,即使沒有遇到,這樣的題目也能夠激發(fā)大家不斷思考和總結(jié)。非常湊巧的是,我在4月份也遇到了一個(gè)類似的而且要更加嚴(yán)重的BUG,這是我自己挖的一個(gè)很深的坑,不填好,整個(gè)項(xiàng)目就無法上線。 現(xiàn)在已經(jīng)過去了一個(gè)多月,趁著有時(shí)間,自己好好總結(jié)一下,希望里面提到的一些經(jīng)驗(yàn)和工具能夠帶給大家一點(diǎn)幫助。

項(xiàng)目背景

我們針對(duì)nginx事件框架和openssl協(xié)議棧進(jìn)行了一些深度改造,以提升nginx的HTTPS完全握手計(jì)算性能。 由于原生nginx使用本地CPURSA計(jì)算,ECDHE_RSA算法的單核處理能力只有400 qps左右。前期測(cè)試時(shí)的并發(fā)性能很低,就算開了24核,性能也無法超過1萬。 核心功能在去年底就完成了開發(fā),線下測(cè)試也沒有發(fā)現(xiàn)問題。經(jīng)過優(yōu)化后的性能提升幾倍,為了測(cè)試最大性能,使用了很多客戶端并發(fā)測(cè)試https性能。很快就遇到了一些問題:

第一個(gè)問題是nginx有極低概率(億分之一)在不同地方 core dump。白天線下壓力測(cè)試2W qps一般都要兩三個(gè)小時(shí)才出一次core。每次晚上睡覺之前都會(huì)將最新的調(diào)試代碼編譯好并啟動(dòng)測(cè)試,到早上醒來第一眼就會(huì)去查看機(jī)器并祈禱不要出core,不幸的是,一般都會(huì)有幾個(gè)到幾十個(gè)core,并且會(huì)發(fā)現(xiàn)經(jīng)常是在一個(gè)時(shí)間點(diǎn)集中core dump。線上灰度測(cè)試運(yùn)行了6天,在第6天的早上才集中core dump了幾十次。這樣算來,這個(gè)core dump的概率至少是億分之一了。 不過和面試題目中多線程不同的是,nginx采用的是多進(jìn)程+全異步事件驅(qū)動(dòng)的編程模式(目前也支持了多線程,但只是針對(duì)IO的優(yōu)化,核心機(jī)制還是多進(jìn)程加異步)。在webserver的實(shí)現(xiàn)背景下,多進(jìn)程異步相比多線程的優(yōu)點(diǎn)是性能高,沒有太多線程間的切換,而且內(nèi)存空間獨(dú)立,省去線程間鎖的競(jìng)爭(zhēng)。當(dāng)然也有缺點(diǎn),就是異步模式編程非常復(fù)雜,將一些邏輯上連續(xù)的事件從空間和時(shí)間切割,不符合人的正常思考習(xí)慣,出了問題后比較難追查。另外異步事件對(duì)網(wǎng)絡(luò)操作系統(tǒng)的底層知識(shí)要求較高,稍不小心就容易挖坑。

第二個(gè)問題是高并發(fā)時(shí)nginx存在內(nèi)存泄漏。在流量低的時(shí)候沒有問題,加大測(cè)試流量就會(huì)出現(xiàn)內(nèi)存泄漏。

第三個(gè)問題,因?yàn)槲覀儗?duì)nginx和openssl的關(guān)鍵代碼都做了一些改造,希望提升它的性能。那么如何找到性能熱點(diǎn)和瓶頸并持續(xù)優(yōu)化呢?

其中第一和第二個(gè)問題的背景都是,只有并發(fā)上萬qps以上時(shí)才有可能出現(xiàn),幾百或者一兩千QPS時(shí),程序沒有任何問題。

core dump的調(diào)試

首先說一下core的解決思路,主要是如下幾點(diǎn):

gdb及debug log定位,發(fā)現(xiàn)作用不大。

如何重現(xiàn)bug?

構(gòu)造高并發(fā)壓力測(cè)試系統(tǒng)。

構(gòu)造穩(wěn)定的異常請(qǐng)求。

gdb及debug log效率太低

因?yàn)橛衏ore dump ,所以這個(gè)問題初看很容易定位。gdb 找到core dump點(diǎn),btrace就能知道基本的原因和上下文了。 core的直接原因非常簡(jiǎn)單和常見,全部都是NULL指針引用導(dǎo)致的。不過從函數(shù)上下文想不通為什么會(huì)出現(xiàn)NULL值,因?yàn)檫@些指針在原生nginx的事件和模塊中都是這么使用的,不應(yīng)該在這些地方變成NULL。由于暫時(shí)找不到根本原因,還是先解決CORE dump吧,修復(fù)辦法也非常簡(jiǎn)單,直接判斷指針是否NULL,如果是NULL就直接返回,不引用不就完事了,這個(gè)地方以后肯定不會(huì)出CORE了。

這樣的防守式編程并不提倡,指針NULL引用如果不core dump,而是直接返回,那么這個(gè)錯(cuò)誤很有可能會(huì)影響用戶的訪問,同時(shí)這樣的BUG還不知道什么時(shí)候能暴露。所以CORE DUMP 在NULL處,其實(shí)是非常負(fù)責(zé)任和有效的做法。

在NULL處返回,確實(shí)避免了在這個(gè)地方的CORE,但是過幾個(gè)小時(shí)又core 在了另外一個(gè)NULL指針引用上。于是我又繼續(xù)加個(gè)判斷并避免NULL指針的引用。悲劇的是,過了幾個(gè)小時(shí),又CORE在了其他地方,就這樣過了幾天,我一直在想為什么會(huì)出現(xiàn)一些指針為NULL的情況?為什么會(huì)CORE在不同地方?為什么我用瀏覽器和curl這樣的命令工具訪問卻沒有任何問題?

熟悉nginx代碼的同學(xué)應(yīng)該很清楚,nginx極少在函數(shù)入口及其他地方判斷指針是否為NULL值。特別是一些關(guān)鍵數(shù)據(jù)結(jié)構(gòu),比如‘ngx_connection_t’及SSL_CTX等,在請(qǐng)求接收的時(shí)候就完成了初始化,所以不可能在后續(xù)正常處理過程中出現(xiàn)NULL的情況。

于是我更加迷惑,顯然NULL值導(dǎo)致出CORE只是表象,真正的問題是,這些關(guān)鍵指針為什么會(huì)被賦值成NULL? 這個(gè)時(shí)候異步事件編程的缺點(diǎn)和復(fù)雜性就暴露了,好好的一個(gè)客戶端的請(qǐng)求,從邏輯上應(yīng)該是連續(xù)的,但是被讀寫及時(shí)間事件拆成了多個(gè)片斷。雖然GDB能準(zhǔn)確地記錄core dump時(shí)的函數(shù)調(diào)用棧,但是卻無法準(zhǔn)確記錄一條請(qǐng)求完整的事件處理?xiàng)?。根本就不知道上次是哪個(gè)事件的哪些函數(shù)將這個(gè)指針賦值為NULL的,甚至都不知道這些數(shù)據(jù)結(jié)構(gòu)上次被哪個(gè)事件使用了。

舉個(gè)例子:客戶端發(fā)送一個(gè)正常的get請(qǐng)求,由于網(wǎng)絡(luò)或者客戶端行為,需要發(fā)送兩次才完成。服務(wù)端第一次read沒有讀取完全部數(shù)據(jù),這次讀事件中調(diào)用了 A,B函數(shù),然后事件返回。第二次數(shù)據(jù)來臨時(shí),再次觸發(fā)read事件,調(diào)用了A,C函數(shù)。并且core dump在了C函數(shù)中。這個(gè)時(shí)候,btrace的stack frame已經(jīng)沒有B函數(shù)調(diào)用的信息了。

所以通過GDB無法準(zhǔn)確定位 core 的真正原因log debug的新嘗試 這時(shí)候強(qiáng)大的GDB已經(jīng)派不上用場(chǎng)了。怎么辦?打印nginx調(diào)試日志。 但是打印日志也很郁悶,只要將nginx的日志級(jí)別調(diào)整到DEBUG,CORE就無法重現(xiàn)。為什么?因?yàn)镈EBUG的日志信息量非常大,頻繁地寫磁盤嚴(yán)重影響了NGINX的性能,打開DEBUG后性能由幾十萬直線下降到幾百qps。 調(diào)整到其他級(jí)別比如 INFO,性能雖然好了,但是日志信息量太少,沒有幫助。盡管如此,日志卻是個(gè)很好的工具,于是又嘗試過以下辦法:

針對(duì)特定客戶端IP開啟debug日志,比如IP是10.1.1.1就打印DEBUG,其他IP就打印最高級(jí)別的日志,nginx本身就支持這樣的配置。

關(guān)閉DEBUG日志,自己在一些關(guān)鍵路徑添加高級(jí)別的調(diào)試日志,將調(diào)試信息通過EMERG級(jí)別打印出來。

nginx只開啟一個(gè)進(jìn)程和少量的connection數(shù)。抽樣打印連接編號(hào)(比如尾號(hào)是1)的調(diào)試日志。

總體思路依然是在不明顯降低性能的前提下打印盡量詳細(xì)的調(diào)試日志,遺憾的是,上述辦法還是不能幫助問題定位,當(dāng)然了,在不斷的日志調(diào)試中,對(duì)代碼和邏輯越來越熟悉。

bug如何重現(xiàn)?

這時(shí)候的調(diào)試效率已經(jīng)很低了,幾萬QPS連續(xù)壓力測(cè)試,幾個(gè)小時(shí)才出一次CORE,然后修改代碼,添加調(diào)試日志。幾天過去了,毫無進(jìn)展。所以必須要在線下構(gòu)造出穩(wěn)定的core dump環(huán)境,這樣才能加快debug效率。 雖然還沒有發(fā)現(xiàn)根本原因,但是發(fā)現(xiàn)了一個(gè)很可疑的地方: 出CORE比較集中,經(jīng)常是在凌晨4,5點(diǎn),早上7,8點(diǎn)的時(shí)候 dump幾十個(gè)CORE。

弱網(wǎng)絡(luò)環(huán)境的構(gòu)造 traffic control

聯(lián)想到夜間有很多的網(wǎng)絡(luò)硬件調(diào)整及故障,我猜測(cè)這些core dump可能跟網(wǎng)絡(luò)質(zhì)量相關(guān)。**特別是網(wǎng)絡(luò)瞬時(shí)不穩(wěn)定,很容易觸發(fā)BUG導(dǎo)致大量的CORE DUMP。** 最開始我考慮過使用TC(traffic control)工具來構(gòu)造弱網(wǎng)絡(luò)環(huán)境,但是轉(zhuǎn)念一想,弱網(wǎng)絡(luò)環(huán)境導(dǎo)致的結(jié)果是什么?顯然是網(wǎng)絡(luò)請(qǐng)求的各種異常啊,所以還不如直接構(gòu)造各種異常請(qǐng)求來復(fù)現(xiàn)問題。于是準(zhǔn)備構(gòu)造測(cè)試工具和環(huán)境,需要滿足兩個(gè)條件:

并發(fā)性能強(qiáng),能夠同時(shí)發(fā)送數(shù)萬甚至數(shù)十萬級(jí)以上qps。

請(qǐng)求需要一定概率的異常。特別是TCP握手及SSL握手階段,需要異常中止。

traffic control是一個(gè)很好的構(gòu)造弱網(wǎng)絡(luò)環(huán)境的工具,我之前用過測(cè)試SPDY協(xié)議性能。能夠控制網(wǎng)絡(luò)速率、丟包率、延時(shí)等網(wǎng)絡(luò)環(huán)境,作為iproute工具集中的一個(gè)工具,由linux系統(tǒng)自帶。但比較麻煩的是TC的配置規(guī)則很復(fù)雜,facebook在tc的基礎(chǔ)上封裝成了一個(gè)開源工具apc,有興趣的可以試試。

WRK壓力測(cè)試工具

由于高并發(fā)流量時(shí)才可能出core,所以首先就需要找一個(gè)性能強(qiáng)大的壓測(cè)工具。 WRK是一款非常優(yōu)秀的開源HTTP壓力測(cè)試工具,采用多線程 + 異步事件驅(qū)動(dòng)的框架,其中事件機(jī)制使用了redis的ae事件框架,協(xié)議解析使用了nginx的相關(guān)代碼。 相比ab(apache bench)等傳統(tǒng)壓力測(cè)試工具的優(yōu)點(diǎn)就是性能好,基本上單臺(tái)機(jī)器發(fā)送幾百萬pqs,打滿網(wǎng)卡都沒有問題。 wrk的缺點(diǎn)就是只支持HTTP類協(xié)議,不支持其他協(xié)議類測(cè)試,比如protobuf,另外數(shù)據(jù)顯示也不是很方便。

nginx的測(cè)試用法: wrk -t500 -c2000 -d30s https://127.0.0.1:8443/index.html

分布式自動(dòng)測(cè)試系統(tǒng)的構(gòu)建

由于是HTTPS請(qǐng)求,使用ECDHE_RSA密鑰交換算法時(shí),客戶端的計(jì)算消耗也比較大,單機(jī)也就10000多qps。也就是說如果server的性能有3W qps,那么一臺(tái)客戶端是無法發(fā)送這么大的壓力的,所以需要構(gòu)建一個(gè)多機(jī)的分布式測(cè)試系統(tǒng),即通過中控機(jī)同時(shí)控制多臺(tái)測(cè)試機(jī)客戶端啟動(dòng)和停止測(cè)試。 之前也提到了,調(diào)試效率太低,整個(gè)測(cè)試過程需要能夠自動(dòng)化運(yùn)行,比如晚上睡覺前,可以控制多臺(tái)機(jī)器在不同的協(xié)議,不同的端口,不同的cipher suite運(yùn)行整個(gè)晚上。白天因?yàn)橐恢痹诙⒅?,運(yùn)行幾分鐘就需要查看結(jié)果。 這個(gè)系統(tǒng)有如下功能: 1. 并發(fā)控制多臺(tái)測(cè)試客戶端的啟停,最后匯總輸出總的測(cè)試結(jié)果。 2. 支持https,http協(xié)議測(cè)試,支持webserver及revers proxy性能測(cè)試。 3. 支持配置不同的測(cè)試時(shí)間、端口、URL。 4. 根據(jù)端口選擇不同的SSL協(xié)議版本,不同的cipher suite。 5. 根據(jù)URL選擇webserver、revers proxy模式。

異常測(cè)試請(qǐng)求的構(gòu)造

壓力測(cè)試工具和系統(tǒng)都準(zhǔn)備好了,還是不能準(zhǔn)確復(fù)現(xiàn)core dump的環(huán)境。接下來還要完成異常請(qǐng)求的構(gòu)造。構(gòu)造哪些異常請(qǐng)求呢? 由于新增的功能代碼主要是和SSL握手相關(guān),這個(gè)過程是緊接著TCP握手發(fā)生的,所以異常也主要發(fā)生在這個(gè)階段。于是我考慮構(gòu)造了如下三種異常情形:

異常的tcp連接。即在客戶端tcp connent系統(tǒng)調(diào)用時(shí),10%概率直接close這個(gè)socket。

異常的ssl連接??紤]兩種情況,full handshake第一階段時(shí),即發(fā)送 client hello時(shí),客戶端10%概率直接close連接。full handshake第二階段時(shí),即發(fā)送clientKeyExchange時(shí),客戶端10%概率直接直接關(guān)閉TCP連接。

異常的HTTPS請(qǐng)求,客戶端10%的請(qǐng)求使用錯(cuò)誤的公鑰加密數(shù)據(jù),這樣nginx解密時(shí)肯定會(huì)失敗。

core bug fix小結(jié)

構(gòu)造好了上述高并發(fā)壓力異常測(cè)試系統(tǒng),果然,幾秒鐘之內(nèi)必然出CORE。有了穩(wěn)定的測(cè)試環(huán)境,那bug fix的效率自然就會(huì)快很多。 雖然此時(shí)通過gdb還是不方便定位根本原因,但是測(cè)試請(qǐng)求已經(jīng)滿足了觸發(fā)CORE的條件,打開debug調(diào)試日志也能觸發(fā)core dump。于是可以不斷地修改代碼,不斷地GDB調(diào)試,不斷地增加日志,一步步地追蹤根源,一步步地接近真相。 最終通過不斷地重復(fù)上述步驟找到了core dump的根本原因。其實(shí)在寫總結(jié)文檔的時(shí)候,core dump的根本原因是什么已經(jīng)不太重要,最重要的還是解決問題的思路和過程,這才是值得分享和總結(jié)的。很多情況下,千辛萬苦排查出來的,其實(shí)是一個(gè)非常明顯甚至愚蠢的錯(cuò)誤。 比如這次core dump的主要原因是: 由于沒有正確地設(shè)置non-reusable,并發(fā)量太大時(shí),用于異步代理計(jì)算的connection結(jié)構(gòu)體被nginx回收并進(jìn)行了初始化,從而導(dǎo)致不同的事件中出現(xiàn)NULL指針并出CORE。

內(nèi)存泄漏

雖然解決了core dump,但是另外一個(gè)問題又浮出了水面,就是**高并發(fā)測(cè)試時(shí),會(huì)出現(xiàn)內(nèi)存泄漏,大概一個(gè)小時(shí)500M的樣子**。

valgrind的缺點(diǎn)

出現(xiàn)內(nèi)存泄漏或者內(nèi)存問題,大家第一時(shí)間都會(huì)想到valgrindvalgrind是一款非常優(yōu)秀的軟件,不需要重新編譯程序就能夠直接測(cè)試。功能也非常強(qiáng)大,能夠檢測(cè)常見的內(nèi)存錯(cuò)誤包括內(nèi)存初始化、越界訪問、內(nèi)存溢出、free錯(cuò)誤等都能夠檢測(cè)出來。推薦大家使用。valgrind 運(yùn)行的基本原理是: 待測(cè)程序運(yùn)行在valgrind提供的模擬CPU上,valgrind會(huì)紀(jì)錄內(nèi)存訪問及計(jì)算值,最后進(jìn)行比較和錯(cuò)誤輸出我通過valgrind測(cè)試nginx也發(fā)現(xiàn)了一些內(nèi)存方面的錯(cuò)誤,簡(jiǎn)單分享下valgrind測(cè)試nginx的經(jīng)驗(yàn):

nginx通常都是使用master fork子進(jìn)程的方式運(yùn)行,使用–trace-children=yes來追蹤子進(jìn)程的信息

測(cè)試nginx + openssl時(shí),在使用rand函數(shù)的地方會(huì)提示很多內(nèi)存錯(cuò)誤。比如Conditional jump or move depends on uninitialised value,Uninitialised value was created by a heap allocation等。這是由于rand數(shù)據(jù)需要一些熵,未初始化是正常的。如果需要去掉valgrind提示錯(cuò)誤,編譯時(shí)需要加一個(gè)選項(xiàng):-DPURIFY

如果nginx進(jìn)程較多,比如超過4個(gè)時(shí),會(huì)導(dǎo)致valgrind的錯(cuò)誤日志打印混亂,盡量減小nginx工作進(jìn)程,保持為1個(gè)。因?yàn)橐话愕膬?nèi)存錯(cuò)誤其實(shí)和進(jìn)程數(shù)目都是沒有關(guān)系的。

上面說了valgrind的功能和使用經(jīng)驗(yàn),但是valgrind也有一個(gè)非常大的缺點(diǎn),就是它會(huì)顯著降低程序的性能,官方文檔說使用memcheck工具時(shí),降低10-50倍也就是說,如果nginx完全握手性能是20000 qps,那么使用valgrind測(cè)試,性能就只有400 qps左右。對(duì)于一般的內(nèi)存問題,降低性能沒啥影響,但是我這次的內(nèi)存泄漏是在大壓力測(cè)試時(shí)才可能遇到的,如果性能降低這么明顯,內(nèi)存泄漏的錯(cuò)誤根本檢測(cè)不出來。只能再考慮其他辦法了。

AddressSanitizer的優(yōu)點(diǎn)

address sanitizer(簡(jiǎn)稱asan)是一個(gè)用來檢測(cè)c/c++程序的快速內(nèi)存檢測(cè)工具。相比valgrind的優(yōu)點(diǎn)就是速度快,官方文檔介紹對(duì)程序性能的降低只有2倍。 對(duì)Asan原理有興趣的同學(xué)可以參考asan的算法這篇文章,它的實(shí)現(xiàn)原理就是在程序代碼中插入一些自定義代碼,如下:

編譯前: *address = ...; // or: ... = *address; 編譯后: if (IsPoisoned(address)) { ReportError(address, kAccessSize, kIsWrite); } *address = ...; // or: ... = *address;`

和valgrind明顯不同的是,asan需要添加編譯開關(guān)重新編譯程序,好在不需要自己修改代碼。而valgrind不需要編程程序就能直接運(yùn)行。 address sanitizer集成在了clang編譯器中,GCC 4.8版本以上才支持。我們線上程序默認(rèn)都是使用gcc4.3編譯,于是我測(cè)試時(shí)直接使用clang重新編譯nginx:

--with-cc="clang" --with-cc-opt="-g -fPIC -fsanitize=address -fno-omit-frame-pointer" 其中with-cc是指定編譯器,with-cc-opt指定編譯選項(xiàng), -fsanitize=address就是開啟AddressSanitizer功能。

由于AddressSanitizer對(duì)nginx的影響較小,所以大壓力測(cè)試時(shí)也能達(dá)到上萬的并發(fā),內(nèi)存泄漏的問題很容易就定位了。 這里就不詳細(xì)介紹內(nèi)存泄漏的原因了,因?yàn)楦鷒penssl的錯(cuò)誤處理邏輯有關(guān),是我自己實(shí)現(xiàn)的,沒有普遍的參考意義。 最重要的是,知道valgrind和asan的使用場(chǎng)景和方法,遇到內(nèi)存方面的問題能夠快速修復(fù)。

性能熱點(diǎn)分析

到此,經(jīng)過改造的nginx程序沒有core dump和內(nèi)存泄漏方面的風(fēng)險(xiǎn)了。但這顯然不是我們最關(guān)心的結(jié)果(因?yàn)榇a本該如此),我們最關(guān)心的問題是: 1. 代碼優(yōu)化前,程序的瓶頸在哪里?能夠優(yōu)化到什么程度? 2. 代碼優(yōu)化后,優(yōu)化是否徹底?會(huì)出現(xiàn)哪些新的性能熱點(diǎn)和瓶頸? 這個(gè)時(shí)候我們就需要一些工具來檢測(cè)程序的性能熱點(diǎn)。

perf,oprofile,gprof,systemtap

linux世界有許多非常好用的性能分析工具,我挑選幾款最常用的簡(jiǎn)單介紹下: 1. [perf](Perf Wiki)應(yīng)該是最全面最方便的一個(gè)性能檢測(cè)工具。由linux內(nèi)核攜帶并且同步更新,基本能滿足日常使用。**推薦大家使用**。 2. oprofile,我覺得是一個(gè)較過時(shí)的性能檢測(cè)工具了,基本被perf取代,命令使用起來也不太方便。比如opcontrol --no-vmlinux , opcontrol --init等命令啟動(dòng),然后是opcontrol --start, opcontrol --dump, opcontrol -h停止,opreport查看結(jié)果等,一大串命令和參數(shù)。有時(shí)候使用還容易忘記初始化,數(shù)據(jù)就是空的。 3. gprof主要是針對(duì)應(yīng)用層程序的性能分析工具,缺點(diǎn)是需要重新編譯程序,而且對(duì)程序性能有一些影響。不支持內(nèi)核層面的一些統(tǒng)計(jì),優(yōu)點(diǎn)就是應(yīng)用層的函數(shù)性能統(tǒng)計(jì)比較精細(xì),接近我們對(duì)日常性能的理解,比如各個(gè)函數(shù)時(shí)間的運(yùn)行時(shí)間,,函數(shù)的調(diào)用次數(shù)等,很人性易讀。 4. systemtap 其實(shí)是一個(gè)運(yùn)行時(shí)程序或者系統(tǒng)信息采集框架,主要用于動(dòng)態(tài)追蹤,當(dāng)然也能用做性能分析,功能最強(qiáng)大,同時(shí)使用也相對(duì)復(fù)雜。不是一個(gè)簡(jiǎn)單的工具,可以說是一門動(dòng)態(tài)追蹤語言。如果程序出現(xiàn)非常麻煩的性能問題時(shí),推薦使用 systemtap。這里再多介紹一下perf命令,tlinux系統(tǒng)上默認(rèn)都有安裝,比如通過perf top就能列舉出當(dāng)前系統(tǒng)或者進(jìn)程的熱點(diǎn)事件,函數(shù)的排序。 perf record能夠紀(jì)錄和保存系統(tǒng)或者進(jìn)程的性能事件,用于后面的分析,比如接下去要介紹的火焰圖。

火焰圖 flame graph

perf有一個(gè)缺點(diǎn)就是不直觀?;鹧鎴D就是為了解決這個(gè)問題。它能夠以矢量圖形化的方式顯示事件熱點(diǎn)及函數(shù)調(diào)用關(guān)系。 比如我通過如下幾條命令就能繪制出原生nginx在ecdhe_rsa cipher suite下的性能熱點(diǎn):

perf record -F 99 -p PID -g -- sleep 10

perf script | ./stackcollapse-perf.pl > out.perf-folded

./flamegraph.pl out.perf-folded>ou.svg

直接通過火焰圖就能看到各個(gè)函數(shù)占用的百分比,比如上圖就能清楚地知道rsaz_1024_mul_avx2和rsaz_1024_sqr_avx2函數(shù)占用了75%的采樣比例。那我們要優(yōu)化的對(duì)象也就非常清楚了,能不能避免這兩個(gè)函數(shù)的計(jì)算?或者使用非本地CPU方案實(shí)現(xiàn)它們的計(jì)算? 當(dāng)然是可以的,我們的異步代理計(jì)算方案正是為了解決這個(gè)問題,

從上圖可以看出,熱點(diǎn)事件里已經(jīng)沒有RSA相關(guān)的計(jì)算了。至于是如何做到的,后面有時(shí)間再寫專門的文章來分享。

心態(tài)

為了解決上面提到的core dump和內(nèi)存泄漏問題,花了大概三周左右時(shí)間。壓力很大,精神高度緊張, 說實(shí)話有些狼狽,看似幾個(gè)很簡(jiǎn)單的問題,搞了這么長時(shí)間。心里當(dāng)然不是很爽,會(huì)有些著急,特別是項(xiàng)目的關(guān)鍵上線期。但即使這樣,整個(gè)過程我還是非常自信并且斗志昂揚(yáng)。我一直在告訴自己:

調(diào)試BUG是一次非常難得的學(xué)習(xí)機(jī)會(huì),不要把它看成是負(fù)擔(dān)。不管是線上還是線下,能夠主動(dòng)地,高效地追查BUG特別是有難度的BUG,對(duì)自己來說一次非常寶貴的學(xué)習(xí)機(jī)會(huì)。面對(duì)這么好的學(xué)習(xí)機(jī)會(huì),自然要充滿熱情,要如饑似渴,回首一看,如果不是因?yàn)檫@個(gè)BUG,我也不會(huì)對(duì)一些工具有更深入地了解和使用,也就不會(huì)有這篇文檔的產(chǎn)生。

不管什么樣的BUG,隨著時(shí)間的推移,肯定是能夠解決的。這樣想想,其實(shí)會(huì)輕松很多,特別是接手新項(xiàng)目,改造復(fù)雜工程時(shí),由于對(duì)代碼,對(duì)業(yè)務(wù)一開始并不是很熟悉,需要一個(gè)過渡期。但關(guān)鍵是,你要把這些問題放在心上。白天上班有很多事情干擾,上下班路上,晚上睡覺前,大腦反而會(huì)更加清醒,思路也會(huì)更加清晰。特別是白天上班時(shí)容易思維定勢(shì),陷入一個(gè)長時(shí)間的誤區(qū),在那里調(diào)試了半天,結(jié)果大腦一片混沌。睡覺前或者上下班路上一個(gè)人時(shí),反而能想出一些新的思路和辦法。

開放地討論。遇到問題不要不好意思,不管多簡(jiǎn)單,多低級(jí),只要這個(gè)問題不是你google一下就能得到的結(jié)論,大膽地,認(rèn)真地和組內(nèi)同事討論。這次BUG調(diào)試,有幾次關(guān)鍵的討論給了我很大的啟發(fā),特別是最后reusable的問題,也是組內(nèi)同事的討論才激發(fā)了我的靈感。謝謝大家的幫助。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4264

    瀏覽量

    62251
  • BUG
    BUG
    +關(guān)注

    關(guān)注

    0

    文章

    155

    瀏覽量

    15635
  • nginx
    +關(guān)注

    關(guān)注

    0

    文章

    142

    瀏覽量

    12148

原文標(biāo)題:高并發(fā)性能調(diào)試經(jīng)驗(yàn)分享

文章出處:【微信號(hào):C_Expert,微信公眾號(hào):C語言專家集中營】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    縮短設(shè)計(jì)周期時(shí)間的一些工具

    的各個(gè)版本。例如,TI 便提供 FilterPro?,作為其模擬濾波器設(shè)計(jì)程序。這些工具,將繁重的濾波器計(jì)算工作交給計(jì)算機(jī)。濾波器設(shè)計(jì)軟件包,可以經(jīng)常設(shè)計(jì)一些或者所有低通、通、帶通、帶阻(切口)和全通型
    發(fā)表于 10-25 09:45

    并發(fā)性程序并發(fā)工作效率低嗎

    、并發(fā)性程序并發(fā)工作效率低在寫裸機(jī)軟件時(shí),不可避免的在主程序中會(huì)有個(gè)超級(jí)大的 while(1) 循環(huán),這里面幾乎包含整個(gè)項(xiàng)目的所有業(yè)務(wù)邏輯。因?yàn)槊總€(gè)業(yè)務(wù)邏輯里面都會(huì)有 delay
    發(fā)表于 08-20 08:26

    并發(fā)性程序在寫裸機(jī)軟件時(shí)有while(1) 循環(huán)

    、并發(fā)性程序并發(fā)工作效率低在寫裸機(jī)軟件時(shí),不可避免的在主程序中會(huì)有個(gè)超級(jí)大的 while(1) 循環(huán),這里面幾乎包含整個(gè)項(xiàng)目的所有業(yè)務(wù)邏輯。因?yàn)槊總€(gè)業(yè)務(wù)邏輯里面都會(huì)有 delay
    發(fā)表于 08-20 06:47

    伺服調(diào)試一些經(jīng)驗(yàn)總結(jié)

    安川伺服調(diào)試一些經(jīng)驗(yàn)在查找慣量比的過程中看到的,這是伺服電機(jī)的驅(qū)動(dòng)過程中發(fā)生的問題,首先應(yīng)當(dāng)匹配慣量,伺服電機(jī)應(yīng)當(dāng)考慮剛性,還有速度,速度,慣量還會(huì)大。再生電阻還是不知道怎么配置。
    發(fā)表于 09-07 08:05

    性能并發(fā)服務(wù)器架構(gòu)分享

    由于自己正在做個(gè)高性能大用戶量的論壇程序,對(duì)高性能并發(fā)服務(wù)器架構(gòu)比較感興趣,于是在網(wǎng)上收集了不少這方面的資料和大家分享。希望能和大家交流
    發(fā)表于 09-16 06:45

    一些硬件電路技術(shù)經(jīng)驗(yàn)整理

    一些硬件電路技術(shù)經(jīng)驗(yàn)整理,感興趣的小伙伴們可以瞧瞧。
    發(fā)表于 09-18 17:15 ?0次下載

    Autium_designer的一些經(jīng)驗(yàn)

    Autium_designer的一些經(jīng)驗(yàn)
    發(fā)表于 02-28 21:16 ?0次下載

    一些制作1969的分享經(jīng)驗(yàn)

    一些制作1969的分享經(jīng)驗(yàn)
    發(fā)表于 03-04 18:25 ?36次下載

    并發(fā)性能調(diào)優(yōu)是在技術(shù)進(jìn)階賽道變得厲害的加分項(xiàng)

    并發(fā)性能調(diào)優(yōu),定是你在技術(shù)進(jìn)階賽道變得牛逼的加分項(xiàng)。不論,你是開發(fā),架構(gòu)還是管理崗,亦或者是其他互聯(lián)網(wǎng)相關(guān)崗位。因?yàn)楹敛豢鋸埖恼f,在現(xiàn)在動(dòng)輒過千萬級(jí)的并發(fā)流量環(huán)境下,懂得
    的頭像 發(fā)表于 09-18 10:39 ?1348次閱讀

    一些與眾不同的PCB布線經(jīng)驗(yàn)規(guī)則

    一些引起熱議的設(shè)計(jì)PCB的經(jīng)驗(yàn)法則進(jìn)行了討論。下面將文章摘錄如下。 如今,我仍然還能看到一些在20年前就常見的PCB布線的經(jīng)驗(yàn)法則,它們現(xiàn)在還被廣泛遵守和適用嗎?確切的答案是“也許吧”
    的頭像 發(fā)表于 11-01 10:33 ?3056次閱讀

    Keil MDK調(diào)試時(shí)軟件和硬件的一些要求

    和事件跟蹤、指令跟蹤等。 有很多網(wǎng)友反應(yīng),Keil MDK在調(diào)試的時(shí)候,會(huì)遇到各種問題,下面就簡(jiǎn)單講述下Keil MDK調(diào)試時(shí),軟件和硬件的一些要求。 1.邏輯分析儀它要求目標(biāo)硬件支
    的頭像 發(fā)表于 11-16 09:15 ?2832次閱讀

    電路設(shè)計(jì)的一些經(jīng)驗(yàn)總結(jié)

    電路設(shè)計(jì)的一些經(jīng)驗(yàn)總結(jié)
    發(fā)表于 12-02 13:57 ?42次下載

    個(gè)并發(fā)、輕量級(jí)的信息泄露掃描工具BBScan

    BBScan 是個(gè)并發(fā)、輕量級(jí)的信息泄露掃描工具。
    的頭像 發(fā)表于 09-21 10:19 ?1498次閱讀

    Linux C開發(fā)中的一些常用的調(diào)試技巧

    在Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語句還具有一些特殊的語法。
    發(fā)表于 06-01 09:03 ?226次閱讀

    分享一些Python代碼加速工具

    這篇文章會(huì)提供一些優(yōu)化代碼的工具。會(huì)讓代碼變得更簡(jiǎn)潔,或者更迅速。
    發(fā)表于 07-07 11:18 ?359次閱讀