Bir Heisenbug daha timsahı geçti

Bir Heisenbug daha timsahı geçti

$> set -o pipefail

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

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

öyle fortune koşulsuz program exit(rand()).

Açıklayabilir misin? burada sorun ne?

Lirik-tarihsel ara söz

Bu Heisenbug'la ilk kez çeyrek asır önce tanıştım. Daha sonra FaxNET'teki ağ geçidi için çeşitli yardımcı programların oluşturulması gerekliydi. borular FreeBSD altında "dama oynamak". Beklendiği gibi kendimi ileri düzey ve oldukça deneyimli bir programcı olarak görüyordum. Bu nedenle, her şeyi mümkün olduğunca dikkatli ve dikkatli bir şekilde yapmaya niyetlendim, hata yönetimine özellikle dikkat ettim...

Sendmail ve uucp/uupc'deki hatalarla uğraşma konusundaki önceki deneyimim, "ayrıntılı hata yönetimi" konusundaki titizliğime katkıda bulundu. O hikayenin detaylarına dalmanın anlamı yok ama ben bu Heisenbug ile iki hafta boyunca 10-14 saat uğraştım. Bu nedenle hatırlandı ve dün bu eski tanıdık tekrar ziyarete uğradı.

TL;DR Cevap

Yarar head kutu kanalı kapat fortune kerede ilk satırı okur okumaz. Eğer fortune birden fazla satırın çıktısını verir, ardından karşılık gelen çağrı write() bir hata döndürecek veya istenenden daha az baytın çıktılandığını bildirecektir. Buna karşılık, dikkatli hata yönetimi ile yazılmıştır fortune bu durumu çıkış durumuna yansıtma hakkına sahiptir. Daha sonra kurulum nedeniyle set -o pipefail çalışacak || echo "Вы проиграли".

Bununla birlikte, head zamanında yetişemeyebilirsin önce kapat fortune veri çıktısını tamamlayacaktır. O zaman işe yarayacak && echo "Повезло!".

Bugünlerimden birinde GNUMakefile böyle var bir parça:

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

İnsana tercüme

Burada yaygın bir durum GNU Yap и darbe seçeneği kullanarak derleyici yolunda --version kim olduğunu sorar ve eğer seçenek desteklenmiyorsa bir taslak eklenir "Lütfen GCC veya CLANG uyumlu derleyiciyi kullanın".

gibi Basmakalıp her yerde bulunabilir. Uzun zaman önce burada ortaya çıktı ve her yerde mükemmel çalıştı (Linux, Solaris, OSX, FreeBSD, WSL vesaire.). Ama dün altlinux platformda Elbruz 2000 (E2K) Farkettim:

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

Açıkçası eski “tanıdığımı” hemen tanıyamadım. Üstelik proje halihazırda Elbrus'ta ve Alt dahil birçok farklı dağıtım altında birçok kez test edildi. Çeşitli derleyiciler, GNU Make ve bash sürümleriyle. Bu nedenle hatamı burada görmek istemedim.

Sorunu yeniden oluşturmaya ve/veya neler olduğunu anlamaya çalışırken daha tuhaf şeyler olmaya başladı.
Komut satırı büyüsü:

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

Arada sırada fazladan metin üretiyordu, sonra çıkmıyordu... Çoğu zaman seçeneklerden biri oldukça uzun bir süre geçerli olurdu, ancak daha uzun süre uğraşırsanız her zaman ikisini de elde edersiniz!

Tabii ki, strace her şeyimiz! Ve bir tirad yazdıktan sonra Enter'a basmaya zamanım olmadığından, eski dostum Bay Heisenbug'u ve geliştiricileri tanıdım. derleyici 25 yıl önceki kendim Nostalji… Ben de üzülüp bu notu yazmaya karar verdim 😉

Bu arada, kendine saygısı olan herkes gibi Heisenbugaltında strace ürememeyi tercih ediyor.

Yani, ne oluyor?

  • Yarar head İstenilen satır sayısını okur okumaz okunmakta olan kanalı kapatma hakkına sahiptir (veya daha doğrusu zorlanır).
  • Veri üreten program yazarı (bu durumda cc) kutu birden fazla satır yazdırın ve özgür bunu birden fazla arama yoluyla yapın write().
  • Eğer okuyucunun, yazar tarafındaki kayıt bitmeden kendi tarafındaki kanalı kapatmak için zamanı olacak, ardından yazar bir hata alacaktır.
  • Yazar programı hakkı var her ikisi de kanal yazma hatasını yok sayar ve bunu tamamlama kodunuza yansıtır.
  • Kurulum nedeniyle set -o pipefail sonuç en az bir öğeden sıfır değilse boru hattı tamamlama kodu sıfır olmayacak (hatalı) ve sonra çalışacak || echo "Please use GCC or CLANG compatible compiler".

Yazar programının sinyallerle nasıl çalıştığına bağlı olarak farklılıklar olabilir. Örneğin, program anormal bir şekilde sona erebilir (sıfır olmayan/hatalı sonlandırma durumunun otomatik olarak oluşturulmasıyla) veya write() istenenden daha az bayt yazmanın sonucunu döndürecek ve ayarlayacaktır errno = EPIPE.

Kim suçlu nedir?

Açıklanan durumda Her şeyden biraz. Hata işleme cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) değil gereksiz. Çoğu durumda dikkatli olmak daha iyidir, ancak bu, geleneksel davranış için tasarlanmış standartlarda ani kusurları ortaya çıkarır.

Ne yapmalı?

yanlış:

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

düzeltin:

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

    Burada, iç içe geçmiş boru hattına servis yapılırken bir borunun erken kapatılması komut yorumlayıcısı tarafından yönetilecektir ("parantez içinde"). Buna göre eğer fortune durumdaki kapalı bir kanala yazılı olarak bir hata bildirecek, ardından çıktı || echo "Ошибка" Kanal zaten kapalı olduğu için hiçbir yere varamayacak.

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

    İşte yardımcı program cat hatayı görmezden geldiği için sönümleyici görevi görür EPIPE çekilme üzerine. Şimdilik bu kadar sonuç yeterli fortune küçüktür (birkaç satır) ve kanal arabelleğine sığar (512 bayttan ≈64K'ya, çoğu işletim sisteminde ≥4K). Aksi takdirde sorun geri dönebilir.

Doğru şekilde nasıl işlenir EPIPE ve diğer kayıt hataları?

Tek bir doğru çözüm yoktur ancak basit öneriler vardır:

  • EPIPE gereken işlenmeli (ve çıkış durumuna yansıtılır) bütünlük gerektiren verilerin çıktısı alınırken. Örneğin arşivleyicilerin veya yedekleme yardımcı programlarının çalışması sırasında.
  • EPIPE görmezden gelmek daha iyi bilgi ve yardımcı mesajları görüntülerken. Örneğin seçeneklerle ilgili bilgileri görüntülerken --help veya --version.
  • Geliştirilmekte olan kod daha önce bir boru hattında kullanılabiliyorsa | headSonra EPIPE Görmezden gelmek daha iyidir, aksi takdirde işlenip çıkış durumuna yansıtılması daha iyidir.

Bu fırsatı kullanarak ekiplere şükranlarımı sunmak isterim. MCST и altlinux büyük verimli çalışma için. Kararlılığınız muhteşem!
Devam edin Camarades, yukarı sonbaharda toplantılar!

Teşekkürler berez yazım hatalarını ve hataları düzeltmek için.
KDPV'den George A.

Kaynak: habr.com

Yorum ekle