jstats.cpp 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jiface.hpp"
  14. #include "jstats.h"
  15. #include "jexcept.hpp"
  16. #include "jiter.ipp"
  17. #include "jlog.hpp"
  18. #include "jregexp.hpp"
  19. #include "jfile.hpp"
  20. #include <math.h>
  21. #ifdef _WIN32
  22. #include <sys/timeb.h>
  23. #endif
  24. static CriticalSection statsNameCs;
  25. static StringBuffer statisticsComponentName;
  26. static StatisticCreatorType statisticsComponentType = SCTunknown;
  27. const static unsigned currentStatisticsVersion = 1;
  28. StatisticCreatorType queryStatisticsComponentType()
  29. {
  30. return statisticsComponentType;
  31. }
  32. const char * queryStatisticsComponentName()
  33. {
  34. CriticalBlock c(statsNameCs);
  35. if (statisticsComponentName.length() == 0)
  36. {
  37. statisticsComponentName.append("unknown").append(GetCachedHostName());
  38. DBGLOG("getProcessUniqueName hasn't been configured correctly");
  39. }
  40. return statisticsComponentName.str();
  41. }
  42. void setStatisticsComponentName(StatisticCreatorType processType, const char * processName, bool appendIP)
  43. {
  44. if (!processName)
  45. return;
  46. CriticalBlock c(statsNameCs);
  47. statisticsComponentType = processType;
  48. statisticsComponentName.clear().append(processName);
  49. if (appendIP)
  50. statisticsComponentName.append("@").append(GetCachedHostName()); // should I use _ instead?
  51. }
  52. //--------------------------------------------------------------------------------------------------------------------
  53. // Textual forms of the different enumerations, first items are for none and all.
  54. static constexpr const char * const measureNames[] = { "", "all", "ns", "ts", "cnt", "sz", "cpu", "skw", "node", "ppm", "ip", "cy", "en", "txt", "bool", NULL };
  55. static constexpr const char * const creatorTypeNames[]= { "", "all", "unknown", "hthor", "roxie", "roxie:s", "thor", "thor:m", "thor:s", "eclcc", "esp", "summary", NULL };
  56. static constexpr const char * const scopeTypeNames[] = { "", "all", "global", "graph", "subgraph", "activity", "allocator", "section", "compile", "dfu", "edge", "function", "workflow", "child", "unknown", nullptr };
  57. static unsigned matchString(const char * const * names, const char * search)
  58. {
  59. if (!search)
  60. return 0;
  61. if (streq(search, "*"))
  62. search = "all";
  63. unsigned i=0;
  64. for (;;)
  65. {
  66. const char * next = names[i];
  67. if (!next)
  68. return 0;
  69. if (strieq(next, search))
  70. return i;
  71. i++;
  72. }
  73. }
  74. //--------------------------------------------------------------------------------------------------------------------
  75. static const StatisticScopeType scoreOrder[] = {
  76. SSTedge,
  77. SSTactivity,
  78. SSTnone,
  79. SSTall,
  80. SSTglobal,
  81. SSTgraph,
  82. SSTsubgraph,
  83. SSTallocator,
  84. SSTsection,
  85. SSTcompilestage,
  86. SSTdfuworkunit,
  87. SSTfunction,
  88. SSTworkflow,
  89. SSTchildgraph,
  90. SSTunknown
  91. };
  92. static int scopePriority[SSTmax];
  93. MODULE_INIT(INIT_PRIORITY_STANDARD)
  94. {
  95. static_assert(_elements_in(scoreOrder) == SSTmax, "Elements missing from scoreOrder[]");
  96. for (unsigned i=0; i < _elements_in(scoreOrder); i++)
  97. scopePriority[scoreOrder[i]] = i;
  98. return true;
  99. }
  100. extern jlib_decl int compareScopeName(const char * left, const char * right)
  101. {
  102. StatsScopeId leftId;
  103. StatsScopeId rightId;
  104. for(;;)
  105. {
  106. leftId.extractScopeText(left, &left);
  107. rightId.extractScopeText(right, &right);
  108. int result = leftId.compare(rightId);
  109. if (result != 0)
  110. return result;
  111. left = strchr(left, ':');
  112. right = strchr(right, ':');
  113. if (!left || !right)
  114. {
  115. if (left)
  116. return +1;
  117. if (right)
  118. return -1;
  119. return 0;
  120. }
  121. left++;
  122. right++;
  123. }
  124. }
  125. //--------------------------------------------------------------------------------------------------------------------
  126. extern jlib_decl unsigned __int64 getTimeStampNowValue()
  127. {
  128. #ifdef _WIN32
  129. struct _timeb now;
  130. _ftime(&now);
  131. return (unsigned __int64)now.time * I64C(1000000) + now.millitm * 1000;
  132. #else
  133. struct timeval tm;
  134. gettimeofday(&tm,NULL);
  135. return (unsigned __int64)tm.tv_sec * I64C(1000000) + tm.tv_usec;
  136. #endif
  137. }
  138. const static unsigned __int64 msUntilResync = 1000; // resync every second ~= 1ms accuracy
  139. static cycle_t cyclesUntilResync;
  140. MODULE_INIT(INIT_PRIORITY_STANDARD)
  141. {
  142. cyclesUntilResync = nanosec_to_cycle(msUntilResync * 1000000);
  143. return true;
  144. }
  145. OptimizedTimestamp::OptimizedTimestamp()
  146. {
  147. lastCycles = get_cycles_now();
  148. lastTimestamp = ::getTimeStampNowValue();
  149. }
  150. #if 0
  151. //This version almost certainly has problems if the computer is suspended and cycles->nanoseconds is only accurate to
  152. //about 0.1% - so should only be used for relatively short periods
  153. unsigned __int64 OptimizedTimestamp::getTimeStampNowValue()
  154. {
  155. cycle_t nowCycles = get_cycles_now();
  156. return lastTimestamp + cycle_to_microsec(nowCycles - lastCycles);
  157. }
  158. #else
  159. //This version will resync every minute, but is not thread safe. Adding a critical section makes it less efficient than recalculating
  160. unsigned __int64 OptimizedTimestamp::getTimeStampNowValue()
  161. {
  162. cycle_t nowCycles = get_cycles_now();
  163. if (nowCycles - lastCycles > cyclesUntilResync)
  164. {
  165. lastCycles = nowCycles;
  166. lastTimestamp = ::getTimeStampNowValue();
  167. }
  168. return lastTimestamp + cycle_to_microsec(nowCycles - lastCycles);
  169. }
  170. #endif
  171. unsigned __int64 getIPV4StatsValue(const IpAddress & ip)
  172. {
  173. unsigned ipValue;
  174. if (ip.getNetAddress(sizeof(ipValue),&ipValue))
  175. return ipValue;
  176. return 0;
  177. }
  178. //--------------------------------------------------------------------------------------------------------------------
  179. const static unsigned __int64 oneMilliSecond = I64C(1000000);
  180. const static unsigned __int64 oneSecond = I64C(1000000000);
  181. const static unsigned __int64 oneMinute = I64C(60000000000);
  182. const static unsigned __int64 oneHour = I64C(3600000000000);
  183. const static unsigned __int64 oneDay = 24 * I64C(3600000000000);
  184. static void formatTime(StringBuffer & out, unsigned __int64 value)
  185. {
  186. //Aim to display at least 3 significant digits in the result string
  187. if (value < oneMilliSecond)
  188. out.appendf("%uns", (unsigned)value);
  189. else if (value < oneSecond)
  190. {
  191. unsigned uvalue = (unsigned)value;
  192. out.appendf("%u.%03ums", uvalue / 1000000, (uvalue / 1000) % 1000);
  193. }
  194. else
  195. {
  196. unsigned days = (unsigned)(value / oneDay);
  197. value = value % oneDay;
  198. unsigned hours = (unsigned)(value / oneHour);
  199. value = value % oneHour;
  200. unsigned mins = (unsigned)(value / oneMinute);
  201. value = value % oneMinute;
  202. unsigned secs = (unsigned)(value / oneSecond);
  203. unsigned ns = (unsigned)(value % oneSecond);
  204. if (days > 0)
  205. out.appendf("%u days ", days);
  206. if (hours > 0 || days)
  207. out.appendf("%u:%02u:%02u", hours, mins, secs);
  208. else if (mins >= 10)
  209. out.appendf("%u:%02u", mins, secs);
  210. else if (mins >= 1)
  211. out.appendf("%u:%02u.%03u", mins, secs, ns / 1000000);
  212. else
  213. out.appendf("%u.%03us", secs, ns / 1000000);
  214. }
  215. }
  216. extern void formatTimeCollatable(StringBuffer & out, unsigned __int64 value, bool nano)
  217. {
  218. unsigned days = (unsigned)(value / oneDay);
  219. value = value % oneDay;
  220. unsigned hours = (unsigned)(value / oneHour);
  221. value = value % oneHour;
  222. unsigned mins = (unsigned)(value / oneMinute);
  223. value = value % oneMinute;
  224. unsigned secs = (unsigned)(value / oneSecond);
  225. unsigned ns = (unsigned)(value % oneSecond);
  226. if (days)
  227. out.appendf(" %3ud ", days); // Two leading spaces helps the cassandra driver force to a single partition
  228. else
  229. out.appendf(" ");
  230. if (nano)
  231. out.appendf("%2u:%02u:%02u.%09u", hours, mins, secs, ns);
  232. else
  233. out.appendf("%2u:%02u:%02u.%03u", hours, mins, secs, ns/1000000);
  234. // More than 999 days, I don't care that it goes wrong.
  235. }
  236. extern unsigned __int64 extractTimeCollatable(const char *s, bool nano)
  237. {
  238. if (!s)
  239. return 0;
  240. unsigned days,hours,mins,secs,fracs;
  241. if (sscanf(s, " %ud %u:%u:%u.%u", &days, &hours, &mins, &secs, &fracs)!=5)
  242. {
  243. days = 0;
  244. if (sscanf(s, " %u:%u:%u.%u", &hours, &mins, &secs, &fracs) != 4)
  245. return 0;
  246. }
  247. unsigned __int64 ret = days*oneDay + hours*oneHour + mins*oneMinute + secs*oneSecond;
  248. if (nano)
  249. ret += fracs;
  250. else
  251. ret += milliToNano(fracs);
  252. return ret;
  253. }
  254. static void formatTimeStamp(StringBuffer & out, unsigned __int64 value)
  255. {
  256. time_t seconds = value / 1000000;
  257. unsigned us = value % 1000000;
  258. char timeStamp[64];
  259. time_t tNow = seconds;
  260. #ifdef _WIN32
  261. struct tm *gmtNow;
  262. gmtNow = gmtime(&tNow);
  263. strftime(timeStamp, 64, "%Y-%m-%dT%H:%M:%S", gmtNow);
  264. #else
  265. struct tm gmtNow;
  266. gmtime_r(&tNow, &gmtNow);
  267. strftime(timeStamp, 64, "%Y-%m-%dT%H:%M:%S", &gmtNow);
  268. #endif //_WIN32
  269. out.append(timeStamp).appendf(".%03uZ", us / 1000);
  270. }
  271. void formatTimeStampAsLocalTime(StringBuffer & out, unsigned __int64 value)
  272. {
  273. time_t seconds = value / 1000000;
  274. unsigned us = value % 1000000;
  275. char timeStamp[64];
  276. time_t tNow = seconds;
  277. #ifdef _WIN32
  278. struct tm *gmtNow;
  279. gmtNow = localtime(&tNow);
  280. strftime(timeStamp, 64, "%H:%M:%S", gmtNow);
  281. #else
  282. struct tm gmtNow;
  283. localtime_r(&tNow, &gmtNow);
  284. strftime(timeStamp, 64, "%H:%M:%S", &gmtNow);
  285. #endif //_WIN32
  286. out.append(timeStamp).appendf(".%03u", us / 1000);
  287. }
  288. static const unsigned oneKb = 1024;
  289. static const unsigned oneMb = 1024 * 1024;
  290. static const unsigned oneGb = 1024 * 1024 * 1024;
  291. static unsigned toPermille(unsigned x) { return (x * 1000) / 1024; }
  292. static void formatSize(StringBuffer & out, unsigned __int64 value)
  293. {
  294. unsigned Gb = (unsigned)(value / oneGb);
  295. unsigned Mb = (unsigned)((value % oneGb) / oneMb);
  296. unsigned Kb = (unsigned)((value % oneMb) / oneKb);
  297. unsigned b = (unsigned)(value % oneKb);
  298. if (Gb)
  299. out.appendf("%u.%03uGb", Gb, toPermille(Mb));
  300. else if (Mb)
  301. out.appendf("%u.%03uMb", Mb, toPermille(Kb));
  302. else if (Kb)
  303. out.appendf("%u.%03uKb", Kb, toPermille(b));
  304. else
  305. out.appendf("%ub", b);
  306. }
  307. static void formatLoad(StringBuffer & out, unsigned __int64 value)
  308. {
  309. //Stored as millionth of a core. Display as a percentage => scale by 10,000
  310. out.appendf("%u.%03u%%", (unsigned)(value / 10000), (unsigned)(value % 10000) / 10);
  311. }
  312. static void formatSkew(StringBuffer & out, unsigned __int64 value)
  313. {
  314. //Skew stored as 10000 = perfect, display as percentage
  315. out.appendf("%.2f%%", ((double)(__int64)value) / 100.0);
  316. }
  317. static void formatIPV4(StringBuffer & out, unsigned __int64 value)
  318. {
  319. byte ip1 = (value & 255);
  320. byte ip2 = ((value >> 8) & 255);
  321. byte ip3 = ((value >> 16) & 255);
  322. byte ip4 = ((value >> 24) & 255);
  323. out.appendf("%d.%d.%d.%d", ip1, ip2, ip3, ip4);
  324. }
  325. void formatStatistic(StringBuffer & out, unsigned __int64 value, StatisticMeasure measure)
  326. {
  327. switch (measure)
  328. {
  329. case SMeasureNone: // Unknown stat - e.g, on old esp accessing a new workunit
  330. out.append(value);
  331. break;
  332. case SMeasureTimeNs:
  333. formatTime(out, value);
  334. break;
  335. case SMeasureTimestampUs:
  336. formatTimeStamp(out, value);
  337. break;
  338. case SMeasureCount:
  339. out.append(value);
  340. break;
  341. case SMeasureSize:
  342. formatSize(out, value);
  343. break;
  344. case SMeasureLoad:
  345. formatLoad(out, value);
  346. break;
  347. case SMeasureSkew:
  348. formatSkew(out, value);
  349. break;
  350. case SMeasureNode:
  351. out.append(value);
  352. break;
  353. case SMeasurePercent:
  354. out.appendf("%.2f%%", (double)value / 10000.0); // stored as ppm
  355. break;
  356. case SMeasureIPV4:
  357. formatIPV4(out, value);
  358. break;
  359. case SMeasureCycle:
  360. out.append(value);
  361. break;
  362. case SMeasureBool:
  363. out.append(boolToStr(value != 0));
  364. break;
  365. default:
  366. out.append(value).append('?');
  367. }
  368. }
  369. void formatStatistic(StringBuffer & out, unsigned __int64 value, StatisticKind kind)
  370. {
  371. formatStatistic(out, value, queryMeasure(kind));
  372. }
  373. //--------------------------------------------------------------------------------------------------------------------
  374. unsigned queryStatisticsDepth(const char * text)
  375. {
  376. unsigned depth = 1;
  377. for (;;)
  378. {
  379. switch (*text)
  380. {
  381. case 0:
  382. return depth;
  383. case ':':
  384. depth++;
  385. break;
  386. }
  387. text++;
  388. }
  389. }
  390. const char * queryMeasurePrefix(StatisticMeasure measure)
  391. {
  392. switch (measure)
  393. {
  394. case SMeasureAll: return NULL;
  395. case SMeasureTimeNs: return "Time";
  396. case SMeasureTimestampUs: return "When";
  397. case SMeasureCount: return "Num";
  398. case SMeasureSize: return "Size";
  399. case SMeasureLoad: return "Load";
  400. case SMeasureSkew: return "Skew";
  401. case SMeasureNode: return "Node";
  402. case SMeasurePercent: return "Per";
  403. case SMeasureIPV4: return "Ip";
  404. case SMeasureCycle: return "Cycle";
  405. case SMeasureEnum: return "";
  406. case SMeasureText: return "";
  407. case SMeasureBool: return "Is";
  408. default:
  409. return "Unknown";
  410. }
  411. }
  412. const char * queryMeasureName(StatisticMeasure measure)
  413. {
  414. return measureNames[measure];
  415. }
  416. StatisticMeasure queryMeasure(const char * measure)
  417. {
  418. //MORE: Use a hash table??
  419. StatisticMeasure ret = (StatisticMeasure)matchString(measureNames, measure);
  420. //Legacy support for an unusual statistic - pretend the sizes are in bytes instead of kb.
  421. if ((ret == SMeasureNone) && measure)
  422. {
  423. if (streq(measure, "kb"))
  424. {
  425. ret = SMeasureSize;
  426. }
  427. else
  428. {
  429. for (unsigned i1=SMeasureAll+1; i1 < SMeasureMax; i1++)
  430. {
  431. const char * prefix = queryMeasurePrefix((StatisticMeasure)i1);
  432. if (strieq(measure, prefix))
  433. return (StatisticMeasure)i1;
  434. }
  435. }
  436. }
  437. return ret;
  438. }
  439. StatsMergeAction queryMergeMode(StatisticMeasure measure)
  440. {
  441. switch (measure)
  442. {
  443. case SMeasureTimeNs: return StatsMergeSum;
  444. case SMeasureTimestampUs: return StatsMergeKeepNonZero;
  445. case SMeasureCount: return StatsMergeSum;
  446. case SMeasureSize: return StatsMergeSum;
  447. case SMeasureLoad: return StatsMergeMax;
  448. case SMeasureSkew: return StatsMergeMax;
  449. case SMeasureNode: return StatsMergeKeepNonZero;
  450. case SMeasurePercent: return StatsMergeReplace;
  451. case SMeasureIPV4: return StatsMergeKeepNonZero;
  452. case SMeasureCycle: return StatsMergeSum;
  453. case SMeasureEnum: return StatsMergeKeepNonZero;
  454. case SMeasureText: return StatsMergeKeepNonZero;
  455. case SMeasureBool: return StatsMergeKeepNonZero;
  456. default:
  457. #ifdef _DEBUG
  458. throwUnexpected();
  459. #else
  460. return StatsMergeSum;
  461. #endif
  462. }
  463. }
  464. extern jlib_decl StatsMergeAction queryMergeMode(StatisticKind kind)
  465. {
  466. //MORE: Optimize by looking up in the meta
  467. return queryMergeMode(queryMeasure(kind));
  468. }
  469. //--------------------------------------------------------------------------------------------------------------------
  470. #define BASE_NAMES(x, y) \
  471. #x #y, \
  472. #x "Min" # y, \
  473. #x "Max" # y, \
  474. #x "Avg" # y, \
  475. "Skew" # y, \
  476. "SkewMin" # y, \
  477. "SkewMax" # y, \
  478. "NodeMin" # y, \
  479. "NodeMax" # y,
  480. #define NAMES(x, y) \
  481. BASE_NAMES(x, y) \
  482. #x "Delta" # y, \
  483. #x "StdDev" #y,
  484. #define WHENNAMES(x, y) \
  485. BASE_NAMES(x, y) \
  486. "TimeDelta" # y, \
  487. "TimeStdDev" # y,
  488. #define BASE_TAGS(x, y) \
  489. "@" #x "Min" # y, \
  490. "@" #x "Max" # y, \
  491. "@" #x "Avg" # y, \
  492. "@Skew" # y, \
  493. "@SkewMin" # y, \
  494. "@SkewMax" # y, \
  495. "@NodeMin" # y, \
  496. "@NodeMax" # y,
  497. //Default tags nothing special overriden
  498. #define TAGS(x, y) \
  499. "@" #x #y, \
  500. BASE_TAGS(x, y) \
  501. "@" #x "Delta" # y, \
  502. "@" #x "StdDev" # y,
  503. //Define the tags for time items.
  504. #define WHENTAGS(x, y) \
  505. "@" #x #y, \
  506. BASE_TAGS(x, y) \
  507. "@TimeDelta" # y, \
  508. "@TimeStdDev" # y,
  509. #define CORESTAT(x, y, m) St##x##y, m, St##x##y, St##x##y, { NAMES(x, y) }, { TAGS(x, y) }
  510. #define STAT(x, y, m) CORESTAT(x, y, m)
  511. //--------------------------------------------------------------------------------------------------------------------
  512. //These are the macros to use to define the different entries in the stats meta table
  513. //#define TIMESTAT(y) STAT(Time, y, SMeasureTimeNs)
  514. #define TIMESTAT(y) St##Time##y, SMeasureTimeNs, St##Time##y, St##Cycle##y##Cycles, { NAMES(Time, y) }, { TAGS(Time, y) }
  515. #define WHENSTAT(y) St##When##y, SMeasureTimestampUs, St##When##y, St##When##y, { WHENNAMES(When, y) }, { WHENTAGS(When, y) }
  516. #define NUMSTAT(y) STAT(Num, y, SMeasureCount)
  517. #define SIZESTAT(y) STAT(Size, y, SMeasureSize)
  518. #define LOADSTAT(y) STAT(Load, y, SMeasureLoad)
  519. #define SKEWSTAT(y) STAT(Skew, y, SMeasureSkew)
  520. #define NODESTAT(y) STAT(Node, y, SMeasureNode)
  521. #define PERSTAT(y) STAT(Per, y, SMeasurePercent)
  522. #define IPV4STAT(y) STAT(IPV4, y, SMeasureIPV4)
  523. #define CYCLESTAT(y) St##Cycle##y##Cycles, SMeasureCycle, St##Time##y, St##Cycle##y##Cycles, { NAMES(Cycle, y##Cycles) }, { TAGS(Cycle, y##Cycles) }
  524. //--------------------------------------------------------------------------------------------------------------------
  525. class StatisticMeta
  526. {
  527. public:
  528. StatisticKind kind;
  529. StatisticMeasure measure;
  530. StatisticKind serializeKind;
  531. StatisticKind rawKind;
  532. const char * names[StNextModifier/StVariantScale];
  533. const char * tags[StNextModifier/StVariantScale];
  534. };
  535. //The order of entries in this table must match the order in the enumeration
  536. static const StatisticMeta statsMetaData[StMax] = {
  537. { StKindNone, SMeasureNone, StKindNone, StKindNone, { "none" }, { "@none" } },
  538. { StKindAll, SMeasureAll, StKindAll, StKindAll, { "all" }, { "@all" } },
  539. { WHENSTAT(GraphStarted) }, // Deprecated - use WhenStart
  540. { WHENSTAT(GraphFinished) }, // Deprecated - use WhenFinished
  541. { WHENSTAT(FirstRow) },
  542. { WHENSTAT(QueryStarted) }, // Deprecated - use WhenStart
  543. { WHENSTAT(QueryFinished) }, // Deprecated - use WhenFinished
  544. { WHENSTAT(Created) },
  545. { WHENSTAT(Compiled) },
  546. { WHENSTAT(WorkunitModified) },
  547. { TIMESTAT(Elapsed) },
  548. { TIMESTAT(LocalExecute) },
  549. { TIMESTAT(TotalExecute) },
  550. { TIMESTAT(Remaining) },
  551. { SIZESTAT(GeneratedCpp) },
  552. { SIZESTAT(PeakMemory) },
  553. { SIZESTAT(MaxRowSize) },
  554. { NUMSTAT(RowsProcessed) },
  555. { NUMSTAT(Slaves) },
  556. { NUMSTAT(Starts) },
  557. { NUMSTAT(Stops) },
  558. { NUMSTAT(IndexSeeks) },
  559. { NUMSTAT(IndexScans) },
  560. { NUMSTAT(IndexWildSeeks) },
  561. { NUMSTAT(IndexSkips) },
  562. { NUMSTAT(IndexNullSkips) },
  563. { NUMSTAT(IndexMerges) },
  564. { NUMSTAT(IndexMergeCompares) },
  565. { NUMSTAT(PreFiltered) },
  566. { NUMSTAT(PostFiltered) },
  567. { NUMSTAT(BlobCacheHits) },
  568. { NUMSTAT(LeafCacheHits) },
  569. { NUMSTAT(NodeCacheHits) },
  570. { NUMSTAT(BlobCacheAdds) },
  571. { NUMSTAT(LeafCacheAdds) },
  572. { NUMSTAT(NodeCacheAdds) },
  573. { NUMSTAT(PreloadCacheHits) },
  574. { NUMSTAT(PreloadCacheAdds) },
  575. { NUMSTAT(ServerCacheHits) },
  576. { NUMSTAT(IndexAccepted) },
  577. { NUMSTAT(IndexRejected) },
  578. { NUMSTAT(AtmostTriggered) },
  579. { NUMSTAT(DiskSeeks) },
  580. { NUMSTAT(Iterations) },
  581. { LOADSTAT(WhileSorting) },
  582. { NUMSTAT(LeftRows) },
  583. { NUMSTAT(RightRows) },
  584. { PERSTAT(Replicated) },
  585. { NUMSTAT(DiskRowsRead) },
  586. { NUMSTAT(IndexRowsRead) },
  587. { NUMSTAT(DiskAccepted) },
  588. { NUMSTAT(DiskRejected) },
  589. { TIMESTAT(Soapcall) },
  590. { TIMESTAT(FirstExecute) },
  591. { TIMESTAT(DiskReadIO) },
  592. { TIMESTAT(DiskWriteIO) },
  593. { SIZESTAT(DiskRead) },
  594. { SIZESTAT(DiskWrite) },
  595. { CYCLESTAT(DiskReadIO) },
  596. { CYCLESTAT(DiskWriteIO) },
  597. { NUMSTAT(DiskReads) },
  598. { NUMSTAT(DiskWrites) },
  599. { NUMSTAT(Spills) },
  600. { TIMESTAT(SpillElapsed) },
  601. { TIMESTAT(SortElapsed) },
  602. { NUMSTAT(Groups) },
  603. { NUMSTAT(GroupMax) },
  604. { SIZESTAT(SpillFile) },
  605. { CYCLESTAT(SpillElapsed) },
  606. { CYCLESTAT(SortElapsed) },
  607. { NUMSTAT(Strands) },
  608. { CYCLESTAT(TotalExecute) },
  609. { NUMSTAT(Executions) },
  610. { TIMESTAT(TotalNested) },
  611. { CYCLESTAT(LocalExecute) },
  612. { NUMSTAT(Compares) },
  613. { NUMSTAT(ScansPerRow) },
  614. { NUMSTAT(Allocations) },
  615. { NUMSTAT(AllocationScans) },
  616. { NUMSTAT(DiskRetries) },
  617. { CYCLESTAT(Elapsed) },
  618. { CYCLESTAT(Remaining) },
  619. { CYCLESTAT(Soapcall) },
  620. { CYCLESTAT(FirstExecute) },
  621. { CYCLESTAT(TotalNested) },
  622. { TIMESTAT(Generate) },
  623. { CYCLESTAT(Generate) },
  624. { WHENSTAT(Started) },
  625. { WHENSTAT(Finished) },
  626. };
  627. //--------------------------------------------------------------------------------------------------------------------
  628. StatisticMeasure queryMeasure(StatisticKind kind)
  629. {
  630. unsigned variant = queryStatsVariant(kind);
  631. switch (variant)
  632. {
  633. case StSkew:
  634. case StSkewMin:
  635. case StSkewMax:
  636. return SMeasureSkew;
  637. case StNodeMin:
  638. case StNodeMax:
  639. return SMeasureNode;
  640. case StDeltaX:
  641. case StStdDevX:
  642. {
  643. StatisticMeasure measure = queryMeasure((StatisticKind)(kind & StKindMask));
  644. switch (measure)
  645. {
  646. case SMeasureTimestampUs:
  647. return SMeasureTimeNs;
  648. default:
  649. return measure;
  650. }
  651. break;
  652. }
  653. }
  654. StatisticKind rawkind = (StatisticKind)(kind & StKindMask);
  655. if (rawkind >= StKindNone && rawkind < StMax)
  656. return statsMetaData[rawkind].measure;
  657. return SMeasureNone;
  658. }
  659. const char * queryStatisticName(StatisticKind kind)
  660. {
  661. StatisticKind rawkind = (StatisticKind)(kind & StKindMask);
  662. unsigned variant = (kind / StVariantScale);
  663. dbgassertex(variant < (StNextModifier/StVariantScale));
  664. if (rawkind >= StKindNone && rawkind < StMax)
  665. return statsMetaData[rawkind].names[variant];
  666. return "Unknown";
  667. }
  668. unsigned __int64 convertMeasure(StatisticMeasure from, StatisticMeasure to, unsigned __int64 value)
  669. {
  670. if (from == to)
  671. return value;
  672. if ((from == SMeasureCycle) && (to == SMeasureTimeNs))
  673. return cycle_to_nanosec(value);
  674. if ((from == SMeasureTimeNs) && (to == SMeasureCycle))
  675. return nanosec_to_cycle(value);
  676. #ifdef _DEBUG
  677. throwUnexpected();
  678. #else
  679. return value;
  680. #endif
  681. }
  682. unsigned __int64 convertMeasure(StatisticKind from, StatisticKind to, unsigned __int64 value)
  683. {
  684. return convertMeasure(queryMeasure(from), queryMeasure(to), value);
  685. }
  686. static double convertSquareMeasure(StatisticMeasure from, StatisticMeasure to, double value)
  687. {
  688. if (from == to)
  689. return value;
  690. const unsigned __int64 largeValue = 1000000000;
  691. double scale;
  692. if ((from == SMeasureCycle) && (to == SMeasureTimeNs))
  693. scale = (double)cycle_to_nanosec(largeValue) / (double)largeValue;
  694. else if ((from == SMeasureTimeNs) && (to == SMeasureCycle))
  695. scale = (double)nanosec_to_cycle(largeValue) / (double)largeValue;
  696. else
  697. {
  698. #ifdef _DEBUG
  699. throwUnexpected();
  700. #else
  701. scale = 1.0;
  702. #endif
  703. }
  704. return value * scale * scale;
  705. }
  706. static double convertSquareMeasure(StatisticKind from, StatisticKind to, double value)
  707. {
  708. return convertSquareMeasure(queryMeasure(from), queryMeasure(to), value);
  709. }
  710. static StatisticKind querySerializedKind(StatisticKind kind)
  711. {
  712. StatisticKind rawkind = (StatisticKind)(kind & StKindMask);
  713. if (rawkind >= StMax)
  714. return kind;
  715. StatisticKind serialKind = statsMetaData[rawkind].serializeKind;
  716. return (StatisticKind)(serialKind | (kind & ~StKindMask));
  717. }
  718. static StatisticKind queryRawKind(StatisticKind kind)
  719. {
  720. StatisticKind basekind = (StatisticKind)(kind & StKindMask);
  721. if (basekind >= StMax)
  722. return kind;
  723. StatisticKind rawKind = statsMetaData[basekind].rawKind;
  724. return (StatisticKind)(rawKind | (kind & ~StKindMask));
  725. }
  726. //--------------------------------------------------------------------------------------------------------------------
  727. void queryLongStatisticName(StringBuffer & out, StatisticKind kind)
  728. {
  729. out.append(queryStatisticName(kind));
  730. }
  731. //--------------------------------------------------------------------------------------------------------------------
  732. const char * queryTreeTag(StatisticKind kind)
  733. {
  734. StatisticKind rawkind = (StatisticKind)(kind & StKindMask);
  735. unsigned variant = (kind / StVariantScale);
  736. dbgassertex(variant < (StNextModifier/StVariantScale));
  737. if (rawkind >= StKindNone && rawkind < StMax)
  738. return statsMetaData[rawkind].tags[variant];
  739. return "@Unknown";
  740. }
  741. //--------------------------------------------------------------------------------------------------------------------
  742. StatisticKind queryStatisticKind(const char * search)
  743. {
  744. if (!search)
  745. return StKindNone;
  746. if (streq(search, "*"))
  747. return StKindAll;
  748. //Slow - should use a hash table....
  749. for (unsigned variant=0; variant < StNextModifier; variant += StVariantScale)
  750. {
  751. for (unsigned i=0; i < StMax; i++)
  752. {
  753. StatisticKind kind = (StatisticKind)(i+variant);
  754. const char * shortName = queryStatisticName(kind);
  755. if (shortName && strieq(shortName, search))
  756. return kind;
  757. }
  758. }
  759. return StKindNone;
  760. }
  761. //--------------------------------------------------------------------------------------------------------------------
  762. const char * queryCreatorTypeName(StatisticCreatorType sct)
  763. {
  764. return creatorTypeNames[sct];
  765. }
  766. StatisticCreatorType queryCreatorType(const char * sct)
  767. {
  768. //MORE: Use a hash table??
  769. return (StatisticCreatorType)matchString(creatorTypeNames, sct);
  770. }
  771. //--------------------------------------------------------------------------------------------------------------------
  772. const char * queryScopeTypeName(StatisticScopeType sst)
  773. {
  774. return scopeTypeNames[sst];
  775. }
  776. extern jlib_decl StatisticScopeType queryScopeType(const char * sst)
  777. {
  778. //MORE: Use a hash table??
  779. return (StatisticScopeType)matchString(scopeTypeNames, sst);
  780. }
  781. //--------------------------------------------------------------------------------------------------------------------
  782. inline void mergeUpdate(StatisticMeasure measure, unsigned __int64 & value, const unsigned __int64 otherValue)
  783. {
  784. switch (measure)
  785. {
  786. case SMeasureTimeNs:
  787. case SMeasureCount:
  788. case SMeasureSize:
  789. case SMeasureLoad:
  790. case SMeasureSkew:
  791. case SMeasureCycle:
  792. value += otherValue;
  793. break;
  794. case SMeasureTimestampUs:
  795. if (otherValue && otherValue < value)
  796. value = otherValue;
  797. break;
  798. }
  799. }
  800. unsigned __int64 mergeStatistic(StatisticMeasure measure, unsigned __int64 value, unsigned __int64 otherValue)
  801. {
  802. mergeUpdate(measure, value, otherValue);
  803. return value;
  804. }
  805. unsigned __int64 mergeStatisticValue(unsigned __int64 prevValue, unsigned __int64 newValue, StatsMergeAction mergeAction)
  806. {
  807. switch (mergeAction)
  808. {
  809. case StatsMergeKeepNonZero:
  810. if (prevValue)
  811. return prevValue;
  812. return newValue;
  813. case StatsMergeAppend:
  814. case StatsMergeReplace:
  815. return newValue;
  816. case StatsMergeSum:
  817. return prevValue + newValue;
  818. case StatsMergeMin:
  819. if (prevValue > newValue)
  820. return newValue;
  821. else
  822. return prevValue;
  823. case StatsMergeMax:
  824. if (prevValue < newValue)
  825. return newValue;
  826. else
  827. return prevValue;
  828. default:
  829. #ifdef _DEBUG
  830. throwUnexpected();
  831. #else
  832. return newValue;
  833. #endif
  834. }
  835. }
  836. //--------------------------------------------------------------------------------------------------------------------
  837. class CComponentStatistics
  838. {
  839. protected:
  840. StringAttr creator;
  841. byte creatorDepth;
  842. byte scopeDepth;
  843. // StatisticArray stats;
  844. };
  845. //--------------------------------------------------------------------------------------------------------------------
  846. static int compareUnsigned(unsigned const * left, unsigned const * right)
  847. {
  848. return (*left < *right) ? -1 : (*left > *right) ? +1 : 0;
  849. }
  850. StatisticsMapping::StatisticsMapping(StatisticKind kind, ...)
  851. {
  852. if (kind != StKindNone)
  853. {
  854. indexToKind.append(kind);
  855. va_list args;
  856. va_start(args, kind);
  857. for (;;)
  858. {
  859. unsigned next = va_arg(args, unsigned);
  860. if (!next)
  861. break;
  862. indexToKind.appendUniq(next);
  863. }
  864. va_end(args);
  865. }
  866. createMappings();
  867. }
  868. StatisticsMapping::StatisticsMapping(const StatisticsMapping * from, ...)
  869. {
  870. ForEachItemIn(idx, from->indexToKind)
  871. indexToKind.append(from->indexToKind.item(idx));
  872. va_list args;
  873. va_start(args, from);
  874. for (;;)
  875. {
  876. unsigned next = va_arg(args, unsigned);
  877. if (!next)
  878. break;
  879. indexToKind.appendUniq(next);
  880. }
  881. va_end(args);
  882. createMappings();
  883. }
  884. StatisticsMapping::StatisticsMapping()
  885. {
  886. for (int i = StKindAll+1; i < StMax; i++)
  887. indexToKind.append(i);
  888. createMappings();
  889. }
  890. void StatisticsMapping::createMappings()
  891. {
  892. //Possibly not needed, but sort the kinds, so that it is easy to merge/stream the results out in the correct order.
  893. indexToKind.sort(compareUnsigned);
  894. //Provide mappings to all statistics to map them to the "unknown" bin by default
  895. for (unsigned i=0; i < StMax; i++)
  896. kindToIndex.append(numStatistics());
  897. ForEachItemIn(i2, indexToKind)
  898. {
  899. unsigned kind = indexToKind.item(i2);
  900. kindToIndex.replace(i2, kind);
  901. }
  902. }
  903. const StatisticsMapping allStatistics;
  904. const StatisticsMapping heapStatistics(StNumAllocations, StNumAllocationScans, StKindNone);
  905. const StatisticsMapping diskLocalStatistics(StCycleDiskReadIOCycles, StSizeDiskRead, StNumDiskReads, StCycleDiskWriteIOCycles, StSizeDiskWrite, StNumDiskWrites, StNumDiskRetries, StKindNone);
  906. const StatisticsMapping diskRemoteStatistics(StTimeDiskReadIO, StSizeDiskRead, StNumDiskReads, StTimeDiskWriteIO, StSizeDiskWrite, StNumDiskWrites, StNumDiskRetries, StKindNone);
  907. const StatisticsMapping diskReadRemoteStatistics(StTimeDiskReadIO, StSizeDiskRead, StNumDiskReads, StNumDiskRetries, StKindNone);
  908. const StatisticsMapping diskWriteRemoteStatistics(StTimeDiskWriteIO, StSizeDiskWrite, StNumDiskWrites, StNumDiskRetries, StKindNone);
  909. //--------------------------------------------------------------------------------------------------------------------
  910. class Statistic
  911. {
  912. public:
  913. Statistic(StatisticKind _kind, unsigned __int64 _value) : kind(_kind), value(_value)
  914. {
  915. }
  916. Statistic(MemoryBuffer & in, unsigned version)
  917. {
  918. unsigned _kind;
  919. in.read(_kind);
  920. kind = (StatisticKind)_kind;
  921. in.read(value);
  922. }
  923. StatisticKind queryKind() const
  924. {
  925. return kind;
  926. }
  927. unsigned __int64 queryValue() const
  928. {
  929. return value;
  930. }
  931. void merge(unsigned __int64 otherValue)
  932. {
  933. mergeUpdate(queryMeasure(kind), value, otherValue);
  934. }
  935. void serialize(MemoryBuffer & out) const
  936. {
  937. //MORE: Could compress - e.g., store as a packed integers
  938. out.append((unsigned)kind);
  939. out.append(value);
  940. }
  941. StringBuffer & toXML(StringBuffer &out) const
  942. {
  943. return out.append(" <Stat name=\"").append(queryStatisticName(kind)).append("\" value=\"").append(value).append("\"/>\n");
  944. }
  945. public:
  946. StatisticKind kind;
  947. unsigned __int64 value;
  948. };
  949. //--------------------------------------------------------------------------------------------------------------------
  950. StringBuffer & StatsScopeId::getScopeText(StringBuffer & out) const
  951. {
  952. switch (scopeType)
  953. {
  954. case SSTgraph:
  955. return out.append(GraphScopePrefix).append(id);
  956. case SSTsubgraph:
  957. return out.append(SubGraphScopePrefix).append(id);
  958. case SSTactivity:
  959. return out.append(ActivityScopePrefix).append(id);
  960. case SSTedge:
  961. return out.append(EdgeScopePrefix).append(id).append("_").append(extra);
  962. case SSTfunction:
  963. return out.append(FunctionScopePrefix).append(name);
  964. case SSTworkflow:
  965. return out.append(WorkflowScopePrefix).append(id);
  966. case SSTchildgraph:
  967. return out.append(ChildGraphScopePrefix).append(id);
  968. case SSTunknown:
  969. return out.append(name);
  970. default:
  971. #ifdef _DEBUG
  972. throwUnexpected();
  973. #endif
  974. return out.append("????").append(id);
  975. }
  976. }
  977. unsigned StatsScopeId::getHash() const
  978. {
  979. switch (scopeType)
  980. {
  981. case SSTfunction:
  982. case SSTunknown:
  983. return hashc((const byte *)name.get(), strlen(name), (unsigned)scopeType);
  984. default:
  985. return hashc((const byte *)&id, sizeof(id), (unsigned)scopeType);
  986. }
  987. }
  988. int StatsScopeId::compare(const StatsScopeId & other) const
  989. {
  990. if (scopeType != other.scopeType)
  991. return scopePriority[scopeType] - scopePriority[other.scopeType];
  992. if (id != other.id)
  993. return (int)(id - other.id);
  994. if (extra != other.extra)
  995. return (int)(extra - other.extra);
  996. if (name && other.name)
  997. return strcmp(name, other.name);
  998. if (name)
  999. return +1;
  1000. if (other.name)
  1001. return -1;
  1002. return 0;
  1003. }
  1004. bool StatsScopeId::matches(const StatsScopeId & other) const
  1005. {
  1006. return (scopeType == other.scopeType) && (id == other.id) && (extra == other.extra) && strsame(name, other.name);
  1007. }
  1008. unsigned StatsScopeId::queryActivity() const
  1009. {
  1010. switch (scopeType)
  1011. {
  1012. case SSTactivity:
  1013. case SSTedge:
  1014. return id;
  1015. default:
  1016. return 0;
  1017. }
  1018. }
  1019. void StatsScopeId::deserialize(MemoryBuffer & in, unsigned version)
  1020. {
  1021. byte scopeTypeByte;
  1022. in.read(scopeTypeByte);
  1023. scopeType = (StatisticScopeType)scopeTypeByte;
  1024. switch (scopeType)
  1025. {
  1026. case SSTgraph:
  1027. case SSTsubgraph:
  1028. case SSTactivity:
  1029. case SSTworkflow:
  1030. case SSTchildgraph:
  1031. in.read(id);
  1032. break;
  1033. case SSTedge:
  1034. in.read(id);
  1035. in.read(extra);
  1036. break;
  1037. case SSTfunction:
  1038. in.read(name);
  1039. break;
  1040. default:
  1041. throwUnexpected();
  1042. break;
  1043. }
  1044. }
  1045. void StatsScopeId::serialize(MemoryBuffer & out) const
  1046. {
  1047. out.append((byte)scopeType);
  1048. switch (scopeType)
  1049. {
  1050. case SSTgraph:
  1051. case SSTsubgraph:
  1052. case SSTactivity:
  1053. case SSTworkflow:
  1054. case SSTchildgraph:
  1055. out.append(id);
  1056. break;
  1057. case SSTedge:
  1058. out.append(id);
  1059. out.append(extra);
  1060. break;
  1061. case SSTfunction:
  1062. out.append(name);
  1063. break;
  1064. default:
  1065. throwUnexpected();
  1066. break;
  1067. }
  1068. }
  1069. void StatsScopeId::setId(StatisticScopeType _scopeType, unsigned _id, unsigned _extra)
  1070. {
  1071. scopeType = _scopeType;
  1072. id = _id;
  1073. extra = _extra;
  1074. }
  1075. bool StatsScopeId::setScopeText(const char * text, char * * next)
  1076. {
  1077. switch (*text)
  1078. {
  1079. case ActivityScopePrefix[0]:
  1080. if (MATCHES_CONST_PREFIX(text, ActivityScopePrefix))
  1081. {
  1082. if (isdigit(text[CONST_STRLEN(ActivityScopePrefix)]))
  1083. {
  1084. unsigned id = strtoul(text + CONST_STRLEN(ActivityScopePrefix), next, 10);
  1085. setActivityId(id);
  1086. return true;
  1087. }
  1088. }
  1089. break;
  1090. case GraphScopePrefix[0]:
  1091. if (MATCHES_CONST_PREFIX(text, GraphScopePrefix))
  1092. {
  1093. if (isdigit(text[CONST_STRLEN(GraphScopePrefix)]))
  1094. {
  1095. unsigned id = strtoul(text + CONST_STRLEN(GraphScopePrefix), next, 10);
  1096. setId(SSTgraph, id);
  1097. return true;
  1098. }
  1099. }
  1100. break;
  1101. case SubGraphScopePrefix[0]:
  1102. if (MATCHES_CONST_PREFIX(text, SubGraphScopePrefix))
  1103. {
  1104. if (isdigit(text[CONST_STRLEN(SubGraphScopePrefix)]))
  1105. {
  1106. unsigned id = strtoul(text + CONST_STRLEN(SubGraphScopePrefix), next, 10);
  1107. setSubgraphId(id);
  1108. return true;
  1109. }
  1110. }
  1111. break;
  1112. case EdgeScopePrefix[0]:
  1113. if (MATCHES_CONST_PREFIX(text, EdgeScopePrefix))
  1114. {
  1115. const char * underscore = strchr(text, '_');
  1116. if (!underscore || !isdigit(underscore[1]))
  1117. return false;
  1118. unsigned id1 = atoi(text + CONST_STRLEN(EdgeScopePrefix));
  1119. unsigned id2 = strtoul(underscore+1, next, 10);
  1120. setEdgeId(id1, id2);
  1121. return true;
  1122. }
  1123. break;
  1124. case FunctionScopePrefix[0]:
  1125. if (MATCHES_CONST_PREFIX(text, FunctionScopePrefix))
  1126. {
  1127. setFunctionId(text+CONST_STRLEN(FunctionScopePrefix));
  1128. return true;
  1129. }
  1130. break;
  1131. case WorkflowScopePrefix[0]:
  1132. if (MATCHES_CONST_PREFIX(text, WorkflowScopePrefix))
  1133. {
  1134. setWorkflowId(atoi(text+CONST_STRLEN(WorkflowScopePrefix)));
  1135. return true;
  1136. }
  1137. break;
  1138. case ChildGraphScopePrefix[0]:
  1139. if (MATCHES_CONST_PREFIX(text, ChildGraphScopePrefix))
  1140. {
  1141. setChildGraphId(atoi(text+CONST_STRLEN(ChildGraphScopePrefix)));
  1142. return true;
  1143. }
  1144. break;
  1145. }
  1146. return false;
  1147. }
  1148. void StatsScopeId::extractScopeText(const char * text, const char * * next)
  1149. {
  1150. if (!setScopeText(text, (char * *)next))
  1151. {
  1152. scopeType = SSTunknown;
  1153. const char * end = strchr(text, ':');
  1154. if (end)
  1155. {
  1156. name.set(text, end-text);
  1157. if (next)
  1158. *next = end;
  1159. }
  1160. else
  1161. {
  1162. name.set(text);
  1163. if (next)
  1164. *next = text + strlen(text);
  1165. }
  1166. }
  1167. }
  1168. void StatsScopeId::setActivityId(unsigned _id)
  1169. {
  1170. setId(SSTactivity, _id);
  1171. }
  1172. void StatsScopeId::setEdgeId(unsigned _id, unsigned _output)
  1173. {
  1174. setId(SSTedge, _id, _output);
  1175. }
  1176. void StatsScopeId::setSubgraphId(unsigned _id)
  1177. {
  1178. setId(SSTsubgraph, _id);
  1179. }
  1180. void StatsScopeId::setFunctionId(const char * _name)
  1181. {
  1182. scopeType = SSTfunction;
  1183. name.set(_name);
  1184. }
  1185. void StatsScopeId::setWorkflowId(unsigned _id)
  1186. {
  1187. setId(SSTworkflow, _id);
  1188. }
  1189. void StatsScopeId::setChildGraphId(unsigned _id)
  1190. {
  1191. setId(SSTchildgraph, _id);
  1192. }
  1193. //--------------------------------------------------------------------------------------------------------------------
  1194. enum
  1195. {
  1196. SCroot,
  1197. SCintermediate,
  1198. SCleaf,
  1199. };
  1200. class CStatisticCollection;
  1201. static CStatisticCollection * deserializeCollection(CStatisticCollection * parent, MemoryBuffer & in, unsigned version);
  1202. //MORE: Create an implementation with no children
  1203. typedef StructArrayOf<Statistic> StatsArray;
  1204. class CollectionHashTable : public SuperHashTableOf<CStatisticCollection, StatsScopeId>
  1205. {
  1206. public:
  1207. ~CollectionHashTable() { _releaseAll(); }
  1208. virtual void onAdd(void *et);
  1209. virtual void onRemove(void *et);
  1210. virtual unsigned getHashFromElement(const void *et) const;
  1211. virtual unsigned getHashFromFindParam(const void *fp) const;
  1212. virtual const void * getFindParam(const void *et) const;
  1213. virtual bool matchesFindParam(const void *et, const void *key, unsigned fphash) const;
  1214. virtual bool matchesElement(const void *et, const void *searchET) const;
  1215. };
  1216. typedef IArrayOf<CStatisticCollection> CollectionArray;
  1217. static int compareCollection(IInterface * const * pl, IInterface * const *pr);
  1218. class SortedCollectionIterator : public ArrayIIteratorOf<IArrayOf<IStatisticCollection>, IStatisticCollection, IStatisticCollectionIterator>
  1219. {
  1220. IArrayOf<IStatisticCollection> elems;
  1221. public:
  1222. SortedCollectionIterator(IStatisticCollectionIterator &iter) : ArrayIIteratorOf<IArrayOf<IStatisticCollection>, IStatisticCollection, IStatisticCollectionIterator>(elems)
  1223. {
  1224. ForEach(iter)
  1225. elems.append(iter.get());
  1226. elems.sort(compareCollection);
  1227. }
  1228. };
  1229. class CStatisticCollection : public CInterfaceOf<IStatisticCollection>
  1230. {
  1231. friend class CollectionHashTable;
  1232. public:
  1233. CStatisticCollection(CStatisticCollection * _parent, const StatsScopeId & _id) : id(_id), parent(_parent)
  1234. {
  1235. }
  1236. CStatisticCollection(CStatisticCollection * _parent, MemoryBuffer & in, unsigned version) : parent(_parent)
  1237. {
  1238. id.deserialize(in, version);
  1239. unsigned numStats;
  1240. in.read(numStats);
  1241. stats.ensure(numStats);
  1242. while (numStats-- > 0)
  1243. {
  1244. Statistic next (in, version);
  1245. stats.append(next);
  1246. }
  1247. unsigned numChildren;
  1248. in.read(numChildren);
  1249. children.ensure(numChildren);
  1250. while (numChildren-- > 0)
  1251. {
  1252. CStatisticCollection * next = deserializeCollection(this, in, version);
  1253. children.add(*next);
  1254. }
  1255. }
  1256. virtual byte getCollectionType() const { return SCintermediate; }
  1257. StringBuffer &toXML(StringBuffer &out) const;
  1258. //interface IStatisticCollection:
  1259. virtual StatisticScopeType queryScopeType() const override
  1260. {
  1261. return id.queryScopeType();
  1262. }
  1263. virtual unsigned __int64 queryWhenCreated() const override
  1264. {
  1265. if (parent)
  1266. return parent->queryWhenCreated();
  1267. return 0;
  1268. }
  1269. virtual StringBuffer & getScope(StringBuffer & str) const override
  1270. {
  1271. return id.getScopeText(str);
  1272. }
  1273. virtual StringBuffer & getFullScope(StringBuffer & str) const override
  1274. {
  1275. if (parent)
  1276. {
  1277. parent->getFullScope(str);
  1278. str.append(':');
  1279. }
  1280. id.getScopeText(str);
  1281. return str;
  1282. }
  1283. virtual unsigned __int64 queryStatistic(StatisticKind kind) const override
  1284. {
  1285. ForEachItemIn(i, stats)
  1286. {
  1287. const Statistic & cur = stats.item(i);
  1288. if (cur.kind == kind)
  1289. return cur.value;
  1290. }
  1291. return 0;
  1292. }
  1293. virtual bool getStatistic(StatisticKind kind, unsigned __int64 & value) const override
  1294. {
  1295. ForEachItemIn(i, stats)
  1296. {
  1297. const Statistic & cur = stats.item(i);
  1298. if (cur.kind == kind)
  1299. {
  1300. value = cur.value;
  1301. return true;
  1302. }
  1303. }
  1304. return false;
  1305. }
  1306. virtual unsigned getNumStatistics() const override
  1307. {
  1308. return stats.ordinality();
  1309. }
  1310. virtual void getStatistic(StatisticKind & kind, unsigned __int64 & value, unsigned idx) const override
  1311. {
  1312. const Statistic & cur = stats.item(idx);
  1313. kind = cur.kind;
  1314. value = cur.value;
  1315. }
  1316. virtual IStatisticCollectionIterator & getScopes(const char * filter, bool sorted) override
  1317. {
  1318. assertex(!filter);
  1319. Owned<IStatisticCollectionIterator> hashIter = new SuperHashIIteratorOf<IStatisticCollection, IStatisticCollectionIterator, false>(children);
  1320. if (!sorted)
  1321. return *hashIter.getClear();
  1322. return * new SortedCollectionIterator(*hashIter);
  1323. }
  1324. virtual void getMinMaxScope(IStringVal & minValue, IStringVal & maxValue, StatisticScopeType searchScopeType) const override
  1325. {
  1326. if (id.queryScopeType() == searchScopeType)
  1327. {
  1328. const char * curMin = minValue.str();
  1329. const char * curMax = maxValue.str();
  1330. StringBuffer name;
  1331. id.getScopeText(name);
  1332. if (!curMin || !*curMin || strcmp(name.str(), curMin) < 0)
  1333. minValue.set(name.str());
  1334. if (!curMax || strcmp(name.str(), curMax) > 0)
  1335. maxValue.set(name.str());
  1336. }
  1337. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  1338. for (iter.first(); iter.isValid(); iter.next())
  1339. iter.query().getMinMaxScope(minValue, maxValue, searchScopeType);
  1340. }
  1341. virtual void getMinMaxActivity(unsigned & minValue, unsigned & maxValue) const override
  1342. {
  1343. unsigned activityId = id.queryActivity();
  1344. if (activityId)
  1345. {
  1346. if ((minValue == 0) || (activityId < minValue))
  1347. minValue = activityId;
  1348. if (activityId > maxValue)
  1349. maxValue = activityId;
  1350. }
  1351. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  1352. for (iter.first(); iter.isValid(); iter.next())
  1353. iter.query().getMinMaxActivity(minValue, maxValue);
  1354. }
  1355. //other public interface functions
  1356. void addStatistic(StatisticKind kind, unsigned __int64 value)
  1357. {
  1358. Statistic s(kind, value);
  1359. stats.append(s);
  1360. }
  1361. void updateStatistic(StatisticKind kind, unsigned __int64 value, StatsMergeAction mergeAction)
  1362. {
  1363. if (mergeAction != StatsMergeAppend)
  1364. {
  1365. ForEachItemIn(i, stats)
  1366. {
  1367. Statistic & cur = stats.element(i);
  1368. if (cur.kind == kind)
  1369. {
  1370. cur.value = mergeStatisticValue(cur.value, value, mergeAction);
  1371. return;
  1372. }
  1373. }
  1374. }
  1375. Statistic s(kind, value);
  1376. stats.append(s);
  1377. }
  1378. CStatisticCollection * ensureSubScope(const StatsScopeId & search, bool hasChildren)
  1379. {
  1380. //MORE: Implement hasChildren
  1381. return resolveSubScope(search, true, false);
  1382. }
  1383. CStatisticCollection * resolveSubScope(const StatsScopeId & search, bool create, bool replace)
  1384. {
  1385. if (!replace)
  1386. {
  1387. CStatisticCollection * match = children.find(&search);
  1388. if (match)
  1389. return LINK(match);
  1390. }
  1391. if (create)
  1392. {
  1393. CStatisticCollection * ret = new CStatisticCollection(this, search);
  1394. children.add(*ret);
  1395. return LINK(ret);
  1396. }
  1397. return NULL;
  1398. }
  1399. virtual void serialize(MemoryBuffer & out) const
  1400. {
  1401. out.append(getCollectionType());
  1402. id.serialize(out);
  1403. out.append(stats.ordinality());
  1404. ForEachItemIn(iStat, stats)
  1405. stats.item(iStat).serialize(out);
  1406. out.append(children.ordinality());
  1407. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  1408. for (iter.first(); iter.isValid(); iter.next())
  1409. iter.query().serialize(out);
  1410. }
  1411. inline const StatsScopeId & queryScopeId() const { return id; }
  1412. private:
  1413. StatsScopeId id;
  1414. CStatisticCollection * parent;
  1415. CollectionHashTable children;
  1416. StatsArray stats;
  1417. };
  1418. StringBuffer &CStatisticCollection::toXML(StringBuffer &out) const
  1419. {
  1420. out.append("<Scope id=\"");
  1421. id.getScopeText(out).append("\">\n");
  1422. if (stats.ordinality())
  1423. {
  1424. out.append(" <Stats>");
  1425. ForEachItemIn(i, stats)
  1426. stats.item(i).toXML(out);
  1427. out.append(" </Stats>\n");
  1428. }
  1429. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  1430. for (iter.first(); iter.isValid(); iter.next())
  1431. iter.query().toXML(out);
  1432. out.append("</Scope>\n");
  1433. return out;
  1434. }
  1435. static int compareCollection(IInterface * const * pl, IInterface * const *pr)
  1436. {
  1437. CStatisticCollection * l = static_cast<CStatisticCollection *>(static_cast<IStatisticCollection *>(*pl));
  1438. CStatisticCollection * r = static_cast<CStatisticCollection *>(static_cast<IStatisticCollection *>(*pr));
  1439. return l->queryScopeId().compare(r->queryScopeId());
  1440. }
  1441. //---------------------------------------------------------------------------------------------------------------------
  1442. void CollectionHashTable::onAdd(void *et)
  1443. {
  1444. }
  1445. void CollectionHashTable::onRemove(void *et)
  1446. {
  1447. CStatisticCollection * elem = reinterpret_cast<CStatisticCollection *>(et);
  1448. elem->Release();
  1449. }
  1450. unsigned CollectionHashTable::getHashFromElement(const void *et) const
  1451. {
  1452. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1453. return elem->id.getHash();
  1454. }
  1455. unsigned CollectionHashTable::getHashFromFindParam(const void *fp) const
  1456. {
  1457. const StatsScopeId * search = reinterpret_cast<const StatsScopeId *>(fp);
  1458. return search->getHash();
  1459. }
  1460. const void * CollectionHashTable::getFindParam(const void *et) const
  1461. {
  1462. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1463. return &elem->id;
  1464. }
  1465. bool CollectionHashTable::matchesFindParam(const void *et, const void *key, unsigned fphash) const
  1466. {
  1467. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1468. const StatsScopeId * search = reinterpret_cast<const StatsScopeId *>(key);
  1469. return elem->id.matches(*search);
  1470. }
  1471. bool CollectionHashTable::matchesElement(const void *et, const void *searchET) const
  1472. {
  1473. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1474. const CStatisticCollection * searchElem = reinterpret_cast<const CStatisticCollection *>(searchET);
  1475. return elem->id.matches(searchElem->id);
  1476. }
  1477. //---------------------------------------------------------------------------------------------------------------------
  1478. class CRootStatisticCollection : public CStatisticCollection
  1479. {
  1480. public:
  1481. CRootStatisticCollection(StatisticCreatorType _creatorType, const char * _creator, const StatsScopeId & _id)
  1482. : CStatisticCollection(NULL, _id), creatorType(_creatorType), creator(_creator)
  1483. {
  1484. whenCreated = getTimeStampNowValue();
  1485. }
  1486. CRootStatisticCollection(MemoryBuffer & in, unsigned version) : CStatisticCollection(NULL, in, version)
  1487. {
  1488. byte creatorTypeByte;
  1489. in.read(creatorTypeByte);
  1490. creatorType = (StatisticCreatorType)creatorTypeByte;
  1491. in.read(creator);
  1492. in.read(whenCreated);
  1493. }
  1494. virtual byte getCollectionType() const { return SCroot; }
  1495. virtual unsigned __int64 queryWhenCreated() const
  1496. {
  1497. return whenCreated;
  1498. }
  1499. virtual void serialize(MemoryBuffer & out) const
  1500. {
  1501. CStatisticCollection::serialize(out);
  1502. out.append((byte)creatorType);
  1503. out.append(creator);
  1504. out.append(whenCreated);
  1505. }
  1506. public:
  1507. StatisticCreatorType creatorType;
  1508. StringAttr creator;
  1509. unsigned __int64 whenCreated;
  1510. };
  1511. //---------------------------------------------------------------------------------------------------------------------
  1512. void serializeStatisticCollection(MemoryBuffer & out, IStatisticCollection * collection)
  1513. {
  1514. out.append(currentStatisticsVersion);
  1515. collection->serialize(out);
  1516. }
  1517. static CStatisticCollection * deserializeCollection(CStatisticCollection * parent, MemoryBuffer & in, unsigned version)
  1518. {
  1519. byte kind;
  1520. in.read(kind);
  1521. switch (kind)
  1522. {
  1523. case SCroot:
  1524. assertex(!parent);
  1525. return new CRootStatisticCollection(in, version);
  1526. case SCintermediate:
  1527. return new CStatisticCollection(parent, in, version);
  1528. default:
  1529. UNIMPLEMENTED;
  1530. }
  1531. }
  1532. IStatisticCollection * createStatisticCollection(MemoryBuffer & in)
  1533. {
  1534. unsigned version;
  1535. in.read(version);
  1536. return deserializeCollection(NULL, in, version);
  1537. }
  1538. //--------------------------------------------------------------------------------------------------------------------
  1539. class StatisticGatherer : implements CInterfaceOf<IStatisticGatherer>
  1540. {
  1541. public:
  1542. StatisticGatherer(CStatisticCollection * scope) : rootScope(scope)
  1543. {
  1544. scopes.append(*scope);
  1545. }
  1546. virtual void beginScope(const StatsScopeId & id)
  1547. {
  1548. CStatisticCollection & tos = scopes.tos();
  1549. scopes.append(*tos.ensureSubScope(id, true));
  1550. }
  1551. virtual void beginActivityScope(unsigned id)
  1552. {
  1553. StatsScopeId scopeId(SSTactivity, id);
  1554. CStatisticCollection & tos = scopes.tos();
  1555. scopes.append(*tos.ensureSubScope(scopeId, false));
  1556. }
  1557. virtual void beginSubGraphScope(unsigned id)
  1558. {
  1559. StatsScopeId scopeId(SSTsubgraph, id);
  1560. CStatisticCollection & tos = scopes.tos();
  1561. scopes.append(*tos.ensureSubScope(scopeId, true));
  1562. }
  1563. virtual void beginEdgeScope(unsigned id, unsigned oid)
  1564. {
  1565. StatsScopeId scopeId(SSTedge, id, oid);
  1566. CStatisticCollection & tos = scopes.tos();
  1567. scopes.append(*tos.ensureSubScope(scopeId, false));
  1568. }
  1569. virtual void endScope()
  1570. {
  1571. scopes.tos().Release();
  1572. scopes.pop();
  1573. }
  1574. virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
  1575. {
  1576. CStatisticCollection & tos = scopes.tos();
  1577. tos.addStatistic(kind, value);
  1578. }
  1579. virtual void updateStatistic(StatisticKind kind, unsigned __int64 value, StatsMergeAction mergeAction)
  1580. {
  1581. CStatisticCollection & tos = scopes.tos();
  1582. tos.updateStatistic(kind, value, mergeAction);
  1583. }
  1584. virtual IStatisticCollection * getResult()
  1585. {
  1586. return LINK(rootScope);
  1587. }
  1588. protected:
  1589. ICopyArrayOf<CStatisticCollection> scopes;
  1590. Linked<CStatisticCollection> rootScope;
  1591. };
  1592. extern IStatisticGatherer * createStatisticsGatherer(StatisticCreatorType creatorType, const char * creator, const StatsScopeId & rootScope)
  1593. {
  1594. //creator unused at the moment.
  1595. Owned<CStatisticCollection> rootCollection = new CRootStatisticCollection(creatorType, creator, rootScope);
  1596. return new StatisticGatherer(rootCollection);
  1597. }
  1598. //--------------------------------------------------------------------------------------------------------------------
  1599. extern IPropertyTree * selectTreeStat(IPropertyTree *node, const char *statName, const char *statType)
  1600. {
  1601. StringBuffer xpath;
  1602. xpath.appendf("att[@name='%s']", statName);
  1603. IPropertyTree *att = node->queryPropTree(xpath.str());
  1604. if (!att)
  1605. {
  1606. att = node->addPropTree("att", createPTree());
  1607. att->setProp("@name", statName);
  1608. att->setProp("@type", statType);
  1609. }
  1610. return att;
  1611. }
  1612. extern void putStatsTreeValue(IPropertyTree *node, const char *statName, const char *statType, unsigned __int64 val)
  1613. {
  1614. if (val)
  1615. selectTreeStat(node, statName, statType)->setPropInt64("@value", val);
  1616. }
  1617. class TreeNodeStatisticGatherer : public CInterfaceOf<IStatisticGatherer>
  1618. {
  1619. public:
  1620. TreeNodeStatisticGatherer(IPropertyTree & root) : node(&root) {}
  1621. virtual void beginScope(const StatsScopeId & id)
  1622. {
  1623. StringBuffer temp;
  1624. id.getScopeText(temp);
  1625. beginScope(temp.str());
  1626. }
  1627. virtual void beginSubGraphScope(unsigned id) { throwUnexpected(); }
  1628. virtual void beginActivityScope(unsigned id) { throwUnexpected(); }
  1629. virtual void beginEdgeScope(unsigned id, unsigned oid) { throwUnexpected(); }
  1630. virtual void endScope()
  1631. {
  1632. node = &stack.popGet();
  1633. }
  1634. virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
  1635. {
  1636. putStatsTreeValue(node, queryStatisticName(kind), "sum", value);
  1637. }
  1638. virtual void updateStatistic(StatisticKind kind, unsigned __int64 value, StatsMergeAction mergeAction)
  1639. {
  1640. if (value)
  1641. {
  1642. IPropertyTree * stat = selectTreeStat(node, queryStatisticName(kind), "sum");
  1643. unsigned __int64 newValue = mergeStatisticValue(stat->getPropInt64("@value"), value, mergeAction);
  1644. stat->setPropInt64("@value", newValue);
  1645. }
  1646. }
  1647. virtual IStatisticCollection * getResult() { throwUnexpected(); }
  1648. protected:
  1649. void beginScope(const char * id)
  1650. {
  1651. stack.append(*node);
  1652. StringBuffer xpath;
  1653. xpath.appendf("scope[@name='%s']", id);
  1654. IPropertyTree *att = node->queryPropTree(xpath.str());
  1655. if (!att)
  1656. {
  1657. att = node->addPropTree("scope", createPTree());
  1658. att->setProp("@name", id);
  1659. }
  1660. node = att;
  1661. }
  1662. protected:
  1663. IPropertyTree * node;
  1664. ICopyArrayOf<IPropertyTree> stack;
  1665. };
  1666. //--------------------------------------------------------------------------------------------------------------------
  1667. void CRuntimeStatistic::merge(unsigned __int64 otherValue, StatsMergeAction mergeAction)
  1668. {
  1669. value = mergeStatisticValue(value, otherValue, mergeAction);
  1670. }
  1671. //--------------------------------------------------------------------------------------------------------------------
  1672. CRuntimeStatisticCollection::~CRuntimeStatisticCollection()
  1673. {
  1674. delete [] values;
  1675. delete queryNested();
  1676. }
  1677. CNestedRuntimeStatisticMap * CRuntimeStatisticCollection::queryNested() const
  1678. {
  1679. return nested.load(std::memory_order_relaxed);
  1680. }
  1681. CNestedRuntimeStatisticMap * CRuntimeStatisticCollection::createNested() const
  1682. {
  1683. return new CNestedRuntimeStatisticMap;
  1684. }
  1685. CNestedRuntimeStatisticMap & CRuntimeStatisticCollection::ensureNested()
  1686. {
  1687. return *querySingleton(nested, nestlock, [this]{ return this->createNested(); });
  1688. }
  1689. CriticalSection CRuntimeStatisticCollection::nestlock;
  1690. unsigned __int64 CRuntimeStatisticCollection::getSerialStatisticValue(StatisticKind kind) const
  1691. {
  1692. unsigned __int64 value = getStatisticValue(kind);
  1693. StatisticKind rawKind= queryRawKind(kind);
  1694. if (kind == rawKind)
  1695. return value;
  1696. unsigned __int64 rawValue = getStatisticValue(rawKind);
  1697. return value + convertMeasure(rawKind, kind, rawValue);
  1698. }
  1699. void CRuntimeStatisticCollection::merge(const CRuntimeStatisticCollection & other)
  1700. {
  1701. if (&mapping == &other.mapping)
  1702. {
  1703. ForEachItemIn(i, other)
  1704. {
  1705. unsigned __int64 value = other.values[i].get();
  1706. if (value)
  1707. {
  1708. StatisticKind kind = getKind(i);
  1709. values[i].merge(value, queryMergeMode(kind));
  1710. }
  1711. }
  1712. }
  1713. else
  1714. {
  1715. ForEachItemIn(i, other)
  1716. {
  1717. StatisticKind kind = other.getKind(i);
  1718. unsigned __int64 value = other.getStatisticValue(kind);
  1719. if (value)
  1720. mergeStatistic(kind, value);
  1721. }
  1722. }
  1723. CNestedRuntimeStatisticMap *otherNested = other.queryNested();
  1724. if (otherNested)
  1725. {
  1726. ensureNested().merge(*otherNested);
  1727. }
  1728. }
  1729. void CRuntimeStatisticCollection::updateDelta(CRuntimeStatisticCollection & target, const CRuntimeStatisticCollection & source)
  1730. {
  1731. ForEachItemIn(i, source)
  1732. {
  1733. StatisticKind kind = source.getKind(i);
  1734. unsigned __int64 sourceValue = source.getStatisticValue(kind);
  1735. if (queryMergeMode(kind) == StatsMergeSum)
  1736. {
  1737. unsigned __int64 prevValue = getStatisticValue(kind);
  1738. if (sourceValue != prevValue)
  1739. {
  1740. target.mergeStatistic(kind, sourceValue - prevValue);
  1741. setStatistic(kind, sourceValue);
  1742. }
  1743. }
  1744. else
  1745. {
  1746. if (sourceValue)
  1747. target.mergeStatistic(kind, sourceValue);
  1748. }
  1749. }
  1750. CNestedRuntimeStatisticMap *sourceNested = source.queryNested();
  1751. if (sourceNested)
  1752. {
  1753. ensureNested().updateDelta(target.ensureNested(), *sourceNested);
  1754. }
  1755. }
  1756. void CRuntimeStatisticCollection::mergeStatistic(StatisticKind kind, unsigned __int64 value)
  1757. {
  1758. queryStatistic(kind).merge(value, queryMergeMode(kind));
  1759. }
  1760. void CRuntimeStatisticCollection::reset()
  1761. {
  1762. unsigned num = mapping.numStatistics();
  1763. for (unsigned i = 0; i <= num; i++)
  1764. values[i].clear();
  1765. }
  1766. void CRuntimeStatisticCollection::reset(const StatisticsMapping & toClear)
  1767. {
  1768. unsigned num = toClear.numStatistics();
  1769. for (unsigned i = 0; i < num; i++)
  1770. queryStatistic(toClear.getKind(i)).clear();
  1771. }
  1772. CRuntimeStatisticCollection & CRuntimeStatisticCollection::registerNested(const StatsScopeId & scope, const StatisticsMapping & mapping)
  1773. {
  1774. return ensureNested().addNested(scope, mapping).queryStats();
  1775. }
  1776. void CRuntimeStatisticCollection::rollupStatistics(unsigned numTargets, IContextLogger * const * targets) const
  1777. {
  1778. ForEachItem(iStat)
  1779. {
  1780. unsigned __int64 value = values[iStat].getClear();
  1781. if (value)
  1782. {
  1783. StatisticKind kind = getKind(iStat);
  1784. for (unsigned iTarget = 0; iTarget < numTargets; iTarget++)
  1785. targets[iTarget]->noteStatistic(kind, value);
  1786. }
  1787. }
  1788. reportIgnoredStats();
  1789. }
  1790. void CRuntimeStatisticCollection::recordStatistics(IStatisticGatherer & target) const
  1791. {
  1792. ForEachItem(i)
  1793. {
  1794. unsigned __int64 value = values[i].get();
  1795. if (value)
  1796. {
  1797. StatisticKind kind = getKind(i);
  1798. StatisticKind serialKind= querySerializedKind(kind);
  1799. if (kind != serialKind)
  1800. value = convertMeasure(kind, serialKind, value);
  1801. StatsMergeAction mergeAction = queryMergeMode(serialKind);
  1802. target.updateStatistic(serialKind, value, mergeAction);
  1803. }
  1804. }
  1805. reportIgnoredStats();
  1806. CNestedRuntimeStatisticMap *qn = queryNested();
  1807. if (qn)
  1808. qn->recordStatistics(target);
  1809. }
  1810. void CRuntimeStatisticCollection::reportIgnoredStats() const
  1811. {
  1812. if (values[mapping.numStatistics()].getClear())
  1813. DBGLOG("Some statistics were added but thrown away");
  1814. }
  1815. StringBuffer & CRuntimeStatisticCollection::toXML(StringBuffer &str) const
  1816. {
  1817. ForEachItem(iStat)
  1818. {
  1819. unsigned __int64 value = values[iStat].get();
  1820. if (value)
  1821. {
  1822. StatisticKind kind = getKind(iStat);
  1823. const char * name = queryStatisticName(kind);
  1824. str.appendf("<%s>%" I64F "d</%s>", name, value, name);
  1825. }
  1826. }
  1827. CNestedRuntimeStatisticMap *qn = queryNested();
  1828. if (qn)
  1829. qn->toXML(str);
  1830. return str;
  1831. }
  1832. StringBuffer & CRuntimeStatisticCollection::toStr(StringBuffer &str) const
  1833. {
  1834. ForEachItem(iStat)
  1835. {
  1836. unsigned __int64 value = values[iStat].get();
  1837. if (value)
  1838. {
  1839. StatisticKind kind = getKind(iStat);
  1840. const char * name = queryStatisticName(kind);
  1841. str.append(' ').append(name).append("=");
  1842. formatStatistic(str, value, kind);
  1843. }
  1844. }
  1845. CNestedRuntimeStatisticMap *qn = queryNested();
  1846. if (qn)
  1847. qn->toStr(str);
  1848. return str;
  1849. }
  1850. void CRuntimeStatisticCollection::deserialize(MemoryBuffer& in)
  1851. {
  1852. unsigned numValid;
  1853. in.readPacked(numValid);
  1854. for (unsigned i=0; i < numValid; i++)
  1855. {
  1856. unsigned kindVal;
  1857. unsigned __int64 value;
  1858. in.readPacked(kindVal).readPacked(value);
  1859. StatisticKind kind = (StatisticKind)kindVal;
  1860. setStatistic(kind, value);
  1861. }
  1862. bool hasNested;
  1863. in.read(hasNested);
  1864. if (hasNested)
  1865. {
  1866. ensureNested().deserializeMerge(in);
  1867. }
  1868. }
  1869. void CRuntimeStatisticCollection::deserializeMerge(MemoryBuffer& in)
  1870. {
  1871. unsigned numValid;
  1872. in.readPacked(numValid);
  1873. for (unsigned i=0; i < numValid; i++)
  1874. {
  1875. unsigned kindVal;
  1876. unsigned __int64 value;
  1877. in.readPacked(kindVal).readPacked(value);
  1878. StatisticKind kind = (StatisticKind)kindVal;
  1879. StatsMergeAction mergeAction = queryMergeMode(kind);
  1880. mergeStatistic(kind, value, mergeAction);
  1881. }
  1882. bool hasNested;
  1883. in.read(hasNested);
  1884. if (hasNested)
  1885. {
  1886. ensureNested().deserializeMerge(in);
  1887. }
  1888. }
  1889. void CRuntimeStatisticCollection::getNodeProgressInfo(IPropertyTree &node) const
  1890. {
  1891. TreeNodeStatisticGatherer gatherer(node);
  1892. recordStatistics(gatherer);
  1893. }
  1894. bool CRuntimeStatisticCollection::serialize(MemoryBuffer& out) const
  1895. {
  1896. unsigned numValid = 0;
  1897. ForEachItem(i1)
  1898. {
  1899. if (values[i1].get())
  1900. numValid++;
  1901. }
  1902. out.appendPacked(numValid);
  1903. ForEachItem(i2)
  1904. {
  1905. unsigned __int64 value = values[i2].get();
  1906. if (value)
  1907. {
  1908. StatisticKind kind = mapping.getKind(i2);
  1909. StatisticKind serialKind= querySerializedKind(kind);
  1910. if (kind != serialKind)
  1911. value = convertMeasure(kind, serialKind, value);
  1912. out.appendPacked((unsigned)serialKind);
  1913. out.appendPacked(value);
  1914. }
  1915. }
  1916. bool nonEmpty = (numValid != 0);
  1917. CNestedRuntimeStatisticMap *qn = queryNested();
  1918. out.append(qn != nullptr);
  1919. if (qn)
  1920. {
  1921. if (qn->serialize(out))
  1922. nonEmpty = true;
  1923. }
  1924. return nonEmpty;
  1925. }
  1926. //---------------------------------------------------------------------------------------------------------------------
  1927. void CRuntimeSummaryStatisticCollection::DerivedStats::mergeStatistic(unsigned __int64 value, unsigned node)
  1928. {
  1929. if (count == 0)
  1930. {
  1931. min = value;
  1932. max = value;
  1933. minNode = node;
  1934. maxNode = node;
  1935. }
  1936. else
  1937. {
  1938. if (value < min)
  1939. {
  1940. min = value;
  1941. minNode = node;
  1942. }
  1943. if (value > max)
  1944. {
  1945. max = value;
  1946. maxNode = node;
  1947. }
  1948. }
  1949. count++;
  1950. double dvalue = (double)value;
  1951. sumSquares += dvalue * dvalue;
  1952. }
  1953. CRuntimeSummaryStatisticCollection::CRuntimeSummaryStatisticCollection(const StatisticsMapping & _mapping) : CRuntimeStatisticCollection(_mapping)
  1954. {
  1955. derived = new DerivedStats[ordinality()+1];
  1956. }
  1957. CRuntimeSummaryStatisticCollection::~CRuntimeSummaryStatisticCollection()
  1958. {
  1959. delete[] derived;
  1960. }
  1961. CNestedRuntimeStatisticMap * CRuntimeSummaryStatisticCollection::createNested() const
  1962. {
  1963. return new CNestedSummaryRuntimeStatisticMap;
  1964. }
  1965. void CRuntimeSummaryStatisticCollection::mergeStatistic(StatisticKind kind, unsigned __int64 value)
  1966. {
  1967. CRuntimeStatisticCollection::mergeStatistic(kind, value);
  1968. unsigned index = queryMapping().getIndex(kind);
  1969. derived[index].mergeStatistic(value, 0);
  1970. }
  1971. static bool isSignificantSkew(StatisticKind kind, unsigned __int64 range, unsigned __int64 count)
  1972. {
  1973. //MORE: Could get more sophisticated!
  1974. return range > 1;
  1975. }
  1976. void CRuntimeSummaryStatisticCollection::recordStatistics(IStatisticGatherer & target) const
  1977. {
  1978. CRuntimeStatisticCollection::recordStatistics(target);
  1979. for (unsigned i = 0; i < ordinality(); i++)
  1980. {
  1981. DerivedStats & cur = derived[i];
  1982. if (cur.count)
  1983. {
  1984. StatisticKind kind = getKind(i);
  1985. StatisticKind serialKind= querySerializedKind(kind);
  1986. unsigned __int64 minValue = convertMeasure(kind, serialKind, cur.min);
  1987. unsigned __int64 maxValue = convertMeasure(kind, serialKind, cur.max);
  1988. if (minValue != maxValue)
  1989. {
  1990. double sum = (double)convertMeasure(kind, serialKind, values[i].get());
  1991. //Sum of squares needs to be translated twice
  1992. double sumSquares = convertSquareMeasure(kind, serialKind, cur.sumSquares);
  1993. double mean = (double)(sum / cur.count);
  1994. double variance = (sumSquares - sum * mean) / cur.count;
  1995. double stdDev = sqrt(variance);
  1996. double maxSkew = (10000.0 * ((maxValue-mean)/mean));
  1997. double minSkew = (10000.0 * ((mean-minValue)/mean));
  1998. unsigned __int64 range = maxValue - minValue;
  1999. target.addStatistic((StatisticKind)(serialKind|StMinX), minValue);
  2000. target.addStatistic((StatisticKind)(serialKind|StMaxX), maxValue);
  2001. target.addStatistic((StatisticKind)(serialKind|StAvgX), (unsigned __int64)mean);
  2002. target.addStatistic((StatisticKind)(serialKind|StDeltaX), range);
  2003. target.addStatistic((StatisticKind)(serialKind|StStdDevX), (unsigned __int64)stdDev);
  2004. //If all nodes are the same then we re actually merging results from multiple runs
  2005. //if the range is less than the count then
  2006. if ((cur.minNode != cur.maxNode) && isSignificantSkew(serialKind, range, cur.count))
  2007. {
  2008. target.addStatistic((StatisticKind)(serialKind|StSkewMin), (unsigned __int64)minSkew);
  2009. target.addStatistic((StatisticKind)(serialKind|StSkewMax), (unsigned __int64)maxSkew);
  2010. target.addStatistic((StatisticKind)(serialKind|StNodeMin), cur.minNode);
  2011. target.addStatistic((StatisticKind)(serialKind|StNodeMax), cur.maxNode);
  2012. }
  2013. }
  2014. else if (cur.count != 1)
  2015. target.addStatistic((StatisticKind)(serialKind|StAvgX), minValue);
  2016. }
  2017. }
  2018. }
  2019. bool CRuntimeSummaryStatisticCollection::serialize(MemoryBuffer & out) const
  2020. {
  2021. UNIMPLEMENTED; // NB: Need to convert sum squares twice.
  2022. }
  2023. void CRuntimeSummaryStatisticCollection::deserialize(MemoryBuffer & in)
  2024. {
  2025. UNIMPLEMENTED;
  2026. }
  2027. void CRuntimeSummaryStatisticCollection::deserializeMerge(MemoryBuffer& in)
  2028. {
  2029. UNIMPLEMENTED;
  2030. }
  2031. //---------------------------------------------------
  2032. bool CNestedRuntimeStatisticCollection::matches(const StatsScopeId & otherScope) const
  2033. {
  2034. return scope.matches(otherScope);
  2035. }
  2036. //NOTE: When deserializing, the scope is deserialized by the caller, and the correct target selected
  2037. //which is why there is no corresponding deserialize() ofthe scope at this point
  2038. void CNestedRuntimeStatisticCollection::deserialize(MemoryBuffer & in)
  2039. {
  2040. stats->deserialize(in);
  2041. }
  2042. void CNestedRuntimeStatisticCollection::deserializeMerge(MemoryBuffer& in)
  2043. {
  2044. stats->deserialize(in);
  2045. }
  2046. void CNestedRuntimeStatisticCollection::merge(const CNestedRuntimeStatisticCollection & other)
  2047. {
  2048. stats->merge(other.queryStats());
  2049. }
  2050. bool CNestedRuntimeStatisticCollection::serialize(MemoryBuffer& out) const
  2051. {
  2052. scope.serialize(out);
  2053. return stats->serialize(out);
  2054. }
  2055. void CNestedRuntimeStatisticCollection::recordStatistics(IStatisticGatherer & target) const
  2056. {
  2057. target.beginScope(scope);
  2058. stats->recordStatistics(target);
  2059. target.endScope();
  2060. }
  2061. StringBuffer & CNestedRuntimeStatisticCollection::toStr(StringBuffer &str) const
  2062. {
  2063. str.append(' ');
  2064. scope.getScopeText(str).append("={");
  2065. stats->toStr(str);
  2066. return str.append(" }");
  2067. }
  2068. StringBuffer & CNestedRuntimeStatisticCollection::toXML(StringBuffer &str) const
  2069. {
  2070. str.append("<Scope id=\"");
  2071. scope.getScopeText(str).append("\">");
  2072. stats->toXML(str);
  2073. return str.append("</Scope>");
  2074. }
  2075. void CNestedRuntimeStatisticCollection::updateDelta(CNestedRuntimeStatisticCollection & target, const CNestedRuntimeStatisticCollection & source)
  2076. {
  2077. stats->updateDelta(*target.stats, *source.stats);
  2078. }
  2079. //---------------------------------------------------
  2080. CNestedRuntimeStatisticCollection & CNestedRuntimeStatisticMap::addNested(const StatsScopeId & scope, const StatisticsMapping & mapping)
  2081. {
  2082. unsigned mapSize;
  2083. unsigned entry;
  2084. {
  2085. ReadLockBlock b(lock);
  2086. mapSize = map.length();
  2087. for (entry = 0; entry < mapSize; entry++)
  2088. {
  2089. CNestedRuntimeStatisticCollection & cur = map.item(entry);
  2090. if (cur.matches(scope))
  2091. return cur;
  2092. }
  2093. }
  2094. {
  2095. WriteLockBlock b(lock);
  2096. // Check no-one added anything between the read and write locks
  2097. mapSize = map.length();
  2098. for (; entry < mapSize; entry++)
  2099. {
  2100. CNestedRuntimeStatisticCollection & cur = map.item(entry);
  2101. if (cur.matches(scope))
  2102. return cur;
  2103. }
  2104. CNestedRuntimeStatisticCollection * stats = new CNestedRuntimeStatisticCollection(scope, createStats(mapping));
  2105. map.append(*stats);
  2106. return *stats;
  2107. }
  2108. }
  2109. void CNestedRuntimeStatisticMap::deserialize(MemoryBuffer& in)
  2110. {
  2111. unsigned numItems;
  2112. in.readPacked(numItems);
  2113. for (unsigned i=0; i < numItems; i++)
  2114. {
  2115. StatsScopeId scope;
  2116. scope.deserialize(in, currentStatisticsVersion);
  2117. //Use allStatistics as the default mapping if it hasn't already been added.
  2118. CNestedRuntimeStatisticCollection & child = addNested(scope, allStatistics);
  2119. child.deserialize(in);
  2120. }
  2121. }
  2122. void CNestedRuntimeStatisticMap::deserializeMerge(MemoryBuffer& in)
  2123. {
  2124. unsigned numItems;
  2125. in.readPacked(numItems);
  2126. for (unsigned i=0; i < numItems; i++)
  2127. {
  2128. StatsScopeId scope;
  2129. scope.deserialize(in, currentStatisticsVersion);
  2130. //Use allStatistics as the default mapping if it hasn't already been added.
  2131. CNestedRuntimeStatisticCollection & child = addNested(scope, allStatistics);
  2132. child.deserializeMerge(in);
  2133. }
  2134. }
  2135. void CNestedRuntimeStatisticMap::merge(const CNestedRuntimeStatisticMap & other)
  2136. {
  2137. ReadLockBlock b(other.lock);
  2138. ForEachItemIn(i, other.map)
  2139. {
  2140. CNestedRuntimeStatisticCollection & cur = other.map.item(i);
  2141. CNestedRuntimeStatisticCollection & target = addNested(cur.scope, cur.queryMapping());
  2142. target.merge(cur);
  2143. }
  2144. }
  2145. void CNestedRuntimeStatisticMap::updateDelta(CNestedRuntimeStatisticMap & target, const CNestedRuntimeStatisticMap & source)
  2146. {
  2147. ReadLockBlock b(source.lock);
  2148. ForEachItemIn(i, source.map)
  2149. {
  2150. CNestedRuntimeStatisticCollection & curSource = source.map.item(i);
  2151. CNestedRuntimeStatisticCollection & curTarget = target.addNested(curSource.scope, curSource.queryMapping());
  2152. CNestedRuntimeStatisticCollection & curDelta = addNested(curSource.scope, curSource.queryMapping());
  2153. curDelta.updateDelta(curTarget, curSource);
  2154. }
  2155. }
  2156. bool CNestedRuntimeStatisticMap::serialize(MemoryBuffer& out) const
  2157. {
  2158. ReadLockBlock b(lock);
  2159. out.appendPacked(map.ordinality());
  2160. bool nonEmpty = false;
  2161. ForEachItemIn(i, map)
  2162. {
  2163. if (map.item(i).serialize(out))
  2164. nonEmpty = true;
  2165. }
  2166. return nonEmpty;
  2167. }
  2168. void CNestedRuntimeStatisticMap::recordStatistics(IStatisticGatherer & target) const
  2169. {
  2170. ReadLockBlock b(lock);
  2171. ForEachItemIn(i, map)
  2172. map.item(i).recordStatistics(target);
  2173. }
  2174. StringBuffer & CNestedRuntimeStatisticMap::toStr(StringBuffer &str) const
  2175. {
  2176. ReadLockBlock b(lock);
  2177. ForEachItemIn(i, map)
  2178. map.item(i).toStr(str);
  2179. return str;
  2180. }
  2181. StringBuffer & CNestedRuntimeStatisticMap::toXML(StringBuffer &str) const
  2182. {
  2183. ReadLockBlock b(lock);
  2184. ForEachItemIn(i, map)
  2185. map.item(i).toXML(str);
  2186. return str;
  2187. }
  2188. CRuntimeStatisticCollection * CNestedRuntimeStatisticMap::createStats(const StatisticsMapping & mapping)
  2189. {
  2190. return new CRuntimeStatisticCollection(mapping);
  2191. }
  2192. CRuntimeStatisticCollection * CNestedSummaryRuntimeStatisticMap::createStats(const StatisticsMapping & mapping)
  2193. {
  2194. return new CRuntimeSummaryStatisticCollection(mapping);
  2195. }
  2196. //---------------------------------------------------
  2197. bool ScopedItemFilter::matchDepth(unsigned low, unsigned high) const
  2198. {
  2199. if (maxDepth && low && maxDepth < low)
  2200. return false;
  2201. if (minDepth && high && minDepth > high)
  2202. return false;
  2203. return true;
  2204. }
  2205. bool ScopedItemFilter::match(const char * search) const
  2206. {
  2207. if (search)
  2208. {
  2209. if (value)
  2210. {
  2211. if (hasWildcard)
  2212. {
  2213. //MORE: If wildcarding ends up being used a lot then this should be replaced with something that creates a DFA
  2214. if (!WildMatch(search, value, false))
  2215. return false;
  2216. }
  2217. else
  2218. {
  2219. return streq(search, value);
  2220. }
  2221. }
  2222. if (minDepth || maxDepth)
  2223. {
  2224. unsigned searchDepth = queryStatisticsDepth(search);
  2225. if (searchDepth < minDepth)
  2226. return false;
  2227. if (maxDepth && searchDepth > maxDepth)
  2228. return false;
  2229. }
  2230. }
  2231. return true;
  2232. }
  2233. bool ScopedItemFilter::recurseChildScopes(const char * curScope) const
  2234. {
  2235. if (maxDepth == 0 || !curScope)
  2236. return true;
  2237. if (queryStatisticsDepth(curScope) >= maxDepth)
  2238. return false;
  2239. return true;
  2240. }
  2241. void ScopedItemFilter::set(const char * _value)
  2242. {
  2243. if (_value && !streq(_value, "*") )
  2244. {
  2245. value.set(_value);
  2246. minDepth = queryStatisticsDepth(_value);
  2247. if (!strchr(_value, '*'))
  2248. {
  2249. maxDepth = minDepth;
  2250. hasWildcard = strchr(_value, '?') != NULL;
  2251. }
  2252. else
  2253. hasWildcard = true;
  2254. }
  2255. else
  2256. value.clear();
  2257. }
  2258. void ScopedItemFilter::setDepth(unsigned _depth)
  2259. {
  2260. minDepth = _depth;
  2261. maxDepth = _depth;
  2262. }
  2263. void ScopedItemFilter::setDepth(unsigned _minDepth, unsigned _maxDepth)
  2264. {
  2265. minDepth = _minDepth;
  2266. maxDepth = _maxDepth;
  2267. }
  2268. StatisticsFilter::StatisticsFilter()
  2269. {
  2270. init();
  2271. }
  2272. StatisticsFilter::StatisticsFilter(const char * filter)
  2273. {
  2274. init();
  2275. setFilter(filter);
  2276. }
  2277. StatisticsFilter::StatisticsFilter(StatisticCreatorType _creatorType, StatisticScopeType _scopeType, StatisticMeasure _measure, StatisticKind _kind)
  2278. {
  2279. init();
  2280. creatorType = _creatorType;
  2281. scopeType = _scopeType;
  2282. measure = _measure;
  2283. kind = _kind;
  2284. }
  2285. StatisticsFilter::StatisticsFilter(const char * _creatorType, const char * _scopeType, const char * _kind)
  2286. {
  2287. init();
  2288. set(_creatorType, _scopeType, _kind);
  2289. }
  2290. StatisticsFilter::StatisticsFilter(const char * _creatorTypeText, const char * _creator, const char * _scopeTypeText, const char * _scope, const char * _measureText, const char * _kindText)
  2291. {
  2292. init();
  2293. set(_creatorTypeText, _creator, _scopeTypeText, _scope, _measureText, _kindText);
  2294. }
  2295. StatisticsFilter::StatisticsFilter(StatisticCreatorType _creatorType, const char * _creator, StatisticScopeType _scopeType, const char * _scope, StatisticMeasure _measure, StatisticKind _kind)
  2296. {
  2297. init();
  2298. creatorType = _creatorType;
  2299. setCreator(_creator);
  2300. scopeType = _scopeType;
  2301. setScope(_scope);
  2302. measure = _measure;
  2303. kind = _kind;
  2304. }
  2305. void StatisticsFilter::init()
  2306. {
  2307. creatorType = SCTall;
  2308. scopeType = SSTall;
  2309. measure = SMeasureAll;
  2310. kind = StKindAll;
  2311. }
  2312. bool StatisticsFilter::matches(StatisticCreatorType curCreatorType, const char * curCreator, StatisticScopeType curScopeType, const char * curScope, StatisticMeasure curMeasure, StatisticKind curKind, unsigned __int64 value) const
  2313. {
  2314. if ((curCreatorType != SCTall) && (creatorType != SCTall) && (creatorType != curCreatorType))
  2315. return false;
  2316. if ((curScopeType != SSTall) && (scopeType != SSTall) && (scopeType != curScopeType))
  2317. return false;
  2318. if ((curMeasure != SMeasureAll) && (measure != SMeasureAll) && (measure != curMeasure))
  2319. return false;
  2320. if ((curKind!= StKindAll) && (kind != StKindAll) && (kind != curKind))
  2321. return false;
  2322. if (!creatorFilter.match(curCreator))
  2323. return false;
  2324. if (!scopeFilter.match(curScope))
  2325. return false;
  2326. if (value != MaxStatisticValue)
  2327. {
  2328. if ((value < minValue) || (value > maxValue))
  2329. return false;
  2330. }
  2331. return true;
  2332. }
  2333. bool StatisticsFilter::recurseChildScopes(StatisticScopeType curScopeType, const char * curScope) const
  2334. {
  2335. switch (curScopeType)
  2336. {
  2337. case SSTgraph:
  2338. // A child of a graph will have depth 2 or more
  2339. if (!scopeFilter.matchDepth(2, (unsigned)-1))
  2340. return false;
  2341. break;
  2342. case SSTsubgraph:
  2343. // A child of a subgraph will have depth 3 or more
  2344. if (!scopeFilter.matchDepth(3, (unsigned)-1))
  2345. return false;
  2346. break;
  2347. }
  2348. if (!curScope)
  2349. return true;
  2350. return scopeFilter.recurseChildScopes(curScope);
  2351. }
  2352. void StatisticsFilter::set(const char * creatorTypeText, const char * scopeTypeText, const char * kindText)
  2353. {
  2354. StatisticCreatorType creatorType = queryCreatorType(creatorTypeText);
  2355. StatisticScopeType scopeType = queryScopeType(scopeTypeText);
  2356. if (creatorType != SCTnone)
  2357. setCreatorType(creatorType);
  2358. if (scopeType != SSTnone)
  2359. setScopeType(scopeType);
  2360. setKind(kindText);
  2361. }
  2362. void StatisticsFilter::set(const char * _creatorTypeText, const char * _creator, const char * _scopeTypeText, const char * _scope, const char * _measureText, const char * _kindText)
  2363. {
  2364. StatisticMeasure newMeasure = queryMeasure(_measureText);
  2365. if (newMeasure != SMeasureNone)
  2366. setMeasure(newMeasure);
  2367. set(_creatorTypeText, _scopeTypeText, _kindText);
  2368. setCreator(_creator);
  2369. setScope(_scope);
  2370. }
  2371. void StatisticsFilter::setCreatorDepth(unsigned _minCreatorDepth, unsigned _maxCreatorDepth)
  2372. {
  2373. creatorFilter.setDepth(_minCreatorDepth, _maxCreatorDepth);
  2374. }
  2375. void StatisticsFilter::setCreator(const char * _creator)
  2376. {
  2377. creatorFilter.set(_creator);
  2378. }
  2379. void StatisticsFilter::setCreatorType(StatisticCreatorType _creatorType)
  2380. {
  2381. creatorType = _creatorType;
  2382. }
  2383. void StatisticsFilter::addFilter(const char * filter)
  2384. {
  2385. //Match a filter of the form category[value] (use square brackets to avoid bash grief)
  2386. const char * openBra = strchr(filter, '[');
  2387. if (!openBra)
  2388. return;
  2389. const char * closeBra = strchr(openBra, ']');
  2390. if (!closeBra)
  2391. return;
  2392. const char * start = openBra + 1;
  2393. StringBuffer value(closeBra - start, start);
  2394. if (hasPrefix(filter, "creator[", false))
  2395. setCreator(value);
  2396. else if (hasPrefix(filter, "creatortype[", false))
  2397. setCreatorType(queryCreatorType(value));
  2398. else if (hasPrefix(filter, "depth[", false))
  2399. {
  2400. const char * comma = strchr(value, ',');
  2401. if (comma)
  2402. setScopeDepth(atoi(value), atoi(comma+1));
  2403. else
  2404. setScopeDepth(atoi(value));
  2405. }
  2406. else if (hasPrefix(filter, "kind[", false))
  2407. setKind(value);
  2408. else if (hasPrefix(filter, "measure[", false))
  2409. setMeasure(queryMeasure(value));
  2410. else if (hasPrefix(filter, "scope[", false))
  2411. setScope(value);
  2412. else if (hasPrefix(filter, "scopetype[", false))
  2413. setScopeType(queryScopeType(value));
  2414. else if (hasPrefix(filter, "value[", false))
  2415. {
  2416. //value[exact|low..high] where low and high are optional
  2417. unsigned __int64 lowValue = 0;
  2418. unsigned __int64 highValue = MaxStatisticValue;
  2419. if (isdigit(*value))
  2420. lowValue = (unsigned __int64)atoi64(value);
  2421. const char * dotdot = strstr(value, "..");
  2422. if (dotdot)
  2423. {
  2424. unsigned __int64 maxValue = (unsigned __int64)atoi64(dotdot + 2);
  2425. if (maxValue != 0)
  2426. highValue = maxValue;
  2427. }
  2428. else
  2429. highValue = lowValue;
  2430. setValueRange(lowValue, highValue);
  2431. }
  2432. else
  2433. throw MakeStringException(1, "Unknown stats filter '%s' - expected creator,creatortype,depth,kind,measure,scope,scopetype", filter);
  2434. }
  2435. void StatisticsFilter::setFilter(const char * filter)
  2436. {
  2437. if (isEmptyString(filter))
  2438. return;
  2439. for (;;)
  2440. {
  2441. const char * closeBra = strchr(filter, ']');
  2442. if (!closeBra)
  2443. throw MakeStringException(1, "Missing close bracket ']' in '%s' ", filter);
  2444. const char * comma = strchr(closeBra, ',');
  2445. if (comma)
  2446. {
  2447. //Take a copy - simplicity rather than efficiency
  2448. StringBuffer temp(comma - filter, filter);
  2449. addFilter(temp);
  2450. filter = comma + 1;
  2451. }
  2452. else
  2453. {
  2454. addFilter(filter);
  2455. return;
  2456. }
  2457. }
  2458. }
  2459. void StatisticsFilter::setScopeDepth(unsigned _scopeDepth)
  2460. {
  2461. scopeFilter.setDepth(_scopeDepth);
  2462. }
  2463. void StatisticsFilter::setScopeDepth(unsigned _minScopeDepth, unsigned _maxScopeDepth)
  2464. {
  2465. scopeFilter.setDepth(_minScopeDepth, _maxScopeDepth);
  2466. }
  2467. void StatisticsFilter::setScope(const char * _scope)
  2468. {
  2469. scopeFilter.set(_scope);
  2470. }
  2471. void StatisticsFilter::setScopeType(StatisticScopeType _scopeType)
  2472. {
  2473. scopeType = _scopeType;
  2474. switch (scopeType)
  2475. {
  2476. case SSTglobal:
  2477. case SSTgraph:
  2478. scopeFilter.setDepth(1);
  2479. break;
  2480. case SSTsubgraph:
  2481. scopeFilter.setDepth(2);
  2482. break;
  2483. case SSTactivity:
  2484. scopeFilter.setDepth(3);
  2485. break;
  2486. }
  2487. }
  2488. void StatisticsFilter::setMeasure(StatisticMeasure _measure)
  2489. {
  2490. measure = _measure;
  2491. }
  2492. void StatisticsFilter::setValueRange(unsigned __int64 _minValue, unsigned __int64 _maxValue)
  2493. {
  2494. minValue = _minValue;
  2495. maxValue = _maxValue;
  2496. }
  2497. void StatisticsFilter::setKind(StatisticKind _kind)
  2498. {
  2499. kind = _kind;
  2500. if (measure == SMeasureAll)
  2501. measure = queryMeasure(kind);
  2502. }
  2503. void StatisticsFilter::setKind(const char * _kind)
  2504. {
  2505. if (!_kind || !*_kind || streq(_kind, "*"))
  2506. {
  2507. if (measure == SMeasureNone)
  2508. measure = SMeasureAll;
  2509. kind = StKindAll;
  2510. return;
  2511. }
  2512. //Convert a kind wildcard to a measure
  2513. for (unsigned i1=SMeasureAll+1; i1 < SMeasureMax; i1++)
  2514. {
  2515. const char * prefix = queryMeasurePrefix((StatisticMeasure)i1);
  2516. size_t len = strlen(prefix);
  2517. if (len && strnicmp(_kind, prefix, len) == 0)
  2518. {
  2519. setMeasure((StatisticMeasure)i1);
  2520. //Treat When* and When as filters on times.
  2521. if (streq(_kind + len, "*") || !_kind[len])
  2522. return;
  2523. }
  2524. }
  2525. //Other wildcards not currently supported
  2526. kind = queryStatisticKind(_kind);
  2527. }
  2528. //---------------------------------------------------
  2529. class CStatsCategory : public CInterface
  2530. {
  2531. public:
  2532. StringAttr longName;
  2533. StringAttr shortName;
  2534. CStatsCategory(const char *_longName, const char *_shortName)
  2535. : longName(_longName), shortName(_shortName)
  2536. {
  2537. }
  2538. bool match(const char *_longName, const char *_shortName)
  2539. {
  2540. bool lm = stricmp(_longName, longName)==0;
  2541. bool sm = stricmp(_shortName, shortName)==0;
  2542. if (lm || sm)
  2543. {
  2544. if (lm && sm)
  2545. return true;
  2546. throw MakeStringException(0, "A stats category %s (%s) is already registered", shortName.get(), longName.get());
  2547. }
  2548. return false;
  2549. }
  2550. };
  2551. static CIArrayOf<CStatsCategory> statsCategories;
  2552. static CriticalSection statsCategoriesCrit;
  2553. extern int registerStatsCategory(const char *longName, const char *shortName)
  2554. {
  2555. CriticalBlock b(statsCategoriesCrit);
  2556. ForEachItemIn(idx, statsCategories)
  2557. {
  2558. if (statsCategories.item(idx).match(longName, shortName))
  2559. return idx;
  2560. }
  2561. statsCategories.append(*new CStatsCategory(longName, shortName));
  2562. return statsCategories.ordinality()-1;
  2563. }
  2564. static void checkKind(StatisticKind kind)
  2565. {
  2566. if (kind < StMax)
  2567. {
  2568. const StatisticMeta & meta = statsMetaData[kind];
  2569. if (meta.kind != kind)
  2570. throw makeStringExceptionV(0, "Statistic %u in the wrong order", kind);
  2571. }
  2572. StatisticKind serialKind = querySerializedKind(kind);
  2573. StatisticKind rawKind = queryRawKind(kind);
  2574. if (kind != serialKind)
  2575. assertex(queryRawKind(serialKind) == kind);
  2576. if (kind != rawKind)
  2577. assertex(querySerializedKind(rawKind) == kind);
  2578. StatisticMeasure measure = queryMeasure(kind);
  2579. const char * shortName = queryStatisticName(kind);
  2580. StringBuffer longName;
  2581. queryLongStatisticName(longName, kind);
  2582. const char * tagName __attribute__ ((unused)) = queryTreeTag(kind);
  2583. const char * prefix = queryMeasurePrefix(measure);
  2584. //Check short names are all correctly prefixed.
  2585. assertex(strncmp(shortName, prefix, strlen(prefix)) == 0);
  2586. }
  2587. static void checkDistributedKind(StatisticKind kind)
  2588. {
  2589. checkKind(kind);
  2590. checkKind((StatisticKind)(kind|StMinX));
  2591. checkKind((StatisticKind)(kind|StMaxX));
  2592. checkKind((StatisticKind)(kind|StAvgX));
  2593. checkKind((StatisticKind)(kind|StSkew));
  2594. checkKind((StatisticKind)(kind|StSkewMin));
  2595. checkKind((StatisticKind)(kind|StSkewMax));
  2596. checkKind((StatisticKind)(kind|StNodeMin));
  2597. checkKind((StatisticKind)(kind|StNodeMax));
  2598. checkKind((StatisticKind)(kind|StDeltaX));
  2599. }
  2600. void verifyStatisticFunctions()
  2601. {
  2602. static_assert(_elements_in(measureNames) == SMeasureMax+1 && !measureNames[SMeasureMax], "measureNames needs updating");
  2603. static_assert(_elements_in(creatorTypeNames) == SCTmax+1 && !creatorTypeNames[SCTmax], "creatorTypeNames needs updating");
  2604. static_assert(_elements_in(scopeTypeNames) == SSTmax+1 && !scopeTypeNames[SSTmax], "scopeTypeNames needs updating");
  2605. //Check the various functions return values for all possible values.
  2606. for (unsigned i1=SMeasureAll; i1 < SMeasureMax; i1++)
  2607. {
  2608. const char * prefix __attribute__((unused)) = queryMeasurePrefix((StatisticMeasure)i1);
  2609. const char * name = queryMeasureName((StatisticMeasure)i1);
  2610. assertex(queryMeasure(name) == i1);
  2611. }
  2612. for (StatisticScopeType sst = SSTnone; sst < SSTmax; sst = (StatisticScopeType)(sst+1))
  2613. {
  2614. const char * name = queryScopeTypeName(sst);
  2615. assertex(queryScopeType(name) == sst);
  2616. }
  2617. for (StatisticCreatorType sct = SCTnone; sct < SCTmax; sct = (StatisticCreatorType)(sct+1))
  2618. {
  2619. const char * name = queryCreatorTypeName(sct);
  2620. assertex(queryCreatorType(name) == sct);
  2621. }
  2622. for (unsigned i2=StKindAll+1; i2 < StMax; i2++)
  2623. {
  2624. checkDistributedKind((StatisticKind)i2);
  2625. }
  2626. }
  2627. #if 0
  2628. MODULE_INIT(INIT_PRIORITY_STANDARD)
  2629. {
  2630. verifyStatisticFunctions();
  2631. return true;
  2632. }
  2633. #endif