Linux ir liels skaits rÄ«ku kodola un lietojumprogrammu atkļūdoÅ”anai. LielÄkajai daļai no tiem ir negatÄ«va ietekme uz lietojumprogrammu veiktspÄju, un tos nevar izmantot ražoÅ”anÄ.
Pirms pÄris gadiem bija
Jau ir daudz lietojumprogrammu utilÄ«tu, kas izmanto eBPF, un Å”ajÄ rakstÄ mÄs apskatÄ«sim, kÄ izveidot savu profilÄÅ”anas utilÄ«tu, pamatojoties uz bibliotÄku.
Kefs ir lÄns
Ceph klasterim ir pievienots jauns saimniekdators. PÄc dažu datu migrÄÅ”anas uz to mÄs pamanÄ«jÄm, ka rakstÄ«Å”anas pieprasÄ«jumu apstrÄdes Ätrums ar to bija daudz mazÄks nekÄ citos serveros.
AtŔķirÄ«bÄ no citÄm platformÄm Å”is resursdators izmantoja bcache un jauno Linux 4.15 kodolu. Å Ä« bija pirmÄ reize, kad Å”eit tika izmantots Ŕīs konfigurÄcijas resursdators. Un tajÄ brÄ«dÄ« bija skaidrs, ka problÄmas sakne teorÄtiski varÄtu bÅ«t jebkas.
Saimnieka izmeklÄÅ”ana
SÄksim ar to, kas notiek ceph-osd procesÄ. Å im nolÅ«kam mÄs izmantosim
AttÄls parÄda, ka funkcija fdatasync() pavadÄ«ja daudz laika, nosÅ«tot pieprasÄ«jumu funkcijÄm generic_make_request(). Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis ir kaut kur Ärpus paÅ”a osd dÄmona. Tas var bÅ«t kodols vai diski. Iostata izvade uzrÄdÄ«ja lielu latentumu, apstrÄdÄjot pieprasÄ«jumus ar bcache diskiem.
PÄrbaudot resursdatoru, mÄs atklÄjÄm, ka systemd-udevd dÄmons patÄrÄ daudz CPU laika - aptuveni 20% no vairÄkiem kodoliem. TÄ ir dÄ«vaina uzvedÄ«ba, tÄpÄc jums ir jÄnoskaidro, kÄpÄc. TÄ kÄ Systemd-udevd darbojas ar notikumiem, mÄs nolÄmÄm tos apskatÄ«t udevadm monitors. IzrÄdÄs, ka katrai sistÄmas blokierÄ«cei tika Ä£enerÄts liels skaits izmaiÅu notikumu. Tas ir diezgan neparasti, tÄpÄc mums bÅ«s jÄskatÄs, kas rada visus Å”os notikumus.
Izmantojot BCC rīku komplektu
KÄ mÄs jau esam noskaidrojuÅ”i, kodols (un ceph dÄmons sistÄmas izsaukumÄ) pavada daudz laika generic_make_request(). MÄÄ£inÄsim izmÄrÄ«t Ŕīs funkcijas Ätrumu. IN
Å Ä« funkcija parasti darbojas Ätri. Viss, ko tas dara, ir nosÅ«tÄ«t pieprasÄ«jumu ierÄ«ces draivera rindai.
Bcache ir sarežģīta ierÄ«ce, kas faktiski sastÄv no trim diskiem:
- atbalsta ierÄ«ce (keÅ”atmiÅÄ saglabÄts disks), Å”ajÄ gadÄ«jumÄ tas ir lÄns HDD;
- keÅ”atmiÅas ierÄ«ce (keÅ”atmiÅas disks), Å”eit Å”is ir viens NVMe ierÄ«ces nodalÄ«jums;
- bcache virtuÄlÄ ierÄ«ce, ar kuru darbojas lietojumprogramma.
MÄs zinÄm, ka pieprasÄ«jumu pÄrraide ir lÄna, bet kurai no Ŕīm ierÄ«cÄm? MÄs ar to nodarbosimies nedaudz vÄlÄk.
Tagad mÄs zinÄm, ka notikumi var radÄ«t problÄmas. Atrast, kas tieÅ”i izraisa viÅu paaudzi, nav tik vienkÄrÅ”i. PieÅemsim, ka Ŕī ir sava veida programmatÅ«ra, kas tiek palaista periodiski. ApskatÄ«sim, kÄda veida programmatÅ«ra darbojas sistÄmÄ, izmantojot skriptu execsnoop no tÄ paÅ”a
PiemÄram, Å”Ädi:
/usr/share/bcc/tools/execsnoop | tee ./execdump
Å eit mÄs neparÄdÄ«sim pilnu execsnoop izvadi, taÄu viena mÅ«s interesÄjoÅ”Ä rinda izskatÄ«jÄs Å”Ädi:
sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'
TreÅ”Ä kolonna ir procesa PPID (parent PID). Process ar PID 5802 izrÄdÄ«jÄs viens no mÅ«su uzraudzÄ«bas sistÄmas pavedieniem. PÄrbaudot monitoringa sistÄmas konfigurÄciju, tika konstatÄti kļūdaini parametri. HBA adaptera temperatÅ«ra tika mÄrÄ«ta ik pÄc 30 sekundÄm, kas ir daudz biežÄk nekÄ nepiecieÅ”ams. PÄc pÄrbaudes intervÄla nomaiÅas uz garÄku, mÄs atklÄjÄm, ka pieprasÄ«juma apstrÄdes latentums Å”ajÄ resursdatorÄ vairs neizceļas salÄ«dzinÄjumÄ ar citiem saimniekdatoriem.
Bet joprojÄm nav skaidrs, kÄpÄc bcache ierÄ«ce bija tik lÄna. MÄs sagatavojÄm testa platformu ar identisku konfigurÄciju un mÄÄ£inÄjÄm reproducÄt problÄmu, palaižot fio uz bcache, periodiski palaižot udevadm trigeri, lai Ä£enerÄtu uevents.
Uz BCC balstītu rīku rakstīŔana
MÄÄ£inÄsim uzrakstÄ«t vienkÄrÅ”u utilÄ«tu, lai izsekotu un parÄdÄ«tu lÄnÄkos zvanus generic_make_request(). MÅ«s interesÄ arÄ« tÄ diska nosaukums, kuram Ŕī funkcija tika izsaukta.
PlÄns ir vienkÄrÅ”s:
- ReÄ£istrÄties kprobe par generic_make_request():
- MÄs saglabÄjam diska nosaukumu atmiÅÄ, kas pieejams, izmantojot funkcijas argumentu;
- MÄs saglabÄjam laika zÄ«mogu.
- ReÄ£istrÄties kretprobe par atgrieÅ”anos no generic_make_request():
- MÄs iegÅ«stam paÅ”reizÄjo laika zÄ«mogu;
- MÄs meklÄjam saglabÄto laikspiedolu un salÄ«dzinÄm to ar paÅ”reizÄjo;
- Ja rezultÄts ir lielÄks par norÄdÄ«to, mÄs atrodam saglabÄtÄ diska nosaukumu un parÄdÄm to terminÄlÄ«.
Kprobes Šø kretzondes izmantojiet pÄrtraukuma punkta mehÄnismu, lai lidojuma laikÄ mainÄ«tu funkcijas kodu. JÅ«s varat lasÄ«t
eBPF teksts python skriptÄ izskatÄs Å”Ädi:
bpf_text = āāā # Here will be the bpf program code āāā
Lai apmainÄ«tos ar datiem starp funkcijÄm, eBPF programmas izmanto
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);
Å eit mÄs reÄ£istrÄjam hash tabulu ar nosaukumu p, ar atslÄgas veidu u64 un tipa vÄrtÄ«bu struct data_t. Tabula bÅ«s pieejama mÅ«su BPF programmas kontekstÄ. Makro BPF_PERF_OUTPUT reÄ£istrÄ citu tabulu ar nosaukumu notikumi, kas tiek izmantots
MÄrot aizkavi starp funkcijas izsaukÅ”anu un atgrieÅ”anos no tÄs vai starp dažÄdu funkciju izsaukumiem, jÄÅem vÄrÄ, ka saÅemtajiem datiem ir jÄpieder vienam un tam paÅ”am kontekstam. Citiem vÄrdiem sakot, jums jÄatceras par iespÄjamu funkciju paralÄlu palaiÅ”anu. Mums ir iespÄja izmÄrÄ«t latentumu starp funkcijas izsaukÅ”anu viena procesa kontekstÄ un atgrieÅ”anos no Ŕīs funkcijas cita procesa kontekstÄ, taÄu tas, visticamÄk, ir bezjÄdzÄ«gi. Å eit bÅ«tu labs piemÄrs
TÄlÄk mums jÄraksta kods, kas darbosies, kad tiks izsaukta pÄtÄmÄ funkcija:
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);
}
Å eit pirmais izsauktÄs funkcijas arguments tiks aizstÄts kÄ otrais arguments
Atgriežoties no, tiks izsaukta Å”Äda funkcija 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);
}
}
Å Ä« funkcija ir lÄ«dzÄ«ga iepriekÅ”Äjai: mÄs noskaidrojam procesa PID un laikspiedolu, bet nepieŔķiram atmiÅu jaunajai datu struktÅ«rai. TÄ vietÄ mÄs meklÄjam jaucÄj tabulÄ jau esoÅ”u struktÅ«ru, izmantojot atslÄgu == paÅ”reizÄjais PID. Ja struktÅ«ra ir atrasta, tad noskaidrojam palaiÅ”anas procesa nosaukumu un pievienojam tam.
BinÄrÄ maiÅa, ko mÄs Å”eit izmantojam, ir nepiecieÅ”ama, lai iegÅ«tu pavedienu GID. tie. GalvenÄ procesa PID, kas aizsÄka pavedienu, kura kontekstÄ mÄs strÄdÄjam. Funkcija, ko mÄs izsaucam
Izvadot uz terminÄli, mÅ«s Å”obrÄ«d neinteresÄ pavediens, bet gan galvenais process. SalÄ«dzinot iegÅ«to aizkavi ar doto slieksni, mÄs izturam savu struktÅ«ru dati lietotÄja telpÄ, izmantojot tabulu notikumi, pÄc kura mÄs izdzÄÅ”am ierakstu no p.
Python skriptÄ, kas ielÄdÄs Å”o kodu, mums jÄaizstÄj MIN_US un FACTOR ar aizkaves sliekÅ”Åiem un laika vienÄ«bÄm, kuras mÄs nodosim, izmantojot argumentus:
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"
Tagad mums ir jÄsagatavo BPF programma, izmantojot
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")
Mums arÄ« bÅ«s jÄnosaka struct data_t mÅ«su skriptÄ, pretÄjÄ gadÄ«jumÄ mÄs nevarÄsim neko izlasÄ«t:
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)]
PÄdÄjais solis ir datu izvadÄ«Å”ana terminÄlÄ«:
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()
Pats skripts ir pieejams vietnÄ
Beidzot! Tagad mÄs redzam, ka tas, kas izskatÄ«jÄs kÄ apstÄjusies bcache ierÄ«ce, patiesÄ«bÄ ir apstÄjies zvans generic_make_request() keÅ”atmiÅÄ saglabÄtam diskam.
IedziļinÄties kodolÄ
Kas tieÅ”i palÄninÄs pieprasÄ«juma pÄrsÅ«tÄ«Å”anas laikÄ? Redzam, ka kavÄÅ”anÄs notiek pat pirms pieprasÄ«juma uzskaites sÄkuma, t.i. vÄl nav sÄkusies konkrÄta pieprasÄ«juma uzskaite tÄlÄkai statistikas izvadÄ«Å”anai par to (/proc/diskstats vai iostat). To var viegli pÄrbaudÄ«t, palaižot iostatu, vienlaikus atkÄrtojot problÄmu, vai
Ja skatÄmies uz funkciju generic_make_request(), tad redzÄsim, ka pirms pieprasÄ«juma uzskaites sÄkÅ”anas tiek izsauktas vÄl divas funkcijas. PirmkÄrt - generic_make_request_checks(), veic diska iestatÄ«jumu pieprasÄ«juma likumÄ«bas pÄrbaudes. Otrais -
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));
TajÄ kodols gaida, lÄ«dz rinda tiek atsaldÄta. IzmÄrÄ«sim kavÄÅ”anos 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 | |
Å Ä·iet, ka esam tuvu risinÄjumam. Rindas iesaldÄÅ”anai/atsaldÄÅ”anai izmantotÄs funkcijas ir
Laiks, kas nepiecieÅ”ams Ŕīs rindas notÄ«rÄ«Å”anai, ir lÄ«dzvÄrtÄ«gs diska latentumam, jo āākodols gaida, lÄ«dz tiks pabeigtas visas rindÄ esoÅ”Äs darbÄ«bas. Kad rinda ir tukÅ”a, tiek piemÄrotas iestatÄ«jumu izmaiÅas. PÄc kura to sauc
Tagad mÄs zinÄm pietiekami daudz, lai situÄciju labotu. udevadm trigera komanda liek lietot blokierÄ«ces iestatÄ«jumus. Å ie iestatÄ«jumi ir aprakstÄ«ti udev noteikumos. MÄs varam noskaidrot, kuri iestatÄ«jumi iesaldÄ rindu, mÄÄ£inot tos mainÄ«t, izmantojot sysfs vai apskatot kodola avota kodu. MÄs varam arÄ« izmÄÄ£inÄt BCC utilÄ«tu
~# /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]
Udev noteikumi mainÄs diezgan reti, un parasti tas notiek kontrolÄtÄ veidÄ. TÄtad mÄs redzam, ka pat jau iestatÄ«to vÄrtÄ«bu piemÄroÅ”ana izraisa aizkavÄÅ”anos pieprasÄ«juma pÄrsÅ«tÄ«Å”anÄ no lietojumprogrammas uz disku. Protams, udev notikumu Ä£enerÄÅ”ana, kad diska konfigurÄcijÄ nav izmaiÅu (piemÄram, ierÄ«ce nav uzstÄdÄ«ta/atvienota), nav laba prakse. TaÄu mÄs varam palÄ«dzÄt kodolam neveikt liekus darbus un iesaldÄt pieprasÄ«jumu rindu, ja tas nav nepiecieÅ”ams.
SecinÄjumi
eBPF ir ļoti elastÄ«gs un spÄcÄ«gs rÄ«ks. RakstÄ apskatÄ«jÄm vienu praktisku gadÄ«jumu un nodemonstrÄjÄm nelielu daļu no tÄ, ko var darÄ«t. Ja jÅ«s interesÄ BCC utilÄ«tu izstrÄde, ir vÄrts to apskatÄ«t
Ir arÄ« citi interesanti atkļūdoÅ”anas un profilÄÅ”anas rÄ«ki, kuru pamatÄ ir eBPF. Viens no viÅiem -
Avots: www.habr.com