Linux duwe akeh alat kanggo debugging kernel lan aplikasi. Umume duwe pengaruh negatif ing kinerja aplikasi lan ora bisa digunakake ing produksi.
Sawetara taun kepungkur ana
Wis akeh utilitas aplikasi sing nggunakake eBPF, lan ing artikel iki kita bakal weruh carane nulis sarana profil dhewe adhedhasar perpustakaan.
Ceph Slow
Tuan rumah anyar wis ditambahake ing kluster Ceph. Sawise migrasi sawetara data menyang, kita weruh sing kacepetan Processing nulis panjalukan dening iku luwih murah tinimbang ing server liyane.
Ora kaya platform liyane, host iki nggunakake bcache lan kernel linux 4.15 anyar. Iki minangka sepisanan host konfigurasi iki digunakake ing kene. Lan ing wayahe iku cetha yen oyod saka masalah bisa teoritis apa wae.
Investigating Host
Ayo diwiwiti kanthi ndeleng apa sing kedadeyan ing proses ceph-osd. Kanggo iki kita bakal nggunakake
Gambar ngandhani yen fungsi kasebut fdatasync() ngginakaken akèh wektu ngirim panjalukan kanggo fungsi generic_make_request(). Iki tegese paling kamungkinan sabab saka masalah nang endi wae njaba daemon osd dhewe. Iki bisa dadi kernel utawa disk. Output iostat nuduhake latensi dhuwur ing pangolahan panjalukan dening disk bcache.
Nalika mriksa host, kita nemokake manawa daemon systemd-udevd nggunakake akeh wektu CPU - udakara 20% ing sawetara inti. Iki minangka prilaku sing aneh, mula sampeyan kudu ngerti sebabe. Wiwit Systemd-udevd dianggo karo uevents, kita mutusakΓ© kanggo ndeleng wong liwat monitor udevadm. Pranyata metu sing nomer akeh acara owah-owahan padha kui kanggo saben piranti pamblokiran ing sistem. Iki cukup ora biasa, mula kita kudu ndeleng apa sing nggawe kabeh acara kasebut.
Nggunakake BCC Toolkit
Kaya sing wis dingerteni, kernel (lan daemon ceph ing panggilan sistem) mbuwang akeh wektu ing generic_make_request(). Ayo nyoba ngukur kacepetan fungsi iki. ING
Fitur iki biasane dianggo kanthi cepet. Kabeh sing ditindakake yaiku ngirim panjalukan menyang antrian driver piranti.
Bcache iku piranti Komplek sing bener kasusun saka telung disk:
- piranti gawe (disk cache), ing kasus iki iku HDD alon;
- piranti caching (caching disk), kene iki salah siji partisi saka piranti NVMe;
- piranti virtual bcache karo kang aplikasi mlaku.
Kita ngerti manawa transmisi panyuwunan alon, nanging kanggo piranti kasebut? Kita bakal ngrampungake iki mengko.
Saiki kita ngerti manawa acara bisa nyebabake masalah. Nggoleki apa sing nyebabake generasi kasebut ora gampang. Ayo nganggep manawa iki minangka piranti lunak sing diluncurake sacara periodik. Ayo ndeleng piranti lunak apa sing mlaku ing sistem nggunakake skrip eksekutor saka padha
Contone kaya iki:
/usr/share/bcc/tools/execsnoop | tee ./execdump
Kita ora bakal nuduhake output lengkap execsnoop ing kene, nanging siji baris sing menarik kanggo kita katon kaya iki:
sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'
Kolom katelu yaiku PPID (parent PID) saka proses kasebut. Proses karo PID 5802 dadi salah sawijining utas sistem pemantauan kita. Nalika mriksa konfigurasi sistem ngawasi, paramèter sing salah ditemokake. Suhu adaptor HBA dijupuk saben 30 detik, sing luwih asring tinimbang sing dibutuhake. Sawise ngganti interval mriksa kanggo maneh, kita nemokake yen latensi pangolahan panjalukan ing host iki ora ana maneh dibandhingake karo host liyane.
Nanging isih ora jelas kenapa piranti bcache dadi alon. We nyiapake platform test karo konfigurasi podho rupo lan nyoba kanggo ngasilaken masalah dening mlaku fio ing bcache, periodik mlaku udevadm pemicu kanggo generate uevents.
Nulis BCC-Based Tools
Ayo nyoba nulis sarana sing gampang kanggo nglacak lan nampilake telpon sing paling alon generic_make_request(). Kita uga kasengsem ing jeneng drive sing fungsi iki diarani.
Rencana kasebut prasaja:
- Ndaftar kprobe ing generic_make_request():
- Kita nyimpen jeneng disk menyang memori, bisa diakses liwat argumen fungsi;
- Kita nyimpen stempel wektu.
- Ndaftar kretprobe kanggo bali saka generic_make_request():
- We njaluk timestamp saiki;
- Kita nggoleki cap wektu sing disimpen lan mbandhingake karo sing saiki;
- Yen asil luwih gedhe tinimbang sing ditemtokake, banjur kita nemokake jeneng disk sing disimpen lan ditampilake ing terminal.
Kprobes ΠΈ kretprobes nggunakake mekanisme breakpoint kanggo ngganti kode fungsi ing fly. Sampeyan bisa maca
Teks eBPF ing skrip python katon kaya iki:
bpf_text = βββ # Here will be the bpf program code βββ
Kanggo ngganti data antarane fungsi, program eBPF digunakake
struct data_t {
u64 pid;
u64 ts;
char comm[TASK_COMM_LEN];
u64 lat;
char disk[DISK_NAME_LEN];
};
BPF_HASH(p, u64, struct data_t);
BPF_PERF_OUTPUT(events);
Ing kene kita ndhaptar tabel hash sing diarani p, kanthi jinis kunci u64 lan nilai saka jinis struct data_t. Tabel kasebut bakal kasedhiya ing konteks program BPF kita. Macro BPF_PERF_OUTPUT ndhaptar tabel liyane sing diarani acara, kang digunakake kanggo
Nalika ngukur wektu tundha antarane nelpon fungsi lan bali saka iku, utawa antarane telpon kanggo fungsi beda, sampeyan kudu njupuk menyang akun sing data ditampa kudu kagungane konteks padha. Ing tembung liya, sampeyan kudu ngelingi babagan kemungkinan peluncuran fungsi paralel. Kita duwe kemampuan kanggo ngukur latensi antarane nelpon fungsi ing konteks siji proses lan bali saka fungsi kasebut ing konteks proses liyane, nanging iki ora ana gunane. Conto apik ing kene
Sabanjure, kita kudu nulis kode sing bakal ditindakake nalika fungsi sing diteliti diarani:
void start(struct pt_regs *ctx, struct bio *bio) {
u64 pid = bpf_get_current_pid_tgid();
struct data_t data = {};
u64 ts = bpf_ktime_get_ns();
data.pid = pid;
data.ts = ts;
bpf_probe_read_str(&data.disk, sizeof(data.disk), (void*)bio->bi_disk->disk_name);
p.update(&pid, &data);
}
Ing kene argumen pisanan saka fungsi sing diarani bakal diganti minangka argumen kapindho
Fungsi ing ngisor iki bakal diarani nalika bali saka generic_make_request():
void stop(struct pt_regs *ctx) {
u64 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
struct data_t* data = p.lookup(&pid);
if (data != 0 && data->ts > 0) {
bpf_get_current_comm(&data->comm, sizeof(data->comm));
data->lat = (ts - data->ts)/1000;
if (data->lat > MIN_US) {
FACTOR
data->pid >>= 32;
events.perf_submit(ctx, data, sizeof(struct data_t));
}
p.delete(&pid);
}
}
Fungsi iki padha karo sing sadurunge: kita ngerteni PID proses lan cap wektu, nanging ora menehi memori kanggo struktur data anyar. Nanging, kita nelusuri tabel hash kanggo struktur sing wis ana nggunakake tombol == PID saiki. Yen struktur ditemokake, banjur kita nemokake jeneng proses sing mlaku lan ditambahake.
Pergeseran binar sing digunakake ing kene dibutuhake kanggo entuk GID benang. sing. PID saka proses utama sing miwiti utas ing konteks sing digunakake. Fungsi sing kita sebut
Nalika outputting menyang terminal, kita saiki ora kasengsem ing thread, nanging kita kasengsem ing proses utama. Sawise mbandhingake wektu tundha sing diasilake karo ambang sing diwenehake, kita ngliwati struktur kita data menyang ruang pangguna liwat tabel acara, sawise kita mbusak entri saka p.
Ing skrip python sing bakal mbukak kode iki, kita kudu ngganti MIN_US lan FACTOR kanthi ambang wektu tundha lan unit wektu, sing bakal kita lewati argumen:
bpf_text = bpf_text.replace('MIN_US',str(min_usec))
if args.milliseconds:
bpf_text = bpf_text.replace('FACTOR','data->lat /= 1000;')
label = "msec"
else:
bpf_text = bpf_text.replace('FACTOR','')
label = "usec"
Saiki kita kudu nyiapake program BPF liwat
b = BPF(text=bpf_text)
b.attach_kprobe(event="generic_make_request",fn_name="start")
b.attach_kretprobe(event="generic_make_request",fn_name="stop")
Kita uga kudu nemtokake struct data_t ing naskah kita, yen ora, kita ora bakal bisa maca apa-apa:
TASK_COMM_LEN = 16 # linux/sched.h
DISK_NAME_LEN = 32 # linux/genhd.h
class Data(ct.Structure):
_fields_ = [("pid", ct.c_ulonglong),
("ts", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN),
("lat", ct.c_ulonglong),
("disk",ct.c_char * DISK_NAME_LEN)]
Langkah pungkasan yaiku output data menyang terminal:
def print_event(cpu, data, size):
global start
event = ct.cast(data, ct.POINTER(Data)).contents
if start == 0:
start = event.ts
time_s = (float(event.ts - start)) / 1000000000
print("%-18.9f %-16s %-6d %-1s %s %s" % (time_s, event.comm, event.pid, event.lat, label, event.disk))
b["events"].open_perf_buffer(print_event)
# format output
start = 0
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
Skrip dhewe kasedhiya ing
Akhire! Saiki kita ndeleng manawa piranti bcache sing macet yaiku telpon sing mandheg generic_make_request() kanggo disk cache.
Dig menyang Kernel
Apa persis sing kalem sajrone transmisi panyuwunan? Kita waca sing wektu tundha ana malah sadurunge wiwitan accounting request, i.e. accounting request tartamtu kanggo output luwih saka statistik ing (/ proc / diskstats utawa iostat) durung diwiwiti. Iki bisa gampang diverifikasi kanthi mbukak iostat nalika ngasilake masalah, utawa
Yen kita katon ing fungsi generic_make_request(), banjur kita bakal weruh yen sadurunge akuntansi panyuwunan diwiwiti, rong fungsi liyane diarani. pisanan- generic_make_request_checks(), nindakake mriksa legitimasi panyuwunan babagan setelan disk. Kapindho -
ret = wait_event_interruptible(q->mq_freeze_wq,
(atomic_read(&q->mq_freeze_depth) == 0 &&
(preempt || !blk_queue_preempt_only(q))) ||
blk_queue_dying(q));
Ing kono, kernel ngenteni antrian unfreeze. Ayo ngukur wektu tundha blk_queue_enter():
~# /usr/share/bcc/tools/funclatency blk_queue_enter -i 1 -m
Tracing 1 functions for "blk_queue_enter"... Hit Ctrl-C to end.
msecs : count distribution
0 -> 1 : 341 |****************************************|
msecs : count distribution
0 -> 1 : 316 |****************************************|
msecs : count distribution
0 -> 1 : 255 |****************************************|
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 | |
Kayane kita wis cedhak karo solusi. Fungsi sing digunakake kanggo beku / unfreeze antrian yaiku
Wektu sing dibutuhake kanggo mbusak antrian iki padha karo latensi disk amarga kernel ngenteni kabeh operasi sing antri rampung. Sawise antrian kosong, pangowahan setelan ditrapake. Sawise iku diarani
Saiki kita ngerti cukup kanggo mbenerake kahanan kasebut. Printah pemicu udevadm nyebabake setelan kanggo piranti pemblokiran ditrapake. Setelan iki diterangake ing aturan udev. Kita bisa nemokake setelan sing beku antrian kanthi nyoba ngganti liwat sysfs utawa kanthi ndeleng kode sumber kernel. Kita uga bisa nyoba sarana BCC
~# /usr/share/bcc/tools/trace blk_freeze_queue -K -U
PID TID COMM FUNC
3809642 3809642 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
elevator_switch+0x29 [kernel]
elv_iosched_store+0x197 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
3809631 3809631 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
queue_requests_store+0xb6 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
Aturan Udev arang banget diganti lan biasane kedadeyan kanthi cara sing dikontrol. Dadi, kita bisa ndeleng manawa malah ngetrapake nilai sing wis disetel nyebabake tundha wektu nransfer panjalukan saka aplikasi menyang disk. Mesthi, ngasilake acara udev nalika ora ana owah-owahan ing konfigurasi disk (Contone, piranti ora dipasang / pedhot) ora laku apik. Nanging, kita bisa nulungi kernel supaya ora nindakake pakaryan sing ora perlu lan beku antrian panyuwunan yen ora perlu.
kesimpulan
eBPF minangka alat sing fleksibel lan kuat. Ing artikel kasebut, kita ndeleng siji kasus praktis lan nuduhake bagean cilik saka apa sing bisa ditindakake. Yen sampeyan kasengsem ing ngembangaken keperluan BCC, iku worth njupuk dipikir ing
Ana alat debugging lan profiling liyane sing menarik adhedhasar eBPF. Salah sijine -
Source: www.habr.com