1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723 |
- #include "mon_lwtrace.h"
- #include <algorithm>
- #include <iterator>
- #include <google/protobuf/text_format.h>
- #include <library/cpp/lwtrace/mon/analytics/all.h>
- #include <library/cpp/lwtrace/all.h>
- #include <library/cpp/monlib/service/pages/mon_page.h>
- #include <library/cpp/monlib/service/pages/resource_mon_page.h>
- #include <library/cpp/monlib/service/pages/templates.h>
- #include <library/cpp/resource/resource.h>
- #include <library/cpp/string_utils/base64/base64.h>
- #include <library/cpp/html/pcdata/pcdata.h>
- #include <util/string/escape.h>
- #include <util/system/condvar.h>
- #include <util/system/execpath.h>
- #include <util/system/hostname.h>
- using namespace NMonitoring;
- #define WWW_CHECK(cond, ...) \
- do { \
- if (!(cond)) { \
- ythrow yexception() << Sprintf(__VA_ARGS__); \
- } \
- } while (false) \
- /**/
- #define WWW_HTML_INNER(out) HTML(out) WITH_SCOPED(tmp, TScopedHtmlInner(out))
- #define WWW_HTML(out) out << NMonitoring::HTTPOKHTML; WWW_HTML_INNER(out)
- namespace NLwTraceMonPage {
- struct TTrackLogRefs {
- struct TItem {
- const TThread::TId ThreadId;
- const NLWTrace::TLogItem* Ptr;
- TItem()
- : ThreadId(0)
- , Ptr(nullptr)
- {}
- TItem(TThread::TId tid, const NLWTrace::TLogItem& ref)
- : ThreadId(tid)
- , Ptr(&ref)
- {}
- TItem(const TItem& o)
- : ThreadId(o.ThreadId)
- , Ptr(o.Ptr)
- {}
- operator const NLWTrace::TLogItem&() const { return *Ptr; }
- };
- using TItems = TVector<TItem>;
- TItems Items;
- TTrackLogRefs() {}
- TTrackLogRefs(const TTrackLogRefs& other)
- : Items(other.Items)
- {}
- void Clear()
- {
- Items.clear();
- }
- ui64 GetTimestampCycles() const
- {
- return Items.empty()? 0: Items.front().Ptr->GetTimestampCycles();
- }
- };
- //
- // Templates to treat both NLWTrace::TLogItem and NLWTrace::TTrackLog in the same way (e.g. in TLogQuery)
- //
- template <class TLog>
- struct TLogTraits {};
- template <>
- struct TLogTraits<NLWTrace::TLogItem> {
- using TLog = NLWTrace::TLogItem;
- using const_iterator = const NLWTrace::TLogItem*;
- using const_reverse_iterator = const NLWTrace::TLogItem*;
- static const_iterator begin(const TLog& log) { return &log; }
- static const_iterator end(const TLog& log) { return &log + 1; }
- static const_reverse_iterator rbegin(const TLog& log) { return &log; }
- static const_reverse_iterator rend(const TLog& log) { return &log + 1; }
- static bool empty(const TLog&) { return false; }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log; }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log; }
- };
- template <>
- struct TLogTraits<NLWTrace::TTrackLog> {
- using TLog = NLWTrace::TTrackLog;
- using const_iterator = NLWTrace::TTrackLog::TItems::const_iterator;
- using const_reverse_iterator = NLWTrace::TTrackLog::TItems::const_reverse_iterator;
- static const_iterator begin(const TLog& log) { return log.Items.begin(); }
- static const_iterator end(const TLog& log) { return log.Items.end(); }
- static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
- static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
- static bool empty(const TLog& log) { return log.Items.empty(); }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
- };
- template <>
- struct TLogTraits<TTrackLogRefs> {
- using TLog = TTrackLogRefs;
- using const_iterator = TTrackLogRefs::TItems::const_iterator;
- using const_reverse_iterator = TTrackLogRefs::TItems::const_reverse_iterator;
- static const_iterator begin(const TLog& log) { return log.Items.begin(); }
- static const_iterator end(const TLog& log) { return log.Items.end(); }
- static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
- static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
- static bool empty(const TLog& log) { return log.Items.empty(); }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
- };
- /*
- * Log Query Language:
- * Data expressions:
- * 1) myparam[0], myparam[-1] // the first and the last myparam in any probe/provider
- * 2) myparam // the first (the same as [0])
- * 3) PROVIDER..myparam // any probe with myparam in PROVIDER
- * 4) MyProbe._elapsedMs // Ms since track begin for the first MyProbe event
- * 5) PROVIDER.MyProbe._sliceUs // Us since previous event in track for the first PROVIDER.MyProbe event
- */
- struct TLogQuery {
- private:
- enum ESpecialParam {
- NotSpecial = 0,
- // The last '*' can be one of: Ms, Us, Ns, Min, Hours, (blank means seconds)
- // '*Time' can be one of: RTime (since cut ts for given dataset), NTime (negative time since now), Time (since machine start)
- TrackDuration = 1, // _track*
- TrackBeginTime = 2, // _begin*Time*
- TrackEndTime = 3, // _end*Time*
- ElapsedDuration = 4, // _elapsed*
- SliceDuration = 5, // _slice*
- ThreadTime = 6, // _thr*Time*
- };
- template <class TLog, class TTr = TLogTraits<TLog>>
- struct TExecuteQuery;
- template <class TLog, class TTr>
- friend struct TExecuteQuery;
- template <class TLog, class TTr>
- struct TExecuteQuery {
- const TLogQuery& Query;
- const TLog* Log = nullptr;
- bool Reversed;
- i64 Skip;
- const NLWTrace::TLogItem* Item = nullptr;
- typename TTr::const_iterator FwdIter;
- typename TTr::const_reverse_iterator RevIter;
- NLWTrace::TTypedParam Result;
- explicit TExecuteQuery(const TLogQuery& query, const TLog& log)
- : Query(query)
- , Log(&log)
- , Reversed(Query.Index < 0)
- , Skip(Reversed? -Query.Index - 1: Query.Index)
- , FwdIter()
- , RevIter()
- {}
- void ExecuteQuery()
- {
- if (!Reversed) {
- for (auto i = TTr::begin(*Log), e = TTr::end(*Log); i != e; ++i) {
- if (FwdIteration(i)) {
- return;
- }
- }
- } else {
- for (auto i = TTr::rbegin(*Log), e = TTr::rend(*Log); i != e; ++i) {
- if (RevIteration(i)) {
- return;
- }
- }
- }
- }
- bool FwdIteration(typename TTr::const_iterator it)
- {
- FwdIter = it;
- Item = &*it;
- return ProcessItem();
- }
- bool RevIteration(typename TTr::const_reverse_iterator it)
- {
- RevIter = it;
- Item = &*it;
- return ProcessItem();
- }
- bool ProcessItem()
- {
- if (Query.Provider && Query.Provider != Item->Probe->Event.GetProvider()) {
- return false;
- }
- if (Query.Probe && Query.Probe != Item->Probe->Event.Name) {
- return false;
- }
- switch (Query.SpecialParam) {
- case NotSpecial:
- if (Item->Probe->Event.Signature.FindParamIndex(Query.ParamName) != size_t(-1)) {
- break; // param found
- } else {
- return false; // param not found
- }
- case TrackDuration: Y_ABORT();
- case TrackBeginTime: Y_ABORT();
- case TrackEndTime: Y_ABORT();
- case ElapsedDuration: break;
- case SliceDuration: break;
- case ThreadTime: break;
- }
- if (Skip > 0) {
- Skip--;
- return false;
- }
- switch (Query.SpecialParam) {
- case NotSpecial:
- Result = NLWTrace::TTypedParam(Item->GetParam(Query.ParamName));
- return true;
- case TrackDuration: Y_ABORT();
- case TrackBeginTime: Y_ABORT();
- case TrackEndTime: Y_ABORT();
- case ElapsedDuration:
- Result = NLWTrace::TTypedParam(Query.Duration(
- Log->GetTimestampCycles(),
- Item->GetTimestampCycles()));
- return true;
- case SliceDuration:
- Result = NLWTrace::TTypedParam(Query.Duration(
- PrevOrSame().GetTimestampCycles(),
- Item->GetTimestampCycles()));
- return true;
- case ThreadTime:
- Result = NLWTrace::TTypedParam(Query.Instant(Item->GetTimestampCycles()));
- return true;
- }
- return true;
- }
- const NLWTrace::TLogItem& PrevOrSame() const
- {
- if (!Reversed) {
- auto i = FwdIter;
- if (i != TTr::begin(*Log)) {
- i--;
- }
- return *i;
- } else {
- auto j = RevIter + 1;
- if (j == TTr::rend(*Log)) {
- return *RevIter;
- }
- return *j;
- }
- }
- };
- TString Text;
- TString Provider;
- TString Probe;
- TString ParamName;
- ESpecialParam SpecialParam = NotSpecial;
- i64 Index = 0;
- double TimeUnitSec = 1.0;
- i64 ZeroTs = 0;
- i64 RTimeZeroTs = 0;
- i64 NTimeZeroTs = 0;
- public:
- TLogQuery() {}
- explicit TLogQuery(const TString& text)
- : Text(text)
- {
- try {
- if (!Text.empty()) {
- ParseQuery(Text);
- }
- } catch (...) {
- ythrow yexception()
- << CurrentExceptionMessage()
- << " while parsing track log query: "
- << Text;
- }
- }
- operator bool() const
- {
- return !Text.empty();
- }
- template <class TLog>
- NLWTrace::TTypedParam ExecuteQuery(const TLog& log) const
- {
- using TTr = TLogTraits<TLog>;
- WWW_CHECK(Text, "execute of empty log query");
- if (TTr::empty(log)) {
- return NLWTrace::TTypedParam();
- }
- if (SpecialParam == TrackDuration) {
- return Duration(
- log.GetTimestampCycles(),
- TTr::back(log).GetTimestampCycles());
- } else if (SpecialParam == TrackBeginTime) {
- return Instant(log.GetTimestampCycles());
- } else if (SpecialParam == TrackEndTime) {
- return Instant(TTr::back(log).GetTimestampCycles());
- }
- TExecuteQuery<TLog, TTr> exec(*this, log);
- exec.ExecuteQuery();
- return exec.Result;
- }
- private:
- NLWTrace::TTypedParam Duration(ui64 ts1, ui64 ts2) const
- {
- double sec = NHPTimer::GetSeconds(ts1 < ts2? ts2 - ts1: 0);
- return NLWTrace::TTypedParam(sec / TimeUnitSec);
- }
- NLWTrace::TTypedParam Instant(ui64 ts) const
- {
- double sec = NHPTimer::GetSeconds(i64(ts) - ZeroTs);
- return NLWTrace::TTypedParam(sec / TimeUnitSec);
- }
- void ParseQuery(const TString& s)
- {
- auto parts = SplitString(s, ".");
- WWW_CHECK(parts.size() <= 3, "too many name specifiers");
- ParseParamSelector(parts.back());
- if (parts.size() >= 2) {
- ParseProbeSelector(parts[parts.size() - 2]);
- }
- if (parts.size() >= 3) {
- ParseProviderSelector(parts[parts.size() - 3]);
- }
- }
- void ParseParamSelector(const TString& s)
- {
- size_t bracket = s.find('[');
- if (bracket == TString::npos) {
- ParseParamName(s);
- Index = 0;
- } else {
- ParseParamName(s.substr(0, bracket));
- size_t bracket2 = s.find(']', bracket);
- WWW_CHECK(bracket2 != TString::npos, "closing braket ']' is missing");
- Index = FromString<i64>(s.substr(bracket + 1, bracket2 - bracket - 1));
- }
- }
- void ParseParamName(const TString& s)
- {
- ParamName = s;
- TString paramName = s;
- const static TVector<std::pair<TString, ESpecialParam>> specials = {
- { "_track", TrackDuration },
- { "_begin", TrackBeginTime },
- { "_end", TrackEndTime },
- { "_elapsed", ElapsedDuration },
- { "_slice", SliceDuration },
- { "_thr", ThreadTime }
- };
- // Check for special params
- SpecialParam = NotSpecial;
- for (const auto& p : specials) {
- if (paramName.StartsWith(p.first)) {
- SpecialParam = p.second;
- paramName.erase(0, p.first.size());
- break;
- }
- }
- if (SpecialParam == NotSpecial) {
- return;
- }
- const static TVector<std::pair<TString, double>> timeUnits = {
- { "Ms", 1e-3 },
- { "Us", 1e-6 },
- { "Ns", 1e-9 },
- { "Min", 60.0 },
- { "Hours", 3600.0 }
- };
- // Parse units for special params
- TimeUnitSec = 1.0;
- for (const auto& p : timeUnits) {
- if (paramName.EndsWith(p.first)) {
- TimeUnitSec = p.second;
- paramName.erase(paramName.size() - p.first.size());
- break;
- }
- }
- if (SpecialParam == ThreadTime ||
- SpecialParam == TrackBeginTime ||
- SpecialParam == TrackEndTime)
- {
- // Parse time zero for special instant params
- const TVector<std::pair<TString, i64>> timeZeros = {
- { "RTime", RTimeZeroTs },
- { "NTime", NTimeZeroTs },
- { "Time", 0ll }
- };
- ZeroTs = -1;
- for (const auto& p : timeZeros) {
- if (paramName.EndsWith(p.first)) {
- ZeroTs = p.second;
- paramName.erase(paramName.size() - p.first.size());
- break;
- }
- }
- WWW_CHECK(ZeroTs != -1, "wrong special param name (postfix '*Time' required): %s", s.data());
- }
- WWW_CHECK(paramName.empty(), "wrong special param name: %s", s.data());
- }
- void ParseProbeSelector(const TString& s)
- {
- Probe = s;
- }
- void ParseProviderSelector(const TString& s)
- {
- Provider = s;
- }
- };
- using TVariants = TVector<std::pair<TString, TString>>;
- using TTags = TSet<TString>;
- TString GetProbeName(const NLWTrace::TProbe* probe, const char* sep = ".")
- {
- return TString(probe->Event.GetProvider()) + sep + probe->Event.Name;
- }
- struct TAdHocTraceConfig {
- NLWTrace::TQuery Cfg;
- TAdHocTraceConfig() {} // Invalid config
- TAdHocTraceConfig(ui16 logSize, ui64 logDurationUs, bool logShuttle)
- {
- auto block = Cfg.AddBlocks(); // Create one block to distinguish valid config
- if (logSize) {
- Cfg.SetPerThreadLogSize(logSize);
- }
- if (logDurationUs) {
- Cfg.SetLogDurationUs(logDurationUs);
- }
- if (logShuttle) {
- block->AddAction()->MutableRunLogShuttleAction();
- }
- }
- TAdHocTraceConfig(const TString& provider, const TString& probe, ui16 logSize = 0, ui64 logDurationUs = 0, bool logShuttle = false)
- : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
- {
- auto block = Cfg.MutableBlocks(0);
- auto pdesc = block->MutableProbeDesc();
- pdesc->SetProvider(provider);
- pdesc->SetName(probe);
- }
- explicit TAdHocTraceConfig(const TString& group, ui16 logSize = 0, ui64 logDurationUs = 0, bool logShuttle = false)
- : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
- {
- auto block = Cfg.MutableBlocks(0);
- auto pdesc = block->MutableProbeDesc();
- pdesc->SetGroup(group);
- }
- TString Id() const
- {
- TStringStream ss;
- for (size_t blockIdx = 0; blockIdx < Cfg.BlocksSize(); blockIdx++) {
- if (!ss.Str().empty()) {
- ss << "/";
- }
- auto block = Cfg.GetBlocks(blockIdx);
- auto pdesc = block.GetProbeDesc();
- if (pdesc.GetProvider()) {
- ss << "." << pdesc.GetProvider() << "." << pdesc.GetName();
- } else if (pdesc.GetGroup()) {
- ss << ".Group." << pdesc.GetGroup();
- }
- // TODO[serxa]: handle predicate
- for (size_t actionIdx = 0; actionIdx < block.ActionSize(); actionIdx++) {
- const NLWTrace::TAction& action = block.GetAction(actionIdx);
- if (action.HasRunLogShuttleAction()) {
- auto ls = action.GetRunLogShuttleAction();
- ss << ".alsr";
- if (ls.GetIgnore()) {
- ss << "-i";
- }
- if (ls.GetShuttlesCount()) {
- ss << "-s" << ls.GetShuttlesCount();
- }
- if (ls.GetMaxTrackLength()) {
- ss << "-t" << ls.GetMaxTrackLength();
- }
- } else if (action.HasEditLogShuttleAction()) {
- auto ls = action.GetEditLogShuttleAction();
- ss << ".alse";
- if (ls.GetIgnore()) {
- ss << "-i";
- }
- } else if (action.HasDropLogShuttleAction()) {
- ss << ".alsd";
- }
- }
- }
- if (Cfg.GetPerThreadLogSize()) {
- ss << ".l" << Cfg.GetPerThreadLogSize();
- }
- if (Cfg.GetLogDurationUs()) {
- ui64 logDurationUs = Cfg.GetLogDurationUs();
- if (logDurationUs % (60 * 1000 * 1000) == 0)
- ss << ".d" << logDurationUs / (60 * 1000 * 1000) << "m";
- if (logDurationUs % (1000 * 1000) == 0)
- ss << ".d" << logDurationUs / (1000 * 1000) << "s";
- else if (logDurationUs % 1000 == 0)
- ss << ".d" << logDurationUs / 1000 << "ms";
- else
- ss << ".d" << logDurationUs << "us";
- }
- return ss.Str();
- }
- bool IsValid() const
- {
- return Cfg.BlocksSize() > 0;
- }
- NLWTrace::TQuery Query() const
- {
- if (!IsValid()) {
- ythrow yexception() << "invalid adhoc trace config";
- }
- return Cfg;
- }
- bool ParseId(const TString& id)
- {
- if (IsAdHocId(id)) {
- for (const TString& block : SplitString(id, "/")) {
- if (block.empty()) {
- continue;
- }
- size_t cutPos = (block[0] == '.'? 1: 0);
- TVector<TString> parts = SplitString(block.substr(cutPos), ".");
- WWW_CHECK(parts.size() >= 2, "too few parts in adhoc trace id '%s' block '%s'", id.data(), block.data());
- auto blockPb = Cfg.AddBlocks();
- auto pdescPb = blockPb->MutableProbeDesc();
- if (parts[0] == "Group") {
- pdescPb->SetGroup(parts[1]);
- } else {
- pdescPb->SetProvider(parts[0]);
- pdescPb->SetName(parts[1]);
- }
- bool defaultAction = true;
- for (auto i = parts.begin() + 2, e = parts.end(); i != e; ++i) {
- const TString& part = *i;
- if (part.empty()) {
- continue;
- }
- switch (part[0]) {
- case 'l': Cfg.SetPerThreadLogSize(FromString<ui16>(part.substr(1))); break;
- case 'd': Cfg.SetLogDurationUs(ParseDuration(part.substr(1))); break;
- case 's': blockPb->MutablePredicate()->SetSampleRate(1.0 / Max<ui64>(1, FromString<ui64>(part.substr(1)))); break;
- case 'p': ParsePredicate(blockPb->MutablePredicate()->AddOperators(), part.substr(1)); break;
- case 'a': ParseAction(blockPb->AddAction(), part.substr(1)); defaultAction = false; break;
- default: WWW_CHECK(false, "unknown adhoc trace part type '%s' in '%s'", part.data(), id.data());
- }
- }
- if (defaultAction) {
- blockPb->AddAction()->MutableLogAction();
- }
- }
- return true;
- }
- return false;
- }
- private:
- static bool IsAdHocId(const TString& id)
- {
- return !id.empty() && id[0] == '.';
- }
- void ParsePredicate(NLWTrace::TOperator* op, const TString& p)
- {
- size_t sign = p.find_first_of("=!><");
- WWW_CHECK(sign != TString::npos, "wrong predicate format in adhoc trace: %s", p.data());
- op->AddArgument()->SetParam(p.substr(0, sign));
- size_t value = sign + 1;
- switch (p[sign]) {
- case '=':
- op->SetType(NLWTrace::OT_EQ);
- break;
- case '!': {
- WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
- WWW_CHECK(p[sign + 1] == '=', "wrong predicate operator format in adhoc trace: %s", p.data());
- value++;
- op->SetType(NLWTrace::OT_NE);
- break;
- }
- case '<': {
- WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
- if (p[sign + 1] == '=') {
- value++;
- op->SetType(NLWTrace::OT_LE);
- } else {
- op->SetType(NLWTrace::OT_LT);
- }
- break;
- }
- case '>': {
- WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
- if (p[sign + 1] == '=') {
- value++;
- op->SetType(NLWTrace::OT_GE);
- } else {
- op->SetType(NLWTrace::OT_GT);
- }
- break;
- }
- default: WWW_CHECK(false, "wrong predicate operator format in adhoc trace: %s", p.data());
- }
- op->AddArgument()->SetValue(p.substr(value));
- }
- void ParseAction(NLWTrace::TAction* action, const TString& a)
- {
- // NOTE: checks for longer action names should go first, your captain.
- if (a.substr(0, 3) == "lsr") {
- auto pb = action->MutableRunLogShuttleAction();
- for (const TString& opt : SplitString(a.substr(3), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 'i': pb->SetIgnore(true); break;
- case 's': pb->SetShuttlesCount(FromString<ui64>(opt.substr(1))); break;
- case 't': pb->SetMaxTrackLength(FromString<ui64>(opt.substr(1))); break;
- default: WWW_CHECK(false, "unknown adhoc trace log shuttle opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else if (a.substr(0, 3) == "lse") {
- auto pb = action->MutableEditLogShuttleAction();
- for (const TString& opt : SplitString(a.substr(3), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 'i': pb->SetIgnore(true); break;
- default: WWW_CHECK(false, "unknown adhoc trace log shuttle opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else if (a.substr(0, 3) == "lsd") {
- action->MutableDropLogShuttleAction();
- } else if (a.substr(0, 1) == "l") {
- auto pb = action->MutableLogAction();
- for (const TString& opt : SplitString(a.substr(1), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 't': pb->SetLogTimestamp(true); break;
- case 'r': pb->SetMaxRecords(FromString<ui32>(opt.substr(1))); break;
- default: WWW_CHECK(false, "unknown adhoc trace log opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else {
- WWW_CHECK(false, "wrong action format in adhoc trace: %s", a.data());
- }
- }
- static ui64 ParseDuration(const TString& s)
- {
- if (s.substr(s.length() - 2) == "us")
- return FromString<ui64>(s.substr(0, s.length() - 2));
- if (s.substr(s.length() - 2) == "ms")
- return FromString<ui64>(s.substr(0, s.length() - 2)) * 1000;
- if (s.substr(s.length() - 1) == "s")
- return FromString<ui64>(s.substr(0, s.length() - 1)) * 1000 * 1000;
- if (s.substr(s.length() - 1) == "m")
- return FromString<ui64>(s.substr(0, s.length() - 1)) * 60 * 1000 * 1000;
- else
- return FromString<ui64>(s);
- }
- };
- // Class that maintains one thread iff there are adhoc traces and cleans'em by deadlines
- class TTraceCleaner {
- private:
- NLWTrace::TManager* TraceMngr;
- TAtomic Quit = 0;
- TMutex Mtx;
- TCondVar WakeCondVar;
- THashMap<TString, TInstant> Deadlines;
- volatile bool ThreadIsRunning = false;
- THolder<TThread> Thread;
- public:
- TTraceCleaner(NLWTrace::TManager* traceMngr)
- : TraceMngr(traceMngr)
- {}
- ~TTraceCleaner()
- {
- AtomicSet(Quit, 1);
- WakeCondVar.Signal();
- // TThread dtor joins thread
- }
- // Returns deadline for specified trace id or zero
- TInstant GetDeadline(const TString& id) const
- {
- TGuard<TMutex> g(Mtx);
- auto iter = Deadlines.find(id);
- return iter != Deadlines.end()? iter->second: TInstant::Zero();
- }
- // Postpone deletion of specified trace for specified timeout
- void Postpone(const TString& id, TDuration timeout, bool allowLowering)
- {
- TGuard<TMutex> g(Mtx);
- TInstant newDeadline = TInstant::Now() + timeout;
- if (Deadlines[id] < newDeadline) {
- Deadlines[id] = newDeadline;
- } else if (allowLowering) { // Deadline lowering requires wake
- Deadlines[id] = newDeadline;
- WakeCondVar.Signal();
- }
- if (newDeadline != TInstant::Max() && !ThreadIsRunning) {
- // Note that dtor joins previous thread if any
- Thread.Reset(new TThread(ThreadProc, this));
- Thread->Start();
- ThreadIsRunning = true;
- }
- }
- // Forget about specified trace deletion
- void Forget(const TString& id)
- {
- TGuard<TMutex> g(Mtx);
- Deadlines.erase(id);
- WakeCondVar.Signal(); // in case thread is not required any more
- }
- private:
- void Exec()
- {
- while (!AtomicGet(Quit)) {
- TGuard<TMutex> g(Mtx);
- // Delete all timed out traces
- TInstant now = TInstant::Now();
- TInstant nextDeadline = TInstant::Max();
- for (auto i = Deadlines.begin(), e = Deadlines.end(); i != e;) {
- const TString& id = i->first;
- TInstant deadline = i->second;
- if (deadline < now) {
- try {
- TraceMngr->Delete(id);
- } catch (...) {
- // already deleted
- }
- Deadlines.erase(i++);
- } else {
- nextDeadline = Min(nextDeadline, deadline);
- ++i;
- }
- }
- // Stop thread if there is no more work
- if (Deadlines.empty() || nextDeadline == TInstant::Max()) {
- ThreadIsRunning = false;
- break;
- }
- // Wait until next deadline or quit
- WakeCondVar.WaitD(Mtx, nextDeadline);
- }
- }
- static void* ThreadProc(void* _this)
- {
- TString name = "LWTraceCleaner";
- // Copy-pasted from kikimr/core/util/thread.h
- #if defined(_linux_)
- TStringStream linuxName;
- linuxName << TStringBuf(GetExecPath()).RNextTok('/') << "." << name;
- TThread::SetCurrentThreadName(linuxName.Str().data());
- #else
- TThread::SetCurrentThreadName(name.data());
- #endif
- static_cast<TTraceCleaner*>(_this)->Exec();
- return nullptr;
- }
- };
- class TChromeTrace {
- private:
- TMultiMap<double, TString> TraceEvents;
- THashMap<TThread::TId, TString> Tids;
- public:
- void Add(TThread::TId tid, ui64 tsCycles, const TString& ph, const TString& cat,
- const NLWTrace::TLogItem* argsItem = nullptr,
- const TString& name = TString(), const TString& id = TString())
- {
- auto tidIter = Tids.find(tid);
- if (tidIter == Tids.end()) {
- tidIter = Tids.emplace(tid, ToString(Tids.size() + 1)).first;
- }
- const TString& shortId = tidIter->second;
- double ts = Timestamp(tsCycles);
- TraceEvents.emplace(ts, Event(shortId, ts, ph, cat, argsItem, name, id));
- }
- void Output(IOutputStream& os)
- {
- os << "{\"traceEvents\":[";
- bool first = true;
- for (auto kv : TraceEvents) {
- if (!first) {
- os << ",\n";
- }
- os << kv.second;
- first = false;
- }
- os << "]}";
- }
- private:
- static TString Event(const TString& tid, double ts, const TString& ph, const TString& cat,
- const NLWTrace::TLogItem* argsItem,
- const TString& name, const TString& id)
- {
- TStringStream ss;
- pid_t pid = 1;
- ss << "{\"pid\":" << pid
- << ",\"tid\":" << tid
- << ",\"ts\":" << Sprintf("%lf", ts)
- << ",\"ph\":\"" << ph << "\""
- << ",\"cat\":\"" << cat << "\"";
- if (name) {
- ss << ",\"name\":\"" << name << "\"";
- }
- if (id) {
- ss << ",\"id\":\"" << id << "\"";
- }
- if (argsItem && argsItem->SavedParamsCount > 0) {
- ss << ",\"args\":{";
- TString paramValues[LWTRACE_MAX_PARAMS];
- argsItem->Probe->Event.Signature.SerializeParams(argsItem->Params, paramValues);
- bool first = true;
- for (size_t pi = 0; pi < argsItem->SavedParamsCount; pi++, first = false) {
- if (!first) {
- ss << ",";
- }
- ss << "\"" << TString(argsItem->Probe->Event.Signature.ParamNames[pi]) << "\":"
- "\"" << paramValues[pi] << "\"";
- }
- ss << "}";
- }
- ss << "}";
- return ss.Str();
- }
- static double Timestamp(ui64 cycles)
- {
- return double(cycles) * 1000000.0 / NHPTimer::GetClockRate();
- }
- };
- TString MakeUrl(const TCgiParameters& e, const THashMap<TString, TString>& values)
- {
- TStringStream ss;
- bool first = true;
- for (const auto& [k, v] : e) {
- if (values.find(k) == values.end()) {
- ss << (first? "?": "&") << k << "=" << v;
- first = false;
- }
- }
- for (const auto& [k, v] : values) {
- ss << (first? "?": "&") << k << "=" << v;
- first = false;
- }
- return ss.Str();
- }
- TString MakeUrl(const TCgiParameters& e, const TString& key, const TString& value, bool keep = false)
- {
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- if (keep || kv.first != key) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- ss << (first? "?": "&") << key << "=" << value;
- return ss.Str();
- }
- TString MakeUrlAdd(const TCgiParameters& e, const TString& key, const TString& value)
- {
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- ss << (first? "?": "&") << key << "=" << value;
- return ss.Str();
- }
- TString MakeUrlReplace(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue)
- {
- TStringStream ss;
- bool first = true;
- bool inserted = false;
- for (const auto& kv : e) {
- if (kv.first == key && (kv.second == oldValue || kv.second == newValue)) {
- if (!inserted) {
- inserted = true;
- ss << (first? "?": "&") << key << "=" << newValue;
- first = false;
- }
- } else {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- if (!inserted) {
- ss << (first? "?": "&") << key << "=" << newValue;
- }
- return ss.Str();
- }
- TString MakeUrlErase(const TCgiParameters& e, const TString& key, const TString& value)
- {
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- if (kv.first != key || kv.second != value) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- return ss.Str();
- }
- TString EscapeSubvalue(const TString& s)
- {
- TString ret;
- ret.reserve(s.size());
- for (size_t i = 0; i < s.size(); i++) {
- char c = s[i];
- if (c == ':') {
- ret.append("^c");
- } else if (c == '^') {
- ret.append("^^");
- } else {
- ret.append(c);
- }
- }
- return ret;
- }
- TString UnescapeSubvalue(const TString& s)
- {
- TString ret;
- ret.reserve(s.size());
- for (size_t i = 0; i < s.size(); i++) {
- char c = s[i];
- if (c == '^' && i + 1 < s.size()) {
- char c2 = s[++i];
- if (c2 == 'c') {
- ret.append(':');
- } else if (c2 == '^') {
- ret.append('^');
- } else {
- ret.append(c);
- ret.append(c2);
- }
- } else {
- ret.append(c);
- }
- }
- return ret;
- }
- TVector<TString> Subvalues(const TCgiParameters& e, const TString& key)
- {
- if (!e.Has(key)) {
- return TVector<TString>();
- } else {
- TVector<TString> ret;
- for (const TString& s : SplitString(e.Get(key), ":", 0, KEEP_EMPTY_TOKENS)) {
- ret.push_back(UnescapeSubvalue(s));
- }
- if (ret.empty()) {
- ret.push_back("");
- }
- return ret;
- }
- }
- TString ParseTagsOut(const TString& taggedStr, TTags& tags)
- {
- auto vec = SplitString(taggedStr, "-");
- if (vec.empty()) {
- return "";
- }
- auto iter = vec.begin();
- TString value = *iter++;
- for (;iter != vec.end(); ++iter) {
- tags.insert(*iter);
- }
- return value;
- }
- TString JoinTags(TTags tags) {
- return JoinStrings(TVector<TString>(tags.begin(), tags.end()), "-");
- }
- TString MakeValue(const TVector<TString>& subvalues)
- {
- TVector<TString> subvaluesEsc;
- for (const TString& s : subvalues) {
- subvaluesEsc.push_back(EscapeSubvalue(s));
- }
- return JoinStrings(subvaluesEsc, ":");
- }
- TString MakeUrlAddSub(const TCgiParameters& e, const TString& key, const TString& subvalue)
- {
- const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- subvalues.push_back(subvalue);
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
- }
- TString MakeUrlReplaceSub(const TCgiParameters& e, const TString& key, const TString& oldSubvalue, const TString& newSubvalue)
- {
- const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- auto iter = std::find(subvalues.begin(), subvalues.end(), oldSubvalue);
- if (iter != subvalues.end()) {
- *iter = newSubvalue;
- } else {
- subvalues.push_back(newSubvalue);
- }
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
- }
- TString MakeUrlEraseSub(const TCgiParameters& e, const TString& key, const TString& subvalue)
- {
- const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- auto iter = std::find(subvalues.begin(), subvalues.end(), subvalue);
- if (iter != subvalues.end()) {
- subvalues.erase(iter);
- }
- if (subvalues.empty()) {
- return MakeUrlErase(e, key, value);
- } else {
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
- }
- }
- template <bool sub> TString UrlAdd(const TCgiParameters& e, const TString& key, const TString& value);
- template <> TString UrlAdd<false>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlAdd(e, key, value);
- }
- template <> TString UrlAdd<true>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlAddSub(e, key, value);
- }
- template <bool sub> TString UrlReplace(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue);
- template <> TString UrlReplace<false>(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue) {
- return MakeUrlReplace(e, key, oldValue, newValue);
- }
- template <> TString UrlReplace<true>(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue) {
- return MakeUrlReplaceSub(e, key, oldValue, newValue);
- }
- template <bool sub> TString UrlErase(const TCgiParameters& e, const TString& key, const TString& value);
- template <> TString UrlErase<false>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlErase(e, key, value);
- }
- template <> TString UrlErase<true>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlEraseSub(e, key, value);
- }
- void OutputCommonHeader(IOutputStream& out)
- {
- out << NResource::Find("lwtrace/mon/static/header.html") << Endl;
- }
- void OutputCommonFooter(IOutputStream& out)
- {
- out << NResource::Find("lwtrace/mon/static/footer.html") << Endl;
- }
- struct TScopedHtmlInner {
- explicit TScopedHtmlInner(IOutputStream& str)
- : Str(str)
- {
- Str << "<!DOCTYPE html>\n"
- "<html>";
- HTML(str) {
- HEAD() { OutputCommonHeader(Str); }
- }
- Str << "<body>";
- }
- ~TScopedHtmlInner()
- {
- OutputCommonFooter(Str);
- Str << "</body></html>";
- }
- inline operator bool () const noexcept { return true; }
- IOutputStream &Str;
- };
- TString NavbarHeader()
- {
- return "<div class=\"navbar-header\">"
- "<a class=\"navbar-brand\" href=\"?mode=\">LWTrace</a>"
- "</div>";
- }
- struct TSelectorsContainer {
- TSelectorsContainer(IOutputStream& str)
- : Str(str)
- {
- Str << "<nav id=\"selectors-container\" class=\"navbar navbar-default\">"
- "<div class=\"container-fluid\">"
- << NavbarHeader() <<
- "<div class=\"navbar-text\" style=\"margin-top:12px;margin-bottom:10px\">";
- }
- ~TSelectorsContainer() {
- try {
- Str <<
- "</div>"
- "<div class=\"container-fluid\">"
- "<div class=\"pull-right\">"
- "<button id=\"download-btn\""
- " type=\"button\" style=\"display: inline-block;margin:7px\""
- " title=\"Chromium trace (load it in chrome://tracing/)\""
- " class=\"btn btn-default hidden\">"
- "<span class=\"glyphicon glyphicon-download-alt\"></span>"
- "</button>"
- "</div>"
- "</div>"
- "</div></nav>";
- } catch(...) {}
- }
- IOutputStream& Str;
- };
- struct TNullContainer {
- TNullContainer(IOutputStream&) {}
- };
- class TPageGenBase: public std::exception {};
- template <class TContainer = TNullContainer>
- class TPageGen: public TPageGenBase {
- private:
- TString Content;
- TString HttpResponse;
- public:
- void BuildResponse()
- {
- TStringStream ss;
- WWW_HTML(ss) {
- TContainer container(ss);
- ss << Content;
- }
- HttpResponse = ss.Str();
- }
- explicit TPageGen(const TString& content = TString())
- : Content(content)
- {
- BuildResponse();
- }
- void Append(const TString& moreContent)
- {
- Content.append(moreContent);
- BuildResponse();
- }
- void Prepend(const TString& moreContent)
- {
- Content.prepend(moreContent);
- BuildResponse();
- }
- virtual const char* what() const noexcept { return HttpResponse.data(); }
- operator bool() const { return !Content.empty(); }
- };
- enum EStyleFlags {
- // bit 1
- Link = 0x0,
- Button = 0x1,
- // bit 2
- NonErasable = 0x0,
- Erasable = 0x2,
- // bit 3-4
- Medium = 0x0,
- Large = 0x4,
- Small = 0x8,
- ExtraSmall = 0xC,
- SizeMask = 0xC,
- // bit 5
- NoCaret = 0x0,
- Caret = 0x10,
- // bit 6
- SimpleValue = 0x0,
- CompositeValue = 0x20
- };
- template <ui64 flags>
- TString BtnClass() {
- if ((flags & SizeMask) == Large) {
- return "btn btn-lg";
- } else if ((flags & SizeMask) == Small) {
- return "btn btn-sm";
- } else if ((flags & SizeMask) == ExtraSmall) {
- return "btn btn-xs";
- }
- return "btn";
- }
- void SelectorTitle(IOutputStream& os, const TString& text)
- {
- if (!text.empty()) {
- os << text;
- }
- }
- template <ui64 flags>
- void BtnHref(IOutputStream& os, const TString& text, const TString& href, bool push = false)
- {
- if (flags & Button) {
- os << "<button type=\"button\" style=\"display: inline-block;margin:3px\" class=\""
- << BtnClass<flags>() << " "
- << (push? "btn-primary": "btn-default")
- << "\" onClick=\"window.location.href='" << href << "';\">"
- << text
- << "</button>";
- } else {
- os << "<a href=\"" << href << "\">"
- << text
- << "</a>";
- }
- }
- void DropdownBeginSublist(IOutputStream& os, const TString& text)
- {
- os << "<li>" << text << "<ul class=\"dropdown-menu\">";
- }
- void DropdownEndSublist(IOutputStream& os)
- {
- os << "</ul></li>";
- }
- void DropdownItem(IOutputStream& os, const TString& text, const TString& href, bool separated = false)
- {
- if (separated) {
- os << "<li role=\"separator\" class=\"divider\"></li>";
- }
- os << "<li><a href=\"" << href << "\">" << text << "</a></li>";
- }
- TString SuggestSelection()
- {
- return "--- ";
- }
- TString RemoveSelection()
- {
- return "Remove";
- }
- TString GetDescription(const TString& value, const TVariants& variants)
- {
- for (const auto& var : variants) {
- if (value == var.first) {
- return var.second;
- }
- }
- if (!value) {
- return SuggestSelection();
- }
- return value;
- }
- template <ui64 flags, bool sub = false>
- void DropdownSelector(IOutputStream& os, const TCgiParameters& e, const TString& param, const TString& value,
- const TString& text, const TVariants& variants, const TString& realValue = TString())
- {
- HTML(os) {
- SelectorTitle(os, text);
- os << "<div class=\"dropdown\" style=\"display:inline-block;margin:3px\">";
- if (flags & Button) {
- os << "<button class=\"" << BtnClass<flags>() << " btn-primary dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\">";
- } else {
- os << "<a href=\"#\" data-toggle=\"dropdown\">";
- }
- os << GetDescription(flags & CompositeValue? realValue: value, variants);
- if (flags & Caret) {
- os << "<span class=\"caret\"></span>";
- }
- if (flags & Button) {
- os <<"</button>";
- } else {
- os <<"</a>";
- }
- UL_CLASS ("dropdown-menu") {
- for (const auto& var : variants) {
- DropdownItem(os, var.second, UrlReplace<sub>(e, param, value, var.first));
- }
- if (flags & Erasable) {
- DropdownItem(os, RemoveSelection(), UrlErase<sub>(e, param, value), true);
- }
- }
- os << "</div>";
- }
- }
- void RequireSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
- const TString& text, const TVariants& variants)
- {
- const TString& value = e.Get(param);
- DropdownSelector<Link>(ss, e, param, value, text, variants);
- if (!value) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
- }
- void RequireMultipleSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
- const TString& text, const TVariants& variants)
- {
- SelectorTitle(ss, text);
- TSet<TString> selectedValues;
- for (const TString& subvalue : Subvalues(e, param)) {
- selectedValues.insert(subvalue);
- }
- for (const TString& subvalue : Subvalues(e, param)) {
- DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
- }
- if (selectedValues.contains("")) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, param, ""));
- if (selectedValues.empty()) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
- }
- }
- void OptionalSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
- const TString& text, const TVariants& variants)
- {
- TSet<TString> selectedValues;
- for (const TString& subvalue : Subvalues(e, param)) {
- selectedValues.insert(subvalue);
- }
- if (!selectedValues.empty()) {
- SelectorTitle(ss, text);
- }
- for (const TString& subvalue : Subvalues(e, param)) {
- DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
- }
- if (selectedValues.empty()) {
- BtnHref<Button|ExtraSmall>(ss, text, MakeUrlAddSub(e, param, ""));
- }
- }
- void OptionalMultipleSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
- const TString& text, const TVariants& variants)
- {
- TSet<TString> selectedValues;
- for (const TString& subvalue : Subvalues(e, param)) {
- selectedValues.insert(subvalue);
- }
- if (!selectedValues.empty()) {
- SelectorTitle(ss, text);
- }
- for (const TString& subvalue : Subvalues(e, param)) {
- DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
- }
- if (selectedValues.contains("")) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, selectedValues.empty()? text: "+", MakeUrlAddSub(e, param, ""));
- }
- }
- TVariants ListColumns(const NAnalytics::TTable& table)
- {
- TSet<TString> cols;
- // bool addSpecialCols = false;
- // if (addSpecialCols) {
- // cols.insert("_count");
- // }
- for (auto& row : table) {
- for (auto& kv : row) {
- cols.insert(kv.first);
- }
- }
- TVariants result;
- for (const auto& s : cols) {
- result.emplace_back(s, s);
- }
- return result;
- }
- TString TaggedValue(const TString& value, const TString& tag)
- {
- if (!tag) {
- return value;
- }
- return value + "-" + tag;
- }
- TVariants ValueVars(const TVariants& values, const TString& tag)
- {
- TVariants ret;
- for (auto& p : values) {
- ret.emplace_back(TaggedValue(p.first, tag), p.second);
- }
- return ret;
- }
- TVariants TagVars(const TString& value, const TVariants& tags)
- {
- TVariants ret;
- for (auto& p : tags) {
- ret.emplace_back(TaggedValue(value, p.first), p.second);
- }
- return ret;
- }
- TVariants SeriesTags()
- {
- TVariants ret; // MSVS2013 doesn't understand complex initializer lists
- ret.emplace_back("", "as is");
- ret.emplace_back("stack", "cumulative");
- return ret;
- }
- void SeriesSelectors(TStringStream& ss, const TCgiParameters& e,
- const TString& xparam, const TString& yparam, const NAnalytics::TTable& data)
- {
- TTags xtags;
- TString xn = ParseTagsOut(e.Get(xparam), xtags);
- DropdownSelector<Erasable, true>(ss, e, xparam, e.Get(xparam), "with Ox:",
- ValueVars(ListColumns(data), JoinTags(xtags)));
- if (xn) {
- DropdownSelector<Link, true>(ss, e, xparam, e.Get(xparam), "",
- TagVars(xn, SeriesTags()));
- }
- TString yns = e.Get(yparam);
- SelectorTitle(ss, "and Oy:");
- bool first = true;
- bool hasEmpty = false;
- for (auto& subvalue : Subvalues(e, yparam)) {
- TTags ytags;
- TString yn = ParseTagsOut(subvalue, ytags);
- DropdownSelector<Erasable, true>(ss, e, yparam, subvalue, first? "": ", ",
- ValueVars(ListColumns(data), JoinTags(ytags)));
- if (yn) {
- DropdownSelector<Link, true>(ss, e, yparam, subvalue, "",
- TagVars(yn, SeriesTags()));
- }
- first = false;
- if (yn.empty()) {
- hasEmpty = true;
- }
- }
- if (hasEmpty) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, yparam, ""));
- }
- if (!xn || !yns) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
- }
- class TProbesHtmlPrinter {
- private:
- TVector<TVector<TString>> TableData;
- static constexpr int TimeoutSec = 15 * 60; // default timeout
- public:
- void Push(const NLWTrace::TProbe* probe)
- {
- TableData.emplace_back();
- auto& row = TableData.back();
- row.emplace_back();
- TString& groups = row.back();
- bool first = true;
- for (const char* const* i = probe->Event.Groups; *i != nullptr; ++i, first = false) {
- groups.append(TString(first? "": ", ") + GroupHtml(*i));
- }
- row.push_back(ProbeHtml(probe->Event.GetProvider(), probe->Event.Name));
- row.emplace_back();
- TString& params = row.back();
- first = true;
- for (size_t i = 0; i < probe->Event.Signature.ParamCount; i++, first = false) {
- params.append(TString(first? "": ", ") + probe->Event.Signature.ParamTypes[i]
- + " " + probe->Event.Signature.ParamNames[i]);
- }
- row.emplace_back(ToString(probe->GetExecutorsCount()));
- }
- void Output(IOutputStream& os)
- {
- HTML(os) {
- TABLE() {
- TABLEHEAD() {
- TABLEH() { os << "Groups"; }
- TABLEH() { os << "Name"; }
- TABLEH() { os << "Params"; }
- TABLEH() { os << "ExecCount"; }
- }
- TABLEBODY() {
- for (auto& row : TableData) {
- TABLER() {
- for (TString& cell : row) {
- TABLED() { os << cell; }
- }
- }
- }
- }
- }
- }
- }
- private:
- TString GroupHtml(const TString& group)
- {
- TStringStream ss;
- ss << "<div class=\"dropdown\" style=\"display:inline-block\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << group << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group).Id() << "'});\">"
- "Trace 1000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 10000).Id() << "'});\">"
- "Trace 10000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 1000000).Id() << "'});\">"
- "Trace 1 second</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 10000000).Id() << "'});\">"
- "Trace 10 seconds</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 0, true).Id() << "'});\">"
- "Trace 1000 tracks</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 10000, 0, true).Id() << "'});\">"
- "Trace 10000 tracks</a></li>"
- "</ul>"
- "</div>";
- return ss.Str();
- }
- TString ProbeHtml(const TString& provider, const TString& probe)
- {
- TStringStream ss;
- ss << "<div class=\"dropdown\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << probe << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe).Id() << "'});\">"
- "Trace 1000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 10000).Id() << "'});\">"
- "Trace 10000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 1000000).Id() << "'});\">"
- "Trace 1 second</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 10000000).Id() << "'});\">"
- "Trace 10 seconds</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 0, true).Id() << "'});\">"
- "Trace 1000 tracks</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 10000, 0, true).Id() << "'});\">"
- "Trace 10000 tracks</a></li>"
- "</ul>"
- "</div>";
- return ss.Str();
- }
- };
- void TDashboardRegistry::Register(const NLWTrace::TDashboard& dashboard) {
- TGuard<TMutex> g(Mutex);
- Dashboards[dashboard.GetName()] = dashboard;
- }
- void TDashboardRegistry::Register(const TVector<NLWTrace::TDashboard>& dashboards) {
- for (const auto& dashboard : dashboards) {
- Register(dashboard);
- }
- }
- void TDashboardRegistry::Register(const TString& dashText) {
- NLWTrace::TDashboard dash;
- if (!google::protobuf::TextFormat::ParseFromString(dashText, &dash)) {
- ythrow yexception() << "Couldn't parse into dashboard";
- }
- Register(dash);
- }
- bool TDashboardRegistry::Get(const TString& name, NLWTrace::TDashboard& dash) {
- TGuard<TMutex> g(Mutex);
- if (!Dashboards.contains(name)) {
- return false;
- }
- dash = Dashboards[name];
- return true;
- }
- void TDashboardRegistry::Output(TStringStream& ss) {
- HTML(ss) {
- TABLE() {
- TABLEHEAD() {
- TABLEH() { ss << "Name"; }
- }
- TABLEBODY() {
- TGuard<TMutex> g(Mutex);
- for (auto& kv : Dashboards) {
- const auto& dash = kv.second;
- TABLER() {
- TABLED() {
- ss << "<a href='?mode=dashboard&name=" << dash.GetName() << "'>" << dash.GetName() << "</a>";
- }
- }
- }
- }
- }
- }
- }
- class ILogSource {
- public:
- virtual ~ILogSource() {}
- virtual TString GetId() = 0;
- virtual TInstant GetStartTime() = 0;
- virtual TDuration GetTimeout(TInstant now) = 0;
- virtual ui64 GetEventsCount() = 0;
- virtual ui64 GetThreadsCount() = 0;
- };
- class TTraceLogSource : public ILogSource {
- private:
- TString Id;
- const TTraceCleaner& Cleaner;
- const NLWTrace::TSession* Trace;
- public:
- TTraceLogSource(const TString& id, const NLWTrace::TSession* trace, const TTraceCleaner& cleaner)
- : Id(id)
- , Cleaner(cleaner)
- , Trace(trace)
- {}
- TString GetId() override
- {
- return Id;
- }
- TInstant GetStartTime() override
- {
- return Trace->GetStartTime();
- }
- TDuration GetTimeout(TInstant now) override
- {
- TInstant deadline = Cleaner.GetDeadline(Id);
- if (deadline < now) {
- return TDuration::Zero();
- } else if (deadline == TInstant::Max()) {
- return TDuration::Max();
- } else {
- return deadline - now;
- }
- }
- ui64 GetEventsCount() override
- {
- return Trace->GetEventsCount();
- }
- ui64 GetThreadsCount() override
- {
- return Trace->GetThreadsCount();
- }
- };
- class TSnapshotLogSource : public ILogSource {
- private:
- TString Sid;
- // Log should be used for read-only purpose, because it can be accessed from multiple threads
- // Atomic pointer is used to avoid thread-safety issues with snapshot deletion
- // (I hope protobuf const-implementation doesn't use any mutable non-thread-safe stuff inside)
- TAtomicSharedPtr<NLWTrace::TLogPb> Log;
- public:
- // Constructor should be called under SnapshotsMtx lock
- TSnapshotLogSource(const TString& sid, const TAtomicSharedPtr<NLWTrace::TLogPb>& log)
- : Sid(sid)
- , Log(log)
- {}
- TString GetId() override
- {
- return Sid + "~";
- }
- TInstant GetStartTime() override
- {
- return TInstant::MicroSeconds(Log->GetCrtTime());
- }
- TDuration GetTimeout(TInstant now) override
- {
- Y_UNUSED(now);
- return TDuration::Max();
- }
- ui64 GetEventsCount() override
- {
- return Log->GetEventsCount();
- }
- ui64 GetThreadsCount() override
- {
- return Log->ThreadLogsSize();
- }
- };
- class TLogSources {
- private:
- TTraceCleaner& Cleaner;
- TInstant Now;
- using TLogSourcePtr = std::unique_ptr<ILogSource>;
- TMap<TString, TLogSourcePtr> LogSources;
- public:
- explicit TLogSources(TTraceCleaner& cleaner, TInstant now = TInstant::Now())
- : Cleaner(cleaner)
- , Now(now)
- {}
- void Push(const TString& sid, const TAtomicSharedPtr<NLWTrace::TLogPb>& log)
- {
- TLogSourcePtr ls(new TSnapshotLogSource(sid, log));
- LogSources.emplace(ls->GetId(), std::move(ls));
- }
- void Push(const TString& id, const NLWTrace::TSession* trace)
- {
- TLogSourcePtr ls(new TTraceLogSource(id, trace, Cleaner));
- LogSources.emplace(ls->GetId(), std::move(ls));
- }
- template <class TFunc>
- void ForEach(TFunc& func)
- {
- for (auto& kv : LogSources) {
- func.Push(kv.second.get());
- }
- }
- template <class TFunc>
- void ForEach(TFunc& func) const
- {
- for (const auto& kv : LogSources) {
- func.Push(kv.second.get());
- }
- }
- };
- class TTracesHtmlPrinter {
- private:
- IOutputStream& Os;
- TInstant Now;
- public:
- explicit TTracesHtmlPrinter(IOutputStream& os)
- : Os(os)
- , Now(TInstant::Now())
- {}
- void Push(ILogSource* src)
- {
- TString id = src->GetId();
- Os << "<tr>";
- Os << "<td>";
- try {
- Os << src->GetStartTime().ToStringUpToSeconds();
- } catch (...) {
- Os << "error: " << CurrentExceptionMessage();
- }
- Os << "</td>"
- << "<td><div class=\"dropdown\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << TimeoutToString(src->GetTimeout(Now)) << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=60', {id:'" << id << "'});\">1 min</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=600', {id:'" << id << "'});\">10 min</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=3600', {id:'" << id << "'});\">1 hour</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=86400', {id:'" << id << "'});\">1 day</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=604800', {id:'" << id << "'});\">1 week</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y', {id:'" << id << "'});\">no timeout</a></li>"
- "</ul>"
- "</div></td>"
- << "<td>" << EncodeHtmlPcdata(id) << "</td>"
- << "<td>" << src->GetEventsCount() << "</td>"
- << "<td>" << src->GetThreadsCount() << "</td>"
- << "<td><a href=\"?mode=log&id=" << id << "\">Text</a></td>"
- << "<td><a href=\"?mode=log&format=json&id=" << id << "\">Json</a></td>"
- << "<td><a href=\"?mode=query&id=" << id << "\">Query</a></td>"
- << "<td><a href=\"?mode=analytics&id=" << id << "\">Analytics</a></td>"
- << "<td><div class=\"dropdown navbar-right\">" // navbar-right is hack to drop left
- "<a href=\"#\" data-toggle=\"dropdown\">Modify</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=make_snapshot&ui=y', {id:'" << id << "'});\">Snapshot</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=delete&ui=y', {id:'" << id << "'});\">Delete</a></li>"
- "</ul>"
- "</div></td>"
- << "</tr>\n";
- }
- private:
- static TString TimeoutToString(TDuration d)
- {
- TStringStream ss;
- if (d == TDuration::Zero()) {
- ss << "0";
- } else if (d == TDuration::Max()) {
- ss << "-";
- } else {
- ui64 us = d.GetValue();
- ui64 ms = us / 1000;
- ui64 sec = ms / 1000;
- ui64 min = sec / 60;
- ui64 hours = min / 60;
- ui64 days = hours / 24;
- ui64 weeks = days / 7;
- us -= ms * 1000;
- ms -= sec * 1000;
- sec -= min * 60;
- min -= hours * 60;
- hours -= days * 24;
- days -= weeks * 7;
- int terms = 0;
- if ((terms > 0 && terms < 2) || ( terms == 0 && weeks)) { ss << (ss.Str()? " ": "") << weeks << "w"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && days)) { ss << (ss.Str()? " ": "") << days << "d"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && hours)) { ss << (ss.Str()? " ": "") << hours << "h"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && min)) { ss << (ss.Str()? " ": "") << min << "m"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && sec)) { ss << (ss.Str()? " ": "") << sec << "s"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && ms)) { ss << (ss.Str()? " ": "") << ms << "ms"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && us)) { ss << (ss.Str()? " ": "") << us << "us"; terms++; }
- }
- return ss.Str();
- }
- };
- class TTracesLister {
- private:
- TVariants& Variants;
- public:
- TTracesLister(TVariants& variants)
- : Variants(variants)
- {}
- void Push(ILogSource* src)
- {
- Variants.emplace_back(src->GetId(), src->GetId());
- }
- };
- TVariants ListTraces(const TLogSources& srcs)
- {
- TVariants variants;
- TTracesLister lister(variants);
- srcs.ForEach(lister);
- return variants;
- }
- class TTimestampCutter {
- private:
- THashMap<TThread::TId, std::pair<ui64, TInstant>> CutTsForThread; // tid -> time of first item
- mutable ui64 CutTsMax = 0;
- mutable TInstant CutInstantMax;
- bool Enabled;
- ui64 NowTs;
- public:
- explicit TTimestampCutter(bool enabled)
- : Enabled(enabled)
- , NowTs(GetCycleCount())
- {}
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- auto it = CutTsForThread.find(tid);
- if (it != CutTsForThread.end()) {
- ui64& ts = it->second.first;
- TInstant& inst = it->second.second;
- ts = Min(ts, item.TimestampCycles);
- inst = Min(inst, item.Timestamp);
- } else {
- CutTsForThread[tid] = std::make_pair(item.TimestampCycles, item.Timestamp);
- }
- }
- // Timestamp from which we are ensured that cyclic log for every thread is not truncated
- // NOTE: should NOT be called from Push(tid, item) functions
- ui64 StartTimestamp() const
- {
- if (CutTsMax == 0) {
- FindStartTime();
- }
- return CutTsMax;
- }
- ui64 NowTimestamp() const
- {
- return NowTs;
- }
- TInstant StartInstant() const
- {
- if (CutInstantMax == TInstant::Zero()) {
- FindStartTime();
- }
- return CutInstantMax;
- }
- // Returns true iff item should be skipped to avoid surprizes
- bool Skip(const NLWTrace::TLogItem& item) const
- {
- return Enabled && item.TimestampCycles < StartTimestamp();
- }
- private:
- void FindStartTime() const
- {
- for (auto& kv : CutTsForThread) {
- CutTsMax = Max(CutTsMax, kv.second.first);
- CutInstantMax = Max(CutInstantMax, kv.second.second);
- }
- }
- };
- class TLogFilter {
- private:
- struct TFilter {
- TString ParamName;
- TString ParamValue;
- bool Parsed;
- TLogQuery Query;
- NLWTrace::TLiteral Value;
- explicit TFilter(const TString& text)
- {
- if (!text) { // Neither ParamName nor ParamValue is selected
- ParamName.clear();
- ParamValue.clear();
- Parsed = false;
- return;
- }
- size_t pos = text.find('=');
- if (pos == TString::npos) { // Only ParamName has been selected
- ParamName = text;
- ParamValue.clear();
- Parsed = false;
- return;
- }
- // Both ParamName and ParamValue have been selected
- ParamValue = text.substr(pos + 1);
- ParamName = text.substr(0, pos);
- Parsed = true;
- Query = TLogQuery(ParamName);
- Value = NLWTrace::TLiteral(ParamValue);
- }
- };
- TVector<TFilter> Filters;
- THashSet<const NLWTrace::TSignature*> Signatures; // Just to list param names
- TVariants ParamNames;
- THashMap<TString, THashSet<TString>> FilteredParamValues; // paramName -> { paramValue }
- public:
- explicit TLogFilter(const TVector<TString>& filters)
- {
- for (const TString& subvalue : filters) {
- TFilter filter(subvalue);
- FilteredParamValues[filter.ParamName]; // just create empty set to gather values later
- if (filter.Parsed) {
- Filters.push_back(filter);
- }
- }
- }
- virtual ~TLogFilter() {}
- template <class TLog>
- bool Filter(const TLog& log)
- {
- Gather(log);
- for (const TFilter& filter : Filters) {
- if (filter.Query.ExecuteQuery(log) != filter.Value) {
- return false;
- }
- }
- return true;
- }
- void FilterSelectors(TStringStream& ss, const TCgiParameters& e, const TString& fparam)
- {
- bool first = true;
- bool allParsed = true;
- for (const TString& subvalue : Subvalues(e, fparam)) {
- TFilter filter(subvalue);
- allParsed = allParsed && filter.Parsed;
- if (first) {
- SelectorTitle(ss, "where");
- }
- DropdownSelector<Erasable | CompositeValue, true>(
- ss, e, fparam, subvalue, first? "": ", ", ListParamNames(),
- filter.ParamName
- );
- if (filter.ParamName) {
- DropdownSelector<Link | CompositeValue, true>(
- ss, e, fparam, subvalue, "=", ListParamValues(filter.ParamName),
- filter.ParamValue? (filter.ParamName + "=" + filter.ParamValue): ""
- );
- }
- first = false;
- }
- if (!allParsed) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, first? "where": "+", MakeUrlAddSub(e, fparam, ""));
- }
- }
- const TVariants& ListParamNames()
- {
- if (ParamNames.empty()) {
- THashSet<TString> paramNames;
- for (const NLWTrace::TSignature* sgn: Signatures) {
- for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
- paramNames.insert(sgn->ParamNames[pi]);
- }
- }
- for (auto& pn : paramNames) {
- ParamNames.emplace_back(pn, pn);
- }
- }
- return ParamNames;
- }
- bool IsFiltered(const TString& paramName) const
- {
- return FilteredParamValues.contains(paramName);
- }
- private:
- // Gather param names and values for selectors
- void Gather(const NLWTrace::TLogItem& item)
- {
- Signatures.insert(&item.Probe->Event.Signature);
- if (!FilteredParamValues.empty() && item.SavedParamsCount > 0) {
- TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- auto iter = FilteredParamValues.find(item.Probe->Event.Signature.ParamNames[pi]);
- if (iter != FilteredParamValues.end()) {
- iter->second.insert(paramValues[pi]);
- }
- }
- }
- }
- void Gather(const NLWTrace::TTrackLog& tl)
- {
- for (const NLWTrace::TLogItem& item : tl.Items) {
- Gather(item);
- }
- }
- TVariants ListParamValues(const TString& paramName) const
- {
- TVariants result;
- auto iter = FilteredParamValues.find(paramName);
- if (iter != FilteredParamValues.end()) {
- for (const TString& paramValue : iter->second) {
- result.emplace_back(paramName + "=" + paramValue, paramValue);
- }
- }
- Sort(result.begin(), result.end());
- return result;
- }
- };
- static void EscapeJSONString(IOutputStream& os, const TString& s)
- {
- for (TString::const_iterator i = s.begin(), e = s.end(); i != e; ++i) {
- char c = *i;
- if (c < ' ') {
- os << Sprintf("\\u%04x", int(c));
- } else if (c == '"') {
- os << "\\\"";
- } else if (c == '\\') {
- os << "\\\\";
- } else {
- os << c;
- }
- }
- }
- static TString EscapeJSONString(const TString& s)
- {
- TStringStream ss;
- EscapeJSONString(ss, s);
- return ss.Str();
- }
- class TLogJsonPrinter {
- private:
- IOutputStream& Os;
- bool FirstThread;
- bool FirstItem;
- public:
- explicit TLogJsonPrinter(IOutputStream& os)
- : Os(os)
- , FirstThread(true)
- , FirstItem(true)
- {}
- void OutputHeader()
- {
- Os << "{\n\t\"source\": \"" << HostName() << "\""
- "\n\t, \"items\": ["
- ;
- }
- void OutputFooter(const NLWTrace::TSession* trace)
- {
- Os << "\n\t\t]"
- "\n\t, \"threads\": ["
- ;
- trace->ReadThreads(*this);
- Os << "]"
- "\n\t, \"events_count\": " << trace->GetEventsCount() <<
- "\n\t, \"threads_count\": " << trace->GetThreadsCount() <<
- "\n\t, \"timestamp\": " << Now().GetValue() <<
- "\n}"
- ;
- }
- void PushThread(TThread::TId tid)
- {
- Os << (FirstThread? "": ", ") << tid;
- FirstThread = false;
- }
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- Os << "\n\t\t" << (FirstItem? "": ", ");
- FirstItem = false;
- Os << "[" << tid <<
- ", " << item.Timestamp.GetValue() <<
- ", \"" << item.Probe->Event.GetProvider() << "\""
- ", \"" << item.Probe->Event.Name << "\""
- ", {"
- ;
- if (item.SavedParamsCount > 0) {
- TString ParamValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
- bool first = true;
- for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
- Os << (first? "": ", ") << "\"" << item.Probe->Event.Signature.ParamNames[i] << "\": \"";
- EscapeJSONString(Os, ParamValues[i]);
- Os << "\"";
- }
- }
- Os << "}]";
- }
- };
- class TLogTextPrinter : public TLogFilter {
- private:
- TMultiMap<NLWTrace::TTypedParam, std::pair<TThread::TId, NLWTrace::TLogItem> > Items;
- TMultiMap<NLWTrace::TTypedParam, NLWTrace::TTrackLog> Depot;
- THashMap<NLWTrace::TProbe*, size_t> ProbeId;
- TVector<NLWTrace::TProbe*> Probes;
- TTimestampCutter CutTs;
- TLogQuery Order;
- bool ReverseOrder = false;
- ui64 Head = 0;
- ui64 Tail = 0;
- bool ShowTs = false;
- public:
- TLogTextPrinter(const TVector<TString>& filters, ui64 head, ui64 tail, const TString& order, bool reverseOrder, bool cutTs, bool showTs)
- : TLogFilter(filters)
- , CutTs(cutTs)
- , Order(order)
- , ReverseOrder(reverseOrder)
- , Head(head)
- , Tail(tail)
- , ShowTs(showTs)
- {}
- TLogTextPrinter(const TCgiParameters& e)
- : TLogTextPrinter(
- Subvalues(e, "f"),
- e.Has("head")? FromString<ui64>(e.Get("head")): 0,
- e.Has("tail")? FromString<ui64>(e.Get("tail")): 0,
- e.Get("s"),
- e.Get("reverse") == "y",
- e.Get("cutts") == "y",
- e.Get("showts") == "y")
- {}
- enum EFormat {
- Text,
- Json
- };
- void Output(IOutputStream& os) const
- {
- OutputItems<Text>(os);
- OutputDepot<Text>(os);
- }
- void OutputJson(IOutputStream& os) const
- {
- os << "{\"depot\":[\n";
- OutputItems<Json>(os);
- OutputDepot<Json>(os);
- os << "],\"probes\":[";
- bool first = true;
- for (const NLWTrace::TProbe* probe : Probes) {
- os << (first? "": ",") << "{\"provider\":\"" << probe->Event.GetProvider()
- << "\",\"name\":\"" << probe->Event.Name << "\"}";
- first = false;
- }
- os << "]}";
- }
- NLWTrace::TTypedParam GetKey(const NLWTrace::TLogItem& item)
- {
- return Order? Order.ExecuteQuery(item): NLWTrace::TTypedParam(item.GetTimestampCycles());
- }
- NLWTrace::TTypedParam GetKey(const NLWTrace::TTrackLog& tl)
- {
- return Order? Order.ExecuteQuery(tl): NLWTrace::TTypedParam(tl.GetTimestampCycles());
- }
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
- if (Filter(item)) {
- AddId(item);
- Items.emplace(GetKey(item), std::make_pair(tid, item));
- }
- }
- void Push(TThread::TId tid, const NLWTrace::TTrackLog& tl)
- {
- Y_UNUSED(tid);
- if (Filter(tl)) {
- AddId(tl);
- Depot.emplace(GetKey(tl), tl);
- }
- }
- private:
- void AddId(const NLWTrace::TLogItem& item)
- {
- if (ProbeId.find(item.Probe) == ProbeId.end()) {
- size_t id = Probes.size();
- ProbeId[item.Probe] = id;
- Probes.emplace_back(item.Probe);
- }
- }
- void AddId(const NLWTrace::TTrackLog& tl)
- {
- for (const auto& item : tl.Items) {
- AddId(item);
- }
- }
- bool HeadTailFilter(ui64 idx, ui64 size) const
- {
- bool headOk = idx < Head;
- bool tailOk = size < Tail + idx + 1ull;
- if (Head && Tail) {
- return headOk || tailOk;
- } else if (Head) {
- return headOk;
- } else if (Tail) {
- return tailOk;
- } else {
- return true;
- }
- }
- template <EFormat Format>
- void OutputItems(IOutputStream& os) const
- {
- ui64 idx = 0;
- ui64 size = Items.size();
- ui64 startTs = ShowTs? CutTs.StartTimestamp(): 0;
- ui64 prevTs = 0;
- bool first = true;
- if (!ReverseOrder) {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
- prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
- }
- }
- } else {
- for (auto i = Items.rbegin(), e = Items.rend(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
- prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
- }
- }
- }
- }
- template <EFormat Format>
- void OutputDepot(IOutputStream& os) const
- {
- ui64 idx = 0;
- ui64 size = Depot.size();
- bool first = true;
- if (!ReverseOrder) {
- for (auto i = Depot.begin(), e = Depot.end(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputTrackLog<Format>(os, i->second, first);
- }
- }
- } else {
- for (auto i = Depot.rbegin(), e = Depot.rend(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputTrackLog<Format>(os, i->second, first);
- }
- }
- }
- }
- template <EFormat Format, bool AsTrack = false>
- void OutputItem(IOutputStream& os, TThread::TId tid, const NLWTrace::TLogItem& item, ui64 startTs, ui64 prevTs, bool& first) const
- {
- if (CutTs.Skip(item)) {
- return;
- }
- if constexpr (Format == Text) {
- if (startTs) {
- if (!prevTs) {
- prevTs = item.GetTimestampCycles();
- }
- os << Sprintf("%10.3lf %+10.3lf ms ",
- NHPTimer::GetSeconds(item.GetTimestampCycles() - startTs) * 1000.0,
- NHPTimer::GetSeconds(item.GetTimestampCycles() - prevTs) * 1000.0);
- }
- if (tid) {
- os << "<" << tid << "> ";
- }
- if (item.Timestamp != TInstant::Zero()) {
- os << "[" << item.Timestamp << "] ";
- } else {
- os << "[" << item.TimestampCycles << "] ";
- }
- os << GetProbeName(item.Probe) << "(";
- if (item.SavedParamsCount > 0) {
- TString ParamValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
- bool first = true;
- for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
- os << (first? "": ", ") << item.Probe->Event.Signature.ParamNames[i] << "='" << EscapeC(ParamValues[i]) << "'";
- }
- }
- os << ")\n";
- } else if constexpr (Format == Json) {
- if (auto probeId = ProbeId.find(item.Probe); probeId != ProbeId.end()) {
- os << (first? "": ",") << (AsTrack? "[":"") << "[\"" << tid << "\",\"";
- if (item.Timestamp != TInstant::Zero()) {
- os << item.Timestamp.MicroSeconds();
- } else {
- os << Sprintf("%.3lf", NHPTimer::GetSeconds(item.TimestampCycles) * 1e9);
- }
- os << "\"," << probeId->second << ",{";
- if (item.SavedParamsCount > 0) {
- TString ParamValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
- bool first = true;
- for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
- os << (first? "": ",") << "\"" << item.Probe->Event.Signature.ParamNames[i] << "\":\"";
- EscapeJSONString(os, ParamValues[i]);
- os << "\"";
- }
- }
- os << "}]" << (AsTrack? "]":"");
- }
- }
- first = false;
- }
- template <EFormat Format>
- void OutputTrackLog(IOutputStream& os, const NLWTrace::TTrackLog& tl, bool& first) const
- {
- if constexpr (Format == Json) {
- os << (first? "": ",") << "[";
- }
- first = false;
- ui64 prevTs = tl.GetTimestampCycles();
- bool firstItem = true;
- for (const NLWTrace::TTrackLog::TItem& item: tl.Items) {
- OutputItem<Format>(os, item.ThreadId, item, tl.GetTimestampCycles(), prevTs, firstItem);
- prevTs = item.GetTimestampCycles();
- }
- if constexpr (Format == Json) {
- os << "]";
- }
- os << "\n";
- }
- };
- class TLogAnalyzer: public TLogFilter {
- private:
- TMultiMap<ui64, std::pair<TThread::TId, NLWTrace::TLogItem>> Items;
- TVector<NLWTrace::TTrackLog> Depot;
- THashMap<TString, TTrackLogRefs> Groups;
- NAnalytics::TTable Table;
- bool TableCreated = false;
- TVector<TString> GroupBy;
- TTimestampCutter CutTs;
- public:
- TLogAnalyzer(const TVector<TString>& filters, const TVector<TString>& groupBy, bool cutTs)
- : TLogFilter(filters)
- , CutTs(cutTs)
- {
- for (const TString& groupParam : groupBy) {
- GroupBy.push_back(groupParam);
- }
- }
- const NAnalytics::TTable& GetTable()
- {
- if (!TableCreated) {
- TableCreated = true;
- if (GroupBy.empty()) {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
- ParseItems(i->second.first, i->second.second);
- }
- ParseDepot();
- } else {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
- Map(i->second.first, i->second.second);
- }
- Reduce();
- }
- }
- return Table;
- }
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
- if (Filter(item)) {
- Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
- }
- }
- void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
- {
- if (Filter(tl)) {
- Depot.emplace_back(tl);
- }
- }
- private:
- void FillRow(NAnalytics::TRow& row, const NLWTrace::TLogItem& item)
- {
- if (item.SavedParamsCount > 0) {
- TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t i = 0; i < item.SavedParamsCount; i++) {
- double value = FromString<double>(paramValues[i].data(), paramValues[i].size(), NAN);
- // If value cannot be cast to double or is inf/nan -- assume it's a string
- if (isfinite(value)) {
- row[item.Probe->Event.Signature.ParamNames[i]] = value;
- } else {
- row[item.Probe->Event.Signature.ParamNames[i]] = paramValues[i];
- }
- }
- }
- }
- TString GetParam(const NLWTrace::TLogItem& item, TString* paramValues, const TString& paramName)
- {
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
- return paramValues[pi];
- }
- }
- return TString();
- }
- TString GetGroup(const NLWTrace::TLogItem& item, TString* paramValues)
- {
- TStringStream ss;
- bool first = true;
- for (const TString& groupParam : GroupBy) {
- ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
- first = false;
- }
- return ss.Str();
- }
- void ParseItems(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- if (CutTs.Skip(item)) {
- return;
- }
- Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- row["_thread"] = tid;
- if (item.Timestamp != TInstant::Zero()) {
- row["_wallTime"] = item.Timestamp.SecondsFloat();
- row["_wallRTime"] = item.Timestamp.SecondsFloat() - CutTs.StartInstant().SecondsFloat();
- }
- row["_cycles"] = item.TimestampCycles;
- row["_thrTime"] = CyclesToDuration((ui64)item.TimestampCycles).SecondsFloat();
- row["_thrRTime"] = double(i64(item.TimestampCycles) - i64(CutTs.StartTimestamp())) / NHPTimer::GetCyclesPerSecond();
- row["_thrNTime"] = double(i64(item.TimestampCycles) - i64(CutTs.NowTimestamp())) / NHPTimer::GetCyclesPerSecond();
- row.Name = GetProbeName(item.Probe);
- FillRow(row, item);
- }
- void Map(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
- TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- TTrackLogRefs& tl = Groups[GetGroup(item, paramValues)];
- tl.Items.emplace_back(tid, item);
- }
- }
- void Reduce()
- {
- for (auto& v : Groups) {
- const TString& group = v.first;
- const TTrackLogRefs& tl = v.second;
- Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- row.Name = group;
- for (const NLWTrace::TLogItem& item : tl.Items) {
- FillRow(row, item);
- }
- }
- }
- void ParseDepot()
- {
- for (NLWTrace::TTrackLog& tl : Depot) {
- Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- for (const NLWTrace::TLogItem& item : tl.Items) {
- FillRow(row, item);
- }
- }
- }
- };
- struct TSampleOpts {
- bool ShowProvider = false;
- size_t SizeLimit = 50;
- };
- enum ENodeType {
- NT_ROOT,
- NT_PROBE,
- NT_PARAM
- };
- class TPatternTree;
- struct TPatternNode;
- struct TTrack : public TTrackLogRefs {
- TString TrackId;
- TPatternNode* LastNode = nullptr;
- };
- using TTrackTr = TLogTraits<TTrackLogRefs>;
- using TTrackIter = TTrackTr::const_iterator;
- // Visitor for tree traversing
- class IVisitor {
- public:
- virtual ~IVisitor() {}
- virtual void Visit(TPatternNode* node) = 0;
- };
- // Per-node classifier
- class TClassifier {
- public:
- explicit TClassifier(TPatternNode* node, ENodeType childType, bool keepHead = false)
- : Node(node)
- , KeepHead(keepHead)
- , ChildType(childType)
- {}
- virtual ~TClassifier() {}
- virtual TPatternNode* Classify(TTrackIter cur, const TTrack& track) = 0;
- virtual void Accept(IVisitor* visitor) = 0;
- virtual bool IsLeaf() = 0;
- ENodeType GetChildType() const { return ChildType; }
- public:
- TPatternNode* Node;
- const bool KeepHead;
- ENodeType ChildType;
- };
- // Track classification tree node
- struct TPatternNode {
- TString Name;
- TPatternNode* Parent = nullptr;
- THolder<TClassifier> Classifier;
- struct TDesc {
- ENodeType Type = NT_ROOT;
- // NT_PROBE
- const NLWTrace::TProbe* Probe = nullptr;
- // NT_PARAM
- size_t Rollbacks = 0;
- TString ParamName;
- TString ParamValue;
- } Desc;
- ui64 TrackCount = 0;
- struct TTrackEntry {
- TTrack* Track;
- ui64 ResTotal;
- ui64 ResLast;
- TTrackEntry(TTrack* track, ui64 resTotal, ui64 resLast)
- : Track(track)
- , ResTotal(resTotal)
- , ResLast(resLast)
- {}
- };
- TVector<TTrackEntry> Tracks;
- ui64 ResTotalSum = 0;
- ui64 ResTotalMax = 0;
- TVector<ui64> ResTotalAll;
- ui64 ResLastSum = 0;
- ui64 ResLastMax = 0;
- TVector<ui64> ResLastAll;
- TVector<ui64> TimelineSum;
- NAnalytics::TTable Slices;
- TString GetPath() const
- {
- if (Parent) {
- return Parent->GetPath() + Name;
- }
- return "/";
- }
- NAnalytics::TTable GetTable() const
- {
- using namespace NAnalytics;
- NAnalytics::TTable ret;
- for (ui64 x : ResTotalAll) {
- ret.emplace_back();
- TRow& row = ret.back();
- row["resTotal"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
- }
- for (ui64 x : ResLastAll) {
- ret.emplace_back();
- TRow& row = ret.back();
- row["resLast"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
- }
- return ret;
- }
- template <typename TReader>
- void OutputSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader) const
- {
- bool filterTotal = false;
- if (bn == "resTotal") {
- filterTotal = true;
- } else {
- WWW_CHECK(bn == "resLast", "wrong sample filter param: %s", bn.data());
- }
- size_t spaceLeft = opts.SizeLimit;
- for (const TTrackEntry& entry : Tracks) {
- const TTrack* track = entry.Track;
- // Filter out tracks that are not in sample
- if (filterTotal) {
- double resTotalMs = double(entry.ResTotal) * 1000.0 / NHPTimer::GetClockRate();
- if (resTotalMs < b1 || resTotalMs > b2) {
- continue;
- }
- } else {
- double resLastMs = double(entry.ResLast) * 1000.0 / NHPTimer::GetClockRate();
- if (resLastMs < b1 || resLastMs > b2) {
- continue;
- }
- }
- NLWTrace::TTrackLog tl;
- for (TTrackIter i = TTrackTr::begin(*track), e = TTrackTr::end(*track); i != e; ++i) {
- const NLWTrace::TLogItem& item = *i;
- const auto threadId = i->ThreadId;
- tl.Items.push_back(NLWTrace::TTrackLog::TItem(threadId, item));
- }
- reader.Push(0, tl);
- if (spaceLeft) {
- spaceLeft--;
- if (!spaceLeft) {
- break;
- }
- }
- }
- }
- };
- // Track classification tree
- class TPatternTree {
- public:
- // Per-node classifier by probe name
- class TClassifyByProbe : public TClassifier {
- private:
- using TChildren = THashMap<NLWTrace::TProbe*, TPatternNode>;
- TChildren Children;
- TVector<TChildren::value_type*> SortedChildren;
- public:
- explicit TClassifyByProbe(TPatternNode* node)
- : TClassifier(node, NT_PROBE)
- {}
- TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
- {
- Y_UNUSED(track);
- const NLWTrace::TLogItem& item = *cur;
- TPatternNode* node = &Children[item.Probe];
- node->Name = "/" + GetProbeName(item.Probe);
- node->Desc.Type = NT_PROBE;
- node->Desc.Probe = item.Probe;
- return node;
- }
- void Accept(IVisitor* visitor) override
- {
- if (SortedChildren.size() != Children.size()) {
- SortedChildren.clear();
- SortedChildren.reserve(Children.size());
- for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
- SortedChildren.push_back(&*i);
- }
- Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
- NLWTrace::TProbe* lp = lhs->first;
- NLWTrace::TProbe* rp = rhs->first;
- if (int cmp = strcmp(lp->Event.GetProvider(), rp->Event.GetProvider())) {
- return cmp < 0;
- }
- return strcmp(lp->Event.Name, rp->Event.Name) < 0;
- });
- }
- for (auto* kv : SortedChildren) {
- visitor->Visit(&kv->second);
- }
- }
- bool IsLeaf() override { return Children.empty(); }
- };
- // Per-node classifier by probe param value
- class TClassifyByParam : public TClassifier {
- private:
- size_t Rollbacks; // How many items should we look back in track to locate probe
- TString ParamName;
- using TChildren = THashMap<TString, TPatternNode>;
- TChildren Children;
- TVector<TChildren::value_type*> SortedChildren;
- public:
- TClassifyByParam(TPatternNode* node, size_t rollbacks, const TString& paramName)
- : TClassifier(node, NT_PARAM, true)
- , Rollbacks(rollbacks)
- , ParamName(paramName)
- {}
- TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
- {
- WWW_CHECK((i64)Rollbacks >= 0 && std::distance(TTrackTr::begin(track), cur) >= (i64)Rollbacks, "wrong rollbacks in node '%s'",
- Node->GetPath().data());
- const NLWTrace::TLogItem& item = *(cur - Rollbacks);
- WWW_CHECK(item.SavedParamsCount > 0, "classify by params on probe w/o param loggging in node '%s'",
- Node->GetPath().data());
- TString paramValues[LWTRACE_MAX_PARAMS];
- TString* paramValue = nullptr;
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (item.Probe->Event.Signature.ParamNames[pi] == ParamName) {
- paramValue = ¶mValues[pi];
- }
- }
- WWW_CHECK(paramValue, "param '%s' not found in probe '%s' at path '%s'",
- ParamName.data(), GetProbeName(item.Probe).data(), Node->GetPath().data());
- TPatternNode* node = &Children[*paramValue];
- // Path example: "//Provider1.Probe1/Provider2.Probe2@1.xxx=123@2.type=harakiri"
- node->Name = "@" + ToString(Rollbacks) + "." + ParamName + "=" + *paramValue;
- node->Desc.Type = NT_PARAM;
- node->Desc.Rollbacks = Rollbacks;
- node->Desc.ParamName = ParamName;
- node->Desc.ParamValue = *paramValue;
- return node;
- }
- void Accept(IVisitor* visitor) override
- {
- if (SortedChildren.size() != Children.size()) {
- SortedChildren.clear();
- SortedChildren.reserve(Children.size());
- for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
- SortedChildren.push_back(&*i);
- }
- Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
- return lhs->first < rhs->first;
- });
- }
- for (auto* kv : SortedChildren) {
- visitor->Visit(&kv->second);
- }
- }
- bool IsLeaf() override { return Children.empty(); }
- };
- private:
- TPatternNode Root;
- THashMap<TString, std::pair<size_t, TString>> ParamClassifiers; // path -> (rollbacks, param)
- TString SelectedPattern;
- TPatternNode* SelectedNode = nullptr;
- TVector<ui64> Timeline; // Just to avoid reallocations
- public:
- TPatternTree(const TCgiParameters& e)
- {
- for (const TString& cl : Subvalues(e, "classify")) {
- size_t at = cl.find_last_of('@');
- if (at != TString::npos) {
- size_t dot = cl.find('.', at + 1);
- if (dot != TString::npos) {
- size_t rollbacks = FromString<size_t>(cl.substr(at + 1, dot - at - 1));
- ParamClassifiers[cl.substr(0, at)] = std::make_pair(rollbacks, cl.substr(dot + 1));
- }
- }
- }
- SelectedPattern = e.Get("pattern");
- InitNode(&Root, nullptr);
- }
- TPatternNode* GetSelectedNode()
- {
- return SelectedNode;
- }
- NAnalytics::TTable GetSelectedTable()
- {
- if (SelectedNode) {
- return SelectedNode->GetTable();
- } else {
- return NAnalytics::TTable();
- }
- }
- template <typename TReader>
- void OutputSelectedSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader)
- {
- if (SelectedNode) {
- SelectedNode->OutputSample(bn, b1, b2, opts, reader);
- }
- }
- // Register track in given node
- void AddTrackToNode(TPatternNode* node, TTrack& track, ui64 resTotal, TVector<ui64>& timeline)
- {
- if (!SelectedNode) {
- if (node->GetPath() == SelectedPattern) {
- SelectedNode = node;
- }
- }
- // Counting
- node->TrackCount++;
- // Resource total
- node->ResTotalSum += resTotal;
- node->ResTotalMax = Max(node->ResTotalMax, resTotal);
- node->ResTotalAll.push_back(resTotal);
- // Resource last
- ui64 resLast = 0;
- resLast = resTotal - (timeline.size() < 2? 0: timeline[timeline.size() - 2]);
- node->ResLastSum += resLast;
- node->ResLastMax = Max(node->ResLastMax, resLast);
- node->ResLastAll.push_back(resLast);
- // Timeline
- if (node->TimelineSum.size() < timeline.size()) {
- node->TimelineSum.resize(timeline.size());
- }
- for (size_t i = 0; i < timeline.size(); i++) {
- node->TimelineSum[i] += timeline[i];
- }
- if (node == SelectedNode && !timeline.empty()) {
- node->Slices.emplace_back();
- NAnalytics::TRow& row = node->Slices.back();
- ui64 prev = 0;
- for (size_t i = 0; i < timeline.size(); i++) {
- // Note that col names should go in lexicographical order
- // in the same way as slices go in pattern timeline
- double sliceMs = double(timeline[i] - prev) * 1000.0 / NHPTimer::GetClockRate();
- row[Sprintf("%09lu", i)] = sliceMs;
- prev = timeline[i];
- }
- }
- // Interlink node and track
- node->Tracks.emplace_back(&track, resTotal, resLast);
- track.LastNode = node;
- }
- bool CheckPattern(const char*& pi, const char* pe, TStringBuf str)
- {
- auto si = str.begin(), se = str.end();
- for (;pi != pe && si != se; ++pi, ++si) {
- if (*pi != *si) {
- return false;
- }
- }
- return si == se;
- }
- #define WWW_CHECK_PATTERN(str) if (!CheckPattern(pi, pe, (str))) { return false; }
- bool MatchTrack(const TTrack& track, const TString& patternStr)
- {
- const char* pi = patternStr.data();
- const char* pe = pi + patternStr.size();
- WWW_CHECK_PATTERN("/");
- for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e; ++i) {
- if (pi == pe) {
- return true;
- }
- const NLWTrace::TLogItem& item = *i;
- WWW_CHECK_PATTERN("/");
- WWW_CHECK_PATTERN(item.Probe->Event.GetProvider());
- WWW_CHECK_PATTERN(".");
- WWW_CHECK_PATTERN(item.Probe->Event.Name);
- while (true) {
- if (pi == pe) {
- return true;
- }
- char c = *pi;
- if (c == '/') {
- break;
- } else if (c == '@') {
- pi++;
- // Parse rollbacks
- TStringBuf p(pi, pe);
- size_t dot = p.find('.');
- if (dot == TStringBuf::npos) {
- return false;
- }
- size_t rollbacks = 0;
- try {
- rollbacks = FromString<size_t>(p.substr(0, dot));
- } catch (...) {
- return false;
- }
- // Parse param name
- size_t equals = p.find('=', dot + 1);
- if (equals == TStringBuf::npos) {
- return false;
- }
- TStringBuf paramName = p.substr(dot + 1, equals - dot - 1);
- pi += equals + 1; // Advance to value
- // Check param value
- if ((i64)rollbacks < 0 || std::distance(TTrackTr::begin(track), i) < (i64)rollbacks) {
- return false;
- }
- const NLWTrace::TLogItem& mitem = *(i - rollbacks);
- if (mitem.SavedParamsCount == 0) {
- return false;
- }
- TString paramValues[LWTRACE_MAX_PARAMS];
- TString* paramValue = nullptr;
- mitem.Probe->Event.Signature.SerializeParams(mitem.Params, paramValues);
- for (size_t pi = 0; pi < mitem.SavedParamsCount; pi++) {
- if (mitem.Probe->Event.Signature.ParamNames[pi] == paramName) {
- paramValue = ¶mValues[pi];
- }
- }
- if (!paramValue) {
- return false;
- }
- WWW_CHECK_PATTERN(*paramValue);
- } else {
- return false;
- }
- }
- }
- return true;
- }
- #undef WWW_CHECK_PATTERN
- // Push new track through pattern tree
- void AddTrack(TTrack& track)
- {
- // Truncate long tracks
- if (track.Items.size() > 50) {
- track.Items.resize(50);
- }
- if (SelectedPattern) {
- if (!MatchTrack(track, SelectedPattern)) {
- return;
- }
- }
- Timeline.clear();
- TPatternNode* node = &Root;
- AddTrackToNode(node, track, 0, Timeline);
- ui64 trackStart = TTrackTr::front(track).TimestampCycles;
- for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e;) {
- // Get or create child by classification
- TPatternNode* parent = node;
- node = node->Classifier->Classify(i, track);
- if (!node->Classifier) {
- InitNode(node, parent);
- }
- const NLWTrace::TLogItem& item = *i;
- ui64 resTotal = item.TimestampCycles - trackStart;
- if (i != TTrackTr::begin(track)) {
- Timeline.push_back(resTotal);
- }
- AddTrackToNode(node, track, resTotal, Timeline);
- // Move through track
- if (!node->Classifier->KeepHead) {
- ++i;
- }
- }
- }
- // Traverse pattern tree (the only way to extract data from it)
- template <class TOnNode, class TOnDescend, class TOnAscend>
- void Traverse(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
- {
- struct TVisitor : public IVisitor {
- TOnNode OnNode;
- TOnDescend OnDescend;
- TOnAscend OnAscend;
- TVisitor(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
- : OnNode(onNode)
- , OnDescend(onDescend)
- , OnAscend(onAscend)
- {}
- virtual void Visit(TPatternNode* node) override
- {
- OnNode(node);
- if (!node->Classifier->IsLeaf()) {
- OnDescend();
- node->Classifier->Accept(this);
- OnAscend();
- }
- }
- };
- TVisitor visitor(std::move(onNode), std::move(onDescend), std::move(onAscend));
- visitor.Visit(&Root);
- }
- TPatternNode* GetRoot()
- {
- return &Root;
- }
- private:
- void InitNode(TPatternNode* node, TPatternNode* parent)
- {
- node->Parent = parent;
- auto iter = ParamClassifiers.find(node->GetPath());
- if (iter != ParamClassifiers.end()) {
- node->Classifier.Reset(new TClassifyByParam(node, iter->second.first, iter->second.second));
- } else {
- node->Classifier.Reset(new TClassifyByProbe(node));
- }
- }
- };
- class TLogTrackExtractor: public TLogFilter {
- private:
- // Data storage
- TMultiMap<ui64, std::pair<TThread::TId, NLWTrace::TLogItem>> Items;
- TVector<NLWTrace::TTrackLog> Depot;
- // Data refs organized in tracks
- THashMap<TString, TTrack> Tracks;
- TVector<TTrack> TracksFromDepot;
- // Analysis
- TVector<TString> GroupBy;
- THashSet<TString> TrackIds; // The same content as in GroupBy
- TTimestampCutter CutTs;
- TPatternTree Tree;
- public:
- TLogTrackExtractor(const TCgiParameters& e, const TVector<TString>& filters, const TVector<TString>& groupBy)
- : TLogFilter(filters)
- , CutTs(true) // Always cut input data for tracks
- , Tree(e)
- {
- for (const TString& groupParam : groupBy) {
- GroupBy.push_back(groupParam);
- TrackIds.insert(groupParam);
- }
- }
- // For reading lwtrace log (input point for all data)
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
- if (Filter(item)) {
- Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
- }
- }
- // For reading lwtrace depot (input point for all data)
- void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
- {
- if (Filter(tl)) {
- Depot.emplace_back(tl);
- }
- }
- // Analyze logs that have been read
- void Run()
- {
- RunImplLog();
- RunImplDepot();
- }
- void RunImplLog()
- {
- // Create tracks by filling them with lwtrace items in order of occurance time
- for (auto& kv : Items) {
- AddItemToTrack(kv.second.first, kv.second.second);
- }
- // Push tracks throught pattern tree
- for (auto& kv : Tracks) {
- TTrack& track = kv.second;
- track.TrackId = kv.first;
- Tree.AddTrack(track);
- }
- }
- void RunImplDepot()
- {
- // Create tracks from depot
- // OPTIMIZE[serxa]: this convertion is not necessary, done just to keep things simple
- for (NLWTrace::TTrackLog& tl : Depot) {
- TTrack& track = TracksFromDepot.emplace_back();
- track.TrackId = ToString(tl.Id);
- for (const NLWTrace::TTrackLog::TItem& i : tl.Items) {
- track.Items.emplace_back(i.ThreadId, i);
- }
- }
- for (TTrack& t : TracksFromDepot) {
- Tree.AddTrack(t);
- }
- }
- // Selected node distribution
- NAnalytics::TTable Distribution(const TString& bn, const TString& b1Str, const TString& b2Str, const TString& widthStr)
- {
- using namespace NAnalytics;
- const NAnalytics::TTable& inputTable = Tree.GetSelectedTable();
- double b1 = b1Str? FromString<double>(b1Str): MinValue(bn, inputTable);
- double b2 = b2Str? FromString<double>(b2Str): MaxValue(bn, inputTable);
- if (isfinite(b1) && isfinite(b2)) {
- WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
- double width = widthStr? FromString<double>(widthStr): 99;
- double dx = (b2 - b1) / width;
- if (!(dx > 0)) {
- dx = 1.0;
- }
- return HistogramAll(inputTable, bn, b1, b2, dx);
- } else {
- // Empty table -- it's ok -- leave data table empty
- return NAnalytics::TTable();
- }
- }
- // Selected sample
- template <typename TReader>
- void OutputSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader)
- {
- Tree.OutputSelectedSample(bn, b1, b2, opts, reader);
- }
- // Tabular representation of tracks data
- void OutputTable(IOutputStream& os, const TCgiParameters& e)
- {
- ui64 tracksTotal = Tree.GetRoot()->TrackCount;
- double maxAvgResTotal = 0;
- double maxMaxResTotal = 0;
- Tree.Traverse([&] (TPatternNode* node) {
- if (node->TrackCount > 0) {
- maxAvgResTotal = Max(maxAvgResTotal, double(node->ResTotalSum) / node->TrackCount);
- maxMaxResTotal = Max(maxMaxResTotal, double(node->ResTotalMax));
- Sort(node->ResTotalAll);
- Sort(node->ResLastAll);
- }
- }, [&] () { // On descend
- }, [&] () { // On ascend
- });
- double maxTime = Min(maxMaxResTotal, 1.25 * maxAvgResTotal);
- double percentile = e.Get("ile")? FromString<double>(e.Get("ile")): 90;
- WWW_CHECK(percentile >= 0.0 && percentile <= 100.0, "wrong percentile: %lf", percentile);
- ui64 row = 0;
- TVector<ui64> chain;
- HTML(os) {
- TABLE_CLASS("tracks-tree") {
- TABLEHEAD() {
- os << "<tr>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">#</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Pattern</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">";
- DIV_CLASS("rotate") { os << "Track Count"; }
- os << "</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Share</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Total, ms</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Last, ms</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" class=\"timelinehead\" align=\"center\">Global Timeline</td>";
- os << "</tr><tr>";
- TABLEH() DIV_CLASS("rotate") { os << "Absolute"; }
- TABLEH() DIV_CLASS("rotate") { os << "Relative"; }
- TABLEH() DIV_CLASS("rotate") { os << "Average"; }
- TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
- TABLEH() DIV_CLASS("rotate") { os << "Average"; }
- TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
- os << "</tr>";
- }
- TABLEBODY() {
- if (tracksTotal == 0) {
- return;
- }
- Tree.Traverse([&] (TPatternNode* node) {
- TString parentClass;
- if (!chain.empty()) {
- parentClass = " treegrid-parent-" + ToString(chain.back());
- }
- TString selectedClass;
- if (e.Get("pattern") == node->GetPath()) {
- selectedClass = " danger";
- }
- TABLER_CLASS("treegrid-" + ToString(++row) + parentClass + selectedClass) {
- // Counting
- ui64 tracksParent = node->Parent? node->Parent->TrackCount: tracksTotal;
- double absShare = double(node->TrackCount) * 100 / tracksTotal;
- double relShare = double(node->TrackCount) * 100 / tracksParent;
- // Resource total
- double avgResTotal = double(node->ResTotalSum) / node->TrackCount;
- size_t ileResTotalIdx = node->ResTotalAll.size() * percentile / 100;
- if (ileResTotalIdx > 0) {
- ileResTotalIdx--;
- }
- double ileResTotal = double(ileResTotalIdx >= node->ResTotalAll.size()? 0: node->ResTotalAll[ileResTotalIdx]);
- double avgResTotalMs = avgResTotal * 1000.0 / NHPTimer::GetClockRate();
- double ileResTotalMs = ileResTotal * 1000.0 / NHPTimer::GetClockRate();
- // Resource last
- double avgResLast = double(node->ResLastSum) / node->TrackCount;
- size_t ileResLastIdx = node->ResLastAll.size() * percentile / 100;
- if (ileResLastIdx > 0) {
- ileResLastIdx--;
- }
- double ileResLast = double(ileResLastIdx >= node->ResLastAll.size()? 0: node->ResLastAll[ileResLastIdx]);
- double avgResLastMs = avgResLast * 1000.0 / NHPTimer::GetClockRate();
- double ileResLastMs = ileResLast * 1000.0 / NHPTimer::GetClockRate();
- // Output
- TABLED() { os << row; }
- TABLED_CLASS("treegrid-element") { OutputPattern(os, e, node); }
- TABLED() { os << node->TrackCount; }
- TABLED() { OutputShare(os, absShare); }
- TABLED() { OutputShare(os, relShare); }
- TABLED() { os << FormatFloat(avgResTotalMs); }
- TABLED() { os << FormatFloat(ileResTotalMs); }
- TABLED() { os << FormatFloat(avgResLastMs); }
- TABLED() { os << FormatFloat(ileResLastMs); }
- TABLED() { OutputTimeline(os, MakeTimeline(node), maxTime); }
- }
- }, [&] () { // On descend
- chain.push_back(row);
- }, [&] () { // On ascend
- chain.pop_back();
- });
- }
- }
- }
- }
- // Chromium-compatible trace representation of tracks data
- void OutputChromeTrace(IOutputStream& os, const TCgiParameters& e)
- {
- Y_UNUSED(e);
- TChromeTrace tr;
- for (TPatternNode::TTrackEntry& entry: Tree.GetRoot()->Tracks) {
- TTrack* track = entry.Track;
- auto first = TTrackTr::begin(*track);
- auto last = TTrackTr::rbegin(*track);
- TString name = track->LastNode->GetPath();
- const NLWTrace::TLogItem& firstItem = *first;
- TThread::TId firstTid = first->ThreadId;
- tr.Add(firstTid, firstItem.TimestampCycles, "b", "track", nullptr, name, track->TrackId);
- for (auto cur = TTrackTr::begin(*track), end = TTrackTr::end(*track); cur != end; ++cur) {
- const NLWTrace::TLogItem& item = *cur;
- tr.Add(cur->ThreadId, item.TimestampCycles, "i", "event", &item, GetProbeName(item.Probe));
- TString sliceName = GetProbeName(item.Probe);
- auto next = cur + 1;
- if (next != end) {
- const NLWTrace::TLogItem& nextItem = *next;
- tr.Add(cur->ThreadId, item.TimestampCycles, "b", "track", &item, sliceName, track->TrackId);
- tr.Add(next->ThreadId, nextItem.TimestampCycles, "e", "track", &nextItem, sliceName, track->TrackId);
- } else {
- tr.Add(cur->ThreadId, item.TimestampCycles, "n", "track", &item, sliceName, track->TrackId);
- }
- }
- const NLWTrace::TLogItem& lastItem = *last;
- tr.Add(last->ThreadId, lastItem.TimestampCycles, "e", "track", nullptr, name, track->TrackId);
- }
- tr.Output(os);
- }
- void OutputSliceCovarianceMatrix(IOutputStream& os, const TCgiParameters& e)
- {
- Y_UNUSED(e);
- TPatternNode* node = Tree.GetSelectedNode();
- if (!node) {
- return;
- }
- NAnalytics::TMatrix covMatrix = NAnalytics::CovarianceMatrix(node->Slices);
- double var = covMatrix.CellSum();
- double covMax = 0.0;
- for (double x : covMatrix) {
- if (covMax < x) {
- covMax = x;
- }
- }
- double dangerCov = covMax * 0.9 * 0.9;
- double warnCov = covMax * 0.5 * 0.5;
- HTML(os) {
- TABLE() {
- TTimeline timeline = MakeTimeline(node);
- TABLED() {};
- TABLEHEAD() TABLER() {
- for (auto& e : timeline) TABLED() {
- TPatternNode* subnode = e.first;
- os << subnode->Name;
- }
- }
- auto tl = timeline.begin();
- TABLEBODY() for (size_t row = 0; row < covMatrix.Rows; row++) TABLER() {
- TABLEH() {
- if (tl != timeline.end()) {
- TPatternNode* subnode = tl->first;
- os << subnode->Name;
- ++tl;
- }
- }
- for (size_t col = 0; col < covMatrix.Cols; col++) {
- double cov = covMatrix.Cell(row, col);
- TString tdClass = (cov >= dangerCov? "danger": (cov >= warnCov? "warning": ""));
- TABLED_CLASS(tdClass) {
- double sigmaX = (covMatrix.Cell(row, row) > 0? sqrt(covMatrix.Cell(row, row)): 0);
- double sigmaY = (covMatrix.Cell(col, col) > 0? sqrt(covMatrix.Cell(col, col)): 0);
- os << Sprintf("cov=%.3lf ms<sup>2</sup> (%.3lf ms) corr=%.1lf%% var_share=%.1lf%%",
- cov, sqrt(abs(cov)), cov * 100.0 / sigmaX / sigmaY, cov * 100.0 / var);
- }
- }
- }
- }
- }
- }
- private:
- TPatternNode* RollbackFind(TPatternNode* node)
- {
- for (;node != nullptr; node = node->Parent) {
- if (node->Desc.Type == NT_PROBE) {
- return node;
- }
- }
- return nullptr;
- }
- void OutputPattern(IOutputStream& os, const TCgiParameters& e, TPatternNode* node)
- {
- // Fill pattern name
- TString patternName;
- TString patternTitle;
- switch (node->Desc.Type) {
- case NT_ROOT:
- patternName = "All Tracks";
- break;
- case NT_PROBE:
- patternTitle = GetProbeName(node->Desc.Probe);
- patternName = node->Desc.Probe->Event.Name;
- break;
- case NT_PARAM:
- patternName.append(node->Desc.ParamName + " = " + node->Desc.ParamValue);
- break;
- }
- os << "<a href=\"" << MakeUrl(e, {
- {"pattern", node->GetPath()},
- {"ptrn_anlz", e.Get("ptrn_anlz") ? e.Get("ptrn_anlz") : "resTotal"},
- {"linesfill", "y"},
- {"linessteps", "y"},
- {"pointsshow", "n"},
- {"sel_x1", e.Get("sel_x1") ? e.Get("sel_x1") : "0"},
- {"sel_x2", e.Get("sel_x2") ? e.Get("sel_x2") : "inf"}}) << "\""
- " title=\"" + patternTitle + "\">" << patternName << "</a>";
- // Add/remove node menu
- if (node->Desc.Type != NT_ROOT) {
- os << "<div class=\"dropdown pull-right\" style=\"display:inline-block\">";
- if (node->Desc.Type == NT_PARAM) {
- os<< "<button class=\"btn btn-xs btn-default\" type=\"button\""
- << "\" onClick=\"window.location.href='"
- << MakeUrlEraseSub(e, "classify", node->Parent->GetPath() + "@"
- + ToString(node->Desc.Rollbacks) + "." + node->Desc.ParamName)
- << "';\">"
- "<span class=\"glyphicon glyphicon-minus\"></span>"
- "</button>";
- }
- if (node->Classifier->GetChildType() != NT_PARAM) {
- os << "<button class=\"btn btn-xs btn-default dropdown-toggle\" type=\"button\""
- " data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"true\">"
- "<span class=\"glyphicon glyphicon-plus\"></span>"
- "</button>"
- "<ul class=\"dropdown-menu\">"
- "<li class=\"dropdown-header\">Classify by param:</li>";
- int rollbacks = 0;
- TPatternNode* probeNode = node;
- while (probeNode = RollbackFind(probeNode)) {
- const NLWTrace::TProbe* probe = probeNode->Desc.Probe;
- os << "<li class=\"dropdown-header\">" << GetProbeName(probe) << "</li>";
- const NLWTrace::TSignature* sgn = &probe->Event.Signature;
- for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
- TString param = sgn->ParamNames[pi];
- if (TrackIds.contains(param) || IsFiltered(param)) {
- continue;
- }
- os << "<li><a href=\""
- << MakeUrlAddSub(e, "classify", node->GetPath() + "@" + ToString(rollbacks) + "." + param)
- << "\">" << param << "</a></li>";
- }
- rollbacks++;
- probeNode = probeNode->Parent;
- }
- os << "</ul>";
- }
- os << "</div>";
- }
- }
- void OutputShare(IOutputStream& os, double share)
- {
- double lshare = share;
- double rshare = 100 - lshare;
- os << "<div class=\"progress\" style=\"margin-bottom:0px;position:relative\">"
- "<div class=\"progress-bar progress-bar-success\" role=\"progressbar\""
- " aria-valuenow=\"" << lshare << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width: " << lshare << "%;\">"
- "</div>"
- "<div class=\"progress-bar progress-bar-danger\" role=\"progressbar\""
- " aria-valuenow=\"" << rshare << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width: " << rshare << "%;\">"
- "</div>"
- "<span style=\"position:absolute;left:0;width:100%;text-align:center;z-index:2;color:white\">"
- << (share == 100? "100%": Sprintf("%2.1lf%%", share)) <<
- "</span>"
- "</div>";
- }
- using TTimeline = TVector<std::pair<TPatternNode*, double>>;
- TTimeline MakeTimeline(TPatternNode* node)
- {
- TTimeline ret;
- if (node->TrackCount == 0) {
- return ret;
- }
- ret.reserve(node->TimelineSum.size());
- for (double time : node->TimelineSum) {
- ret.emplace_back(nullptr, double(time) / node->TrackCount);
- }
- TPatternNode* n = node;
- for (auto i = ret.rbegin(), e = ret.rend(); i != e; ++i) {
- WWW_CHECK(n, "internal bug: wrong timeline length at pattern node '%s'", node->GetPath().data());
- i->first = n;
- n = n->Parent;
- }
- return ret;
- }
- void OutputTimeline(IOutputStream& os, const TTimeline& timeline, double maxTime)
- {
- static const char *barClass[] = {
- "progress-bar-info",
- "progress-bar-warning"
- };
- if (timeline.empty()) {
- return;
- }
- os << "<div class=\"progress\" style=\"margin-bottom:0px;color:black\">";
- double prevPos = 0.0;
- double prevTime = 0.0;
- size_t i = 0;
- for (auto& e : timeline) {
- TPatternNode* node = e.first;
- double time = e.second;
- double pos = time * 100 / maxTime;
- if (pos > 100) {
- pos = 100;
- }
- double width = pos - prevPos;
- os << "<div class=\"progress-bar " << barClass[i % 2] << "\" role=\"progressbar\""
- " aria-valuenow=\"" << width << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width:" << width << "%;color:black\""
- " title=\"" << FormatTimelineTooltip(time, prevTime, node) << "\">";
- if (width > 20) { // To ensure text will fit the bar
- os << FormatCycles(time - prevTime);
- }
- os << "</div>";
- prevPos = pos;
- prevTime = time;
- i++;
- }
- os << "</div>";
- }
- TString FormatTimelineTooltip(double time, double prevTime, TPatternNode* node)
- {
- return FormatCycles(time - prevTime) + ": "
- + FormatCycles(prevTime) + " -> " + FormatCycles(time)
- + "(" + node->Name + ")";
- }
- TString FormatFloat(double value)
- {
- if (value == 0.0) {
- return "0";
- }
- if (value > 1.0) {
- if (value > 100.0) {
- return Sprintf("%.0lf", value);
- }
- if (value > 10.0) {
- return Sprintf("%.1lf", value);
- }
- return Sprintf("%.2lf", value);
- } else if (value > 1e-3) {
- if (value > 1e-1) {
- return Sprintf("%.3lf", value);
- }
- if (value > 1e-2) {
- return Sprintf("%.4lf", value);
- }
- return Sprintf("%.5lf", value);
- } else if (value > 1e-6) {
- if (value > 1e-4) {
- return Sprintf("%.6lf", value);
- }
- if (value > 1e-5) {
- return Sprintf("%.7lf", value);
- }
- return Sprintf("%.8lfus", value);
- } else {
- if (value > 1e-7) {
- return Sprintf("%.9lfns", value);
- }
- if (value > 1e-8) {
- return Sprintf("%.10lfns", value);
- }
- return Sprintf("%.2le", value);
- }
- }
- TString FormatCycles(double timeCycles)
- {
- double timeSec = timeCycles / NHPTimer::GetClockRate();
- if (timeSec > 1.0) {
- if (timeSec > 100.0) {
- return Sprintf("%.0lfs", timeSec);
- }
- if (timeSec > 10.0) {
- return Sprintf("%.1lfs", timeSec);
- }
- return Sprintf("%.2lfs", timeSec);
- } else if (timeSec > 1e-3) {
- if (timeSec > 1e-1) {
- return Sprintf("%.0lfms", timeSec * 1e3);
- }
- if (timeSec > 1e-2) {
- return Sprintf("%.1lfms", timeSec * 1e3);
- }
- return Sprintf("%.2lfms", timeSec * 1e3);
- } else if (timeSec > 1e-6) {
- if (timeSec > 1e-4) {
- return Sprintf("%.0lfus", timeSec * 1e6);
- }
- if (timeSec > 1e-5) {
- return Sprintf("%.1lfus", timeSec * 1e6);
- }
- return Sprintf("%.2lfus", timeSec * 1e6);
- } else {
- if (timeSec > 1e-7) {
- return Sprintf("%.0lfns", timeSec * 1e9);
- }
- if (timeSec > 1e-8) {
- return Sprintf("%.1lfns", timeSec * 1e9);
- }
- return Sprintf("%.2lfns", timeSec * 1e9);
- }
- }
- TString GetParam(const NLWTrace::TLogItem& item, TString* paramValues, const TString& paramName)
- {
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
- return paramValues[pi];
- }
- }
- return TString();
- }
- TString GetGroup(const NLWTrace::TLogItem& item, TString* paramValues)
- {
- TStringStream ss;
- bool first = true;
- for (const TString& groupParam : GroupBy) {
- ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
- first = false;
- }
- return ss.Str();
- }
- void AddItemToTrack(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- // Ensure cyclic per thread lwtrace logs wont drop *inner* items of a track
- // (note that some *starting* items can be dropped)
- if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
- TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- Tracks[GetGroup(item, paramValues)].Items.emplace_back(tid, item);
- }
- }
- };
- NLWTrace::TProbeRegistry g_Probes;
- TString g_sanitizerTest("TString g_sanitizerTest");
- NLWTrace::TManager g_SafeManager(g_Probes, false);
- NLWTrace::TManager g_UnsafeManager(g_Probes, true);
- TDashboardRegistry g_DashboardRegistry;
- class TLWTraceMonPage : public NMonitoring::IMonPage {
- private:
- NLWTrace::TManager* TraceMngr;
- TString StartTime;
- TTraceCleaner Cleaner;
- TMutex SnapshotsMtx;
- THashMap<TString, TAtomicSharedPtr<NLWTrace::TLogPb>> Snapshots;
- public:
- explicit TLWTraceMonPage(bool allowUnsafe = false)
- : NMonitoring::IMonPage("trace", "Tracing")
- , TraceMngr(&TraceManager(allowUnsafe))
- , Cleaner(TraceMngr)
- {
- time_t stime = TInstant::Now().TimeT();
- StartTime = CTimeR(&stime);
- }
- virtual void Output(NMonitoring::IMonHttpRequest& request) {
- TStringStream out;
- try {
- if (request.GetParams().Get("mode") == "") {
- OutputTracesAndSnapshots(request, out);
- } else if (request.GetParams().Get("mode") == "probes") {
- OutputProbes(request, out);
- } else if (request.GetParams().Get("mode") == "dashboards") {
- OutputDashboards(request, out);
- } else if (request.GetParams().Get("mode") == "dashboard") {
- OutputDashboard(request, out);
- } else if (request.GetParams().Get("mode") == "log") {
- OutputLog(request, out);
- } else if (request.GetParams().Get("mode") == "query") {
- OutputQuery(request, out);
- } else if (request.GetParams().Get("mode") == "builder") {
- OutputBuilder(request, out);
- } else if (request.GetParams().Get("mode") == "analytics") {
- OutputAnalytics(request, out);
- } else if (request.GetParams().Get("mode") == "new") {
- PostNew(request, out);
- } else if (request.GetParams().Get("mode") == "delete") {
- PostDelete(request, out);
- } else if (request.GetParams().Get("mode") == "make_snapshot") {
- PostSnapshot(request, out);
- } else if (request.GetParams().Get("mode") == "settimeout") {
- PostSetTimeout(request, out);
- } else {
- ythrow yexception() << "Bad request";
- }
- } catch (TPageGenBase& gen) {
- out.Clear();
- out << gen.what();
- } catch (...) {
- out.Clear();
- if (request.GetParams().Get("error") == "text") {
- // Text error reply is helpful for ajax requests
- out << NMonitoring::HTTPOKTEXT;
- out << CurrentExceptionMessage();
- } else {
- WWW_HTML(out) {
- out << "<h2>Error</h2><pre>"
- << CurrentExceptionMessage()
- << Endl;
- }
- }
- }
- request.Output() << out.Str();
- }
- private:
- void OutputNavbar(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TString active = " class=\"active\"";
- out <<
- "<nav class=\"navbar navbar-default\"><div class=\"container-fluid\">"
- << NavbarHeader() <<
- "<ul class=\"nav navbar-nav\">"
- "<li" << (request.GetParams().Get("mode") == ""? active: "") << "><a href=\"?mode=\">Traces</a></li>"
- "<li" << (request.GetParams().Get("mode") == "probes"? active: "") << "><a href=\"?mode=probes\">Probes</a></li>"
- "<li" << (request.GetParams().Get("mode") == "dashboards"? active: "") << "><a href=\"?mode=dashboards\">Dashboard</a></li>"
- "<li" << (request.GetParams().Get("mode") == "builder"? active: "") << "><a href=\"?mode=builder\">Builder</a></li>"
- "<li" << (request.GetParams().Get("mode") == "analytics"? active: "") << "><a href=\"?mode=analytics&id=\">Analytics</a></li>"
- "<li><a href=\"https://wiki.yandex-team.ru/development/poisk/arcadia/library/cpp/lwtrace/\" target=\"_blank\">Documentation</a></li>"
- "</ul>"
- "</div></nav>"
- ;
- }
- template <class TReader>
- void ReadSnapshots(TReader& reader) const
- {
- TGuard<TMutex> g(SnapshotsMtx);
- for (const auto& kv : Snapshots) {
- reader.Push(kv.first, kv.second);
- }
- }
- void OutputTracesAndSnapshots(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TLogSources logSources(Cleaner);
- TraceMngr->ReadTraces(logSources);
- ReadSnapshots(logSources);
- TStringStream ss;
- TTracesHtmlPrinter printer(ss);
- logSources.ForEach(printer);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out <<
- "<table class=\"table table-striped\">"
- "<tr><th>Start Time</th><th>Timeout</th><th>Name</th><th>Events</th><th>Threads</th><th></th><th></th><th></th><th></th><th></th></tr>"
- << ss.Str() <<
- "</table>"
- ;
- out << "<hr/><p><strong>Start time:</strong> " << StartTime;
- out << "<br/><strong>Build date:</strong> ";
- out << __DATE__ << " " << __TIME__ << "</p>" << Endl;
- }
- }
- void OutputProbes(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TStringStream ss;
- TProbesHtmlPrinter printer;
- TraceMngr->ReadProbes(printer);
- printer.Output(ss);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << ss.Str();
- }
- }
- void OutputDashboards(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TStringStream ss;
- g_DashboardRegistry.Output(ss);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << ss.Str();
- }
- }
- void OutputDashboard(const NMonitoring::IMonHttpRequest& request, IOutputStream& out) {
- if (!request.GetParams().Has("name")) {
- ythrow yexception() << "Cgi-parameter 'name' is not specified";
- } else {
- auto name = request.GetParams().Get("name");
- NLWTrace::TDashboard dash;
- if (!g_DashboardRegistry.Get(name, dash)) {
- ythrow yexception() << "Dashboard doesn't exist";
- }
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << "<style type='text/css'>html, body { height: 100%; }</style>";
- out << "<h2>" << dash.GetName() << "</h2>";
- if (dash.GetDescription()) {
- out << "<h3>" << dash.GetDescription() << "</h3>";
- }
- int height = 85; // %
- int minHeight = 100; // px
- out << "<table height='" << height << "%' width='100%' cellpadding='4'><tbody height='100%' width='100%'>";
- ui32 rows = 0;
- auto maxRowSpan = [](const auto& row) {
- ui32 rowSpan = 1;
- for (const auto& cell : row.GetCells()) {
- rowSpan = Max(rowSpan, cell.GetRowSpan());
- }
- return rowSpan;
- };
- for (const auto& row : dash.GetRows()) {
- rows += maxRowSpan(row);
- }
- for (const auto& row : dash.GetRows()) {
- int rowSpan = maxRowSpan(row);
- out << "<tr align='left' valign='top' style='height:" << (height * rowSpan / rows) << "%; min-height:" << (minHeight * rowSpan)<< "px'>";
- for (const auto& cell : row.GetCells()) {
- TString url = cell.GetUrl();
- TString title = cell.GetTitle();
- TString text = cell.GetText();
- auto rowSpan = Max<ui64>(1, cell.GetRowSpan());
- auto colSpan = Max<ui64>(1, cell.GetColSpan());
- if (url) {
- if (title) {
- out << "<td rowspan='" << rowSpan << "' colSpan='1'><a href=" << url << ">" << title << "</a><br>";
- }
- out << "<iframe scrolling='no' width='" << 100 * colSpan << "%' height='" << height << "%' style='border: 0' src=" << url << "></iframe></td>";
- // Add fake cells to fix html table
- for (ui32 left = 1; left < colSpan; ++left) {
- out << "<td height='100%' rowspan='" << rowSpan << "' colSpan='1'>"
- << "<iframe scrolling='no' width='100%' height='100%' style='border: 0' src=" << "" << "></iframe></td>";
- }
- } else {
- out << "<td style='font-size: 25px' align='left' rowspan='" << rowSpan << "' colSpan='" << colSpan << "'>" << text << "</td>";
- }
- }
- }
- out << "</tbody></table>";
- }
- }
- }
- static double ParseDouble(const TString& s)
- {
- if (s == "inf") {
- return std::numeric_limits<double>::infinity();
- } else if (s == "-inf") {
- return -std::numeric_limits<double>::infinity();
- } else {
- return FromString<double>(s);
- }
- }
- void OutputLog(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- if (request.GetParams().NumOfValues("id") == 0) {
- ythrow yexception() << "Cgi-parameter 'id' is not specified";
- } else {
- const TCgiParameters& e = request.GetParams();
- TStringStream ss;
- if (e.Get("format") == "json") {
- TLogJsonPrinter printer(ss);
- printer.OutputHeader();
- TString id = e.Get("id");
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- printer.OutputFooter(TraceMngr->GetTrace(id));
- out << HTTPOKJSON;
- out << ss.Str();
- } if (e.Get("format") == "json2") {
- TLogTextPrinter printer(e);
- for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- TraceMngr->ReadDepot(id, printer);
- }
- printer.OutputJson(ss);
- out << HTTPOKJSON;
- out << ss.Str();
- } else if (e.Get("format") == "analytics" && e.Get("aggr") == "tracks") {
- TLogTrackExtractor logTrackExtractor(e,
- Subvalues(e, "f"),
- Subvalues(e, "g")
- );
- for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, logTrackExtractor);
- TraceMngr->ReadDepot(id, logTrackExtractor);
- }
- TString patternAnalyzer;
- if (e.Get("pattern")) {
- patternAnalyzer = e.Get("ptrn_anlz");
- }
- logTrackExtractor.Run();
- TLogTextPrinter printer(e);
- const TString& distBy = patternAnalyzer;
- double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
- double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
- TSampleOpts opts;
- opts.ShowProvider = (e.Get("show_provider") == "y");
- if (e.Get("size_limit")) {
- opts.SizeLimit = FromString<size_t>(e.Get("size_limit"));
- }
- logTrackExtractor.OutputSample(distBy, sel_x1, sel_x2, opts, printer);
- printer.Output(ss);
- out << HTTPOKTEXT;
- out << ss.Str();
- } else {
- TLogTextPrinter printer(e);
- for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- TraceMngr->ReadDepot(id, printer);
- }
- printer.Output(ss);
- out << HTTPOKTEXT;
- out << ss.Str();
- }
- }
- }
- void OutputQuery(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- if (request.GetParams().NumOfValues("id") == 0) {
- ythrow yexception() << "Cgi-parameter 'id' is not specified";
- } else {
- TString id = request.GetParams().Get("id");
- const NLWTrace::TQuery& query = TraceMngr->GetTrace(id)->GetQuery();
- TString queryStr = query.DebugString();
- WWW_HTML(out) {
- out << "<h2>Trace Query: " << id << "</h2><pre>" << queryStr;
- }
- }
- }
- void OutputBuilder(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- Y_UNUSED(request);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << "<form class=\"form-horizontal\" action=\"?mode=new&ui=y\" method=\"POST\">";
- DIV_CLASS("form-group") {
- LABEL_CLASS_FOR("col-sm-1 control-label", "inputId") { out << "Name"; }
- DIV_CLASS("col-sm-11") {
- out << "<input class=\"form-control\" id=\"inputId\" name=\"id\" placeholder=\"mytrace\">";
- }
- }
- DIV_CLASS("form-group") {
- LABEL_CLASS_FOR("col-sm-1 control-label", "textareaQuery") { out << "Query"; }
- DIV_CLASS("col-sm-11") {
- out << "<textarea class=\"form-control\" id=\"textareaQuery\" name=\"query\" rows=\"10\"></textarea>";
- }
- }
- DIV_CLASS("form-group") {
- DIV_CLASS("col-sm-offset-1 col-sm-11") {
- out << "<button type=\"submit\" class=\"btn btn-default\">Trace</button>";
- }
- }
- out << "</form>";
- }
- }
- void OutputAnalytics(const NMonitoring::IMonHttpRequest& request, TStringStream& out)
- {
- using namespace NAnalytics;
- const TCgiParameters& e = request.GetParams();
- TLogSources logSources(Cleaner);
- TraceMngr->ReadTraces(logSources);
- ReadSnapshots(logSources);
- RequireMultipleSelection(out, e, "id", "Analyze ", ListTraces(logSources));
- THolder<TLogFilter> logFilter;
- TLogAnalyzer* logAnalyzer = nullptr;
- TLogTrackExtractor* logTracks = nullptr;
- if (request.GetParams().Get("aggr") == "tracks") {
- logFilter.Reset(logTracks = new TLogTrackExtractor(e,
- Subvalues(request.GetParams(), "f"),
- Subvalues(request.GetParams(), "g")
- ));
- for (const TString& id : Subvalues(request.GetParams(), "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, *logTracks);
- TraceMngr->ReadDepot(id, *logTracks);
- }
- } else {
- logFilter.Reset(logAnalyzer = new TLogAnalyzer(
- Subvalues(request.GetParams(), "f"),
- Subvalues(request.GetParams(), "g"),
- request.GetParams().Get("cutts") == "y"
- ));
- for (const TString& id : Subvalues(request.GetParams(), "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, *logAnalyzer);
- TraceMngr->ReadDepot(id, *logAnalyzer);
- }
- }
- logFilter->FilterSelectors(out, e, "f");
- OptionalMultipleSelection(out, e, "g", "group by", logFilter->ListParamNames());
- {
- auto paramNamesList = logFilter->ListParamNames();
- if (e.Get("aggr") == "tracks") {
- paramNamesList.emplace_back("_trackMs", "_trackMs");
- }
- OptionalSelection(out, e, "s", "order by", paramNamesList);
- }
- if (e.Get("s")) {
- TVariants variants;
- variants.emplace_back("", "asc");
- variants.emplace_back("y", "desc");
- DropdownSelector<Link>(out, e, "reverse", e.Get("reverse"), "", variants);
- }
- TString aggr = e.Get("aggr");
- TVariants variants1; // MSVS2013 doesn't understand complex initializer lists
- variants1.emplace_back("", "without aggregation");
- variants1.emplace_back("hist", "as histogram");
- variants1.emplace_back("tracks", "tracks");
- DropdownSelector<Link>(out, e, "aggr", e.Get("aggr"), "", variants1);
- unsigned refresh = e.Get("refresh")?
- FromString<unsigned>(e.Get("refresh")):
- 1000;
- if (aggr == "tracks") {
- TVariants ileVars;
- ileVars.emplace_back("0", "0");
- ileVars.emplace_back("25", "25");
- ileVars.emplace_back("50", "50");
- ileVars.emplace_back("75", "75");
- ileVars.emplace_back("", "90");
- ileVars.emplace_back("95", "95");
- ileVars.emplace_back("99", "99");
- ileVars.emplace_back("99.9", "99.9");
- ileVars.emplace_back("100", "100");
- DropdownSelector<Link>(out, e, "ile", e.Get("ile"), "and show", ileVars);
- out << "%-ile. ";
- TString patternAnalyzer;
- TString distBy;
- TString distType;
- if (e.Get("pattern")) {
- TVariants analyzePatternVars;
- analyzePatternVars.emplace_back("resTotal", "distribution by total");
- analyzePatternVars.emplace_back("resLast", "distribution by last");
- analyzePatternVars.emplace_back("covMatrix", "covariance matrix");
- DropdownSelector<Link>(
- out, e, "ptrn_anlz", e.Get("ptrn_anlz"),
- "Pattern", analyzePatternVars
- );
- patternAnalyzer = e.Get("ptrn_anlz");
- TVariants distTypeVars;
- distTypeVars.emplace_back("", "as is");
- distTypeVars.emplace_back("-stack", "cumulative");
- DropdownSelector<Link>(out, e, "dist_type", e.Get("dist_type"), "", distTypeVars);
- distType = e.Get("dist_type");
- } else {
- out << "<i>Select pattern for more options</i>";
- }
- logTracks->Run();
- if (e.Get("download") == "y") {
- out.Clear();
- out <<
- "HTTP/1.1 200 Ok\r\n"
- "Content-Type: application/force-download\r\n"
- "Content-Transfer-Encoding: binary\r\n"
- "Content-Disposition: attachment; filename=\"trace_chrome.json\"\r\n"
- "\r\n"
- ;
- logTracks->OutputChromeTrace(out, e);
- return;
- }
- NAnalytics::TTable distData;
- bool showSample = false;
- TLogTextPrinter printer(e);
- if (patternAnalyzer == "resTotal" || patternAnalyzer == "resLast") {
- distBy = patternAnalyzer;
- distData = logTracks->Distribution(distBy, "", "", e.Get("width"));
- double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
- double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
- if (!isnan(sel_x1) && !isnan(sel_x2)) {
- showSample = true;
- TSampleOpts opts;
- opts.ShowProvider = (e.Get("show_provider") == "y");
- if (e.Get("size_limit")) {
- opts.SizeLimit = FromString<size_t>(e.Get("size_limit"));
- }
- logTracks->OutputSample(distBy, sel_x1, sel_x2, opts, printer);
- }
- }
- TString selectors = out.Str();
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- if (distBy) {
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = null;\n"
- " var datajson = " << ToJsonFlot(distData, distBy, {"_count_sum" + distType}) << ";\n"
- " var refreshPeriod = 0;\n"
- " var xn = \"" << distBy << "\";\n"
- " var navigate = false;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- " embededMode();"
- " enableSelection();"
- "});\n"
- "</script>\n";
- }
- // Show download button
- out <<
- "<script type=\"text/javascript\">"
- "$(function() {"
- " $(\"#download-btn\").click(function(){window.location.href='"
- << MakeUrlAdd(e, "download", "y") <<
- "';});"
- " $(\"#download-btn\").removeClass(\"hidden\");"
- "});"
- "</script>\n";
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- logTracks->OutputTable(out, e);
- if (distBy) {
- out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
- if (showSample) {
- static const THashSet<TString> keepParams = {
- "f",
- "g",
- "head",
- "tail",
- "s",
- "reverse",
- "cutts",
- "showts",
- "show_provider",
- "size_limit",
- "aggr",
- "id",
- "pattern",
- "ptrn_anlz",
- "sel_x1",
- "sel_x2"
- };
- TCgiParameters cgiParams;
- for (const auto& kv : request.GetParams()) {
- if (keepParams.count(kv.first)) {
- cgiParams.insert(kv);
- }
- }
- cgiParams.insert(std::pair<TString, TString>("mode", "log"));
- BtnHref<Button|Medium>(out, "Open logs", MakeUrlAdd(cgiParams, "format", "analytics"));
- out << "<pre>\n";
- printer.Output(out);
- out << "</pre>\n";
- }
- }
- if (patternAnalyzer == "covMatrix") {
- logTracks->OutputSliceCovarianceMatrix(out, e);
- }
- }
- }
- }
- } else {
- double width = e.Get("width")? FromString<double>(e.Get("width")): 99;
- NAnalytics::TTable data;
- if (aggr == "") {
- data = logAnalyzer->GetTable();
- } else if (aggr == "hist") {
- RequireSelection(out, e, "bn", "by", logFilter->ListParamNames());
- const NAnalytics::TTable& inputTable = logAnalyzer->GetTable();
- TString bn = e.Get("bn");
- double b1 = e.Get("b1")? FromString<double>(e.Get("b1")): MinValue(bn, inputTable);
- double b2 = e.Get("b2")? FromString<double>(e.Get("b2")): MaxValue(bn, inputTable);
- if (isfinite(b1) && isfinite(b2)) {
- WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
- double dx = e.Get("dx")? FromString<double>(e.Get("dx")): (b2-b1)/width;
- data = HistogramAll(inputTable, e.Get("bn"), b1, b2, dx);
- } else {
- // Empty table -- it's ok -- leave data table empty
- }
- }
- TString xn = e.Get("xn");
- TString outFormat = e.Get("out");
- TVariants variants2;
- variants2.emplace_back("html", "table");
- variants2.emplace_back("flot", "chart");
- variants2.emplace_back("gantt", "gantt");
- variants2.emplace_back("text", "text");
- variants2.emplace_back("csv", "CSV");
- variants2.emplace_back("json_flot", "JSON");
- RequireSelection(out, e, "out", "and show", variants2);
- if (outFormat == "csv") {
- TString sep = e.Get("sep")? e.Get("sep"): TString("\t");
- out.Clear();
- out << NMonitoring::HTTPOKTEXT;
- out << ToCsv(data, sep, e.Get("head") != "n");
- } else if (outFormat == "html") {
- TString selectors = out.Str();
- out.Clear();
- WWW_HTML(out) {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << ToHtml(data);
- }
- } else if (outFormat == "json_flot") {
- SeriesSelectors(out, e, "xn", "yns", data);
- out.Clear();
- out << NMonitoring::HTTPOKJSON;
- out << ToJsonFlot(data, xn, SplitString(e.Get("yns"), ":"));
- } else if (outFormat == "flot") {
- SeriesSelectors(out, e, "xn", "yns", data);
- TString selectors = out.Str();
- TVector<TString> ynos = SplitString(e.Get("yns"), ":");
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = \"" << EscapeJSONString(MakeUrl(e, "out", "json_flot")) << "\";\n"
- " var refreshPeriod = " << refresh << ";\n"
- " var xn = \"" << ParseName(xn) << "\";\n"
- " var navigate = true;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- "});\n"
- "</script>\n"
- ;
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
- }
- }
- }
- } else if (outFormat == "gantt") {
- TString selectors = out.Str();
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = \"" << EscapeJSONString(MakeUrl(e, {{"mode", "log"}, {"format", "json2"}, {"gantt",""}})) << "\";\n"
- " var refreshPeriod = " << refresh << ";\n"
- " var xn = \"" << ParseName(xn) << "\";\n"
- " var navigate = true;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- "});\n"
- "</script>\n"
- ;
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << NResource::Find("lwtrace/mon/static/analytics.gantt.html") << Endl;
- }
- }
- }
- } else if (outFormat = "text") {
- out << " <input type='text' id='logsLimit' size='2' placeholder='Limit'>" << Endl;
- out <<
- R"END(<script>
- {
- var url = new URL(window.location.href);
- if (url.searchParams.has('head')) {
- document.getElementById('logsLimit').value = url.searchParams.get('head');
- }
- }
- $('#logsLimit').on('keypress', function(ev) {
- if (ev.keyCode == 13) {
- var url = new URL(window.location.href);
- var limit_value = document.getElementById('logsLimit').value;
- if (limit_value && !isNaN(limit_value)) {
- url.searchParams.set('head', limit_value);
- window.location.href = url.toString();
- } else if (!limit_value) {
- url.searchParams.delete('head');
- window.location.href = url.toString();
- }
- }
- });
- </script>)END";
- TString selectors = out.Str();
- TLogTextPrinter printer(e);
- TStringStream ss;
- for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- TraceMngr->ReadDepot(id, printer);
- }
- printer.Output(ss);
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- static const THashSet<TString> keepParams = {
- "s",
- "head",
- "reverse",
- "cutts",
- "showts",
- "id",
- "out"
- };
- TCgiParameters cgiParams;
- for (const auto& kv : request.GetParams()) {
- if (keepParams.count(kv.first)) {
- cgiParams.insert(kv);
- }
- }
- cgiParams.insert(std::pair<TString, TString>("mode", "analytics"));
- auto toggledButton = [&out, &e, &cgiParams] (const TString& label, const TString& cgiKey) {
- if (e.Get(cgiKey) == "y") {
- BtnHref<Button|Medium>(out, label, MakeUrlErase(cgiParams, cgiKey, "y"), true);
- } else {
- BtnHref<Button|Medium>(out, label, MakeUrlAdd(cgiParams, cgiKey, "y"));
- }
- };
- toggledButton("Cut Tails", "cutts");
- toggledButton("Relative Time", "showts");
- cgiParams.erase("mode");
- cgiParams.insert(std::pair<TString, TString>("mode", "log"));
- BtnHref<Button|Medium>(out, "Fullscreen", MakeUrlAdd(cgiParams, "format", "text"));
- out << "<pre>\n";
- out << ss.Str() << Endl;
- out << "</pre>\n";
- }
- }
- }
- }
- }
- }
- TDuration GetGetTimeout(const NMonitoring::IMonHttpRequest& request)
- {
- return (request.GetParams().Has("timeout")?
- TDuration::Seconds(FromString<double>(request.GetParams().Get("timeout"))):
- TDuration::Max());
- }
- void PostNew(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
- const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TDuration timeout = GetGetTimeout(request);
- if (!CheckAdHocTrace(id, timeout)) {
- NLWTrace::TQuery query;
- TString queryStr = request.GetPostParams().Get("query");
- if (!ui) {
- queryStr = Base64Decode(queryStr); // Needed for trace.sh (historically)
- }
- WWW_CHECK(queryStr, "Empty trace query");
- bool parsed = NProtoBuf::TextFormat::ParseFromString(queryStr, &query);
- WWW_CHECK(parsed, "Trace query text protobuf parse failed"); // TODO[serxa]: report error line/col and message
- TraceMngr->New(id, query);
- Cleaner.Postpone(id, timeout, false);
- } else {
- WWW_CHECK(!request.GetPostParams().Has("query"), "trace id '%s' is reserved for ad-hoc traces", id.data());
- }
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Trace created successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
- void PostDelete(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
- const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TraceMngr->Delete(id);
- Cleaner.Forget(id);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Trace deleted successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
- void PostSnapshot(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
- const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TInstant now = TInstant::Now();
- TGuard<TMutex> g(SnapshotsMtx);
- const NLWTrace::TSession* trace = TraceMngr->GetTrace(id);
- struct tm tm0;
- TString sid = id + Strftime("_%Y%m%d-%H%M%S", now.GmTime(&tm0));
- TAtomicSharedPtr<NLWTrace::TLogPb>& pbPtr = Snapshots[sid];
- pbPtr.Reset(new NLWTrace::TLogPb());
- trace->ToProtobuf(*pbPtr);
- pbPtr->SetName(sid);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Snapshot created successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
- void PostSetTimeout(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
- const TString& id = request.GetPostParams().Get("id");
- TDuration timeout = GetGetTimeout(request);
- bool ui = (request.GetParams().Get("ui") == "y");
- Cleaner.Postpone(id, timeout, true);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Timeout changed successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
- void RegisterDashboard(const TString& dashConfig) {
- g_DashboardRegistry.Register(dashConfig);
- }
- private:
- // Returns true iff trace is ad-hoc and ensures trace is created
- bool CheckAdHocTrace(const TString& id, TDuration timeout)
- {
- TAdHocTraceConfig cfg;
- if (cfg.ParseId(id)) {
- if (!TraceMngr->HasTrace(id)) {
- TraceMngr->New(id, cfg.Query());
- }
- Cleaner.Postpone(id, timeout, false);
- return true;
- }
- return false;
- }
- };
- void RegisterPages(NMonitoring::TIndexMonPage* index, bool allowUnsafe) {
- THolder<NLwTraceMonPage::TLWTraceMonPage> p = MakeHolder<NLwTraceMonPage::TLWTraceMonPage>(allowUnsafe);
- index->Register(p.Release());
- #define WWW_STATIC_FILE(file, type) \
- index->Register(new TResourceMonPage(file, file, NMonitoring::TResourceMonPage::type));
- WWW_STATIC_FILE("lwtrace/mon/static/common.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/common.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/css/bootstrap.min.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/css/d3-gantt.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/css/jquery.treegrid.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/analytics.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.eot", FONT_EOT);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.svg", SVG);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.ttf", FONT_TTF);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff2", FONT_WOFF2);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff", FONT_WOFF);
- WWW_STATIC_FILE("lwtrace/mon/static/img/collapse.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/expand.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/file.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/folder.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/js/bootstrap.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3.v4.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3-gantt.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3-tip-0.8.0-alpha.1.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/filesaver.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.extents.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.navigate.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.selection.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.bootstrap3.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.url.min.js", JAVASCRIPT);
- #undef WWW_STATIC_FILE
- }
- NLWTrace::TProbeRegistry& ProbeRegistry() {
- return g_Probes;
- }
- NLWTrace::TManager& TraceManager(bool allowUnsafe) {
- return allowUnsafe? g_UnsafeManager: g_SafeManager;
- }
- TDashboardRegistry& DashboardRegistry() {
- return g_DashboardRegistry;
- }
- } // namespace NLwTraceMonPage
|