<meta charset="utf-8">
霸爺博客,干貨滿滿岗仑。有兩篇文章現(xiàn)在還記得聚请,《Linux下如何知道文件被哪個(gè)進(jìn)程寫》和《巧用Systemtap注入延遲模擬IO設(shè)備抖動(dòng)》,周末突然想起來炸卑,發(fā)現(xiàn)能看懂了:)
什么是 systemtap
Systemtap is a tool that allows developers and administrators to write and reuse simple scripts to deeply examine the activities of a live Linux system. Data may be extracted, filtered, and summarized quickly and safely, to enable diagnoses of complex performance or functional problems.
我們一般調(diào)試程序盖文,業(yè)務(wù)程序加日志蚯姆,打 log, 基本能滿足需求洒敏。再不濟(jì)返帕,使用 strace荆萤、lsof铣卡、perf 足夠看到性能瓶勁,可以參考我 go gc 的文章敞峭。但是系統(tǒng)編程蝉仇,就不能狂打日志轿衔,而且很多調(diào)用棧都處于 kernel space,那么普通的調(diào)試手段就顯得捉襟見肘了害驹。
此時(shí) systemtap 就能派上用場宛官,他會在內(nèi)核函數(shù)加 probe 探針,對 kernel space 函數(shù)調(diào)用進(jìn)行統(tǒng)計(jì)匯總腋么,甚至可以對其進(jìn)行干預(yù)亥揖。但是對 user space 調(diào)試支持不是很好徐块。
安裝
本機(jī)環(huán)境:DELL R720, Ubuntu 14.04 3.19.0-25-generic x86_64
apt-get install systemtap systemtap-client systemtap-common systemtap-runtime systemtap-server -y
對于 centos 系統(tǒng)也一樣,yum install 即可扳剿。此時(shí)還要使用執(zhí)行 stap-prep 安裝缺失的內(nèi)核鏡像調(diào)試包昼激。比如我的就是
linux-image-3.19.0-25-generic-dbgsym_3.19.0-25.26~14.04.1_amd64.ddeb
遇到缺什么包直接安裝锡搜,或是從網(wǎng)上下載耕餐。systemtap 最好不要用源碼安裝辟狈,涉及內(nèi)核的包都很惡心,版本必須匹配明未,可以用 uname -r 查看壹蔓。
1. Linux下如何知道文件被哪個(gè)進(jìn)程寫
有個(gè)文件佣蓉,不定期的被修改,如果只是瞬間的寫入行疏,lsof 也沒有辦法套像,就算定期執(zhí)行,也可能有缺失贞让。那么此時(shí) systemtap 就該大顯身手了柳譬,先上代碼:
#!/usr/bin/env stap
probe vfs.write, vfs.read
{
if (@defined($file->f_path->dentry)) {
dev_nr=$file->f_path->dentry->d_inode->i_sb->s_dev
inode_nr = $file->f_path->dentry->d_inode->i_ino
} else {
dev_nr=$file->f_dentry->d_inode->i_sb->s_dev
inode_nr = $file->f_dentry->d_inode->i_ino
}
# dev and ino are defined by vfs.write and vfs.read
if (dev_nr == MKDEV($1,$2) && inode_nr==$3){
printf ("%s(%d) %s 0x%x/%u\n",execname(), pid(), ppfunc(), dev_nr, inode_nr)
}
}
probe timer.ms(10) {
exit()
}
語法類似 awk 代碼很簡單美澳,probe 定義探針,后面緊跟著探測點(diǎn)舅桩,可以是具體的函數(shù)名雨膨,支持 * 匹配聊记,大括號定義探針觸發(fā)動(dòng)作恢暖。
file 是函數(shù) vfs.read, vfs.write 的參數(shù)狰右,dev_nr,inode_nr 根據(jù) file 結(jié)構(gòu)體獲取設(shè)備號和 inode棋蚌,探測點(diǎn)是針對內(nèi)核函數(shù)的,所以可以獲取函數(shù)所有參數(shù)脱拼。
execname 執(zhí)行 vfs.write 或 vfs.read 程序名
pid 執(zhí)行 vfs.write 或 vfs.read 進(jìn)程號
ppfunc 是控測點(diǎn)函數(shù)名坷备,這個(gè)內(nèi)置函數(shù)在不同版本可能不一樣省撑,比如霸爺文章里是 probefuc
1.1 開終端執(zhí)行 dd
打開終端執(zhí)行 dd 不斷的寫入數(shù)據(jù)俯在,并查看文件 inode 號
dd if=/dev/zero of=test.dat
stat -c "%i" /disk1/test.dat
ls -al /dev/sdb1
這里 /dev/sdb1 是掛載在 /disk1 目錄下的設(shè)備
1.2 執(zhí)行 stap 探測
stap -v inodewatch.stp 8 17 15
Pass 1: parsed user script and 95 library script(s) using 84976virt/30204res/5152shr/25852data kb, in 200usr/0sys/456real ms.
Pass 2: analyzed script: 3 probe(s), 7 function(s), 5 embed(s), 0 global(s) using 610884virt/195324res/12432shr/180716data kb, in 1810usr/290sys/3605real ms.
Pass 3: translated to C into "/tmp/stapJEOYcQ/stap_20c430109956cd1ffc28c7ceaf0aa2f1_6899_src.c" using 599240virt/188844res/8908shr/180712data kb, in 0usr/0sys/73real ms.
Pass 4: compiled C into "stap_20c430109956cd1ffc28c7ceaf0aa2f1_6899.ko" in 1840usr/320sys/4180real ms.
Pass 5: starting run.
dd(25763) vfs_write 0x800011/15
dd(25763) vfs_write 0x800011/15
dd(25763) vfs_write 0x800011/15
dd(25763) vfs_write 0x800011/15
dd(25763) vfs_write 0x800011/15
Pass 5: run completed in 0usr/40sys/724real ms.
stap 執(zhí)行腳本需要 5 個(gè)步驟肥败,解析腳本愕提,分析,生成 c 代碼纽谒,編繹成內(nèi)核模塊 ko 文件如输。最后執(zhí)行模塊不见,可以看到 dd 任務(wù)在寫文件,調(diào)用 vfs_write
2. 巧用Systemtap注入延遲模擬IO設(shè)備抖動(dòng)
霸爺?shù)倪@篇例子很有意思稳吮,systemtap 模擬磁盤 IO 抖動(dòng)盖高,對于一些存儲系統(tǒng)眼虱,壓測時(shí)可以試一下捏悬。原理還是很簡單的润梯,在 vfs_write, vfs_read 時(shí) sleep 一小段時(shí)間即可,時(shí)間可以隨機(jī)寇钉。先上代碼
cat inject_ka.stp
global inject, ka_cnt
probe procfs("cnt").read {
$value = sprintf("%d\n", ka_cnt);
}
probe procfs("inject").write {
inject= $value;
printf("inject count %d, ka %s", ka_cnt, inject);
}
probe vfs.read.return,
vfs.write.return {
if (@defined($file->f_path->dentry)) {
dev_nr=$file->f_path->dentry->d_inode->i_sb->s_dev
inode_nr = $file->f_path->dentry->d_inode->i_ino
} else {
dev_nr=$file->f_dentry->d_inode->i_sb->s_dev
inode_nr = $file->f_dentry->d_inode->i_ino
}
if ($return &&
dev_nr == MKDEV($1,$2) &&
inject == "on\n")
{
# printf("dev %x func: %s\n", dev_nr, ppfunc())
ka_cnt++;
udelay($3);
}
}
probe begin{
println("ik module begin:)");
}
代碼有些略長扫倡,先看探針 probe vfs.read.return, vfs.write.return 表示在退出前執(zhí)行探針代碼竟纳,判斷 dev_nr 是不是目標(biāo)設(shè)備锥累,并且打開了 ineject, 如果打開缘挑,那么 udelay 一小段時(shí)間。
至于另外兩個(gè)探針桶略,procfs("cnt"), procfs("inject") 讀取 /proc/systemtap 時(shí)觸發(fā)语淘,修改全局變量 inject 來決定是否打開 IO 注入。
2.1 執(zhí)行代碼
這個(gè)腳本執(zhí)行可能會遇到 vfs_lookup_path 報(bào)錯(cuò)际歼,很惡心惶翻,我把 procfs.c 升級了一個(gè)版本,并注釋掉 vfs_lookup_path 部份才解決 ...
stap -DMAXSKIPPED=9999 -m ik -g inject_ka.stp 8 17 400
ik module begin:)
8蹬挺,17 表示磁盤設(shè)備號维贺,400表示 udelay 時(shí)間,此時(shí)腳本阻塞在這里巴帮,并沒有開始執(zhí)行 IO 注入溯泣。打開另一個(gè)終端執(zhí)行注入榕茧,持續(xù) 30 秒垃沦。
echo on| tee /proc/systemtap/ik/inject && sleep 30 && echo off| tee /proc/systemtap/ik/inject
此時(shí)可以看到 stap 有輸出。
2.2 測試磁盤性能
簡單的使用 dd 來測試 IO 延遲對順序?qū)懙挠绊?/p>
注入前
dd if=/dev/zero of=test.dat bs=8k count=1000000
1000000+0 records in
1000000+0 records out
8192000000 bytes (8.2 GB) copied, 34.8372 s, 235 MB/s
注入后
dd if=/dev/zero of=test.dat bs=8k count=1000000
1000000+0 records in
1000000+0 records out
8192000000 bytes (8.2 GB) copied, 79.5475 s, 103 MB/s
可以看到 dd 性能下降很大用押,通過調(diào)整 udelay 時(shí)間可以模擬不同延遲下的性能肢簿。如果是隨機(jī)的,或是符合正態(tài)分布的可能更好。
小結(jié)
官網(wǎng) 有很多 systemtap 使用例子和介紹池充,還可以抓網(wǎng)絡(luò)協(xié)義棧桩引,性能很強(qiáng)大。同時(shí)需要有一定的內(nèi)核功底收夸,至少要知道探針埋在哪里坑匠,openresty 大量使用 systemtap 進(jìn)行調(diào)試,可以參考學(xué)習(xí)卧惜。
另外厘灼,安裝是個(gè)很大問題,一定要注意版本咽瓷,太新也不可以设凹,ubuntu 系統(tǒng) apt 源是 2.3,嘗試過源碼安裝高版本茅姜,各種報(bào)錯(cuò)闪朱。