Heisenbug moja zaidi nyuma ya mamba

Heisenbug moja zaidi nyuma ya mamba

$> set -o pipefail

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

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

Hapa fortune mpango wa masharti bila exit(rand()).

Unaweza elezea? kuna nini hapa?

Mchepuko wa sauti-kihistoria

Nilianza kufahamiana na Heisenbug hii robo ya karne iliyopita. Kisha kwa lango katika FaxNET ilikuwa ni lazima kuunda huduma kadhaa kupitia mabomba "kucheza cheki" chini ya FreeBSD. Kama ilivyotarajiwa, nilijiona kuwa mtayarishaji programu mahiri na mwenye uzoefu. Kwa hivyo, nilikusudia kufanya kila kitu kwa uangalifu na kwa uangalifu iwezekanavyo, nikizingatia sana kushughulikia makosa ...

Uzoefu wangu wa awali wa kushughulika na hitilafu katika sendmail na uucp/uupc uliongeza kwa bidii yangu katika "kushughulikia makosa kikamilifu." Hakuna maana katika kupiga mbizi katika maelezo ya hadithi hiyo, lakini nilijitahidi na Heisenbug hii kwa wiki mbili kwa saa 10-14. Kwa hivyo, ilikumbukwa, na jana jamaa huyu wa zamani aliacha kutembelea tena.

Jibu la TL; DR

Huduma head Unaweza funga kituo kutoka fortune mara moja mara tu anaposoma mstari wa kwanza. Kama fortune hutoa zaidi ya mstari mmoja, kisha simu inayolingana write() itarudisha hitilafu au itaripoti kuwa kaiti chache hutolewa kuliko ilivyoombwa. Kwa upande wake, imeandikwa na utunzaji wa makosa kwa uangalifu fortune ina haki ya kuakisi hali hii katika hali yake ya kutoka. Kisha kutokana na ufungaji set -o pipefail itafanya kazi || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ".

Hata hivyo, head inaweza isifike kwa wakati karibu kabla fortune itamaliza kutoa data. Kisha itafanya kazi && echo "ПовСзло!".

Katika moja ya leo yangu GNUMakefile kuna moja kipande:

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

Imetafsiriwa kwa mwanadamu

Ni kawaida hapa kwa Tengeneza GNU ΠΈ bash kwa njia ya mkusanyaji kwa kutumia chaguo --version inauliza yeye ni nani, na ikiwa chaguo halihimiliwi, basi stub imeingizwa "Tafadhali tumia kikusanyaji kinacholingana cha GCC au CLANG".

kama boilerplate inaweza kupatikana popote. Ilionekana mahali hapa muda mrefu uliopita na ilifanya kazi kikamilifu kila mahali (Linux, Solaris, OSX, FreeBSD, WSL na kadhalika.). Lakini jana katika altlinux kwenye jukwaa Elbrus 2000 (E2K) Niligundua:

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

Kwa kweli, sikumtambua mara moja β€œrafiki” wangu wa zamani. Zaidi ya hayo, mradi tayari umejaribiwa mara nyingi kwenye Elbrus na chini ya usambazaji mwingi tofauti, pamoja na Alt. Na wakusanyaji mbalimbali, matoleo ya GNU Tengeneza na bash. Kwa hivyo, sikutaka kuona kosa langu hapa.

Wakati wa kujaribu kuzalisha tatizo na/au kuelewa kilichokuwa kikiendelea, mambo ya ajabu zaidi yalianza kutokea.
Tahajia ya mstari wa amri:

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

Kila mara ingetokeza maandishi ya ziada, basi sivyo... Mara nyingi moja ya chaguo ingeshikamana kwa muda mrefu, lakini ikiwa ungepiga kwa muda mrefu, utapata zote mbili!

Bila shaka, strace kila kitu chetu! Na baada ya kuandika maandishi ya maandishi, lakini bila kuwa na wakati wa kubonyeza Enter, nilimtambua rafiki yangu wa zamani Bw. Heisenbug, na watengenezaji. mkusanyaji mimi mwenyewe miaka 25 iliyopita, Nostalgie… Na niliamua kuwa na huzuni na kuandika barua hii πŸ˜‰

Kwa njia, kama mtu yeyote anayejiheshimu Heisenbug, chini strace haipendi kuzaliana.

Kwa hiyo nini kinaendelea?

  • Huduma head ina haki (au tuseme, hata inalazimishwa) kufunga chaneli inayosomwa mara tu inaposoma nambari iliyoombwa ya mistari.
  • Mwandishi wa programu ya kutengeneza data (katika kesi hii cc) Unaweza chapisha mistari mingi na bure fanya hivyo kupitia simu nyingi write().
  • Kama msomaji atakuwa na muda wa kufunga chaneli upande wake kabla ya mwisho wa kurekodi upande wa mwandishi, kisha mwandishi atapata makosa.
  • Mpango wa mwandishi ana haki zote mbili hupuuza hitilafu ya uandishi wa kituo na kuiakisi katika msimbo wako wa kukamilisha.
  • Kutokana na ufungaji set -o pipefail msimbo wa kukamilisha bomba hautakuwa sifuri (makosa) ikiwa matokeo sio sifuri kutoka kwa angalau kipengele kimoja, na kisha itafanya kazi. || echo "Please use GCC or CLANG compatible compiler".

Kunaweza kuwa na tofauti kulingana na jinsi programu ya mwandishi inavyofanya kazi na ishara. Kwa mfano, programu inaweza kusitishwa kwa njia isiyo ya kawaida (kwa kuzalisha kiotomatiki hali isiyo ya sufuri/hitilafu ya kukomesha), au write() itarudisha matokeo ya kuandika baiti chache kuliko ilivyoombwa na kuweka errno = EPIPE.

Nani ana hatia?

Katika kesi iliyoelezwa kidogo ya kila kitu. Hitilafu katika kushughulikia cc (lcc:1.23.20:Sepβ€”4-2019:e2k-v3-linux) sio isiyohitajika. Katika hali nyingi ni bora kukosea kwa tahadhari, ingawa hii inafichua dosari za ghafla kwenye sahani iliyoundwa kwa tabia ya kitamaduni.

Nini cha kufanya?

Si sahihi:

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

Haki:

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

    Hapa, kufungwa kwa bomba mapema kutashughulikiwa na mkalimani wa amri wakati wa kuhudumia bomba lililowekwa kiota ("ndani" ya mabano). Ipasavyo, ikiwa fortune itaripoti hitilafu katika maandishi kwa kituo kilichofungwa katika hali, kisha matokeo || echo "Ошибка" haitafika popote, kwani kituo tayari kimefungwa.

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

    Hapa kuna matumizi cat hufanya kama damper kwa sababu inapuuza kosa EPIPE baada ya kujiondoa. Hii inatosha kwa hitimisho sasa fortune ndogo (mistari kadhaa) na inafaa katika bafa ya kituo (kutoka baiti 512 hadi β‰ˆ64K, katika OS nyingi β‰₯4K). Vinginevyo tatizo linaweza kurudi.

Jinsi ya kusindika kwa usahihi EPIPE na makosa mengine ya kurekodi?

Hakuna suluhisho moja sahihi, lakini kuna mapendekezo rahisi:

  • EPIPE inahitajika lazima kuchakatwa (na kuonyeshwa katika hali ya kuondoka) wakati wa kutoa data inayohitaji uadilifu. Kwa mfano, wakati wa uendeshaji wa kumbukumbu au huduma za chelezo.
  • EPIPE bora kupuuza wakati wa kuonyesha habari na ujumbe msaidizi. Kwa mfano, wakati wa kuonyesha habari juu ya chaguzi --help au --version.
  • Ikiwa nambari inayotengenezwa inaweza kutumika katika bomba hapo awali | head, Basi EPIPE Ni bora kupuuza, vinginevyo ni bora kusindika na kutafakari katika hali ya kuondoka.

Napenda kuchukua fursa hii kutoa shukurani zangu kwa timu MCST ΠΈ altlinux kwa kazi kubwa yenye tija. Uamuzi wako ni wa kushangaza!
Keep it up Camarades, up mikutano katika vuli!

Shukrani berez kwa kurekebisha makosa na makosa.
KDPV kutoka George A.

Chanzo: mapenzi.com

Kuongeza maoni