Bun’ın yeni çökme raporlayıcısı
(bun.sh)- Bun v1.1.5, çökme veya panic durumunda Zig/C++ yığın bilgilerini kişisel veri içermeyen yaklaşık 150 baytlık bir URL ile ileten
bun.reportçökme raporlayıcısını ekledi - Mevcut işletim sistemi çökme raporlayıcıları ve core dump’lar; debug sembolleri, performans, gizlilik ve yürütülebilir dosya boyutu açısından büyük yük getirdiği için Bun gibi bir CLI aracına uygulanması zor
- Yeni yöntem, ASLR nedeniyle anlamı belirsizleşen adresleri modüle göreli adreslere dönüştürüyor; sunucu da commit SHA’sı ve platforma uygun debug sembolleriyle fonksiyon adlarını geri yüklüyor
- URL’de platform, alt komut, commit SHA’sı, özellik bayrakları, yığın adresleri, çökme tipi ve mesajı bulunuyor; yığın adresleri kısa olması için base64 VLQ ile kodlanıyor
- JavaScript/TypeScript kaynak kodu veya ortam değişkenleri gönderilmiyor; Bun ekibinin tanılama için ihtiyaç duyduğu Zig/C++ yığın bilgileri ve bazı metaveriler iletiliyor
Bun neden kendi çökme raporlayıcısını geliştirdi?
- Yazı yazıldığı sırada Bun’da 2.600’den fazla açık GitHub issue bulunuyor ve bazı issue’ların yeniden üretilmesi ve debug edilmesi özellikle zor
- Sentry gibi çökme raporlama servisleri uygulamalar ve SaaS ürünleri için uygun olsa da, Bun gibi CLI araçlarında core dump yüklemek gizlilik, performans ve yürütülebilir dosya boyutu sorunlarını büyütüyor
- Bun v1.1.5, Zig ve C++ çökme raporları için küçük, yeni bir format sunuyor
- Çökme raporu yaklaşık 150 baytlık bir URL’ye sığıyor
- Kişisel veri içermiyor
Yalnızca işletim sistemi çökme raporlayıcılarının yetersiz kaldığı noktalar
- macOS gibi bazı işletim sistemlerinde yerleşik çökme raporlayıcıları var; ancak bunlardan doğru şekilde yararlanmak için genellikle uygulamayla birlikte debug sembollerini de dağıtmak gerekiyor
- Debug sembolleri Bun dağıtım boyutunu ciddi ölçüde artırıyor
- Linux debug sembolleri yaklaşık 30 MB
- macOS debug sembolleri yaklaşık 9 MB
- Windows
.pdbdosyası 250 MB’ın üzerinde
- Bun yürütülebilir dosyası örneğinde
llvm-stripöncesi ve sonrası boyut60M’den51M’ye düşüyor - Debug sembolleri olmadan çökme meydana geldiğinde yığın izinde yalnızca
???ve adresler kalıyor; bu da kullanım değerini azaltıyor - ASLR(Address space layout randomization) nedeniyle fonksiyon adreslerine rastgele ofsetler karışıyor ve bu haliyle fonksiyon adları geri yüklenemiyor
bun.report nasıl çalışıyor?
- Bun v1.1.5’te bir çökme veya panic gerçekleştiğinde Bun; sürüm, platform, çalıştırma argümanları, bellek kullanımı ve çökme mesajıyla birlikte bir
bun.reportbağlantısı yazdırıyor - Kullanıcı bağlantıyı açtığında önceden doldurulmuş bir GitHub issue formuna yönlendiriliyor
- URL’nin içinde yeniden eşlenmiş yığın izi kodlanmış halde bulunuyor
- Sunucu, URL’deki bilgilere dayanarak yığın adreslerini geri yüklüyor ve Bun ekibinin okuyabileceği bir çökme raporuna dönüştürüyor
Adresleri okunabilir bir yığın izine dönüştürme süreci
- Fonksiyon adresi, uygulama kodunun bellekte yüklendiği konumu gösteren bir pointer’dır ve güvenlik nedeniyle rastgele ofset içerir
- Temel fikir, ham adresten ikilinin base address değerini çıkararak göreli adres elde etmektir
- Gerçek uygulama, platformlara göre değişen API farkları nedeniyle daha karmaşıktır
- Windows,
GetModuleHandleExWüzerindeGET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESSbayrağını kullanır ve modül pointer’ını base address olarak alır - Linux,
dl_iterate_phdrile yüklü modülleri dolaşır ve adresi içeren modülündl_phdr_info.dlpi_addrdeğerini base address olarak kullanır - macOS,
_dyld_image_countve_dyld_get_image_headerile modülleri dolaşır;_dyld_get_image_vmaddr_slideile ASLR slide değerini alır- macOS sonuç adreslerinde image offset kalır; Bun için bu değer
0x100000000’dır - URL’yi kısa tutmak için bu ofset kaldırılır, ancak
llvm-symbolizerile yeniden eşlemeden önce tekrar eklenmesi gerekir
- macOS sonuç adreslerinde image offset kalır; Bun için bu değer
- Windows,
- Linux ve macOS’ta ilk modül ana uygulama ikilisini gösterir
- Windows’ta modül adı ile
peb.ProcessParameters.ImagePathNamekarşılaştırılarak ana ikili olup olmadığı anlaşılabilir - Bun, debug sembollerini yerelde indirip ayrıştırmak yerine demangling işlemini sunucuya bırakıyor
- Sunucu debug sembollerini önbelleğe alabilir
- Yığın izini birkaç saniye içinde demangle edebilir
- Aynı zamanda yeni bir GitHub issue açan bağlantı görevini görür
bun.report URL yapısı
bun.reportURL’sinde şu bilgiler kodlanır- Platform: Platformu gösteren tek harf. Örneğin
w, x86_64 Windows’u;M, aarch64 macOS’u gösterir - Subcommand:
bun test,bun install,bun rungibi alt komutları gösteren tek harf - Commit SHA: Geçerli Bun sürümünün commit SHA’sı; daha sonra debug sembollerini almak için kullanılır
- Feature Flags: Çökmeden önce kullanılan API’leri ve özellikleri gösteren işaretler
- Stack Trace Addresses: Önceki adımda hesaplanan adresler
- Crash Type: Çökme tipini gösteren tek harf
- Crash Message: Çökme tipine göre biçimi değişen mesaj
- Platform: Platformu gösteren tek harf. Örneğin
- URL’deki sürüm numarası, gerçek işleme için değil insanlar tarafından okunabilmesi için gösterilir
- Yalnızca bu bilgilerle bile bazı çökme özellikleri elle anlaşılabilir
wtanımlayıcısı görüldüğünde bunun bir Windows çökmesi olduğu hızla anlaşılır- Dizenin sonundaki
A2görüldüğünde segmentasyon hatası tanımlanabilir
Kısa URL için VLQ kodlama
- Yığın izi adresleri, URL’yi kısa tutmak için base64 Variable Length Quantity(VLQ) sayıları olarak kodlanır
- VLQ, küçük sayıları daha az karakterle ifade ederken büyük sayıları da kodlayabilir
- JavaScript source map’lerinde satır numaralarını saklarken de aynı teknik kullanılır
- Sunucu, VLQ değerlerini tekrar göreli adreslere çözer; commit hash’i ve platformu kullanarak debug sembollerini indirir ve
llvm-symbolizerile fonksiyon adlarını demangle eder - Örnek çökmede, Windows’un modül çözücü kodunun bir parçası olan
dirInfoCachedMaybeLogiçinde bir assertion’ın başarısız olduğu ortaya çıkar
Özellik bayraklarının kodlanması
- URL ayrıca 64 bit tamsayı da kodlar; her bit, Bun’ın belirli bir özelliğinin kullanılıp kullanılmadığına karşılık gelir
- Bu bayraklar, hangi API’lerin ve sistemlerin çökmeye etki etmiş olabileceğine dair ipucu verir
.envdosyası otomatik yüklendiğindedotenvözelliği ayarlanırfetch()kullanıldığındafetchözelliği ayarlanır
- Bun, özellik kullanımını global değişken konteyneriyle izler ve her API’nin içinde ilgili sayıyı artırarak kullanımı işaretler
- Zig’in derleme zamanı metaprogramlamasını kullanarak özellik listesini dolaşır ve her özellik için 1 bit kullanan packed struct’ı dinamik olarak oluşturur
inline forkullanıldığında özellik listesi derleme zamanında dolaşılır, ancak gerçek bit ayarlama çalışma zamanında yapılabilir- Mevcut
Featuresstruct’ına yeni bir özellik eklendiğinde çökme raporlayıcısı da tekrar tekrar kod yazmadan bunu işler - Aynı yöntem C veya Rust makrolarıyla da mümkün olsa da, Bun tarafındaki uygulamada Zig
comptimedaha basit ve okunabilir bir yol olarak kullanılmıştır
Core dump ile farkı
- Core dump çok daha fazla bilgi içerir; ancak boyutu büyüktür, faydalı olması için debug sembolleri gerekir ve hassas ya da gizli birçok bilgi içerebilir
- Bun’ın yeni raporlama yöntemi JavaScript/TypeScript kaynak kodu, ortam değişkenleri ve diğer hassas bilgilerin gönderilmesini önler
- Varsayılan olarak her şeyi göndermek yerine, sorunun tanılanması için gerekli olma olasılığı yüksek Zig/C++ yığın izi ve birkaç ayrıntı gönderilir
- Ek bilgi gerekirse kullanıcıdan ayrıca istenebilir
- Önceden olduğu gibi yalnızca eşlenmemiş adreslerin kaldığı duruma kıyasla, Bun ekibinin çökmeleri tanılaması kolaylaşır
Demo
- Çökme raporlayıcısını test etmek için küçük bir web uygulaması bun.report adresinde sunuluyor
- Herhangi bir çökme raporu URL’sinin sonuna
/vieweklerseniz ilgili web uygulaması ekranına gidilir
1 yorum
Hacker News yorumları
Genel stack trace yerine bu yöntemin kullanılmasının nedeni birkaç MB’lık debug sembollerini dağıtmamaksa, daha iyi bir seçenek olan debug tablolarına yalnızca fonksiyon adlarını koyma yolunu göz ardı etmiş gibi görünüyor.
Stack trace’i görmek için bir web servisi kullanmak zorunda kalmaktan çok daha iyi bir yöntem ve bu yalnızca teoride değil, LLVM’de zaten uygulanmış durumda: https://clang.llvm.org/docs/UsersManual.html#cmdoption-gline...
Tek bir URL ile gerekli içeriği neredeyse otomatik doldurursanız bu yeterince kolaylaşır; geliştiricilerin gerçekten crash report göndermesi de ancak böyle olur. Boyut da önemliydi, kullanıcıya bir dezavantaj yaratmamaya çalıştılar; ama asıl mesele tüm süreci çok kolay hale getirmek.
Bu kullanım senaryosunda stack trace’i bir web servisiyle görmek zorunda olmak büyük bir dezavantaj değil. Frontend JavaScript bundle’ını obfuscate/minify edip source map’i Sentry’ye yükledikten sonra, kullanıcının tarayıcısından gelen stack trace’i Sentry’de geri çözerek görmeye çok benziyor. Kullanıcı zaten o stack trace’i görmeyecek; benim de Sentry ile bakmam rahatsız edici değil. Öyle olmasaydı zaten hiç göremeyecektim.
Alternatif önermenin birçok yolu var.
Bu platformlarda sistem kütüphanelerinin fonksiyon adlarını, tam debug sembolleri olmadan da sembolleştirmenin bulma yöntemi bu.
Harika ve çok yaratıcı. Birçok projenin bu yöntemi izlemesi iyi olur. Temel nokta, çalıştırılabilir dosya/paylaşımlı nesne bazında göreli program sayacı ile stack trace bırakmak.
Bildiğim kadarıyla Bun statik linklenmiş; dinamik linklenen bir sistemde ise normalleştirilmiş her program sayacının başına küçük sayısal bir paylaşımlı nesne ID’si eklemek gerekir.
Örneğin Unreal Engine crash reporter yıllardır böyle basit bir format gönderebiliyor ve her stack frame’den oldukça doğru fonksiyon/satır numarası geri çıkarabiliyor. Yine de genelde stack değişkenleri de varsa ne olduğuna dair ek ipuçları verdiği için minidump daha çok tercih ediliyor.
Microsoft bu tür şeylerde gerçekten iyi. SQL Server’da kişisel verileri çıkarılmış minidump kullanılıyordu; çok küçüktü ve son derece yararlıydı.
O zaman bile, 15 yıl önce, production SQL Server’ın tam dump’ı taşınması zor olacak kadar devasa bir dosyaydı.
Zig ile ilgili ilk tweet’i gördükten sonra birkaç yıl Bun’ı takip ettim, yakın zamanda kullanmaya başladım; ekstra bir uğraş olmadan gayet iyi çalışıyor.
Bun oldukça çekici. Birkaç küçük örnek projede denedim; hızı iyi ve paket yönetimi ile JavaScript runtime’ını birleştirmiş olması hoşuma gidiyor.
Ancak ciddi projelerimin çoğunda Dependabot kullanıyorum. Dependabot’un Bun desteğinin üzerinde çalışıldığını ya da en azından bazı depo issue’larında tartışıldığını biliyorum; bu yüzden destek yayınlanana kadar kullanmayı erteliyorum.
Hiç pişman değiliz. Hızlanan kısımların birikerek sağladığı tasarruf ve geliştirici deneyimindeki büyük iyileşme, beklediğimiz kadar değerli.
Buna ne kadar çok özen gösterildiğini fark edecek çok kişi olmayacak. Bun ekibinin craft’ına ne kadar önem verdiğini göstermesi güzel.
Bun şaşırtıcı, ama yakın zamanda Fastify ile HTTP/2 sunucusu kurmaya çalıştığımda olmadı.
node:http2 createServer is not yet implemented in Bunhatası çıktı ve mesajın işaret ettiği issue aslında HTTP/2 istemci desteğiyle ilgili. İstemci desteği v1.0.13’te zaten yayınlandı: https://bun.sh/blog/bun-v1.0.13#http2-client-supportNotImplementedErrormesajı sunucu tarafındaki issue’ya işaret edecek şekilde değiştirilmeli: https://github.com/oven-sh/bun/issues/8823HTTP/2 sunucu desteği en üst sıralardaki özellik isteklerinden biri: https://github.com/oven-sh/bun/issues?q=is%3Aissue+is%3Aopen...
Bu özellik geldiğinde çok daha fazla kişi Bun’a geçebilir gibi görünüyor.
Bun yaşam döngüsünde hâlâ çok erken bir aşamada. Yine de projeden beklentim yüksek.
Gerçekten Bun kullanan var mı merak ediyorum. Beklendiği kadar iyi mi?
TypeScript Node ortamında ts-node, ts-jest, ESM desteği, top-level await vb. ayarlamak gereğinden fazla zahmetli. Son Node sürümleri bazı sıkıntıları azaltsa da
bun initkadar basit değil. Bun shell API’sini de keyifle kullanıyorum: https://bun.sh/blog/the-bun-shellHata mesajları da Node’a kıyasla epey kötü. Bir süre kullandım ama bugünlerde Node’a
—loader tsxeklemek istediğim her şeyi sağlıyor ve dezavantajı da yok. Basit bir sunucu, örneğin WebSocket kullanan ve native modüle gerek duymayacağınızdan emin olduğunuz bir şeyse Bun değerlendirilebilir. Hatta böyle birkaç servisi çalıştırıyorum.Anında başlama hızı hâlâ şaşırtıyor.
Hâlâ eksikleri var, ama benim için şimdiden Node’dan daha iyi.
Bu yazı aynı zamanda harika bir Zig vaka çalışması gibi hissettiriyor. İlginç.
Bun, REPL’i kullanmadan önce 37 paket indirmek zorunda. İnternet yoksa REPL de yok.
bun replçalıştırıldığındabun-replpaket manifest’inin indirilemediğine dair hata veriyor. Büyük bir sorun değil, ama tek bir çalıştırılabilir dosyayı PATH’e koyunca kurulum yapmadan hemen çalışmasını bekliyordum ve buna bayağı heveslenmiştim.İçeride
bun repl,bunx bun-replile aynı işi yapıyor.