- 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/wordsiç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 longvarsa 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ızcaantidisestablishmentarianismkalır
- Örnek program bir dosyadan satırları okur ve uzunluğu 25’ten büyük bir satır varsa
Daha güçlü reducer’lar ve Shrink Ray
- 78 satırlık C programı örneği,
FAST=0veFAST=1ayarlarında farklı çıktı üretilmesi sorununu ele alır - İlginçlik testi, iki ayarla derleme yaptıktan sonra yalnızca
FAST=0çıktısı0d754a56olduğunda veFAST=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=0eklenirse ç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-deltaeklendiğ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-=1gibi 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
timeoutdeğ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([])==0nedeniyle sıfıra bölme hatası üretir; ancakrandom.random() < 0.33koş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
ntekrarı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 “nkez 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ısajit-asmçıktısı hata ayıklamayı kolaylaştırırwc -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_bestiç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
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?
İ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.
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.
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.
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.
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.
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.