日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

用户空间的SystemTap探测是怎么工作的

發(fā)布時(shí)間:2024/3/24 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用户空间的SystemTap探测是怎么工作的 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這篇文章介紹SystemTap在用戶層的實(shí)現(xiàn)原理。對(duì)文檔進(jìn)行了部分摘抄翻譯,原文易懂,建議閱讀原文:How SystemTap Userspace Probes Work。另外介紹另一篇文檔:動(dòng)態(tài)追蹤技術(shù)漫談

在給Bitcoin增加SystemTap時(shí),對(duì)它的實(shí)現(xiàn)感到很疑惑,還有這會(huì)增加多少開(kāi)銷(xiāo)。 實(shí)際上,我想知道的是:

  • 使用SystemTap的可執(zhí)行程序會(huì)增加什么指令?
  • 使用stap跟蹤進(jìn)程時(shí)實(shí)際上會(huì)發(fā)生什么?
  • 不跟蹤程序時(shí)開(kāi)銷(xiāo)有多少?
  • 在跟蹤程序時(shí)開(kāi)銷(xiāo)有多少?

SystemTap的wiki上有一些解釋 userspace probe implementation, 但是不夠詳細(xì)。

增加了SystemTap探點(diǎn)的代碼

這個(gè)例子中會(huì)看一下Bitcoin的函數(shù)IsInitialBlockDownload()。源碼修改后是這樣的:

bool IsInitialBlockDownload() {// Once this function has returned false, it must remain false.static std::atomic<bool> latchToFalse{false};// Optimization: pre-test latch before taking the lock.if (latchToFalse.load(std::memory_order_relaxed))return false;LOCK(cs_main);if (latchToFalse.load(std::memory_order_relaxed))return false;if (fImporting || fReindex)return true;if (chainActive.Tip() == nullptr)return true;if (chainActive.Tip()->nChainWork < nMinimumChainWork)return true;if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))return true;LogPrintf("Leaving InitialBlockDownload (latching to false)\n");latchToFalse.store(true, std::memory_order_relaxed);if (PROBE_FINISH_IBD_ENABLED()) { // 關(guān)注這兩句PROBE_FINISH_IBD();}return false; }

跟探測(cè)相關(guān)的是PROBE_FINISH_IBD_ENABLED()和PROBE_FINISH_IBD()。這些宏定義是作為構(gòu)建程序的一部分由SystemTap自動(dòng)生成的。

生成的x86匯編

下面是GDB disas反匯編IsInitialBlockDownload()生成的代碼,附帶源碼:

$ gdb ./src/bitcoind(gdb) disas /s IsInitialBlockDownload1170 if (PROBE_FINISH_IBD_ENABLED()) {0x00000000001a2954 <+420>: cmpw $0x0,0x4f6724(%rip) # 0x699080 <bitcoin_finish_ibd_semaphore>0x00000000001a295c <+428>: je 0x1a2831 <IsInitialBlockDownload()+129>1171 PROBE_FINISH_IBD();0x00000000001a2962 <+434>: nop0x00000000001a2963 <+435>: jmpq 0x1a2831 <IsInitialBlockDownload()+129>

GDB disas默認(rèn)生成的是AT&T語(yǔ)法。x86指令可以有變長(zhǎng)操作數(shù)長(zhǎng)度,AT&T語(yǔ)法在指令中用長(zhǎng)度后綴注釋。在這個(gè)例子中,cmpw中的w后綴表示”word”(16位)操作數(shù)。jmpq表示跳轉(zhuǎn)指令有一個(gè)”quad”(64位)操作數(shù)?!眎f”語(yǔ)句編譯成cmp指令,檢查內(nèi)存地址0x699080上的數(shù)值是否等于0。這里的注釋說(shuō)實(shí)際上在ELF文件中0x699080是bitcoin_finish_ibd_semaphore。如果這個(gè)內(nèi)存地址的值是0,je指令直接跳出”if”語(yǔ)句。否則指令會(huì)走到nop指令,nop是一個(gè)字節(jié)的指令,什么都不做。在nop指令之后是一個(gè)無(wú)條件跳轉(zhuǎn)的jmp。注意兩個(gè)跳轉(zhuǎn)(je和jmpq)都跳轉(zhuǎn)到了相同的地址0x1a2831。這個(gè)地址是return false語(yǔ)句。GCC通常會(huì)生成與源碼順序不一致的x86代碼,這是為了優(yōu)化,所以不能簡(jiǎn)單的自動(dòng)向下流轉(zhuǎn)到return語(yǔ)句。

這個(gè)代碼看起來(lái)有點(diǎn)小怪,因?yàn)檫@個(gè)控制流程是這樣的:

  • 檢查這個(gè)值:bitcoin_finish_ibd_semaphore
    • 如果不是0,執(zhí)行一個(gè)no-op指令,然后跳轉(zhuǎn)到return false語(yǔ)句。
    • 如果是0,立即跳轉(zhuǎn)到return false語(yǔ)句。
      為什么要特定條件下執(zhí)行no-op指令,并且實(shí)際上什么都不會(huì)做?如果對(duì)類似于GDB的調(diào)試器工作原理比較熟悉,就可能已經(jīng)猜到了。

觀察SystemTap跟蹤時(shí)的內(nèi)存

首先看一下運(yùn)行時(shí)的程序disas的輸出。我啟動(dòng)了一個(gè)bitcoind進(jìn)程,進(jìn)程號(hào)是4077。當(dāng)執(zhí)行disas時(shí),看起來(lái)有點(diǎn)不一樣:

$ gdb -p 4077(gdb) disas /s IsInitialBlockDownload1170 if (PROBE_FINISH_IBD_ENABLED()) {0x000056352db8d954 <+420>: cmpw $0x0,0x4f6724(%rip) # 0x56352e084080 <bitcoin_finish_ibd_semaphore>0x000056352db8d95c <+428>: je 0x56352db8d831 <IsInitialBlockDownload()+129>1171 PROBE_FINISH_IBD();0x000056352db8d962 <+434>: nop0x000056352db8d963 <+435>: jmpq 0x56352db8d831 <IsInitialBlockDownload()+129>

這些指令看起來(lái)跟之前一樣,除了內(nèi)存地址。內(nèi)存地址因?yàn)锳SLR的原因改變了,但是整個(gè)流程還是不變。還是看bitcoin_finish_ibd_semaphore:

(gdb) x/hx &bitcoin_finish_ibd_semaphore 0x56352e084080 <bitcoin_finish_ibd_semaphore>: 0x0000

這里顯示是0x0000,這樣我們就知道cmpw語(yǔ)句總會(huì)跳過(guò)nop。

關(guān)聯(lián)SystemTap腳本

我現(xiàn)在準(zhǔn)備關(guān)聯(lián)一個(gè)SystemTap腳本,探測(cè)前面的內(nèi)容。這是腳本內(nèi)容:

# This probe is run when stap initially attaches. probe begin {println("attached to bitcoind process...") }# This probe is run when our IBD probe is triggered. probe process("./src/bitcoind").mark("finish_ibd") {println("IBD finished") }

begin可以告訴我們SystemTap什么時(shí)候初始化結(jié)束,這個(gè)時(shí)候就真正的attach了。用這個(gè)腳本執(zhí)行:

# Run our stap script and attach to process 4077. $ stap -x 4077 demo.stp attached to bitcoind process...

再次在同一個(gè)地址執(zhí)行disas:

(gdb) disas /s IsInitialBlockDownload1170 if (PROBE_FINISH_IBD_ENABLED()) {0x000056352db8d954 <+420>: cmpw $0x0,0x4f6724(%rip) # 0x56352e084080 <bitcoin_finish_ibd_semaphore>0x000056352db8d95c <+428>: je 0x56352db8d831 <IsInitialBlockDownload()+129>1171 PROBE_FINISH_IBD();0x000056352db8d962 <+434>: int3 0x000056352db8d963 <+435>: jmpq 0x56352db8d831 <IsInitialBlockDownload()+129>

事情變得不一樣了。nop指令變成了int3。這個(gè)就是”trap”指令。工作原理是:當(dāng)進(jìn)程執(zhí)行這個(gè)指令時(shí),會(huì)在內(nèi)核產(chǎn)生一個(gè)中斷。內(nèi)核可以使用任何手段處理它。正常情況下會(huì)產(chǎn)生一個(gè)SIGTRAP信號(hào)給進(jìn)程。GDB這樣的調(diào)試器就使用這樣的機(jī)制實(shí)現(xiàn)斷點(diǎn)。SystemTap有些不一樣,稍后解釋。bitcoin_finish_ibd_semaphore發(fā)生了什么?

(gdb) x/hx &bitcoin_finish_ibd_semaphore 0x56352e084080 <bitcoin_finish_ibd_semaphore>: 0x0001

變成了0x0001,之前是0x0000。這就意味著當(dāng)程序跑到cmpw指令時(shí),就會(huì)執(zhí)行”if”語(yǔ)句,然后在內(nèi)核中產(chǎn)生中斷。

SystemTap中斷怎么工作的

執(zhí)行一個(gè)SystemTap腳本時(shí),會(huì)發(fā)生這些事情:

  • stap命令將.stp代碼轉(zhuǎn)換成Linux內(nèi)核模塊C代碼。
  • C代碼編譯成一個(gè)本地內(nèi)核模塊(.ko文件)。
  • 內(nèi)核模塊注冊(cè)感興趣的trap事件,探測(cè)要求的探測(cè)點(diǎn),在內(nèi)核上下文中作為本地x86代碼執(zhí)行。

這種實(shí)現(xiàn)讓SystemTap非???。但是因?yàn)槟_本運(yùn)行在內(nèi)核態(tài),所以可能會(huì)導(dǎo)致內(nèi)核的不穩(wěn)定,因此需要執(zhí)行stap的用戶具有權(quán)限。

其它的探測(cè)點(diǎn)會(huì)發(fā)生什么

我執(zhí)行了一些其它的測(cè)試,發(fā)現(xiàn)了更多的工作原理。當(dāng)SystemTap運(yùn)行一個(gè)腳本時(shí),它會(huì)觀察你想要探測(cè)的那個(gè)點(diǎn),只有你想要監(jiān)控的探測(cè)點(diǎn)會(huì)改變。比如,我在另外一個(gè)函數(shù)CCoinsViewCache::FetchCoin中增加了一個(gè)探測(cè)點(diǎn)。即使IsInitialBlockDownload()已經(jīng)被SystemTap修改,這個(gè)函數(shù)還是沒(méi)有變化:

(gdb) disas /s CCoinsViewCache::FetchCoin49 if (PROBE_CACHE_MISS_ENABLED() && m_enable_probing) {0x000056352dc2bea8 <+328>: cmpb $0x0,0x80(%rbx)0x000056352dc2beaf <+335>: je 0x56352dc2bdd6 <CCoinsViewCache::FetchCoin(COutPoint const&) const+118>49 if (PROBE_CACHE_MISS_ENABLED() && m_enable_probing) {0x000056352dc2bdc8 <+104>: cmpw $0x0,0x4582ae(%rip) # 0x56352e08407e <bitcoin_cache_miss_semaphore>0x000056352dc2bdd0 <+112>: jne 0x56352dc2bea8 <CCoinsViewCache::FetchCoin(COutPoint const&) const+328>50 PROBE_CACHE_MISS();0x000056352dc2beb5 <+341>: nop0x000056352dc2beb6 <+342>: jmpq 0x56352dc2bdd6 <CCoinsViewCache::FetchCoin(COutPoint const&) const+118>

這個(gè)代碼看起來(lái)有點(diǎn)不一樣,因?yàn)檫€要檢查一個(gè)成員變量,不過(guò)基本流程還是一樣的,而且”if”語(yǔ)句都是一樣的。正如你所看到的,這里還是有一個(gè)nop指令,并沒(méi)有變化。跟預(yù)期相符,bitcoin_cache_miss_semaphore的值是0:

(gdb) x/hx &bitcoin_cache_miss_semaphore 0x56352e08407e <bitcoin_cache_miss_semaphore>: 0x0000

SystemTap detach的時(shí)候發(fā)生什么?

如果用原先的stap從進(jìn)程上detach下來(lái),IsInitialBlockDownload 函數(shù)就會(huì)恢復(fù)到原來(lái)的樣子,bitcoin_finish_ibd_semaphore這個(gè)值又變成了0.當(dāng)semaphore值變成0時(shí),對(duì)應(yīng)的int3指令也會(huì)變回nop。

這個(gè)檢查會(huì)增加多少開(kāi)銷(xiāo)?

當(dāng)考慮到SystemTap探測(cè)的開(kāi)銷(xiāo)時(shí),需要考慮兩種場(chǎng)景:沒(méi)有被跟蹤的進(jìn)程和已經(jīng)跟蹤的進(jìn)程。
當(dāng)進(jìn)程沒(méi)有被跟蹤時(shí),每個(gè)探測(cè)增加的開(kāi)銷(xiāo)就是一個(gè)cmp指令和一個(gè)jmp指令。因?yàn)檫@兩個(gè)指令總是同時(shí)出現(xiàn),現(xiàn)代處理器把它們?nèi)诤显诹艘黄?。在一個(gè)現(xiàn)代處理器上(比如Intel Skylake),這個(gè)組合會(huì)消耗0.5到2指令周期??偨Y(jié):沒(méi)有跟蹤的進(jìn)程開(kāi)銷(xiāo)幾乎是0
當(dāng)進(jìn)程被跟蹤時(shí),每個(gè)探測(cè)點(diǎn)增加的開(kāi)銷(xiāo)是一個(gè)cmp指令和上下文切換。具體的上下文開(kāi)銷(xiāo)取決于內(nèi)核版本和CPU。根據(jù)我發(fā)現(xiàn)的最好的資源(從2010年開(kāi)始)顯示,開(kāi)銷(xiāo)在2到50微秒之間,取決于CPU類型和其它的一些細(xì)節(jié)。探測(cè)點(diǎn)的代碼可能會(huì)很復(fù)雜,SystemTap語(yǔ)言允許執(zhí)行循環(huán),調(diào)用其它函數(shù),hash map等等),所以這個(gè)開(kāi)銷(xiāo)可能會(huì)很大。SystemTap考慮到了這點(diǎn),會(huì)監(jiān)測(cè)探測(cè)會(huì)執(zhí)行多少時(shí)間。如果執(zhí)行太長(zhǎng)時(shí)間,stap腳本會(huì)自動(dòng)終止。這個(gè)可以通過(guò)stap命令行選項(xiàng)調(diào)整,比如有一個(gè)復(fù)雜的探測(cè)邏輯并且不在乎消耗。在任何事件中,我認(rèn)為這個(gè)開(kāi)銷(xiāo)對(duì)跟蹤的事件來(lái)說(shuō)都不是那么重要,因?yàn)橹粫?huì)在調(diào)試一個(gè)程序的時(shí)候才會(huì)跟蹤。不會(huì)占據(jù)主要時(shí)間。

總結(jié)

以上是生活随笔為你收集整理的用户空间的SystemTap探测是怎么工作的的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。