2 पॉइंट द्वारा GN⁺ 2024-04-27 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Bun v1.1.5 ने bun.report क्रैश रिपोर्टर जोड़ा है, जो क्रैश या panic होने पर भी लगभग 150-बाइट के बिना निजी जानकारी वाले URL के ज़रिए Zig/C++ stack जानकारी भेजता है
  • मौजूदा OS crash reporters और core dumps में debug symbols·performance·privacy·executable size का बड़ा बोझ होता है, इसलिए इन्हें Bun जैसे CLI टूल पर लागू करना मुश्किल है
  • नया तरीका ASLR की वजह से अस्पष्ट हुए addresses को module-relative addresses में बदलता है, और server commit SHA व platform के हिसाब से debug symbols से function names को restore करता है
  • URL में platform, subcommand, commit SHA, feature flags, stack addresses, crash type और message शामिल होते हैं, और stack addresses को base64 VLQ से छोटा encode किया जाता है
  • JavaScript/TypeScript source code या environment variables नहीं भेजे जाते; Bun टीम को diagnosis के लिए ज़रूरी Zig/C++ stack जानकारी और कुछ metadata ही भेजे जाते हैं

Bun ने अपना क्रैश रिपोर्टर क्यों बनाया

  • लिखे जाने के समय Bun में 2,600 से अधिक खुले GitHub issues हैं, और कुछ issues को reproduce करना और debug करना खास तौर पर कठिन है
  • Sentry जैसी crash reporting services apps और SaaS products के लिए उपयुक्त हैं, लेकिन Bun जैसे CLI टूल में core dumps upload करने पर privacy, performance और executable size की समस्याएँ बढ़ जाती हैं
  • Bun v1.1.5 ने Zig और C++ crash reports के लिए एक छोटा नया format पेश किया है
    • crash report लगभग 150-बाइट URL में समा जाती है
    • इसमें निजी जानकारी शामिल नहीं होती

सिर्फ OS crash reporter से क्या कमी रह जाती है

  • macOS जैसे कुछ operating systems में built-in crash reporter होता है, लेकिन उसका ठीक से उपयोग करने के लिए आमतौर पर application के साथ debug symbols भी distribute करने पड़ते हैं
  • debug symbols, Bun distribution का आकार काफ़ी बढ़ा देते हैं
    • Linux debug symbols लगभग 30MB
    • macOS debug symbols लगभग 9MB
    • Windows .pdb files 250MB से अधिक
  • Bun executable का एक उदाहरण llvm-strip से पहले और बाद में 60M से 51M तक घटता है
  • debug symbols के बिना crash होने पर stack trace में सिर्फ ??? और addresses बचते हैं, इसलिए उसका उपयोग सीमित रहता है
  • ASLR(Address space layout randomization) की वजह से function addresses में random offset मिल जाता है, इसलिए उन्हें उसी रूप में function names में restore नहीं किया जा सकता

bun.report कैसे काम करता है

  • Bun v1.1.5 में crash या panic होने पर Bun, version, platform, execution arguments, memory usage, crash message के साथ bun.report लिंक प्रिंट करता है
  • जब उपयोगकर्ता लिंक खोलता है, तो उसे पहले से भरे हुए GitHub issue form पर redirect किया जाता है
  • URL के अंदर remapped stack trace encode होकर रहती है
  • server, URL में मौजूद जानकारी के आधार पर stack addresses को restore करता है और उन्हें Bun टीम के पढ़ने लायक crash report में बदल देता है

addresses को पढ़ने योग्य stack trace में बदलने की प्रक्रिया

  • function address एक pointer होता है, जो memory में load हुए application code की location दिखाता है, और सुरक्षा कारणों से उसमें random offset शामिल होता है
  • मूल विचार यह है कि raw address से binary का base address घटाकर relative address निकाला जाए
  • वास्तविक implementation platform-specific API differences की वजह से अधिक जटिल है
    • Windows में GetModuleHandleExW के साथ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS flag का उपयोग किया जाता है, और module pointer को base address माना जाता है
    • Linux में dl_iterate_phdr से loaded modules को iterate किया जाता है, और जिस module में address आता है उसके dl_phdr_info.dlpi_addr को base address के रूप में उपयोग किया जाता है
    • macOS में _dyld_image_count, _dyld_get_image_header से modules को iterate किया जाता है और _dyld_get_image_vmaddr_slide से ASLR slide लिया जाता है
      • macOS के result address में image offset बचा रहता है, जो Bun के मामले में 0x100000000 है
      • URL को छोटा रखने के लिए इस offset को हटा दिया जाता है, लेकिन llvm-symbolizer से remap करने से पहले इसे फिर जोड़ना पड़ता है
  • Linux और macOS में पहला module main application binary को दर्शाता है
  • Windows में module name और peb.ProcessParameters.ImagePathName की तुलना कर यह तय किया जा सकता है कि वह main binary है या नहीं
  • Bun local machine पर debug symbols download और parse नहीं करता, बल्कि demangling server को सौंपता है
    • server debug symbols को cache कर सकता है
    • stack trace को कुछ सेकंड में demangle किया जा सकता है
    • और साथ ही यह नया GitHub issue खोलने वाले लिंक की भूमिका भी निभाता है

bun.report URL की संरचना

  • bun.report URL में नीचे दी गई जानकारी encode होती है
    • Platform: platform को दिखाने वाला एक अक्षर। उदाहरण के लिए w मतलब x86_64 Windows, M मतलब aarch64 macOS
    • Subcommand: bun test, bun install, bun run जैसे subcommand को दिखाने वाला एक अक्षर
    • Commit SHA: मौजूदा Bun version का commit SHA, जिसका बाद में debug symbols लाने में उपयोग होता है
    • Feature Flags: crash से पहले इस्तेमाल किए गए APIs और features को दिखाने वाले संकेत
    • Stack Trace Addresses: पिछले चरण में निकाले गए addresses
    • Crash Type: crash प्रकार को दिखाने वाला एक अक्षर
    • Crash Message: crash type के अनुसार अलग format में आने वाला message
  • URL में शामिल version number, वास्तविक processing से ज़्यादा इंसानों के पढ़ने के लिए होता है
  • सिर्फ इस जानकारी से भी कुछ crash characteristics को हाथ से समझा जा सकता है
    • w identifier देखकर जल्दी समझा जा सकता है कि यह Windows crash है
    • string के अंत में A2 देखकर segmentation fault पहचाना जा सकता है

छोटे URL के लिए VLQ encoding

  • stack trace addresses को URL छोटा रखने के लिए base64 Variable Length Quantity(VLQ) numbers के रूप में encode किया जाता है
  • VLQ छोटे numbers को कम characters में व्यक्त कर सकता है, जबकि बड़े numbers को भी encode कर सकता है
  • JavaScript source maps में line numbers store करने के लिए भी यही तकनीक उपयोग होती है
  • server VLQ values को फिर relative addresses में decode करता है, commit hash और platform का उपयोग कर debug symbols download करता है, और फिर llvm-symbolizer से function names को demangle करता है
  • उदाहरण crash में यह सामने आता है कि assertion, Windows के module resolver code के एक हिस्से dirInfoCachedMaybeLog में fail हुआ था

feature flags encoding

  • URL 64-bit integer भी encode करता है, और हर bit Bun की किसी खास feature के उपयोग से मेल खाती है
  • ये flags यह संकेत देते हैं कि कौन-से APIs और systems ने crash को प्रभावित किया हो सकता है
    • .env file auto-load होने पर dotenv feature set होता है
    • fetch() इस्तेमाल होने पर fetch feature set होता है
  • Bun global variable container की मदद से feature usage track करता है, और हर API के अंदर संबंधित संख्या बढ़ाकर usage को mark करता है
  • Zig के compile-time metaprogramming का उपयोग करके feature list को iterate किया जाता है और हर feature के लिए 1 bit इस्तेमाल करने वाला packed struct dynamic रूप से बनाया जाता है
  • inline for के उपयोग से compile time पर feature list iterate की जा सकती है, जबकि actual bit setting runtime पर की जाती है
  • मौजूदा Features struct में नया feature जोड़ने पर crash reporter में भी बिना दोहराव के उसका handling हो जाता है
  • यही तरीका C या Rust macros से भी संभव है, लेकिन Bun के implementation में Zig comptime को अधिक सरल और पढ़ने में आसान तरीके से इस्तेमाल किया गया है

core dump से अंतर

  • core dumps में कहीं ज़्यादा जानकारी होती है, लेकिन उनका आकार बड़ा होता है, वे debug symbols के बिना कम उपयोगी होते हैं, और उनमें संवेदनशील या गोपनीय जानकारी काफ़ी अधिक शामिल हो सकती है
  • Bun की नई reporting method, JavaScript/TypeScript source code, environment variables और दूसरी संवेदनशील जानकारी भेजे जाने की स्थिति से बचती है
  • डिफ़ॉल्ट रूप से सब कुछ भेजने के बजाय, यह सिर्फ Zig/C++ stack trace और कुछ विवरण भेजती है, जिनकी समस्या के diagnosis में ज़रूरत होने की संभावना अधिक होती है
  • अगर अतिरिक्त जानकारी चाहिए, तो उपयोगकर्ता से अलग से माँगी जा सकती है
  • पहले की तरह सिर्फ unmapped addresses बचने की स्थिति की तुलना में, अब Bun टीम के लिए crash का diagnosis करना आसान हो जाता है

डेमो

  • crash reporter को test करने के लिए एक छोटा web app bun.report पर उपलब्ध है
  • किसी भी crash report URL के अंत में /view जोड़ने पर उस web app की screen पर जाया जा सकता है

1 टिप्पणियां

 
GN⁺ 2024-04-27
Hacker News की राय
  • अगर सामान्य stack trace के बजाय यह तरीका इस्तेमाल करने की वजह कई MB के debug symbols को वितरित करने से बचना है, तो लगता है कि उन्होंने बेहतर विकल्प—debug table में सिर्फ function names डालना—नज़रअंदाज़ कर दिया है
    stack trace देखने के लिए web service इस्तेमाल करने से यह कहीं बेहतर तरीका है, और यह सिर्फ थ्योरी नहीं है; LLVM में पहले से लागू है: https://clang.llvm.org/docs/UsersManual.html#cmdoption-gline...

    • सामान्य stack trace के बजाय यह तरीका इस्तेमाल करने की मुख्य वजह debug symbols का size नहीं है, बल्कि यह है कि बहुत कम लोगों में इतनी धैर्य होती है कि वे GitHub issue में crash report पोस्ट करें
      अगर एक URL से ज़रूरी चीज़ें लगभग अपने-आप भर जाती हैं, तो यह काफी आसान हो जाता है, और तभी developers वाकई crash reports डालते हैं। size भी महत्वपूर्ण था ताकि users के लिए कोई downside न बने, लेकिन मुख्य बात पूरे process को बहुत आसान बनाना है
    • “बेहतर विकल्प नज़रअंदाज़ किया”, “जाहिर तौर पर बेहतर है” जैसी बातें थोड़ी ज़्यादा निश्चित लगती हैं। संभव है कि उन्हें उस संभावना की जानकारी रही हो
      इस use case में stack trace देखने के लिए web service की ज़रूरत होना कोई बड़ा नुकसान नहीं है। यह लगभग वैसा ही है जैसे frontend JavaScript bundle को obfuscate/minify करना, source map को Sentry पर upload करना, और फिर user browser से आए stack trace को Sentry में restore करके देखना। user वैसे भी वह stack trace नहीं देखने वाला, और मुझे भी Sentry से देखना असुविधाजनक नहीं लगता। वरना शायद मैं उसे देख ही नहीं पाता
    • discussion का context जाने बिना, और कौन-से trade-offs थे यह जाने बिना, “जाहिर है” “बस” कुछ और कर लेना चाहिए—ऐसी आलोचना थोड़ी अहंकारी लगती है
      alternative सुझाने के कई तरीके होते हैं
    • macOS/iOS पर Mach-O binary में सिर्फ LC_FUNCTION_STARTS section डालकर distribute करने का तरीका भी है
      इन platforms पर बिना पूरे debug symbols के भी system libraries के function names symbolization के ज़रिए इसी तरह मिलते हैं
    • फिर भी यह काफी बड़ा हो सकता है। व्यक्तिगत रूप से मुझे यह valuable लगता है, इसलिए आम तौर पर हमेशा include करता हूं, लेकिन ज़्यादातर software ऐसा नहीं करते
  • शानदार और बहुत creative है। अच्छा होगा अगर कई projects इस approach को अपनाएं। मूल बात executable/shared object के हिसाब से relative program counter के साथ stack trace छोड़ना है
    मेरी जानकारी में Bun statically linked है, लेकिन dynamic linking system हो तो normalized program counter के आगे छोटे number के रूप में shared object ID लगानी होगी

    • यह पूरी तरह नया तरीका नहीं है। games जैसी जगहों पर, जहां symbols distribute नहीं किए जा सकते, player PC पर crash होने पर यह आम तौर पर इस्तेमाल होता है
      उदाहरण के लिए Unreal Engine crash reporter कई सालों से ऐसा simple format भेज सकता है, और हर stack frame से काफी सटीक function/line number restore कर सकता है। हालांकि आम तौर पर minidump को अधिक पसंद किया जाता है, क्योंकि stack variables भी हों तो क्या हुआ था, इसके extra hints मिल जाते हैं
  • Microsoft ऐसे कामों में सच में बहुत अच्छा है। SQL Server में उन्होंने privacy हटाए हुए minidump इस्तेमाल किए थे, जो बहुत छोटे और बेहद उपयोगी थे
    उस समय भी, 15 साल पहले, production SQL Server का full dump इतना विशाल file होता था कि उसे transfer करना मुश्किल था

    • जिज्ञासा है कि यह Microsoft की internal services के लिए था या customer deployment environments के लिए। अगर दूसरा, तो उन्हें कैसे पता चला होगा कि क्या personally identifiable information है?
  • Zig से जुड़े पहले tweet को देखने के बाद कई सालों तक Bun को follow करता रहा और हाल ही में इस्तेमाल करना शुरू किया; बिना किसी खास झंझट के बस ठीक से काम करता है

  • Bun काफी आकर्षक है। कुछ छोटे example projects में इस्तेमाल करके देखा; speed अच्छी है, और मुझे package management व JavaScript runtime को साथ जोड़ना पसंद आया
    हालांकि ज्यादातर serious projects में मैं Dependabot इस्तेमाल कर रहा हूं। मेरी जानकारी में Dependabot का Bun support work in progress है, या कम-से-कम कुछ repository issues में discuss हो रहा है, इसलिए support release होने तक इस्तेमाल टाल रहा हूं

    • switch करने से पहले हम भी Dependabot support न होने की वजह से हिचक रहे थे, लेकिन पता चला कि Renovate Bun के साथ काम करता है और फिलहाल पर्याप्त replacement है
      कोई regret नहीं है। जिन चीज़ों में speed बढ़ी है, उनसे मिलने वाली cumulative saving और developer experience में बड़ा सुधार उम्मीद के मुताबिक worth it है
  • बहुत लोग notice नहीं करेंगे कि ऐसी चीज़ों में कितना ध्यान दिया गया है। अच्छा है कि इससे दिखता है कि Bun team अपने craft की कितनी परवाह करती है

  • Bun शानदार है, लेकिन हाल में Fastify से HTTP/2 server बनाने की कोशिश की तो नहीं हो पाया
    node:http2 createServer is not yet implemented in Bun error आया, और message जिस issue की ओर इशारा करता है वह असल में HTTP/2 client support से जुड़ा है। client support v1.0.13 में पहले ही release हो चुका है: https://bun.sh/blog/bun-v1.0.13#http2-client-support
    NotImplementedError message को server-side issue की ओर point करने के लिए बदला जाना चाहिए: https://github.com/oven-sh/bun/issues/8823
    HTTP/2 server support top feature requests में से एक है: https://github.com/oven-sh/bun/issues?q=is%3Aissue+is%3Aopen...
    यह feature आ जाए तो लगता है काफी ज़्यादा लोग Bun पर shift कर पाएंगे

    • अभी Bun की हालत यही है। किसी implementation का इंतज़ार करो, फिर पूरा होने के बाद पता चलता है कि किसी और API implementation की और ज़रूरत है; फिर इंतज़ार करो, वह आ तो जाता है पर कई edge conditions में crash करता है; फिर फिर इंतज़ार—ऐसा cycle चलता रहता है
      Bun अभी अपने lifecycle में बहुत शुरुआती stage पर है। फिर भी project से काफी उम्मीद है
  • जानना चाहता हूं कि क्या सच में लोग Bun इस्तेमाल कर रहे हैं। क्या यह उम्मीद जितना अच्छा है?

    • production में अभी नहीं इस्तेमाल किया, लेकिन one-off scripts और side projects के लिए बहुत अच्छा रहा
      TypeScript Node environment में ts-node, ts-jest, ESM support, top-level await आदि set up करना ज़रूरत से ज़्यादा झंझट भरा है। हाल के Node releases ने कुछ परेशानियां घटाई हैं, लेकिन यह bun init जितना simple नहीं है। bun shell API भी मज़े से इस्तेमाल कर रहा हूं: https://bun.sh/blog/the-bun-shell
    • अगर आपको REPL की ज़रूरत है या native modules इस्तेमाल करने का plan नहीं है, तो ठीक है। REPL है तो सही, लेकिन हर update पर हमेशा 6 सेकंड से ज़्यादा delay होता है, इसलिए बहुत inconvenient है
      error messages भी Node से काफी खराब हैं। कुछ समय तक इस्तेमाल किया, लेकिन आजकल Node में —loader tsx जोड़ दूं तो मेरी सारी ज़रूरतें पूरी हो जाती हैं और downsides भी नहीं हैं। simple server, जैसे WebSocket इस्तेमाल करता हो और आपको पक्का हो कि native modules की ज़रूरत नहीं पड़ेगी, तो Bun consider करने लायक है। ऐसी कुछ services सच में चला रहा हूं
    • 1.0 आते ही इस्तेमाल करना शुरू किया और फिर वापस नहीं गया। अब सभी projects में apply कर रहा हूं
    • करीब 15 हज़ार lines वाले programming language project में development और test runner के रूप में इस्तेमाल कर रहा हूं, और अब तक Bun-specific issues नहीं आए
      instant startup speed अभी भी हैरान करती है
    • हाल में इस्तेमाल कर रहा हूं और बहुत अच्छा है। TypeScript compile की चिंता न करनी पड़े जैसी quality-of-life improvements सच में सुविधाजनक हैं और speed भी तेज़ है
      अभी कुछ चीज़ें missing हैं, लेकिन मेरे लिए यह पहले ही Node से बेहतर है
  • यह article Zig case study के तौर पर भी बहुत अच्छा लगता है। दिलचस्प है

  • Bun को REPL इस्तेमाल करने से पहले 37 packages download करने पड़ते हैं। internet न हो तो REPL भी नहीं चलता
    bun repl चलाने पर bun-repl package manifest download fail होने का error आता है। यह कोई बड़ा issue नहीं है, लेकिन मेरी उम्मीद थी कि single executable को PATH में डालते ही बिना installation के तुरंत काम करेगा, और इसे लेकर काफी उत्साहित था

    • REPL implementation को अभी priority नहीं दी गई है। मौजूदा REPL community द्वारा implement किया गया bun-repl npm package है
      internally bun repl, bunx bun-repl जैसा ही काम करता है