Epidemjoloġija tal-core dumps: insewwu bug ta’ 18-il sena
Użu ta’ analiżi fil-livell tal-popolazzjoni biex niddebuggjaw crashes diffiċli fl-infrastruttura tad-data tagħna.
Il-mudelli u l-aġenti ta’ OpenAI qed jiddependu dejjem aktar fuq infrastruttura tad-data skalabbli biex ifittxu data rilevanti waqt l-inferenza: meta l-mudelli jkunu qed jaħsbu dwar il-mistoqsija tiegħek. Xi wħud minn dawn is-servizzi huma miktuba f’C++, li bil-kontroll ta’ livell baxx tiegħu tas-sistema jħallina nimmassimizzaw il-prestazzjoni u nnaqqsu l-użu tal-memorja. Dawn il-benefiċċji fl-effiċjenza huma importanti hekk kif niskalaw, iżda n-nuqqas ta’ sikurezza tal-memorja f’C++ ifisser li bugs jistgħu jikkawżaw crashes billi jiktbu f’indirizzi tal-memorja żbaljati jew ineżistenti.
Ftit xhur ilu osservajna xi crashes minn ġewwa s-servizz Rockset, parti apposta mill-infrastruttura tad-data tagħna għal ChatGPT li hija kruċjali għal ħafna plugins tad-data u għat-tiftix fil-konverżazzjonijiet. F’kull wieħed minn dawn il-crashes, funzjoni normali ta’ C++ dehret li tispiċċa u mbagħad tirritorna lejn indirizz falz, u b’hekk il-kernel waqqaf il-programm għax l-instruction pointer ma baqax jindika kodiċi. Kultant is-slot tal-indirizz tar-ritorn fl-stack frame kien NULL. Kultant ir-reġistru CPU tal-stack pointer innifsu deher imċaqlaq bi 8 bytes, bħallikieku %rsp b’xi mod kien tnaqqas f’nofs eżekuzzjoni normali. Fiż-żewġ każijiet il-crash seħħ mar-ritorn.
Dawn mhumiex modi normali ta’ falliment għal kodiċi ta’ applikazzjoni. Kitba żbaljata li tolqot biss indirizz tar-ritorn salvat hija possibbli, iżda improbabbli ħafna. Bug li jallinja ħażin %rsp bi 8 mingħajr inline assembly, setcontext, jew longjmp (l-ebda wieħed minnhom ma nużaw) huwa saħansitra aktar stramb, għax kodiċi kkumpilat jaġġusta dak ir-reġistru direttament biss fil-prologue u l-epilogue tal-funzjoni. Kull ipoteżi li stajna naħsbu fiha aħna (jew ChatGPT) kellha evidenza qawwija kontra tagħha, għalhekk il-bug deher impossibbli.
Dak li ħsibna li kien problema waħda fl-aħħar irriżulta li kienu żewġ bugs mhux relatati, skoperti b’kumbinazzjoni fl-istess ħin. L-ewwel, korruzzjoni siekta tal-hardware fuq host wieħed ta’ Azure, fejn is-CPU sempliċement ma kienx jagħmel il-kalkoli sew. It-tieni, race condition ta’ 18-il sena f’GNU libunwind, bug li baqa’ inosservat f’librerija open source użata ħafna.
Din il-pubblikazzjoni tirrakkonta kif identifikajna u sewwiejna crashes li dehru inspjegabbli billi ħsibna bħal epidemjologu u bnejna sett ta’ data ta’ kwalità għolja dwar il-popolazzjoni kollha tal-crashes.
L-ewwel, ejja nidħlu aktar fil-fond fuq Rockset. Hija sistema tad-data cloud-native għat-tiftix u l-analitika f’ħin reali li nużaw għal ħafna każijiet interni f’OpenAI, bħal sync connectors (Rockset ġiet akkwistata minn OpenAI fl-2024). Aġġornamenti streaming jintużaw biex iżommu indiċi aġġornat tal-bażi tal-għarfien ta’ workspace sabiex ChatGPT ikun jista’ jfittex informazzjoni rilevanti meta jwieġeb mistoqsijiet jew iwettaq azzjonijiet.
Is-saff ta’ eżekuzzjoni ta’ Rockset huwa miktub f’C++. Il-lingwa C++ tipprovdi aċċess ta’ livell baxx għas-CPU, ħaġa tajba għall-prestazzjoni u l-effiċjenza, iżda tfisser li bugs fl-applikazzjoni jistgħu jwasslu għal aċċessi invalidi għall-memorja u segfaults. Biex ngħinu nsibuhom nużaw il-fatal signal handler ta’ folly biex nirreġistraw stack trace meta jseħħ crash, u ntellgħu l-core dumps korrispondenti (snapshot tal-istat tal-programm meta ġġarraf) fil-ħażna Azure blob għal analiżi aktar tard. Il-leaves kollha tal-ipproċessar tal-queries ta’ Rockset huma replikati, u dan inaqqas l-impatt ta’ crash fuq il-klijent. Madankollu, kull segfault jikkorrispondi għal bug li jeħtieġ jissewwa biex nilħqu l-għanijiet tagħna ta’ affidabbiltà u kwalità.
L-approċċ inizjali tagħna kien li nittrattaw dawn il-cores bħal problema konvenzjonali ta’ debugging: nispezzjonaw ftit core dumps mill-qrib ħafna, nifformaw ipoteżijiet, u neskluduhom waħda waħda.
Ħafna mill-crashes seħħew f’metodu msejjaħ DocumentTree::updateDocument. F’dawn il-crashes deher li updateDocument kien sejjaħ xi funzjoni mhux magħrufa X, l-stack ġiet korrotta waqt li X kienet attiva, u mbagħad X irritornat lejn indirizz li ma kienx kodiċi eżegwibbli. F’xi każijiet, il-frame ta’ X li kien għadu kif tneħħa deher validu ħlief li l-indirizz tar-ritorn salvat tiegħu kien NULL. F’każijiet oħra l-stack pointer innifsu deher ħażin, iżda l-frame validu li jmiss xorta deher li kien updateDocument.
Ma konniex nafu meta l-stack kienet qed tiġi korrotta, u dan ħalla spazju enormi fejn infittxu. updateDocument huwa metodu kbir li jgħaddi minn ħafna inlining, għalhekk in-numru ta’ kandidati għal X kien kbir wisq.
Kien dan bug fil-kodiċi C++ tagħna? Kwistjoni tal-compiler jew tal-linkage? Problema f’waħda mil-libreriji runtime tagħna? Bug fil-kernel Linux madwar it-twassil tas-sinjali jew il-context switching? Xi ħaġa saħansitra aktar rari? Jekk din kienet kitba żbaljata, għaliex ma nqabaditx mill-ambjent ASAN tagħna tal-istaging?
Ippruvajna nużaw il-logs fil-livell tal-applikazzjoni biex nidentifikaw l-okkorrenzi kollha tal-problema, iżda bugs ta’ korruzzjoni tal-stack huma diffiċli biex jiġu kklassifikati mil-logs biss għax l-stack traces irreġistrati jkunu huma stess korrotti jew neqsin. Ma rnexxilniex nibnu log query li ma kellhiex kemm false positives kif ukoll false negatives. Spezzjonajna aktar cores manwalment u sibna xi eżempji oħra, iżda dak il-proċess kien jinvolvi wisq xogħol biex jagħtina sett ta’ data affidabbli.
F’dan l-istadju tal-investigazzjoni, aħna (b’mod żbaljat) eskludejna bug tal-hardware, għax rajna crashes f’diversi reġjuni u fuq diversi tipi ta’ hardware, għalhekk konna għadna nfittxu kawżi tas-software biss. Għal ftit jiem, dħalna fil-fond ħafna fuq crash wieħed b’%rsp allinjat ħażin, billi rrikostruwejna l-istorja ta’ qabel il-crash bl-użu tal-kontenut tal-stack u tar-reġistri. Dan ta xi ħjiel possibbli, iżda għax ma tlaqniex mill-konklużjoni inizjali tagħna li l-bugs kollha kellhom l-istess kawża, ma għenniex noħorġu mill-impass.
Qabel naslu għall-punt ta’ bidla fl-investigazzjoni tagħna, huwa importanti nispjegaw x’tip ta’ informazzjoni konna noħorġu mill-core files.
Rockset huwa kkumpilat b’-fno-omit-frame-pointer, għalhekk l-stack frame attiv dejjem jista’ jintlaħaq permezz ta’ %rbp, u l-callers jiffurmaw lista marbuta ta’ frame pointers.
Fuq Linux x86_64, l-AMD64 System V ABI jirriżerva wkoll 128 bytes taħt %rsp bħala r-red zone. Dik ir-reġjun huwa disponibbli għall-kodiċi tal-userspace u, b’mod importanti, il-kernel iwiegħed li ma jmissux meta jwassal sinjal, bħala parti mill-kuntratt tal-ABI.
Ir-red zone kienet ċentrali għad-debugging tagħna ta’ crash wara ritorn, għax iżżomm xi informazzjoni minn qabel ir-ritorn. Meta jiġi attivat SIGSEGV, il-fatal signal handler ta’ folly jaħdem fuq l-stack tat-thread li qed jiġġarraf. Stack frames li m’għadhomx attivi (għax il-funzjoni tagħhom irritornat) jinkitbu fuqhom mis-signal handler, ħlief għall-aħħar 128 bytes. Għalhekk nistgħu ngħidu affarijiet bħal “l-stack frame ta’ X li kien għadu kif tneħħa deher validu, ħlief għal indirizz tar-ritorn NULL.” Ir-red zone iżżomm xi wħud mill-frames inattivi, jew kultant biss it-tarf ta’ frame inattiv wieħed.
Sibna crash wieħed b’stack allinjata ħażin fejn il-funzjonijiet kollha involuti kienu żgħar ħafna. Dan ħalliena naraw li %rsp kien sar allinjat ħażin waqt l-eżekuzzjoni ta’ funzjoni relattivament sempliċi, u li wara kien irnexxa aktar calls. Il-programm ġġarraf biss meta l-funzjoni attiva fl-aħħar ippruvat tirritorna. L-ebda waħda minn dawk il-mogħdijiet tal-kodiċi ma użat exceptions, inline assembly, setcontext, jew longjmp, għalhekk jekk l-stack pointer tassew inbidel kif issuġġerixxa l-core, l-ebda bug plawsibbli fil-kodiċi userspace ma spjega l-kwistjoni.
Dan imbuttana lejn il-kernel.
Rockset juża sinjali b’mod aktar aggressiv mill-biċċa l-kbira tal-programmi. L-eżekuzzjoni tal-queries tinqasam f’ħafna kompiti ħfief li jiskambjaw id-data. Dan huwa importanti biex jiġu ttrattati workloads b’QPS għoli b’mod effiċjenti, iżda jagħmel l-accounting tas-CPU għal kull query diffiċli għax ix-xogħol għal ħafna queries jiġi multiplexed fuq l-istess thread pool.
Is-soluzzjoni tagħna hija xi ħaġa li nsejħu coarse_thread_cputime_clock, li tapprossima clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) bi spiża baxxa biżżejjed biex tieħu kampjun f’kull konfini ta’ kompitu. L-API timer_create tista’ tintuża biex tiskeda twassil perjodiku ta’ sinjal ibbażat fuq diversi kunċetti tal-mogħdija taż-żmien, inkluż l-akkumulazzjoni tal-ħin tas-CPU. Niskedaw sinjal (SIGUSR2) biex jitwassal kull ftit millisekondi ta’ ħin tas-CPU, u f’dak il-punt is-signal handler jaġġorna valur lokali għat-thread. Anki jekk ħafna kompiti ma jarawx l-arloġġ oħxon javvanza waqt li jkunu qed jeżegwixxu, is-somma tad-deltas kollha tipproduċi stima mingħajr preġudizzju tal-ħin tas-CPU attwali għal query.
Minħabba li nwasslu sinjali daqshekk ta’ spiss, bug rari fil-kernel madwar context switching jew it-twassil tas-sinjali deher plawsibbli. Qattajna ħin naqraw bug reports, il-kodiċi sors tal-kernel, u l-patches tal-kernel speċifiċi għal Azure. Ippruvajna stress tests. Ma rnexxilniex insibu xi ħaġa li dehret relatata.
F’dak il-punt iddeċidejna nieħdu pass lura u nippruvaw approċċ differenti.
Hemm żewġ modi wesgħin kif tiddibaggja problema bħal din.
Wieħed huwa li taġixxi bħal tabib: tiffoka fuq pazjent wieħed, tagħmel ħafna testijiet, u tipprova tiddijanjostika każ wieħed minn evidenza dettaljata.
L-ieħor huwa li taġixxi aktar bħal epidemjologu: tħares lejn il-popolazzjoni kollha u tistaqsi jekk hemmx mudelli li każ wieħed ma jistax jikxef. Il-bug beda f’release speċifiku? Jikkorrelata ma’ SKU wieħed tal-hardware (il-mudell speċifiku tas-CPU u s-server), reġjun wieħed, jew verżjoni waħda tal-kernel? Hemm diversi clusters distinti moħbija ġewwa dak li jidher bħala sindromu wieħed?
Konna l-aktar fil-mod ta’ tabib. Il-bidla kruċjali kienet li ddeċidejna li kellna niġbru data ta’ popolazzjoni ta’ kwalità għolja.
It-tentattivi preċedenti tagħna biex insibu awtomatikament l-istanzi kollha tal-problema fallew għax konna qed nippruvaw nużaw tfittxijiet testwali fil-logs. Il-core dumps infushom għandhom ħafna aktar informazzjoni, iżda li nħarsu lejhom manwalment ma kienx jiskala. Iddeċidejna ninvestu l-isforz biex nibnu pipeline li seta’ janalizza awtomatikament il-core dumps.
Tlabna lil ChatGPT jikteb script li niżżel prefix ta’ kull core file, estratta r-reġistri, iffiltra false positives magħrufa bl-użu tal-logs, u ttikketta awtomatikament il-crash bħala return-to-null, misaligned-stack, jew ieħor. Imbagħad ħaddimna dak l-script b’mod parallel fuq kull core dump ta’ Rockset fil-produzzjoni mis-sena ta’ qabel.
Dan kien il-punt ta’ bidla.
Ladarba kellna sett ta’ data nadif, il-korrelazzjonijiet dehru minnufih. Dak li konna nittrattaw bħala bug stramb wieħed fil-fatt kien żewġ popolazzjonijiet separati ta’ crashes.
Il-cores return-to-null kienu mifruxa fuq ħafna clusters u reġjuni ġeografiċi. Il-frekwenza tagħhom kienet żdiedet dan l-aħħar, iżda ma kienx hemm data tal-bidu ċara u lanqas konfini infrastrutturali nodfa.
Il-crashes b’stack allinjata ħażin dehru kompletament differenti. Kollha ġew minn reġjun wieħed, kellhom data tal-bidu ċara, u qatt ma seħħew fuq nodi li kienu ilhom jaħdmu għal żmien twil. Għalkemm kienu jinvolvu diversi VMs ta’ Azure (magni virtwali ospitati fil-cloud), il-mudell deher bħal magna fiżika waħda b’hardware ħażin li kienet tikkawża problemi għal kwalunkwe VM li nzertat tpoġġiet fuqha.
Dak kien il-mument meta rrealizzajna li mentalment konna qed inħalltu żewġ bugs. Minħabba li konna qed inħalltu kontro-eżempji miż-żewġ bugs, ma stajniex insibu spjegazzjoni waħda koerenti.
B’lista nadifa ta’ nodi Kubernetes u timestamps, stajna nsegwu l-crashes b’stack allinjata ħażin lura għal host fiżiku wieħed, li kien faċli ndaħħluh f’denylist.
Ma rnexxilniex nirriproduċu l-korruzzjoni tar-reġistri fuq dak il-host f’ambjent ikkontrollat, anki wara diversi ġimgħat ta’ stress testing. Madankollu, ladarba l-host problematiku tneħħa mis-servizz, il-crashes b’stack allinjata ħażin għebu.
It-tneħħija tal-host ħażin mhijiex soluzzjoni permanenti, fis-sens li ma tipprevjenix okkorrenza ġdida tal-istess problema. Madankollu nistgħu nbiddlu s-software sabiex, jekk kwistjoni simili terġa’ sseħħ, tkun faċli biex tinstab u tiġi ttrattata. Tejjibna l-fatal signal handler tagħna biex jinkludi l-istat tar-reġistri sabiex inkunu nistgħu nindunaw b’rikorrenza mil-logs biss (mingħajr bżonn ta’ core dump). Biddilna l-control plane sabiex il-VMs normalment jerġgħu jintużaw minflok jiġu riċiklati, u dan jagħmilha ħafna aktar faċli nindunaw b’nodu ħażin fil-livell tagħna tal-infrastruttura. Aġġornajna wkoll ir-runbooks tagħna (u l-mudelli mentali tat-tim tagħna) biex jinkludu din il-possibbiltà.
Bil-crashes tal-host ħażin separati, il-cores return-to-null li kien fadal saru ħafna aktar faċli biex nifhmuhom. Qabel konna eskludejna exception unwinding għax ħsibna li kellna kontro-eżempji: crashes f’mogħdijiet tal-kodiċi fejn exceptions żgur ma ntużawx. Iżda dawk il-kontro-eżempji kollha kienu mill-cluster tal-korruzzjoni tal-hardware.
Meta erġajna ħarisna lejn il-cores li kien fadal b’dan f’moħħna sibna li din il-konklużjoni kienet eżattament bil-maqlub: il-crashes kollha kienu qed iseħħu waqt exception unwinding.
Meta C++ jitfa’ exception, ir-runtime irid jiskopri liema catch block għandu jirċeviha u liema destructors jew cleanup handlers għandhom jitħaddmu matul it-triq. Il-compiler joħroġ din il-metadata, iżda t-tqabbil attwali jsir b’mod dinamiku waqt ir-runtime.
Exception unwinding fil-fatt ma jitwettaqx mill-funzjoni li tinvoka throw, iżda minn funzjonijiet helper imsejħa mill-kodiċi kkumpilat li jirriżulta. Dawk ir-routines runtime jeżaminaw l-stack, iġibu metadata dwar il-funzjonijiet misjuba fuq l-stack, ifittxu b’mod dinamiku cleanup handlers u catch blocks, u mbagħad jittrasferixxu l-kontroll lejn wieħed minn dawk il-postijiet. It-trasferiment tal-kontroll jinkludi l-unwinding tal-stack frames kollha ta’ bejniethom (inklużi dawk tal-funzjonijiet helper).
Operazzjonalment, dan huwa ħafna eqreb lejn longjmp jew fiber switch milli lejn call u return normali. Ir-reġistri callee-save għandhom jiġu rrestawrati, kif ukoll ir-reġistri tal-stack frame %rbp u %rsp.
Il-binary tagħna jillinkja ma’ żewġ libreriji li fihom implimentazzjonijiet tal-funzjonijiet li jwettqu C++ exception unwinding: libgcc u GNU libunwind. Id-definizzjonijiet ta’ GNU libunwind kienu dawk magħżula mid-dynamic linker. Dan issorprendiena; konna stennew li tirbaħ l-implimentazzjoni ta’ libgcc minħabba r-regoli tas-symbol versioning; madankollu, l-ispezzjoni ta’ binaries li kienu qed jaħdmu wriet li ma kienx hekk.
F’dan il-punt l-ipoteżi tax-xogħol tagħna nbidlet, hekk kif irrilassajna assunzjoni oħra li konna għamilna meta ħsibna li kien hemm bug wieħed biss.
Forsi ma konniex qed naraw ritorn ordinarju ta’ funzjoni lejn NULL. Forsi konna qed naraw trasferiment ta’ unwind—effettivament restawr tar-reġistri stil setcontext—fejn id-destination instruction pointer kien sar NULL qabel it-trasferiment tal-kontroll. Fi kliem ieħor, data żbaljata mill-librerija tal-unwind aktar milli slot żbaljat tal-indirizz tar-ritorn fuq l-stack.
Dan naqqas il-problema b’mod drastiku. Jew GNU libunwind kienet qed tikkalkula l-istat tad-destinazzjoni ħażin, jew kienet qed tikkalkula l-istat it-tajjeb u xi ħaġa kienet qed tikkorrompih qabel ma seta’ jiġi applikat.
Qrajna s-sors ta’ GNU libunwind u sibna li sintetizza ucontext_t fuq l-stack, jimla l-istat mixtieq tar-reġistri għall-frame tal-cleanup handler, u mbagħad jgħaddi pointer għal dik l-struct lil routine interna tal-assembly: _Ux86_64_setcontext.
F’dan il-punt kellna l-biċċiet kollha.
Il-ucontext_t sintetizzat jgħix f’wieħed mill-stack frames li jiġi unwound minn _Ux86_64_setcontext, waqt l-eżekuzzjoni ta’ dik il-funzjoni. _Ux86_64_setcontext kien qed jaqra mill-struct wara li biddel %rsp, f’liema punt l-struct ma baqgħetx parti mill-stack attiva? Dan jagħmilha vulnerabbli biex tinkiteb fuqha minn twassil ta’ sinjal, bħall-SIGUSR2 frekwenti tagħna.
It-tweġiba kienet iva.
Dawn huma l-aħħar sitt istruzzjonijiet ta’ _Ux86_64_setcontext fil-verżjoni ta’ GNU libunwind li konna qed nużaw, li jikkonsistu l-aktar minn istruzzjonijiet mov li jtellgħu minn memorja għal reġistru destinatarju:
(%rdi jindika lejn ucontext_t allokat fuq l-stack, u l-macros UC_MCONTEXT_* sempliċement jespandu għall-offset fiss fejn jinħażen reġistru partikolari.)
L-ewwel istruzzjoni hija l-bidu tat-tieqa tar-race. Taġġorna %rsp biex jindika l-qiegħ il-ġdid tal-stack attiva. Malli jiġri dan, l-struct li %rdi jindika lejha ma tibqax parti mill-stack attiva (jew red zone), u ma tibqax off-limits għall-kernel.
Normalment dan ma jikkawżax problemi, iżda jekk sinjal jasal eżatt fil-mument it-tajjeb (jew il-ħażin?), il-kernel jibni s-signal frame f’%rsp-128. Dan jista’ jikteb fuq il-memorja li %rdi jindika lejha.
Jekk dan jiġri qabel ma l-istruzzjoni li jmiss taqra UC_MCONTEXT_GREGS_RIP(%rdi), allura l-instruction pointer irrestawrat jista’ jiġi korrott. Fil-crashes tagħna, sar NULL.
Dak hu l-bug.
Din l-assembly tispjega wkoll waħda mill-osservazzjonijiet li ħawduna: għaliex il-funzjoni X kellha NULL fis-slot tal-indirizz tar-ritorn tal-stack frame preċedenti.
setcontext inkitbet biex tirrestawra r-reġistri kollha, inkluż %rdi, għalhekk ma tistax tuża dak ir-reġistru biex taqra UC_MCONTEXT_GREGS_RIP(%rdi) fl-aħħar mument tat-trasferiment tal-kontroll. Minflok, taqra l-valur qabel, issalvah fuq l-stack, tirrestawra ftit reġistri oħra, u mbagħad tuża retq biex taqra l-valur salvat u tittrasferixxi l-kontroll.
Dak li fil-cores deher bħala “funzjoni rritornat lejn NULL” fil-fatt kien “l-unwinder sintetizza indirizz tar-ritorn fil-mira fuq l-stack, iżda dik il-mira kienet ġiet korrotta qabel ma tlesta t-trasferiment.” Aħna konna assumejna li l-korruzzjoni tas-slot tal-indirizz tar-ritorn trid isseħħ fuq il-post, għax ma konniex nafu b’postijiet fejn data (korrotta faċilment) tinkiteb apposta fis-slot tal-indirizz tar-ritorn.
Dak li jagħmel dan il-bug jidher assurd huwa kemm hija dejqa din it-tieqa tar-race. F’dan it-tip ta’ race condition, l-avveniment estern (is-sinjal) irid iseħħ bejn żewġ passi meħuda minn thread ieħor. Iktar ma dawk il-passi jkunu qrib xulxin, inqas ikun probabbli li sseħħ ir-race condition.
F’dan il-każ it-tieqa vulnerabbli hija litteralment wiesgħa istruzzjoni waħda! Sinjal irid jitwassal wara li %rsp ikun inbidel, iżda qabel ma l-istruzzjoni li jmiss ittella’ %rip. Diversi istruzzjonijiet sempliċi bħal dawn jistgħu jitħaddmu għal kull ċiklu fuq CPU modern super-scalar out-of-order, għalhekk it-tieqa tar-race hija madwar mitt pikosekonda.
Meta sibna din ir-race, l-ewwel reazzjoni tagħna kienet li żgur kienet rari wisq biex tispjega r-rata ta’ crashes osservata. Konna qed naraw aktar minn tużżana crashes return-to-null kuljum madwar il-flotta. Setgħet race ta’ istruzzjoni waħda waqt it-tindif tal-exceptions tassew tispjega dan?
Dorna għall-istima ta’ Fermat. Jekk it-tieqa vulnerabbli hija tal-ordni ta’ sekondi u SIGUSR2 jasal kull sekondi ta’ ħin tas-CPU, allura kull exception cleanup handler jew catch block għandu probabbiltà ta’ madwar li jitlef ir-race.
Rockset juża exceptions bħala parti mill-mekkaniżmu intern tiegħu ta’ backpressure għall-ingest. Host wieħed mgħobbi żżejjed jista’ jitfa’ madwar exceptions kull sekonda. Dan jimplika li l-ħin medju bejn fallimenti ta’ host li juża backpressure huwa sekondi, jew crash wieħed kull ftit sigħat. Fuq skala ta’ flotta, dan huwa aktar minn biżżejjed biex jispjega l-frekwenza osservata tal-crashes.
Il-bug ta’ GNU libunwind huwa antik—aktar minn 18-il sena, preżenti fl-ewwel verżjoni x86_64 li appoġġjat C++ exception unwinding.
Allura għaliex deher issa?
Ir-rata ta’ crashes hija bejn wieħed u ieħor proporzjonali għal kemm jintremew exceptions u kemm jitwasslu sinjali. Tiddependi wkoll fuq kemm stack jikkonsma s-signal handler.
Rockset mhuwiex tas-soltu fuq it-tliet assi kollha. Narmu exceptions b’rati għoljin bħala parti mill-kontroll normali tat-tagħbija żejda; inwasslu SIGUSR2 b’mod mhux tas-soltu ta’ spiss minħabba coarse_thread_cputime_clock; u aktar kmieni din is-sena għamilna l-handler ta’ SIGUSR2 juża aktar stack billi żidna call għal timer_getoverrun, sabiex inkunu nistgħu nqisu sinjali magħquda.
Dik l-aħħar bidla tidher li kienet importanti. Jekk il-handler juża ftit biżżejjed stack, jista’ ma jilħaqx u ma jiktibx fuq il-memorja skaduta ucontext_t. Qabel dik il-bidla, ma nosservawx dawn il-crashes xejn. Wara l-bidla r-rata baqgħet baxxa sakemm żidna t-tagħbija għal xi każijiet ta’ użu li għafsu l-mekkaniżmu tal-backpressure.
Fi kliem ieħor, il-bug ta’ libunwind dejjem kien hemm, iżda l-prodott tar-rata ta’ exceptions, ir-rata ta’ sinjali, u l-użu tal-stack mill-handler tagħna kien biss dan l-aħħar qabeż il-limitu fejn sar viżibbli operazzjonalment.
Dan il-mekkaniżmu jispjega wkoll il-kumbinazzjoni li kemm il-bug tal-hardware kif ukoll il-bug ta’ libunwind iġġarrfu l-aktar ġewwa DocumentTree::updateDocument. Il-crashes minn libunwind kienu mxaqilba ħafna lejn dan il-metodu, għax ikun dejjem attiv fil-punt fejn narmu exception biex napplikaw backpressure għall-ingest. Intgħażel ħafna wkoll għall-crashes ta’ allinjament ħażin ta’ %rsp għax in-nodu b’hardware ħażin kien ta’ SKU li nużaw għal bulk ingest, li jqatta’ l-biċċa l-kbira tal-ħin tas-CPU tiegħu f’dak il-metodu.
Il-mitigazzjoni immedjata tagħna kienet li naqilbu minn GNU libunwind għall-unwinder ta’ libgcc. Din kienet għażla tajba minnha nnifisha: l-implimentazzjoni ta’ libgcc ibbenefikat minn ħafna xogħol biex titnaqqas il-lock contention, ħaġa importanti meta tiskala għal VMs kbar.
Aħna upstreamjajna wkoll reproducer awtonomu u fix(jinfetaħ f’tieqa ġdida) lil GNU libunwind, u vverifikajna li l-unwinders l-oħra ma għandhomx kwistjoni simili.
Dan il-vjaġġ ta’ debugging għallimna ħafna dwar id-dettalji speċifiċi ta’ dynamic linking, metadata DWARF tal-unwind, it-twassil tas-sinjali fil-Linux, l-System V ABI, u l-mekkaniżmu tal-exceptions f’C++. Iżda l-lezzjoni ewlenija kienet aktar sempliċi minn dan kollu.
L-aktar pass importanti ma kienx il-qari intelliġenti tal-assembly jew għarfien profond tad-dettalji. Kien li nibnu sett ta’ data ta’ kwalità għolja. Fin-nuqqas ta’ dan is-sett ta’ data, konna qed inħalltu żewġ fenomeni distinti fi storja waħda u nippruvaw nirraġunaw biex noħorġu mill-konfużjoni. Ladarba kellna data tal-popolazzjoni preċiża u kompleta, l-istruttura tal-problema saret ovvja: popolazzjoni waħda ta’ crashes kienet ta’ host ħażin, u l-oħra ta’ race f’libunwind. Meta d-data tjiebet, id-debugging sar aktar faċli.
Għal sistemi ta’ infrastruttura bħal Rockset, dan jgħodd ħafna. Din l-investigazzjoni saħħet l-impenn tagħna għal strumentazzjoni profonda, investigazzjonijiet awtomatizzati, u titjib kontinwu fl-għodod operazzjonali tagħna. L-affidabbiltà mhijiex biss li ssewwi bugs wara li jseħħu—hija li tibni d-data, il-workflows, u l-ħiliet li jbiddlu problemi impossibbli f’oħrajn li jistgħu jiġu ddijanjostikati u solvuti.
Awturi
By Nathan Bronson u Member of Technical Staff


