Noch ien Heisenbug foarby de krokodil

Noch ien Heisenbug foarby de krokodil

$> set -o pipefail

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли

it is fortune betingst programma sûnder exit(rand()).

Kinsto útlizze? wat is hjir mis?

Lyrysk-histoaryske ôfwiking

Mei dizze Heisenbug kaam ik in kwart ieu lyn foar it earst yn de kunde. Dan foar de poarte yn FaxNET wie it nedich om ferskate nutsbedriuwen te meitsjen fia pipes "dammen spielje" ûnder FreeBSD. Lykas ferwachte, beskôge ik mysels in avansearre en frij betûfte programmeur. Dêrom wie ik fan doel om alles sa foarsichtich en foarsichtich mooglik te dwaan, spesjaal omtinken te jaan oan flaterôfhanneling ...

Myn eardere ûnderfining fan it omgean mei bugs yn sendmail en uucp/uupc foege ta oan myn warberens yn "yngeande flaterôfhanneling." It hat gjin punt om yn 'e details fan dat ferhaal te dûken, mar ik haw twa wiken lang 10-14 oeren muoite mei dizze Heisenbug. Dêrom waard it betocht, en juster stie dizze âlde kunde nochris op besite.

TL; DR Antwurd

Utility head kin slute it kanaal út fortune сразу sa gau as er de earste rigel lêst. As fortune útfiert mear as ien rigel, dan de oerienkommende oprop write() sil in flater weromjaan of rapportearje dat minder bytes wurde útfierd dan frege. Op syn beurt skreaun mei soarchfâldige flater ôfhanneling fortune hat it rjocht om dizze situaasje te reflektearjen yn syn útgongsstatus. Dan fanwege ynstallaasje set -o pipefail sil wurkje || echo "Вы проиграли".

Mar, head kin it net op 'e tiid meitsje ticht foardat fortune sil it útfieren fan gegevens foltôgje. Dan sil it wurkje && echo "Повезло!".

Yn ien fan myn hjoed GNUMakefile der is ien brokstik:

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

Oerset yn minsklik

It is hjir gewoan foar GNU meitsje и bash op 'e kompilator manier mei de opsje --version it freget wa't hy is, en as de opsje net stipe wurdt, dan wurdt in stub ynfoege "Gebrûk asjebleaft GCC- of CLANG-kompatibele kompilator".

lykas boilerplate kin oeral fûn wurde. It ferskynde op dit plak in lange tiid lyn en wurke perfekt oeral (Linux, Solaris, OSX, FreeBSD, WSL ensfh.). Mar juster yn altlinux op it platfoarm Elbrus 2000 (E2K) Ik fernaam:

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

Earlik sein, ik herkende myn âlde "bekende" net daliks. Boppedat is it projekt al in protte kearen testen op Elbrus en ûnder in protte ferskillende distribúsjes, ynklusyf Alt. Mei ferskate gearstallers, ferzjes fan GNU Make en bash. Dêrom woe ik myn flater hjir net sjen.

By it besykjen om it probleem te reprodusearjen en/of te begripen wat der bart, begûnen mear frjemde dingen te barren.
Kommandorigel stavering:

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

Sa no en dan soe it ekstra tekst opleverje, dan net... Faak bliuwde ien fan de opsjes nochal lang, mar ast langer poarste, krigest altyd beide!

Fansels, strace ús alles! En nei't ik in strace tirade typearre, mar gjin tiid hie om op Enter te drukken, herkende ik myn âlde freon Mr. Heisenbug, en de ûntwikkelders gearstaller mysels 25 jier lyn, Nostalgie… En ik besleat om tryst te wêzen en dizze notysje te skriuwen 😉

Troch de wei, lykas alle sels-respektearjen Heisenbug, ûnder strace leaver net reprodusearje.

Wat is der geande?

  • Utility head hat it rjocht (of leaver, wurdt sels twongen) te sluten it kanaal wurdt lêzen sa gau as it lêst it frege oantal rigels.
  • De data-generearjende programma-skriuwer (yn dit gefal cc) kin print meardere rigels en frij doch dit troch meardere oproppen write().
  • as de lêzer sil tiid hawwe om it kanaal oan syn kant te sluten foar it ein fan de opname oan 'e kant fan 'e skriuwer, dan krijt de skriuwer in flater.
  • Skriuwersprogramma it rjocht hawwe om beide negearje de skriuwflater fan it kanaal en reflektearje it yn jo foltôgingskoade.
  • Troch ynstallaasje set -o pipefail de koade foar foltôging fan pipeline sil net-nul wêze (ferkeard) as it resultaat net-nul is fan op syn minst ien elemint, en dan sil it wurkje || echo "Please use GCC or CLANG compatible compiler".

D'r kinne fariaasjes wêze ôfhinklik fan hoe't it skriuwerprogramma wurket mei sinjalen. Bygelyks, it programma kin abnormaal beëinigje (mei automatyske generaasje fan in net-nul/flater beëinigingsstatus), of write() sil it resultaat weromjaan fan it skriuwen fan minder bytes dan oanfrege en ynsteld errno = EPIPE.

Wa is skuldich?

Yn it beskreaune gefal bytsje fan alles. Flater behanneling yn cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) is net oerstallich. Yn in protte gefallen is it better om te fersin oan 'e kant fan foarsichtigens, hoewol't dit docht bleat ynienen gebreken yn in boilerplate ûntwurpen foar tradisjoneel gedrach.

Wat moat ik dwaan?

Ferkeard:

fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"

Korrekt:

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

    Hjir sil it betiid sluten fan in piip wurde behannele troch de kommando-tolk by it betsjinjen fan de nestele pipeline ("binnen" de haakjes). As gefolch, as fortune sil rapportearje in flater skriftlik nei in sletten kanaal yn 'e status, dan de útfier || echo "Ошибка" it sil net komme oeral, sûnt it kanaal is al sletten.

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

    Hjir is it nut cat fungearret as in damper omdat it negearret de flater EPIPE by weromlûken. Dit is genôch foar no konklúzje fortune lyts (ferskate rigels) en past yn it kanaal buffer (fan 512 bytes to ≈64K, yn de measte OS ≥4K). Oars kin it probleem weromkomme.

Hoe korrekt te ferwurkjen EPIPE en oare opname flaters?

D'r is gjin ienige juste oplossing, mar d'r binne ienfâldige oanbefellings:

  • EPIPE ferplicht moat wurde ferwurke (en wjerspegele yn 'e útgongsstatus) by it útfieren fan gegevens dy't yntegriteit nedich binne. Bygelyks, tidens de wurking fan argiven of reservekopy-nutsbedriuwen.
  • EPIPE better om te negearjen by it werjaan fan ynformaasje en helpberjochten. Bygelyks by it werjaan fan ynformaasje oer opsjes --help of --version.
  • As de koade wurdt ûntwikkele kin brûkt wurde yn in pipeline foar | head, dan EPIPE It is better om te negearjen, oars is it better om te ferwurkjen en te reflektearjen yn 'e útgongsstatus.

Ik wol dizze gelegenheid brûke om myn tankberens út te sprekken oan de teams MCST и altlinux foar grut produktyf wurk. Jo fêststelling is geweldig!
Keep it up Camarades, up gearkomsten yn 'e hjerst!

Спасибо berez foar it korrigearjen fan typfouten en flaters.
KDPV út Georgy A.

Boarne: www.habr.com

Add a comment