Vēl viens Heisenbugs garām krokodilam

Vēl viens Heisenbugs garām krokodilam

$> set -o pipefail

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

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

Šeit fortune nosacītā programma bez exit(rand()).

Vai tu vari paskaidrot? kas te vainas?

Liriski vēsturiska atkāpe

Pirmo reizi ar šo Heisenbugu iepazinos pirms ceturtdaļgadsimta. Tad vārtejai FaxNET bija nepieciešams izveidot vairākas utilītas, izmantojot caurules "spēlējot dambreti" zem FreeBSD. Kā gaidīts, es uzskatīju sevi par progresīvu un diezgan pieredzējušu programmētāju. Tāpēc biju iecerējis visu darīt pēc iespējas rūpīgi un rūpīgi, īpašu uzmanību pievēršot kļūdu apstrādei...

Mana iepriekšējā pieredze darbā ar kļūdām programmā Sendmail un uucp/uupc palielināja manu centību “rūpīgā kļūdu apstrādē”. Nav jēgas ienirt šī stāsta detaļās, bet es cīnījos ar šo Heisenbugu divas nedēļas 10–14 stundas. Tāpēc atcerējās, un vakar šī senā paziņa piestāja vēlreiz ciemos.

TL;DR Atbilde

Lietderība head var aizveriet kanālu no fortune сразу tiklīdz viņš izlasa pirmo rindiņu. Ja fortune izvada vairāk nekā vienu līniju, pēc tam atbilstošo zvanu write() atgriezīs kļūdu vai ziņos, ka tiek izvadīts mazāk baitu, nekā pieprasīts. Savukārt rakstīts ar rūpīgu kļūdu apstrādi fortune ir tiesības atspoguļot šo situāciju savā izejas statusā. Tad uzstādīšanas dēļ set -o pipefail strādās || echo "Вы проиграли".

Tomēr, head var nepaspēt laikā aizvērt pirms fortune beigs izvadīt datus. Tad tas darbosies && echo "Повезло!".

Vienā no manām šodienas GNUMakefile tur ir viens fragments:

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

Tulkots cilvēkā

Šeit tas ir ierasts GNU Marka и stipri iesist kompilatora veidā, izmantojot opciju --version tas jautā, kas viņš ir, un, ja opcija netiek atbalstīta, tad tiek ievietots stubs "Lūdzu, izmantojiet ar GCC vai CLANG saderīgu kompilatoru".

tāpat katlu plāksne var atrast jebkur. Tas parādījās šajā vietā jau sen un lieliski strādāja visur (Linux, Solaris, OSX, FreeBSD, WSL utt.). Bet vakar iekšā altlinux uz platformas Elbrus 2000 (E2K) ES pamanīju:

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

Atklāti sakot, es uzreiz neatpazinu savu veco "paziņu". Turklāt projekts jau ir daudzkārt pārbaudīts uz Elbrus un daudzos dažādos izplatījumos, tostarp Alt. Ar dažādiem kompilatoriem, GNU Make un bash versijām. Tāpēc es negribēju šeit redzēt savu kļūdu.

Mēģinot atveidot problēmu un/vai saprast, kas notiek, sāka notikt dīvainākas lietas.
Komandrindas pareizrakstība:

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

Ik pa brīdim radīja lieku tekstu, tad nē... Bieži vien kāds no variantiem pielipa diezgan ilgi, bet, ja pabāza ilgāk, vienmēr dabūja abus!

Protams, strace mūsu viss! Un ierakstījis strace tirādi, bet man nebija laika nospiest Enter, es atpazinu savu veco draugu Mr. Heizenbuga un izstrādātājus. kompilators es pirms 25 gadiem, Nostalģija… Un es nolēmu būt skumji un uzrakstīt šo piezīmi 😉

Starp citu, kā jau jebkurš sevi cienošs Heisenbugs, zem strace dod priekšroku nevairot.

Tātad, kas notiek?

  • Lietderība head ir tiesības (pareizāk sakot, ir pat spiestas) aizvērt nolasīto kanālu, tiklīdz tas nolasa pieprasīto rindu skaitu.
  • Datu ģenerēšanas programmas rakstītājs (šajā gadījumā cc) var izdrukāt vairākas rindiņas un bezmaksas dariet to, izmantojot vairākus zvanus write().
  • Ja lasītājam būs laiks aizvērt kanālu savā pusē pirms ierakstīšanas beigām rakstītāja pusē, tad rakstītājs saņems kļūdu.
  • Rakstnieku programma ir tiesības abi ignorē kanāla rakstīšanas kļūdu un atspoguļo to pabeigšanas kodā.
  • Sakarā ar uzstādīšanu set -o pipefail konveijera pabeigšanas kods nebūs nulle (kļūdains), ja vismaz viena elementa rezultāts nav nulle, un tad tas darbosies || echo "Please use GCC or CLANG compatible compiler".

Var būt atšķirības atkarībā no tā, kā rakstīšanas programma darbojas ar signāliem. Piemēram, programma var beigties neparasti (ar automātisku pārtraukšanas statusa, kas nav nulle/kļūda, ģenerēšanu), vai write() atgriezīs rezultātu, rakstot mazāk baitu, nekā pieprasīts un iestatīts errno = EPIPE.

Kurš vainīgs?

Aprakstītajā gadījumā mazliet no visa. Rīkojoties, radās kļūda cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) nav lieks. Daudzos gadījumos labāk ir kļūdīties piesardzīgi, lai gan tas atklāj pēkšņus defektus tradicionālajai uzvedībai paredzētajā plāksnē.

Ko darīt?

Nepareizi:

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

Pareizi:

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

    Šeit agru caurules aizvēršanu apstrādās komandu tulks, apkalpojot ligzdoto cauruļvadu (iekavās). Attiecīgi, ja fortune ziņos par kļūdu rakstveidā uz slēgtu kanālu statusā, pēc tam izvadē || echo "Ошибка" tas nekur nenonāks, jo kanāls jau ir slēgts.

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

    Šeit ir lietderība cat darbojas kā slāpētājs, jo ignorē kļūdu EPIPE pēc izstāšanās. Tagad secinājumam ar to pietiek fortune mazs (vairākas rindas) un iekļaujas kanāla buferī (no 512 baitiem līdz ≈64K, lielākajā daļā OS ≥4K). Pretējā gadījumā problēma var atgriezties.

Kā pareizi apstrādāt EPIPE un citas ierakstīšanas kļūdas?

Nav viena pareizā risinājuma, taču ir vienkārši ieteikumi:

  • EPIPE nepieciešams ir jāapstrādā (un atspoguļots izejas statusā), izvadot datus, kuriem nepieciešama integritāte. Piemēram, arhivētāju vai rezerves utilītu darbības laikā.
  • EPIPE labāk ignorēt kad tiek parādīta informācija un palīgziņojumi. Piemēram, kad tiek parādīta informācija par opcijām --help vai --version.
  • Ja izstrādāto kodu var izmantot konveijerā pirms | headtad EPIPE Labāk ir ignorēt, pretējā gadījumā labāk ir apstrādāt un atspoguļot izejas statusu.

Vēlos izmantot iespēju, lai izteiktu pateicību komandām MCST и altlinux par lielisku produktīvu darbu. Jūsu apņēmība ir pārsteidzoša!
Tā turpināt, Camarades, tā tikšanās rudenī!

Paldies berez drukas kļūdu un kļūdu labošanai.
KDPV no Džordžs A.

Avots: www.habr.com

Pievieno komentāru