1 puan yazan GN⁺ 5 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Büyük bir girdide soruna yol açan kısmı bulurken test case reducer’lar girdiyi otomatik olarak küçülterek hata ayıklamayı kolaylaştırır
  • Reducer, bir programı, girdiyi ve bir ilginçlik testini alır; daha kısa aday girdilerin aynı sorunu yeniden üretip üretmediğini tekrar tekrar kontrol eder
  • Basit bir satır silme reducer’ı bile /usr/share/dict/words içinden tek bir uzun kelimeyi bırakabilir ve C örneğinde 10 saniyeden kısa sürede 78 satırı 54 satıra indirebilir
  • İlginçlik testi; aşırı küçültme, yavaş çalışma, sonsuz çalışma ve paralel yürütme ortamları nedeniyle doğru ve hızlı yazılmalıdır
  • Girdi uzunluğunun yanı sıra hata oluşma sıklığı veya yürütme izi uzunluğu gibi ölçütleri ilginçlik testine eklemek, deterministik olmayan hatalar ve büyük iz günlüklerini hata ayıklamada yardımcı olur

Test case küçültme

  • Büyük bir girdide program çöküyorsa ve girdinin hangi kısmının buna neden olduğu bilinmiyorsa, girdiyi küçültmek sorunun kaynağını anlamayı kolaylaştırır
  • Elle küçültme, bir metin düzenleyicide girdinin bazı bölümlerini silip aynı çökmenin sürüp sürmediğini kontrol etme yöntemidir
  • Elle küçültmede insanlar birçok silme fırsatını kolayca kaçırabilir ve silme sonrasında program normal şekilde sonlanabilir ya da başka, normal bir hata verebilir
  • Birbirinden uzak A ve B bölümlerinin birlikte silinmesi gerektiğinde arama uzayı ciddi biçimde büyür

Test case reducer’ın temel yapısı

  • Test case reducer, bir programı, girdiyi ve bir ilginçlik testini alıp girdiyi daha kısa hale getiren bir araçtır
  • İlginçlik testi, küçültülmüş girdinin ilgilenilen hatayı yeniden üretmesi durumunda 0 döndürür; aksi halde 0 dışı bir değer döndürür
  • Test case reducer’larda %95–99 küçültme yaygındır ve hata ayıklamayı çok daha kolay hale getirebilir
  • Reducer, girdinin hangi kısmının çıkarılması gerektiğini anlamsal olarak anlamasa da çalışabilir
  • Basit reducer örneği

    • Örnek program bir dosyadan satırları okur ve uzunluğu 25’ten büyük bir satır varsa Word too long çıktısını verir
    • İlginçlik testi, program çıktısında Word too long varsa 0, yoksa 1 döndürür
    • Basit Python reducer’ı girdiyi satır satır okur, bir satırı silmiş aday girdiyi geçici dosyaya yazar ve ardından ilginçlik testini çalıştırır
    • Aday girdi ilginçse mevcut girdi adayla değiştirilir ve artık daha fazla küçültülemiyorsa sonuç stdout’a yazdırılır
    • /usr/share/dict/words üzerinde çalıştırıldığında sonuç olarak yalnızca antidisestablishmentarianism kalır

Daha güçlü reducer’lar ve Shrink Ray

  • 78 satırlık C programı örneği, FAST=0 ve FAST=1 ayarlarında farklı çıktı üretilmesi sorununu ele alır
  • İlginçlik testi, iki ayarla derleme yaptıktan sonra yalnızca FAST=0 çıktısı 0d754a56 olduğunda ve FAST=1 çıktısı bu değerden farklı olduğunda başarılı sayılır
  • Basit reducer, 10 saniyeden kısa sürede 78 satırlık C girdisini 54 satıra indirerek satır bazında yaklaşık %30 küçültme sağlar
  • Her ilginç aday bulunduğunda ilk satırdan yeniden silmeye başlamak için i=0 eklenirse çalışma süresi neredeyse 10 kat artar ama 3 satır daha azaltılır
  • Shrink Ray, birden fazla küçültme kuralı ve paralel yürütme sunar; --no-clang-delta eklendiğinde C hakkında özel bilgi kullanmaz
  • Shrink Ray yaklaşık 15 dakika sonra girdiyi bayt bazında %60’tan fazla küçülttü; başka bir örnekte ise yaklaşık 20 dakika sonra %90 küçültmeye ulaşıp ardından %99’a kadar indirdi
  • Shrink Ray standart yorum sözdizimini bilir ve erken aşamada bunları kaldırmayı dener; ayrıca tamsayıları daha küçük değerlere indirmeyi de dener

İlginçlik testi yazmanın zorlukları

  • Test case reducer, ilginçlik testini harfiyen izlediğinden, test yanlışlıkla başarılı olursa istenenden daha fazla küçülmeye yol açan aşırı küçültme ortaya çıkabilir
  • Shrink Ray, ilginçlik testinin boş girdiyi kabul edip etmediğini açıkça kontrol eder ve bu tür durumlar sanıldığından sık görülebilir
  • C örneğinde yalnızca iki çıktının farklı olup olmadığına bakılırsa, önemsiz ya da yanıltıcı çıktı farkları ilginç girdi olarak sınıflandırılabilir
  • test "$slow_out" = "0d754a56" kontrolü, yavaş sürümün gerçekten beklenen davranışı verip vermediğini doğrulayarak aşırı küçültme olasılığını azaltır
  • Hız ve zaman aşımı

    • İlginçlik testi hızlıysa reducer saniyede yüzlerce kez çalışabilir
    • Orta boy örnekler bile yüz binlerce küçültme denemesine yol açabildiğinden, ilginçlik testini optimize etmek toplam süre üzerinde büyük etkiye sahiptir
    • Otomatik core dump üretimini kapatarak ilginçlik testini yaklaşık 3 kat hızlandıran bir örnek vardır
    • Reducer, i-=1 gibi bir satırı kaldırıp sonlanan bir programı sonsuza kadar çalışan bir programa dönüştürebilir
    • Program 0,1 saniyede çalışırken zaman aşımı 60 saniye olarak ayarlanırsa toplam küçültme süreci ciddi biçimde yavaşlar
    • Hızlı programlarda timeout değerini 1–2 saniyeye yuvarlamak, diğer durumlarda ise ilk çalışma süresinin yaklaşık 1,5–2 katını kullanmak yaygın bir yaklaşımdır
  • Paralel yürütme

    • Shrink Ray gibi reducer’lar ilginçlik testlerini paralel çalıştırır
    • Shrink Ray her ilginçlik testini geçici bir dizinde çalıştırır ve o dizini otomatik olarak temizler
    • Ancak yalnızca geçici dizinler her zaman yeterli olmayabilir ve gerekli önlemler örneğe göre değişir

İlginçlik testiyle determinizm sağlama

  • Örnek parça, len([])==0 nedeniyle sıfıra bölme hatası üretir; ancak random.random() < 0.33 koşulu yüzünden sorun yalnızca çalıştırmaların yaklaşık üçte birinde ortaya çıkar
  • Deterministik olmayan hatalar, hatanın rastgele görünüp kaybolmasına neden olarak hipotez test etmeyi daha zor ve daha uzun hale getirir
  • Reducer, random.random() çağrısını kaldırır ya da koşul ifadesini değiştirirse deterministik olmayan hata deterministik bir hataya dönüşebilir
  • Gerçek dünyadaki determinizmsizlik çoğu zaman girdinin birden çok bölümünün olumsuz etkileşmesinden kaynaklandığı için ortadan kaldırılması zor olabilir
  • Test case reducer, girdi uzunluğunu “daha iyi” için vekil ölçüt olarak kullanan bir hill-climbing algoritması gibi davranır
  • Hill-climbing yaklaşımı yerel optimumlarda kolayca sıkışabilir ve daha kısa girdiler hata araştırması için her zaman daha iyi değildir
  • Tekrarlı çalıştırma yöntemi

    • Deterministik olmayan hatalarla uğraşırken, ilginçlik testinin girdiyi birden çok kez çalıştırıp ilgilenilen hata en az bir kez ortaya çıkarsa girdiyi kabul etmesi yöntemi kullanılabilir
    • Bu yaklaşım hata ortaya çıkma sıklığını artırmaya yardımcı olabilir
    • En az bir kez hata görülürse başarılı sayan test, deterministik olmayan girdileri de kabul edeceğinden küçültme ilerledikçe determinizmsizlik hatta artabilir
    • Daha katı bir yaklaşım, girdiyi ancak n tekrarın hepsinde de hata oluşursa kabul eden testtir
    • Katı testte başlangıç girdisinin başarılı olma olasılığı düşük olduğu için Shrink Ray’i başlatmak zorlaşır; örnekte 3 tekrar koşulunda başlangıçta başarılı olma olasılığı %3,6’dır
    • Pratik bir çözüm, önce “n çalıştırmadan en az 1’inde hata” testiyle başlayıp hata daha sık ortaya çıkan küçültülmüş girdi elde edildiğinde “n kez art arda hata” testine geçmektir

Genel sayaçlar ve diğer hedef ölçütler

  • Elle müdahale güçlü olabilir ama Shrink Ray’i izlemeyi gerektirir ve müdahale anını kaçırmak kolaydır
  • Reducer’ı girdi uzunluğu yerine başka özelliklere göre yönlendirmek için bu özellik tek bir ilginçlik testi içinde zorlanabilir
  • yk hata ayıklamada, girdi uzunluğundan çok yürütme izi uzunluğu, yani programın çalıştırdığı komut sayısına yakın bir değer daha önemli olabilir
  • YKD_LOG="$t:jit-asm" çıktısı, metin tabanlı iz IR’sini ve makine komutlarını bir dosyaya yazar; daha kısa jit-asm çıktısı hata ayıklamayı kolaylaştırır
  • wc -l, günlük dosyasındaki satır sayısını sayarak iz uzunluğu için yaklaşık bir vekil ölçüt olarak kullanılabilir
  • İlginçlik testi, mevcut iz satırı sayısı önceki en iyi değerden büyükse girdiyi ilginç olmayan olarak işaretler; en iyi değer /tmp/global_best içinde saklanır
  • Bu yaklaşım paralel küçültmede güvenli değildir ve reducer’ın çağrılma biçimi hakkında varsayımlar içerir; ancak atılıp gidecek kısa bir script için kabul edilebilir bir kusur olarak görülür
  • yk segfault örneğinde normal küçültme 40K satırlık iz bırakırken, bu teknik daha büyük küçültülmüş girdi pahasına 10.1K satırlık bir iz üretti ve 30 dakika içinde temel hatayı anlamayı sağladı

Temel çıkarımlar

  • Test case reducer’lar yalnızca derleyici geliştiricileri için yararlı araçlar değildir; derleyici dışı sorunlarda da kullanılabilir
  • Girdi uzunluğunu azaltma temel amacının ötesinde, hata sıklığı, wall-clock süresi, determinizmsizlik seviyesi ve iz uzunluğu gibi özellikler ilginçlik testleriyle yönlendirilebilir
  • İlginçlik testinin doğruluğu, çalışma hızı, zaman aşımı ve paralel çalışmada güvenliği reducer’ın pratik etkisini belirler
  • Reducer’lar girdi ile programın anlamını neredeyse hiç anlamasalar bile, sorunu daha küçük bir biçimde koruyarak hata ayıklama verimliliğini artırabilir

1 yorum

 
GN⁺ 5 시간 전
Lobste.rs görüşleri
  • Gerçekten merak ediyorum, otomatik test vakası küçültmenin değerini kabul etmeyen biri var mı? “Hak ettiği değeri görmüyor” ifadesi, sanki test vakası küçültmeyi her zaman istemeyen insanlar varmış gibi geliyor.
    Hatayı hemen saptayabilseniz bile, regresyon testi için küçültülmüş bir örneğe ihtiyaç duymaz mısınız?

    • Sanırım çoğu geliştirici bunu bir teknik olarak bile hiç düşünmemiştir.
    • Yazının ilk cümlesi “Test-case reducers are less well known than they should be, [...]” ve birkaç yıldır insanlara fuzzing ile özellik tabanlı test kullanmalarını öneren biri olarak benim izlenimim de tam buydu.
      İkisi de çoğu zaman başarısız örnek küçültme ya da “shrinking” gibi bir biçim içeriyor ve bu sayede çok daha kullanışlı hale geliyor.
    • Fuzzer tarafında kabaca biliyorum. Fuzzer başarısız bir örnek bulduktan sonra onu otomatik olarak küçültüyor ve o kısım gerçekten çok iyi çalışıyor.
      Ama genel olarak fuzzing, özellikle AmericanFuzzyLop ve AFL++ kullanma deneyimime göre, kurulumu o kadar sancılı ki genelde kaçınıyorum.
      Karşılaştığım hataların çoğu da “bu girdi dosyasını verirsen yanlış çalışıyor” türünden değil, daha çok “bir yerdeki bir kullanıcı için yanlış çalışıyor” şeklinde. Bazen bunu “belirli koşullarda bir dizi adımı izlersen yanlış çalışıyor” seviyesine kadar indirebiliyorum, ama 1) “kullanıcının sırayla bir şeyler yapması” gibi bir duruma otomatik test vakası küçültücüyü nasıl uygulayacağımı bilmiyorum, 2) yerelde yeniden üretmenin bir yolunu bulunca hata ayıklamanın %99’u zaten bitmiş oluyor.
      Sanırım yazının yazarı benim bu tutumumu “değer vermemek” olarak görebilir.
    • Test vakası küçültmenin ne olduğunu bilen insan sayısı bile azdır bence.
  • Bu yazı ve örnekler, küçültücülerin derleyici dışı durumlarda da daha yaygın kullanılması gerektiğini söylerken bile bakış açısı epey derleyici yazarı tarafına kayıyor.
    ~silentbicycle’ın yazdığı gibi, test vakası küçültmenin çoğu fuzzer ya da özellik tabanlı test bağlamında olur ve daha büyük bir çerçevenin içine gömülü gelir. Derleyiciler, bağımsız test vakası küçültücünün işe yaradığı sıra dışı alanlardan biri. Bağımsız küçültücülerin faydalı olduğu başka durumlar var mı pek emin değilim.
    Determinizm kısmı da ilginç. Örnek, hatayı tetikleyen girdi dosyası yani betik nedeniyle deterministik olan bir durumla başlıyor; hatalı programın, yani yorumlayıcının kendi doğası gereği deterministik olmasıyla değil. Yazının, “interestingness” tekniğinin hatalı programın kendisinin deterministik olmadığı derleyici dışı durumlarda da işe yaradığı anlamına gelip gelmediği net değil.
    Test problemini fuzzing ve test vakası küçültmeye uygun hale getirmenin bir yolu olarak, numaralandırılmış buyruksal komutlar kümesi oluşturmayı öneririm. Her komuta, test hatasını algılayabilecek hafif bir tutarlılık kontrolü koyun; böylece anında çökme olmayan durumlar da yakalanır. Ağır tutarlılık kontrollerini ayrı komutlara çıkarmak, testleri fazla yavaşlatmaz. Basit rastgele testte test harness’i bir şey bozulana kadar komutları rastgele seçebilir; sonra bir fuzzer harness’ine geçtiğinizde, komutları fuzz girdisi byte akışına göre seçebilirsiniz. Böylece deterministik regresyon testleri ve test vakası küçültme gibi güzel şeyleri otomatik olarak elde edersiniz.
    libfuzzer’a açıkça test vakasını küçültmesini söyleyerek pek sonuç alamadım; sanırım libfuzzer girdiyi üretirken bunu zaten yapmış oluyordu. Bu yüzden genel amaçlı bir fuzzer’ın test vakasını küçültmesine yardımcı olacak ek interestingness kontrollerini denemek için pek motivasyon oluşmadı. Başka insanların bunda başarılı olup olmadığını merak ediyorum.

    • Tamamen katılıyorum. Bu yaklaşımı sık kullanıyorum. Durumlu arayüzün simgesel bir temsilini oluşturup, genelde switch-case ya da match tabanlı basit bir yorumlayıcı yazıyor, ardından işlemler listesini rastgele üretip çalıştırıyor ve ön/son koşul ihlali ya da iç bozulmayı yakalayan assert’leri tetikleyen girdiyi küçültüyorum.
      Buna ister özellik tabanlı test, ister fuzzing, ister hafif model denetimi deyin, derin hataları bulmada şaşırtıcı derecede etkili olabiliyor. Tek tek doğru olan ama birbirlerinin varsayımlarından biraz sapan çok sayıda durumlu arayüz gördüm; bu işlemler beklenmedik biçimlerde birleşince iç bozulmaya dönüşebiliyor.
      İşlem listesini, bellekteki hash tablosu ya da liste tabanlı basit bir gerçeklemenin yanında paralel olarak çalıştırıp sonuçların eşleşip eşleşmediğini kontrol etmek de faydalı oluyor. Fark çıkarsa bu genelde ya bir hatadır ya da daha iyi belgelenmesi gereken bir sınır durumdur.
    • Bazen taşımacılık optimizasyonu ile uğraşıyorum ve değişmezlerin bozulduğu epey karmaşık senaryolarla sık karşılaşıyorum. Test vakası küçültücüm olsa harika olurdu ama eskiden bunu elle küçültmekle yetinmek zorunda kalıyordum[0].
      Ne yazık ki veri dosyaları o kadar karmaşık ki shrinkray’in bunu işlemesi zor görünür. Birden çok farklı “dosya”dan tablo biçimli veri okunuyor ve uzun menzilli bağımlılıklar da var; bu yüzden küçültme yöntemine dair alan bilgisini doğrudan kodlamak gerekecek gibi.
      Yapay zekadaki ilerleme hızına bakınca, bir dahaki sefere böyle bir senaryo çıkarsa özel bir küçültücü yazacağımı düşünüyorum.
      [0] Belirsiz bir ontolojiye başvurursak, optimizasyon problemi maliyeti en aza indiren bir arama problemidir ve bu da aslında derleyiciyle aynı şey sayılır; dolayısıyla mükemmel bir örnek değil.
  • Bunu pytest ile yazılmış testlere nasıl uygulayacağımı anlamak için üç kez okudum.
    Test paketimin karmaşıklığını azaltmak istiyorum; iş dışında bir zamanda dördüncü kez okumayı düşünüyorum.

    • Python kullanıyorsanız, ilk adım test vakası küçültmesi yerleşik gelen Hypothesis’i kullanmaya başlamak olur.
  • Geçen yıl CI’daki test çalıştırma sırası sorununa bakarken, test listesini küçültmeye yardımcı olan bir araç yaptım.
    Temelde satırları ikiye bölüp tekrar deneyen bir yaklaşımdı.
    Betiğin kendisinde epey hata var ama 5000 testlik bir listenin benim eşzamanlılık hatamı tetikleyen yaklaşık 4 testlik bir listeye inmesi çok güzeldi.
    Benim durumumda Shrink Ray’in doğrudan çalışıp çalışmayacağını gerçekten merak ediyorum. “Bir testi ölçüt alarak satır kümesini küçültme”nin standart komut satırı araçları grubunda yer alması gereken bir özellik olduğunu içtenlikle düşünüyorum.

  • Bu konuyla bağlantılı olarak, özellik tabanlı test de testin karşı örneklerini üretmek için oluşturulan girdilerin durum uzayını “küçültmek” gibi oldukça benzer bir yaklaşım kullanır.
    Özellik tabanlı testin avantajı, arama uzayını yönlendirebilmeniz ve yapılandırabilmenizdir. Girdiyi, programı modelleyen bir durum makinesini süren geçişler kümesi haline getirebilirsiniz.
    Bu tekniğin, özellikle veritabanları ve dağıtık sistemler gibi çok uygun alanlarda bile ne kadar az kullanıldığını her gördüğümde şaşırıyorum. Daha geçen hafta $WORK’te birkaç saatten kısa sürede böyle bir test hazırladım ve sistemimizin yakınsamadığını hızla fark ettim. Test, iş arkadaşlarıma gösterdiğimde anında anlayabildikleri temiz bir iz çıktısı üretti.
    Kişisel olarak, karmaşık sistemlerde hata ayıklarken yatırım getirisi en yüksek test tekniğinin bu olduğunu düşünüyorum.