Heisenbug bat gehiago krokodiloaren ondotik

Heisenbug bat gehiago krokodiloaren ondotik

$> set -o pipefail

$> fortune | head -1 > /dev/null && echo "ПовСзло!" || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ"
ПовСзло!

$> fortune | head -1 > /dev/null && echo "ПовСзло!" || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ"
Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ

Hemen fortune baldintzapeko programarik gabe exit(rand()).

Azaldu dezakezu zer dago gaizki hemen?

Digresio liriko-historikoa

Duela mende laurden ezagutu nuen lehen aldiz Heisenbug hau. Ondoren, FaxNET-eko atebiderako hainbat utilitate sortzea beharrezkoa zen bidez hodiak FreeBSD pean "dama jolasten". Espero bezala, programatzaile aurreratu eta nahiko esperientziaduntzat hartzen nuen nire burua. Hori dela eta, dena ahalik eta arreta handien eta arreta handiz egiteko asmoa nuen, erroreen kudeaketari arreta berezia jarriz...

Sendmail-en eta uucp/uupc-en akatsei aurre egiteko nire aurreko esperientziak "erroreen kudeaketa sakonean" gehitu zitzaidan diligentziari. Ez du balio istorio horren xehetasunetan murgiltzeak, baina Heisenbug honekin borrokatu nintzen bi astez 10-14 orduz. Hori dela eta, gogoratu zen, eta atzo ezagun zahar hau berriro bisita egitera pasa zen.

TL;DR Erantzuna

Erabilgarritasuna head ahal itxi kanala fortune сразу lehen lerroa irakurri bezain pronto. Bada fortune linea bat baino gehiago ateratzen du, gero dagokion deia write() errore bat itzuliko du edo eskatutakoa baino byte gutxiago ateratzen direla jakinaraziko du. Era berean, erroreak kontu handiz kudeatuz idatzia fortune egoera hori bere irteera-egoeran islatzeko eskubidea du. Ondoren, instalazioa dela eta set -o pipefail funtzionatuko du || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ".

Hala eta guztiz ere, head baliteke garaiz ez egitea itxi aurretik fortune datuak ateratzen amaituko du. Orduan funtzionatuko du && echo "ПовСзло!".

Nire gaurko batean GNUMakefile halakorik dago zatia:

echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'

Gizakira itzulita

Hemen ohikoa da GNU Make ΠΈ golpear konpiladore moduan aukera erabiliz --version nor den galdetzen du, eta aukera onartzen ez bada, zirriborro bat sartzen da "Mesedez, erabili GCC edo CLANG konpilatzaile bateragarria".

bezalako boilerplate edozein lekutan aurki daiteke. Aspaldi agertu zen leku honetan eta primeran funtzionatu zuen edonon (Linux, Solaris, OSX, FreeBSD, WSL etab.). Baina atzo bertan altlinux plataforman Elbrus 2000 (E2K) Konturatu nintzen:

#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"

Egia esan, ez nuen berehala ezagutu nire "ezagun" zaharra. Gainera, proiektua dagoeneko hainbat aldiz probatu da Elbrusen eta banaketa ezberdin askoren pean, Alt. Hainbat konpilatzailerekin, GNU Make eta bash-en bertsioekin. Horregatik, ez nuen nire akatsa hemen ikusi nahi.

Arazoa erreproduzitu eta/edo gertatzen ari zena ulertzen saiatzean, gauza arraroagoak gertatzen hasi ziren.
Komando-lerroko ortografia:

echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"

Tarteka testu gehigarria sortzen zuen, gero ez... Askotan aukeretako bat nahiko denbora luzez geratzen zen, baina luzeago sartzen bazenuen, beti lortzen zenituen biak!

Jakina, strace gure dena! Eta tirada bat idatzi nuen, baina Sartu sakatzeko astirik izan gabe, Heisenbug jauna nire lagun zaharra eta garatzaileak ezagutu nituen. konpilatzailea neuk duela 25 urte, Nostalgia… Eta triste egotea eta ohar hau idaztea erabaki nuen πŸ˜‰

Bide batez, nork bere burua errespetatzen duen bezala Heisenbug, azpian strace nahiago du ez ugaltzea.

Beraz, zer gertatzen da?

  • Erabilgarritasuna head eskubidea du (edo hobeto esanda, behartuta dago) irakurtzen ari den kanala ixteko eskatutako lerro kopurua irakurri bezain laster.
  • Datuak sortzen dituen programa-idazlea (kasu honetan cc) ahal inprimatu hainbat lerro eta dohainik egin hau hainbat deiren bidez write().
  • Bada irakurleak denbora izango du bere alboan kanala ixteko idazlearen grabazioa amaitu baino lehen, orduan akats bat jasoko du idazleak.
  • Idazle programa eskubidea biek ez diote jaramonik egiten kanalaren idazketa-erroreari eta islatu zure osatze-kodean.
  • Instalazioa dela eta set -o pipefail kanalizazioa osatzeko kodea ez-zeroa izango da (okerra) emaitza gutxienez elementu batetik zero ez bada, eta orduan funtzionatuko du || echo "Please use GCC or CLANG compatible compiler".

Idazle programak seinaleekin lan egiten duenaren arabera aldakuntzak egon daitezke. Adibidez, programa modu anormalean amai daiteke (zeroa ez den/errorearen amaiera-egoera automatikoki sortuz), edo write() eskatu eta ezarritako baino byte gutxiago idaztearen emaitza itzuliko du errno = EPIPE.

Nor da erruduna?

Deskribatutako kasuan denetarik apur bat. Errore bat manipulatzean cc (lcc:1.23.20:Sepβ€”4-2019:e2k-v3-linux) ez da erredundantea. Kasu askotan, hobe da zuhurtziaz erratea, nahiz eta horrek ohiko jokabiderako diseinatutako boilerplate baten bat-bateko akatsak agerian uzten dituen.

Zer egin?

oker:

fortune | head -1 && echo "ПовСзло, Π½ΠΎ Π²Ρ‹ рискуСтС!" || echo "WTF?"

zuzentzeko:

  1. (fortune && echo "УспСшно" || echo "Ошибка") | head -1

    Hemen, kanalizazio baten itxiera goiztiarra komando-interpretatzaileak kudeatuko du habiatutako kanalizazioari ("parentesien" barruan) zerbitzua ematen dionean. Horren arabera, bada fortune Idazketan errore bat jakinaraziko dio egoeran itxitako kanal bati, ondoren irteera || echo "Ошибка" ez da inora iritsiko, kanala itxita baitago.

  2. fortune | cat - | head -1 && echo "УспСшно" || echo "Ошибка"

    Hona hemen erabilgarritasuna cat motelgailu gisa jarduten du errorea baztertzen duelako EPIPE erretiratzean. Hau nahikoa da oraingo ondorioetarako fortune txikia (hainbat lerro) eta kanaleko bufferean sartzen da (512 bytetik β‰ˆ64Kra, OS gehienetan β‰₯4K). Bestela, arazoa itzul daiteke.

Nola prozesatu behar bezala EPIPE eta grabaketa akatsak?

Ez dago irtenbide zuzen bakarra, baina gomendio sinpleak daude:

  • EPIPE beharrezkoa prozesatu egin behar da (eta irteera-egoeran islatzen da) osotasuna eskatzen duten datuak ateratzen direnean. Esate baterako, artxiboen edo babeskopien utilitateen funtzionamenduan.
  • EPIPE hobe alde batera uztea informazioa eta mezu laguntzaileak bistaratzen direnean. Adibidez, aukerei buruzko informazioa bistaratzen denean --help edo --version.
  • Garatzen ari den kodea aurretik kanalizazio batean erabil badaiteke | headondoren EPIPE Hobe da alde batera uztea, bestela hobe da irteera egoeran prozesatu eta islatzea.

Aukera hau aprobetxatu nahi dut taldeei nire esker ona adierazteko MCST ΠΈ altlinux lan produktibo handirako. Zure erabakia harrigarria da!
Segi horrela Camarades, gora bilerak udazkenean!

Eskerrik asko berez akatsak eta akatsak zuzentzeko.
KDPV-tik Georgi A.

Iturria: www.habr.com

Gehitu iruzkin berria