Még egy Heisenbug a krokodil mellett

Még egy Heisenbug a krokodil mellett

$> set -o pipefail

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

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

Itt fortune feltételes program nélkül exit(rand()).

Meg tudod magyarázni? mi a baj itt?

Lírai-történeti kitérő

Ezzel a Heisenbuggal negyedszázaddal ezelőtt ismerkedtem meg először. Ezután a FaxNET-ben lévő átjáróhoz több segédprogramot kellett létrehozni ezen keresztül csövek "dámajáték" FreeBSD alatt. Ahogy az várható volt, haladó és meglehetősen tapasztalt programozónak tartottam magam. Ezért szándékomban állt mindent a lehető legkörültekintőbben és körültekintően megtenni, különös figyelmet fordítva a hibakezelésre...

A sendmail és az uucp/uupc hibáinak kezelésével kapcsolatos korábbi tapasztalataim növelték az „alapos hibakezelés” terén végzett szorgalmamat. Nincs értelme belemerülni a történet részleteibe, de két hétig 10-14 órán keresztül küzdöttem ezzel a Heisenbug-gal. Ezért emlékeztek rá, és tegnap ez a régi ismerős ismét beugrott, hogy meglátogassa.

TL;DR Válasz

Hasznosság head képes zárja be a csatornát fortune сразу amint elolvassa az első sort. Ha fortune egynél több sort ad ki, majd a megfelelő hívást write() hibát ad vissza, vagy azt jelenti, hogy a kértnél kevesebb bájt kerül kiadásra. Viszont gondos hibakezeléssel írva fortune jogában áll ezt a helyzetet a kilépési státuszában tükrözni. Aztán telepítés miatt set -o pipefail működni fog || echo "Вы проиграли".

Azonban, head lehet, hogy nem ér rá időben előtt zárja be fortune befejezi az adatok kiadását. Akkor menni fog && echo "Повезло!".

Az egyik mai napon GNUMakefile van ilyen töredék:

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

Emberre lefordítva

Ez gyakori itt azért GNU gyártmány и horpadás fordítói módon az opció használatával --version megkérdezi, hogy ki ő, és ha az opció nem támogatott, akkor beszúr egy csonkot "Kérjük, használjon GCC vagy CLANG kompatibilis fordítót".

mint boilerplate bárhol megtalálható. Nagyon régen jelent meg ezen a helyen, és mindenhol tökéletesen működött (Linux, Solaris, OSX, FreeBSD, WSL stb.). De tegnap bent altlinux a platformon Elbrus 2000 (E2K) Észrevettem:

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

Őszintén szólva, nem ismertem fel azonnal régi „ismerősömet”. Sőt, a projektet már sokszor tesztelték Elbruson és számos különféle disztribúció alatt, beleértve az Alt-ot is. Különféle fordítókkal, a GNU Make és a bash verzióival. Ezért nem akartam itt látni a hibámat.

Amikor megpróbáltuk reprodukálni a problémát és/vagy megérteni, hogy mi történik, furcsább dolgok kezdtek történni.
Parancssori varázslat:

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

Időnként plusz szöveget produkált, aztán nem... Sokszor az egyik opció elég sokáig kitartott, de ha hosszabban piszkáltál, mindig megkaptad mindkettőt!

Természetesen, strace mindenünk! És miután beírtam egy strace tirádát, de nem volt időm megnyomni az Entert, felismertem régi barátomat, Mr. Heisenbug-ot és a fejlesztőket. fordítóprogram én 25 évvel ezelőtt, Nosztalgia… És úgy döntöttem, hogy szomorú leszek és megírom ezt a jegyzetet 😉

Mellesleg, mint minden önbecsülő Heisenbugalatt strace inkább nem szaporodik.

Nos, miújság?

  • Hasznosság head joga van (vagy inkább kénytelen) bezárni az olvasott csatornát, amint az beolvassa a kívánt számú sort.
  • Az adatgeneráló programíró (ebben az esetben cc) képes több sort nyomtatni és ingyenes ezt több hívás útján teheti meg write().
  • Ha az olvasónak lesz ideje bezárni a csatornát az oldalán, mielőtt az írói oldalon a felvétel vége, akkor az író hibaüzenetet kap.
  • Írói program jogosult mindkettő figyelmen kívül hagyja a csatornaírási hibát, és tükrözi azt a befejezési kódban.
  • Beépítés miatt set -o pipefail a folyamat befejezési kódja nem nulla (hibás), ha az eredmény legalább egy elemből nem nulla, és akkor működik || echo "Please use GCC or CLANG compatible compiler".

Eltérések lehetnek attól függően, hogy az íróprogram hogyan működik a jelekkel. Például előfordulhat, hogy a program rendellenesen leáll (nem nulla/hiba leállási állapot automatikus generálásával), vagy write() a kértnél és beállítottnál kevesebb bájt írásának eredményét adja vissza errno = EPIPE.

Ki a hibás?

A leírt esetben egy kicsit mindenből. Hiba a kezelés során cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) nem redundáns. Sok esetben jobb az óvatosság mellett tévedni, bár ez felfedi a hagyományos viselkedésre tervezett kazánok hirtelen hibáit.

Mit kell tenni?

Rossz:

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

korrigálni:

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

    Itt a cső korai zárását a parancsértelmező kezeli a beágyazott folyamat kiszolgálása során (a zárójelben "belül"). Ennek megfelelően, ha fortune írási hibát jelez egy zárt csatornának az állapotában, majd a kimeneten || echo "Ошибка" nem jut sehova, mivel a csatorna már bezárt.

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

    Itt van a segédprogram cat csillapítóként működik, mert figyelmen kívül hagyja a hibát EPIPE visszavonáskor. Ez most elég a következtetéshez fortune kicsi (több sor), és elfér a csatornapufferben (512 bájttól ≈64K-ig, a legtöbb operációs rendszerben ≥4K). Ellenkező esetben a probléma visszatérhet.

Hogyan kell helyesen feldolgozni EPIPE és egyéb felvételi hibák?

Nincs egyetlen helyes megoldás, de vannak egyszerű ajánlások:

  • EPIPE kötelező fel kell dolgozni (és tükröződik a kilépési állapotban) az integritást igénylő adatok kiadásakor. Például archiválók vagy biztonsági mentési segédprogramok működése közben.
  • EPIPE jobb figyelmen kívül hagyni információk és segédüzenetek megjelenítésekor. Például amikor információkat jelenít meg a lehetőségekről --help vagy --version.
  • Ha a fejlesztés alatt álló kód egy folyamatban használható korábban | head, Akkor EPIPE Jobb figyelmen kívül hagyni, különben jobb feldolgozni és tükrözni a kilépési állapotot.

Szeretném megragadni az alkalmat, hogy kifejezzem hálámat a csapatoknak MCST и altlinux nagyszerű eredményes munkáért. Elképesztő az elszántságod!
Csak így tovább Camarades, csak így tovább találkozások ősszel!

Köszönöm berez az elírások és hibák kijavítására.
KDPV innen György A.

Forrás: will.com

Hozzászólás