jstats.cpp 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767
  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. #ifdef _WIN32
  20. #include <sys/timeb.h>
  21. #endif
  22. static CriticalSection statsNameCs;
  23. static StringBuffer statisticsComponentName;
  24. static StatisticCreatorType statisticsComponentType = SCTunknown;
  25. StatisticCreatorType queryStatisticsComponentType()
  26. {
  27. return statisticsComponentType;
  28. }
  29. const char * queryStatisticsComponentName()
  30. {
  31. CriticalBlock c(statsNameCs);
  32. if (statisticsComponentName.length() == 0)
  33. {
  34. statisticsComponentName.append("unknown").append(GetCachedHostName());
  35. DBGLOG("getProcessUniqueName hasn't been configured correctly");
  36. }
  37. return statisticsComponentName.str();
  38. }
  39. void setStatisticsComponentName(StatisticCreatorType processType, const char * processName, bool appendIP)
  40. {
  41. if (!processName)
  42. return;
  43. CriticalBlock c(statsNameCs);
  44. statisticsComponentType = processType;
  45. statisticsComponentName.clear().append(processName);
  46. if (appendIP)
  47. statisticsComponentName.append("@").append(GetCachedHostName()); // should I use _ instead?
  48. }
  49. //--------------------------------------------------------------------------------------------------------------------
  50. // Textual forms of the different enumerations, first items are for none and all.
  51. static const char * const measureNames[] = { "", "all", "ns", "ts", "cnt", "sz", "cpu", "skw", "node", "ppm", "ip", NULL };
  52. static const char * const creatorTypeNames[]= { "", "all", "unknown", "hthor", "roxie", "roxie:s", "thor", "thor:m", "thor:s", "eclcc", "esp", "summary", NULL };
  53. static const char * const scopeTypeNames[] = { "", "all", "global", "graph", "subgraph", "activity", "allocator", "section", "compile", "dfu", "edge", NULL };
  54. static unsigned matchString(const char * const * names, const char * search)
  55. {
  56. if (!search)
  57. return 0;
  58. if (streq(search, "*"))
  59. search = "all";
  60. unsigned i=0;
  61. loop
  62. {
  63. const char * next = names[i];
  64. if (!next)
  65. return 0;
  66. if (streq(next, search))
  67. return i;
  68. i++;
  69. }
  70. }
  71. //--------------------------------------------------------------------------------------------------------------------
  72. extern jlib_decl unsigned __int64 getTimeStampNowValue()
  73. {
  74. #ifdef _WIN32
  75. struct _timeb now;
  76. _ftime(&now);
  77. return (unsigned __int64)now.time * I64C(1000000) + now.millitm * 1000;
  78. #else
  79. struct timeval tm;
  80. gettimeofday(&tm,NULL);
  81. return (unsigned __int64)tm.tv_sec * I64C(1000000) + tm.tv_usec;
  82. #endif
  83. }
  84. const static unsigned __int64 msUntilResync = 1000; // resync every second ~= 1ms accuracy
  85. static cycle_t cyclesUntilResync;
  86. MODULE_INIT(INIT_PRIORITY_STANDARD)
  87. {
  88. cyclesUntilResync = nanosec_to_cycle(msUntilResync * 1000000);
  89. return true;
  90. }
  91. OptimizedTimestamp::OptimizedTimestamp()
  92. {
  93. lastCycles = get_cycles_now();
  94. lastTimestamp = ::getTimeStampNowValue();
  95. }
  96. #if 0
  97. //This version almost certainly has problems if the computer is suspended and cycles->nanoseconds is only accurate to
  98. //about 0.1% - so should only be used for relatively short periods
  99. unsigned __int64 OptimizedTimestamp::getTimeStampNowValue()
  100. {
  101. cycle_t nowCycles = get_cycles_now();
  102. return lastTimestamp + cycle_to_microsec(nowCycles - lastCycles);
  103. }
  104. #else
  105. //This version will resync every minute, but is not thread safe. Adding a critical section makes it less efficient than recalculating
  106. unsigned __int64 OptimizedTimestamp::getTimeStampNowValue()
  107. {
  108. cycle_t nowCycles = get_cycles_now();
  109. if (nowCycles - lastCycles > cyclesUntilResync)
  110. {
  111. lastCycles = nowCycles;
  112. lastTimestamp = ::getTimeStampNowValue();
  113. }
  114. return lastTimestamp + cycle_to_microsec(nowCycles - lastCycles);
  115. }
  116. #endif
  117. unsigned __int64 getIPV4StatsValue(const IpAddress & ip)
  118. {
  119. unsigned ipValue;
  120. if (ip.getNetAddress(sizeof(ipValue),&ipValue))
  121. return ipValue;
  122. return 0;
  123. }
  124. //--------------------------------------------------------------------------------------------------------------------
  125. const static unsigned __int64 oneSecond = I64C(1000000000);
  126. const static unsigned __int64 oneMinute = I64C(60000000000);
  127. const static unsigned __int64 oneHour = I64C(3600000000000);
  128. const static unsigned __int64 oneDay = 24 * I64C(3600000000000);
  129. static void formatTime(StringBuffer & out, unsigned __int64 value)
  130. {
  131. unsigned days = (unsigned)(value / oneDay);
  132. value = value % oneDay;
  133. unsigned hours = (unsigned)(value / oneHour);
  134. value = value % oneHour;
  135. unsigned mins = (unsigned)(value / oneMinute);
  136. value = value % oneMinute;
  137. unsigned secs = (unsigned)(value / oneSecond);
  138. unsigned ns = (unsigned)(value % oneSecond);
  139. if (days > 0)
  140. out.appendf("%u days ", days);
  141. if (hours > 0 || days)
  142. out.appendf("%u:%02u:%02u", hours, mins, secs);
  143. else if (mins >= 5)
  144. out.appendf("%u:%02u", mins, secs);
  145. else if (mins >= 1)
  146. out.appendf("%u:%02u.%03u", mins, secs, ns / 1000000);
  147. else if (secs >= 10)
  148. out.appendf("%u.%03u", secs, ns / 1000000);
  149. else
  150. out.appendf("%u.%06u", secs, ns / 1000);
  151. }
  152. static void formatTimeStamp(StringBuffer & out, unsigned __int64 value)
  153. {
  154. time_t seconds = value / 1000000;
  155. unsigned us = value % 1000000;
  156. char timeStamp[64];
  157. time_t tNow = seconds;
  158. #ifdef _WIN32
  159. struct tm *gmtNow;
  160. gmtNow = gmtime(&tNow);
  161. strftime(timeStamp, 64, "%Y-%m-%d %H:%M:%S", gmtNow);
  162. #else
  163. struct tm gmtNow;
  164. gmtime_r(&tNow, &gmtNow);
  165. strftime(timeStamp, 64, "%Y-%m-%d %H:%M:%S", &gmtNow);
  166. #endif //_WIN32
  167. out.append(timeStamp).appendf(".%03u", us / 1000);
  168. }
  169. static const unsigned oneKb = 1024;
  170. static const unsigned oneMb = 1024 * 1024;
  171. static const unsigned oneGb = 1024 * 1024 * 1024;
  172. static unsigned toPermille(unsigned x) { return (x * 1000) / 1024; }
  173. static void formatSize(StringBuffer & out, unsigned __int64 value)
  174. {
  175. unsigned Gb = (unsigned)(value / oneGb);
  176. unsigned Mb = (unsigned)((value % oneGb) / oneMb);
  177. unsigned Kb = (unsigned)((value % oneMb) / oneKb);
  178. unsigned b = (unsigned)(value % oneKb);
  179. if (Gb)
  180. out.appendf("%u.%03uGb", Gb, toPermille(Mb));
  181. else if (Mb)
  182. out.appendf("%u.%03uMb", Mb, toPermille(Kb));
  183. else if (Kb)
  184. out.appendf("%u.%03uKb", Kb, toPermille(b));
  185. else
  186. out.appendf("%ub", b);
  187. }
  188. static void formatLoad(StringBuffer & out, unsigned __int64 value)
  189. {
  190. //Stored as millionth of a core. Display as a percentage => scale by 10,000
  191. out.appendf("%u.%03u%%", (unsigned)(value / 10000), (unsigned)(value % 10000) / 10);
  192. }
  193. static void formatSkew(StringBuffer & out, unsigned __int64 value)
  194. {
  195. //Skew stored as 10000 = perfect, display as percentage
  196. out.appendf("%.2f%%", ((double)(__int64)value) / 100.0);
  197. }
  198. static void formatIPV4(StringBuffer & out, unsigned __int64 value)
  199. {
  200. byte ip1 = (value & 255);
  201. byte ip2 = ((value >> 8) & 255);
  202. byte ip3 = ((value >> 16) & 255);
  203. byte ip4 = ((value >> 24) & 255);
  204. out.appendf("%d.%d.%d.%d", ip1, ip2, ip3, ip4);
  205. }
  206. void formatStatistic(StringBuffer & out, unsigned __int64 value, StatisticMeasure measure)
  207. {
  208. switch (measure)
  209. {
  210. case SMeasureTimeNs:
  211. formatTime(out, value);
  212. break;
  213. case SMeasureTimestampUs:
  214. formatTimeStamp(out, value);
  215. break;
  216. case SMeasureCount:
  217. out.append(value);
  218. break;
  219. case SMeasureSize:
  220. formatSize(out, value);
  221. break;
  222. case SMeasureLoad:
  223. formatLoad(out, value);
  224. break;
  225. case SMeasureSkew:
  226. formatSkew(out, value);
  227. break;
  228. case SMeasureNode:
  229. out.append(value);
  230. break;
  231. case SMeasurePercent:
  232. out.appendf("%.2f%%", (double)value / 10000.0); // stored as ppm
  233. break;
  234. case SMeasureIPV4:
  235. formatIPV4(out, value);
  236. break;
  237. default:
  238. throwUnexpected();
  239. }
  240. }
  241. void formatStatistic(StringBuffer & out, unsigned __int64 value, StatisticKind kind)
  242. {
  243. formatStatistic(out, value, queryMeasure(kind));
  244. }
  245. //--------------------------------------------------------------------------------------------------------------------
  246. unsigned queryStatisticsDepth(const char * text)
  247. {
  248. unsigned depth = 1;
  249. loop
  250. {
  251. switch (*text)
  252. {
  253. case 0:
  254. return depth;
  255. case ':':
  256. depth++;
  257. break;
  258. }
  259. text++;
  260. }
  261. }
  262. const char * queryMeasurePrefix(StatisticMeasure measure)
  263. {
  264. switch (measure)
  265. {
  266. case SMeasureAll: return NULL;
  267. case SMeasureTimeNs: return "Time";
  268. case SMeasureTimestampUs: return "When";
  269. case SMeasureCount: return "Num";
  270. case SMeasureSize: return "Size";
  271. case SMeasureLoad: return "Load";
  272. case SMeasureSkew: return "Skew";
  273. case SMeasureNode: return "Node";
  274. case SMeasurePercent: return "Per";
  275. case SMeasureIPV4: return "Ip";
  276. default:
  277. throwUnexpected();
  278. }
  279. }
  280. const char * queryMeasureName(StatisticMeasure measure)
  281. {
  282. return measureNames[measure];
  283. }
  284. StatisticMeasure queryMeasure(const char * measure)
  285. {
  286. //MORE: Use a hash table??
  287. return (StatisticMeasure)matchString(measureNames, measure);
  288. }
  289. //--------------------------------------------------------------------------------------------------------------------
  290. StatisticMeasure queryMeasure(StatisticKind kind)
  291. {
  292. unsigned varient = (kind & ~StKindMask);
  293. switch (varient)
  294. {
  295. case StSkew:
  296. case StSkewMin:
  297. case StSkewMax:
  298. return SMeasureSkew;
  299. case StNodeMin:
  300. case StNodeMax:
  301. return SMeasureNode;
  302. case StDeltaX:
  303. {
  304. StatisticMeasure measure = queryMeasure((StatisticKind)(kind & StKindMask));
  305. switch (measure)
  306. {
  307. case SMeasureTimestampUs:
  308. return SMeasureTimeNs;
  309. default:
  310. return measure;
  311. }
  312. break;
  313. }
  314. }
  315. switch (kind & StKindMask)
  316. {
  317. case StKindNone:
  318. return SMeasureNone;
  319. case StKindAll:
  320. return SMeasureAll;
  321. case StWhenGraphStarted:
  322. case StWhenGraphFinished:
  323. case StWhenFirstRow:
  324. case StWhenQueryStarted:
  325. case StWhenQueryFinished:
  326. case StWhenCreated:
  327. case StWhenCompiled:
  328. case StWhenWorkunitModified:
  329. return SMeasureTimestampUs;
  330. case StTimeElapsed:
  331. case StTimeLocalExecute:
  332. case StTimeTotalExecute:
  333. case StTimeRemaining:
  334. case StTimeSoapcall:
  335. return SMeasureTimeNs;
  336. case StSizeGeneratedCpp:
  337. case StSizePeakMemory:
  338. case StSizeMaxRowSize:
  339. return SMeasureSize;
  340. case StNumRowsProcessed:
  341. case StNumSlaves:
  342. case StNumStarted:
  343. case StNumStopped:
  344. case StNumIndexSeeks:
  345. case StNumIndexScans:
  346. case StNumIndexWildSeeks:
  347. case StNumIndexSkips:
  348. case StNumIndexNullSkips:
  349. case StNumIndexMerges:
  350. case StNumIndexMergeCompares:
  351. case StNumPreFiltered:
  352. case StNumPostFiltered:
  353. case StNumBlobCacheHits:
  354. case StNumLeafCacheHits:
  355. case StNumNodeCacheHits:
  356. case StNumBlobCacheAdds:
  357. case StNumLeafCacheAdds:
  358. case StNumNodeCacheAdds:
  359. case StNumPreloadCacheHits:
  360. case StNumPreloadCacheAdds:
  361. case StNumServerCacheHits:
  362. case StNumIndexAccepted:
  363. case StNumIndexRejected:
  364. case StNumAtmostTriggered:
  365. case StNumDiskSeeks:
  366. case StNumIterations:
  367. case StNumLeftRows:
  368. case StNumRightRows:
  369. case StNumDiskRowsRead:
  370. case StNumIndexRowsRead:
  371. case StNumDiskAccepted:
  372. case StNumDiskRejected:
  373. return SMeasureCount;
  374. case StLoadWhileSorting: // Average load while processing a sort?
  375. return SMeasureLoad;
  376. case StPerReplicated:
  377. return SMeasurePercent;
  378. default:
  379. throwUnexpected();
  380. }
  381. }
  382. //NOTE: Min/Max/Avg still contain the type prefix, otherwise you cannot deduce the measure from the name
  383. #define DEFINE_DEFAULTSHORTNAME_BASE(x, y) \
  384. case St##x##y: return #x #y; \
  385. case (St ## x ## y | StMinX): return #x "Min" # y; \
  386. case (St ## x ## y | StMaxX): return #x "Max" # y; \
  387. case (St ## x ## y | StAvgX): return #x "Avg" # y; \
  388. case (St ## x ## y | StSkew): return "Skew" # y; \
  389. case (St ## x ## y | StSkewMin): return "SkewMin" # y; \
  390. case (St ## x ## y | StSkewMax): return "SkewMax" # y; \
  391. case (St ## x ## y | StNodeMin): return "NodeMin" # y; \
  392. case (St ## x ## y | StNodeMax): return "NodeMax" # y;
  393. #define DEFINE_DEFAULTSHORTNAME(x, y) \
  394. DEFINE_DEFAULTSHORTNAME_BASE(x, y) \
  395. case (St ## x ## y | StDeltaX): return #x "Delta" # y;
  396. #define DEFINE_TIMESTAMPSHORTNAME(x, y) \
  397. DEFINE_DEFAULTSHORTNAME_BASE(x, y) \
  398. case (St ## x ## y | StDeltaX): return "TimeDelta" # y;
  399. const char * queryStatisticName(StatisticKind kind)
  400. {
  401. switch (kind)
  402. {
  403. case StKindNone: return "";
  404. case StKindAll: return "all";
  405. DEFINE_DEFAULTSHORTNAME(When, GraphStarted);
  406. DEFINE_DEFAULTSHORTNAME(When, GraphFinished);
  407. DEFINE_DEFAULTSHORTNAME(When, FirstRow);
  408. DEFINE_DEFAULTSHORTNAME(When, QueryStarted);
  409. DEFINE_DEFAULTSHORTNAME(When, QueryFinished);
  410. DEFINE_DEFAULTSHORTNAME(When, Created);
  411. DEFINE_DEFAULTSHORTNAME(When, Compiled);
  412. DEFINE_DEFAULTSHORTNAME(When, WorkunitModified);
  413. DEFINE_TIMESTAMPSHORTNAME(Time, Elapsed);
  414. DEFINE_TIMESTAMPSHORTNAME(Time, LocalExecute);
  415. DEFINE_TIMESTAMPSHORTNAME(Time, TotalExecute);
  416. DEFINE_TIMESTAMPSHORTNAME(Time, Remaining);
  417. DEFINE_TIMESTAMPSHORTNAME(Time, Soapcall);
  418. DEFINE_DEFAULTSHORTNAME(Size, GeneratedCpp);
  419. DEFINE_DEFAULTSHORTNAME(Size, PeakMemory);
  420. DEFINE_DEFAULTSHORTNAME(Size, MaxRowSize);
  421. DEFINE_DEFAULTSHORTNAME(Num, RowsProcessed);
  422. DEFINE_DEFAULTSHORTNAME(Num, Slaves);
  423. DEFINE_DEFAULTSHORTNAME(Num, Started);
  424. DEFINE_DEFAULTSHORTNAME(Num, Stopped);
  425. DEFINE_DEFAULTSHORTNAME(Num, IndexSeeks);
  426. DEFINE_DEFAULTSHORTNAME(Num, IndexScans);
  427. DEFINE_DEFAULTSHORTNAME(Num, IndexWildSeeks);
  428. DEFINE_DEFAULTSHORTNAME(Num, IndexSkips);
  429. DEFINE_DEFAULTSHORTNAME(Num, IndexNullSkips);
  430. DEFINE_DEFAULTSHORTNAME(Num, IndexMerges);
  431. DEFINE_DEFAULTSHORTNAME(Num, IndexMergeCompares);
  432. DEFINE_DEFAULTSHORTNAME(Num, PreFiltered);
  433. DEFINE_DEFAULTSHORTNAME(Num, PostFiltered);
  434. DEFINE_DEFAULTSHORTNAME(Num, BlobCacheHits);
  435. DEFINE_DEFAULTSHORTNAME(Num, LeafCacheHits);
  436. DEFINE_DEFAULTSHORTNAME(Num, NodeCacheHits);
  437. DEFINE_DEFAULTSHORTNAME(Num, BlobCacheAdds);
  438. DEFINE_DEFAULTSHORTNAME(Num, LeafCacheAdds);
  439. DEFINE_DEFAULTSHORTNAME(Num, NodeCacheAdds);
  440. DEFINE_DEFAULTSHORTNAME(Num, PreloadCacheHits);
  441. DEFINE_DEFAULTSHORTNAME(Num, PreloadCacheAdds);
  442. DEFINE_DEFAULTSHORTNAME(Num, ServerCacheHits);
  443. DEFINE_DEFAULTSHORTNAME(Num, IndexAccepted);
  444. DEFINE_DEFAULTSHORTNAME(Num, IndexRejected);
  445. DEFINE_DEFAULTSHORTNAME(Num, AtmostTriggered);
  446. DEFINE_DEFAULTSHORTNAME(Num, DiskSeeks);
  447. DEFINE_DEFAULTSHORTNAME(Num, Iterations);
  448. DEFINE_DEFAULTSHORTNAME(Load, WhileSorting);
  449. DEFINE_DEFAULTSHORTNAME(Num, LeftRows);
  450. DEFINE_DEFAULTSHORTNAME(Num, RightRows);
  451. DEFINE_DEFAULTSHORTNAME(Per, Replicated);
  452. DEFINE_DEFAULTSHORTNAME(Num, DiskRowsRead);
  453. DEFINE_DEFAULTSHORTNAME(Num, IndexRowsRead);
  454. DEFINE_DEFAULTSHORTNAME(Num, DiskAccepted);
  455. DEFINE_DEFAULTSHORTNAME(Num, DiskRejected);
  456. default:
  457. throwUnexpected();
  458. }
  459. }
  460. #undef DEFINE_DEFAULTSHORTNAME_BASE // prevent it being used accidentally
  461. #undef DEFINE_DEFAULTSHORTNAME // prevent it being used accidentally
  462. #undef DEFINE_TIMESTAMPSHORTNAME
  463. //--------------------------------------------------------------------------------------------------------------------
  464. void queryLongStatisticName(StringBuffer & out, StatisticKind kind)
  465. {
  466. out.append(queryStatisticName(kind));
  467. }
  468. //--------------------------------------------------------------------------------------------------------------------
  469. //Keep prefixes on min/max/avg so they are consistent with short names (tags will eventually go...)
  470. #define DEFINE_TAGNAME(x, y, dft) \
  471. case St##x##y: return dft; \
  472. case (St ## x ## y | StMinX): return "@" #x "Min" # y; \
  473. case (St ## x ## y | StMaxX): return "@" #x "Max" # y; \
  474. case (St ## x ## y | StAvgX): return "@" #x "Avg" # y; \
  475. case (St ## x ## y | StSkew): return "@Skew" # y; \
  476. case (St ## x ## y | StSkewMin): return "@SkewMin" # y; \
  477. case (St ## x ## y | StSkewMax): return "@SkewMax" # y; \
  478. case (St ## x ## y | StNodeMin): return "@NodeMin" # y; \
  479. case (St ## x ## y | StNodeMax): return "@NodeMax" # y; \
  480. case (St ## x ## y | StDeltaX): return "@" #x "Delta" # y;
  481. #define DEFINE_DEFAULTTAGNAME(x, y) DEFINE_TAGNAME(x, y, "@" #x #y)
  482. const char * queryTreeTag(StatisticKind kind)
  483. {
  484. //For backward compatibility - where it matters. Will eventually be deleted.
  485. switch (kind)
  486. {
  487. DEFINE_DEFAULTTAGNAME(When, GraphStarted);
  488. DEFINE_DEFAULTTAGNAME(When, GraphFinished);
  489. DEFINE_DEFAULTTAGNAME(When, FirstRow);
  490. DEFINE_DEFAULTTAGNAME(When, QueryStarted);
  491. DEFINE_DEFAULTTAGNAME(When, QueryFinished);
  492. DEFINE_DEFAULTTAGNAME(When, Created);
  493. DEFINE_DEFAULTTAGNAME(When, Compiled);
  494. DEFINE_DEFAULTTAGNAME(When, WorkunitModified);
  495. DEFINE_DEFAULTTAGNAME(Time, Elapsed);
  496. DEFINE_TAGNAME(Time, LocalExecute, "@localTime");
  497. DEFINE_TAGNAME(Time, TotalExecute, "@totalTime");
  498. DEFINE_DEFAULTTAGNAME(Time, Remaining);
  499. DEFINE_DEFAULTTAGNAME(Time, Soapcall);
  500. DEFINE_DEFAULTTAGNAME(Size, GeneratedCpp);
  501. DEFINE_DEFAULTTAGNAME(Size, PeakMemory);
  502. DEFINE_DEFAULTTAGNAME(Size, MaxRowSize);
  503. //Primarily used for graph progress - backward compatible to simplify migration.
  504. DEFINE_TAGNAME(Num, RowsProcessed, "@count");
  505. DEFINE_TAGNAME(Num, Slaves, "@slaves");
  506. DEFINE_TAGNAME(Num, Started, "@started");
  507. DEFINE_TAGNAME(Num, Stopped, "@stopped");
  508. DEFINE_TAGNAME(Num, IndexSeeks, "@seeks");
  509. DEFINE_TAGNAME(Num, IndexScans, "@scans");
  510. DEFINE_TAGNAME(Num, IndexWildSeeks, "@wildscans");
  511. DEFINE_TAGNAME(Num, IndexSkips, "@skips");
  512. DEFINE_TAGNAME(Num, IndexNullSkips, "@nullskips");
  513. DEFINE_TAGNAME(Num, IndexMerges, "@merges");
  514. DEFINE_TAGNAME(Num, IndexMergeCompares, "@mergecompares");
  515. DEFINE_TAGNAME(Num, PreFiltered, "@prefiltered");
  516. DEFINE_TAGNAME(Num, PostFiltered, "@postfiltered");
  517. DEFINE_TAGNAME(Num, BlobCacheHits, "@blobhit");
  518. DEFINE_TAGNAME(Num, LeafCacheHits, "@leafhit");
  519. DEFINE_TAGNAME(Num, NodeCacheHits, "@nodehit");
  520. DEFINE_TAGNAME(Num, BlobCacheAdds, "@blobadd");
  521. DEFINE_TAGNAME(Num, LeafCacheAdds, "@leadadd");
  522. DEFINE_TAGNAME(Num, NodeCacheAdds, "@nodeadd");
  523. DEFINE_TAGNAME(Num, PreloadCacheHits, "@preloadhits");
  524. DEFINE_TAGNAME(Num, PreloadCacheAdds, "@preloadadds");
  525. DEFINE_TAGNAME(Num, ServerCacheHits, "@sschits");
  526. DEFINE_TAGNAME(Num, IndexAccepted, "@accepted");
  527. DEFINE_TAGNAME(Num, IndexRejected, "@rejected");
  528. DEFINE_TAGNAME(Num, AtmostTriggered, "@atmost");
  529. DEFINE_TAGNAME(Num, DiskSeeks, "@fseeks");
  530. DEFINE_DEFAULTTAGNAME(Num, Iterations);
  531. DEFINE_DEFAULTTAGNAME(Load, WhileSorting);
  532. DEFINE_DEFAULTTAGNAME(Num, LeftRows);
  533. DEFINE_DEFAULTTAGNAME(Num, RightRows);
  534. DEFINE_DEFAULTTAGNAME(Per, Replicated);
  535. DEFINE_DEFAULTTAGNAME(Num, DiskRowsRead);
  536. DEFINE_DEFAULTTAGNAME(Num, IndexRowsRead);
  537. DEFINE_DEFAULTTAGNAME(Num, DiskAccepted);
  538. DEFINE_DEFAULTTAGNAME(Num, DiskRejected);
  539. default:
  540. throwUnexpected();
  541. }
  542. }
  543. #undef DEFINE_DEFAULTTAGNAME
  544. //--------------------------------------------------------------------------------------------------------------------
  545. StatisticKind queryStatisticKind(const char * search)
  546. {
  547. if (!search)
  548. return StKindNone;
  549. if (streq(search, "*"))
  550. return StKindAll;
  551. //Slow - should use a hash table....
  552. for (unsigned i=0; i < StMax; i++)
  553. {
  554. StatisticKind kind = (StatisticKind)i;
  555. const char * shortName = queryStatisticName(kind);
  556. if (strieq(shortName, search))
  557. return kind;
  558. }
  559. return StKindNone;
  560. }
  561. //--------------------------------------------------------------------------------------------------------------------
  562. const char * queryCreatorTypeName(StatisticCreatorType sct)
  563. {
  564. return creatorTypeNames[sct];
  565. }
  566. StatisticCreatorType queryCreatorType(const char * sct)
  567. {
  568. //MORE: Use a hash table??
  569. return (StatisticCreatorType)matchString(creatorTypeNames, sct);
  570. }
  571. //--------------------------------------------------------------------------------------------------------------------
  572. const char * queryScopeTypeName(StatisticScopeType sst)
  573. {
  574. return scopeTypeNames[sst];
  575. }
  576. extern jlib_decl StatisticScopeType queryScopeType(const char * sst)
  577. {
  578. //MORE: Use a hash table??
  579. return (StatisticScopeType)matchString(scopeTypeNames, sst);
  580. }
  581. //--------------------------------------------------------------------------------------------------------------------
  582. inline void mergeUpdate(StatisticMeasure measure, unsigned __int64 & value, const unsigned __int64 otherValue)
  583. {
  584. switch (measure)
  585. {
  586. case SMeasureTimeNs:
  587. case SMeasureCount:
  588. case SMeasureSize:
  589. case SMeasureLoad:
  590. case SMeasureSkew:
  591. value += otherValue;
  592. break;
  593. case SMeasureTimestampUs:
  594. if (otherValue && otherValue < value)
  595. value = otherValue;
  596. break;
  597. }
  598. }
  599. unsigned __int64 mergeStatistic(StatisticMeasure measure, unsigned __int64 value, unsigned __int64 otherValue)
  600. {
  601. mergeUpdate(measure, value, otherValue);
  602. return value;
  603. }
  604. //--------------------------------------------------------------------------------------------------------------------
  605. class CComponentStatistics
  606. {
  607. protected:
  608. StringAttr creator;
  609. byte creatorDepth;
  610. byte scopeDepth;
  611. // StatisticArray stats;
  612. };
  613. //--------------------------------------------------------------------------------------------------------------------
  614. static int compareUnsigned(unsigned const * left, unsigned const * right)
  615. {
  616. return (*left < *right) ? -1 : (*left > *right) ? +1 : 0;
  617. }
  618. class StatisticsMapping
  619. {
  620. public:
  621. StatisticsMapping(StatisticKind kind, ...)
  622. {
  623. indexToKind.append(kind);
  624. va_list args;
  625. va_start(args, kind);
  626. for (;;)
  627. {
  628. unsigned next = va_arg(args, unsigned);
  629. if (!next)
  630. break;
  631. indexToKind.append(next);
  632. }
  633. va_end(args);
  634. process();
  635. }
  636. unsigned getIndex(StatisticKind kind) const { return kindToIndex.item(kind); }
  637. StatisticKind getKind(unsigned index) const { return (StatisticKind)indexToKind.item(index); }
  638. unsigned numStatistics() const { return indexToKind.ordinality(); }
  639. protected:
  640. void process()
  641. {
  642. //Possibly not needed, but sort the kinds, so that it is easy to merge/stream the results out in the correct order.
  643. indexToKind.sort(compareUnsigned);
  644. ForEachItemIn(i, indexToKind)
  645. {
  646. unsigned kind = indexToKind.item(i);
  647. while (kindToIndex.ordinality() < kind)
  648. kindToIndex.append(0);
  649. kindToIndex.replace(i, kind);
  650. }
  651. }
  652. protected:
  653. UnsignedArray kindToIndex;
  654. UnsignedArray indexToKind;
  655. };
  656. //--------------------------------------------------------------------------------------------------------------------
  657. class Statistic : public CInterfaceOf<IStatistic>
  658. {
  659. public:
  660. Statistic(StatisticKind _kind, unsigned __int64 _value) : kind(_kind), value(_value)
  661. {
  662. }
  663. static Statistic * deserialize(MemoryBuffer & in, unsigned version)
  664. {
  665. return new Statistic(in, version);
  666. }
  667. virtual StatisticKind queryKind() const
  668. {
  669. return kind;
  670. }
  671. virtual unsigned __int64 queryValue() const
  672. {
  673. return value;
  674. }
  675. void merge(unsigned __int64 otherValue)
  676. {
  677. mergeUpdate(queryMeasure(kind), value, otherValue);
  678. }
  679. void serialize(MemoryBuffer & out) const
  680. {
  681. //MORE: Could compress - e.g., store as a packed integers
  682. out.append((unsigned)kind);
  683. out.append(value);
  684. }
  685. protected:
  686. Statistic(MemoryBuffer & in, unsigned version)
  687. {
  688. unsigned _kind;
  689. in.read(_kind);
  690. kind = (StatisticKind)_kind;
  691. in.read(value);
  692. }
  693. public:
  694. StatisticKind kind;
  695. unsigned __int64 value;
  696. };
  697. //--------------------------------------------------------------------------------------------------------------------
  698. StringBuffer & StatsScopeId::getScopeText(StringBuffer & out) const
  699. {
  700. switch (scopeType)
  701. {
  702. case SSTgraph:
  703. return out.append(GraphScopePrefix).append(id);
  704. case SSTsubgraph:
  705. return out.append(SubGraphScopePrefix).append(id);
  706. case SSTactivity:
  707. return out.append(ActivityScopePrefix).append(id);
  708. case SSTedge:
  709. return out.append(EdgeScopePrefix).append(id).append("_").append(extra);
  710. default:
  711. throwUnexpected();
  712. }
  713. }
  714. unsigned StatsScopeId::getHash() const
  715. {
  716. return hashc((const byte *)&id, sizeof(id), (unsigned)scopeType);
  717. }
  718. bool StatsScopeId::matches(const StatsScopeId & other) const
  719. {
  720. return (scopeType == other.scopeType) && (id == other.id) && (extra == other.extra);
  721. }
  722. unsigned StatsScopeId::queryActivity() const
  723. {
  724. switch (scopeType)
  725. {
  726. case SSTactivity:
  727. case SSTedge:
  728. return id;
  729. default:
  730. return 0;
  731. }
  732. }
  733. void StatsScopeId::deserialize(MemoryBuffer & in, unsigned version)
  734. {
  735. byte scopeTypeByte;
  736. in.read(scopeTypeByte);
  737. scopeType = (StatisticScopeType)scopeTypeByte;
  738. switch (scopeType)
  739. {
  740. case SSTgraph:
  741. case SSTsubgraph:
  742. case SSTactivity:
  743. in.read(id);
  744. break;
  745. case SSTedge:
  746. in.read(id);
  747. in.read(extra);
  748. break;
  749. default:
  750. throwUnexpected();
  751. }
  752. }
  753. void StatsScopeId::serialize(MemoryBuffer & out) const
  754. {
  755. out.append((byte)scopeType);
  756. switch (scopeType)
  757. {
  758. case SSTgraph:
  759. case SSTsubgraph:
  760. case SSTactivity:
  761. out.append(id);
  762. break;
  763. case SSTedge:
  764. out.append(id);
  765. out.append(extra);
  766. break;
  767. default:
  768. throwUnexpected();
  769. }
  770. }
  771. void StatsScopeId::setId(StatisticScopeType _scopeType, unsigned _id, unsigned _extra)
  772. {
  773. scopeType = _scopeType;
  774. id = _id;
  775. extra = _extra;
  776. }
  777. bool StatsScopeId::setScopeText(const char * text)
  778. {
  779. if (MATCHES_CONST_PREFIX(text, ActivityScopePrefix))
  780. setActivityId(atoi(text + CONST_STRLEN(ActivityScopePrefix)));
  781. else if (MATCHES_CONST_PREFIX(text, GraphScopePrefix))
  782. setId(SSTgraph, atoi(text + CONST_STRLEN(GraphScopePrefix)));
  783. else if (MATCHES_CONST_PREFIX(text, SubGraphScopePrefix))
  784. setSubgraphId(atoi(text + CONST_STRLEN(SubGraphScopePrefix)));
  785. else if (MATCHES_CONST_PREFIX(text, EdgeScopePrefix))
  786. {
  787. const char * underscore = strchr(text, '_');
  788. if (!underscore)
  789. return false;
  790. setEdgeId(atoi(text + CONST_STRLEN(EdgeScopePrefix)), atoi(underscore+1));
  791. }
  792. else
  793. return false;
  794. return true;
  795. }
  796. void StatsScopeId::setActivityId(unsigned _id)
  797. {
  798. setId(SSTactivity, _id);
  799. }
  800. void StatsScopeId::setEdgeId(unsigned _id, unsigned _output)
  801. {
  802. setId(SSTedge, _id, _output);
  803. }
  804. void StatsScopeId::setSubgraphId(unsigned _id)
  805. {
  806. setId(SSTsubgraph, _id);
  807. }
  808. //Use an atom table to minimimize memory usage in esp.
  809. //The class could try and use a combination of the scope type and an unsigned, but I suspect not worth it.
  810. IAtom * createStatsScope(const char * name)
  811. {
  812. //MORE: Should this use a separate atom table?
  813. return createAtom(name);
  814. }
  815. IAtom * createActivityScope(unsigned value)
  816. {
  817. char temp[12];
  818. sprintf(temp, ActivityScopePrefix "%u", value);
  819. return createStatsScope(temp);
  820. }
  821. static IAtom * createSubGraphScope(unsigned value)
  822. {
  823. char temp[13];
  824. sprintf(temp, SubGraphScopePrefix "%u", value);
  825. return createStatsScope(temp);
  826. }
  827. IAtom * createEdgeScope(unsigned value1, unsigned value2)
  828. {
  829. StringBuffer temp;
  830. temp.append(EdgeScopePrefix).append(value1).append("_").append(value2);
  831. return createStatsScope(temp);
  832. }
  833. //--------------------------------------------------------------------------------------------------------------------
  834. enum
  835. {
  836. SCroot,
  837. SCintermediate,
  838. SCleaf,
  839. };
  840. class CStatisticCollection;
  841. static CStatisticCollection * deserializeCollection(CStatisticCollection * parent, MemoryBuffer & in, unsigned version);
  842. //MORE: Create an implementation with no children
  843. typedef StructArrayOf<Statistic> StatsArray;
  844. class CollectionHashTable : public SuperHashTableOf<CStatisticCollection, StatsScopeId>
  845. {
  846. public:
  847. ~CollectionHashTable() { releaseAll(); }
  848. virtual void onAdd(void *et);
  849. virtual void onRemove(void *et);
  850. virtual unsigned getHashFromElement(const void *et) const;
  851. virtual unsigned getHashFromFindParam(const void *fp) const;
  852. virtual const void * getFindParam(const void *et) const;
  853. virtual bool matchesFindParam(const void *et, const void *key, unsigned fphash) const;
  854. virtual bool matchesElement(const void *et, const void *searchET) const;
  855. };
  856. typedef IArrayOf<CStatisticCollection> CollectionArray;
  857. class CStatisticCollection : public CInterfaceOf<IStatisticCollection>
  858. {
  859. friend class CollectionHashTable;
  860. public:
  861. CStatisticCollection(CStatisticCollection * _parent, const StatsScopeId & _id) : parent(_parent), id(_id)
  862. {
  863. }
  864. CStatisticCollection(CStatisticCollection * _parent, MemoryBuffer & in, unsigned version) : parent(_parent)
  865. {
  866. id.deserialize(in, version);
  867. unsigned numStats;
  868. in.read(numStats);
  869. stats.ensure(numStats);
  870. while (numStats-- > 0)
  871. {
  872. Statistic * next = Statistic::deserialize(in, version);
  873. stats.append(*next);
  874. }
  875. unsigned numChildren;
  876. in.read(numChildren);
  877. children.ensure(numChildren);
  878. while (numChildren-- > 0)
  879. {
  880. CStatisticCollection * next = deserializeCollection(this, in, version);
  881. children.add(*next);
  882. }
  883. }
  884. virtual byte getCollectionType() const { return SCintermediate; }
  885. //interface IStatisticCollection:
  886. virtual StatisticScopeType queryScopeType() const
  887. {
  888. return id.queryScopeType();
  889. }
  890. virtual unsigned __int64 queryWhenCreated() const
  891. {
  892. if (parent)
  893. return parent->queryWhenCreated();
  894. return 0;
  895. }
  896. virtual StringBuffer & getScope(StringBuffer & str) const
  897. {
  898. return id.getScopeText(str);
  899. }
  900. virtual StringBuffer & getFullScope(StringBuffer & str) const
  901. {
  902. if (parent)
  903. {
  904. parent->getFullScope(str);
  905. str.append(':');
  906. }
  907. id.getScopeText(str);
  908. return str;
  909. }
  910. virtual unsigned __int64 queryStatistic(StatisticKind kind) const
  911. {
  912. ForEachItemIn(i, stats)
  913. {
  914. const Statistic & cur = stats.item(i);
  915. if (cur.kind == kind)
  916. return cur.value;
  917. }
  918. return 0;
  919. }
  920. virtual unsigned getNumStatistics() const
  921. {
  922. return stats.ordinality();
  923. }
  924. virtual void getStatistic(StatisticKind & kind, unsigned __int64 & value, unsigned idx) const
  925. {
  926. const Statistic & cur = stats.item(idx);
  927. kind = cur.kind;
  928. value = cur.value;
  929. }
  930. virtual IStatisticCollectionIterator & getScopes(const char * filter)
  931. {
  932. assertex(!filter);
  933. return * new SuperHashIIteratorOf<IStatisticCollection, IStatisticCollectionIterator, false>(children);
  934. }
  935. virtual void getMinMaxScope(IStringVal & minValue, IStringVal & maxValue, StatisticScopeType searchScopeType) const
  936. {
  937. if (id.queryScopeType() == searchScopeType)
  938. {
  939. const char * curMin = minValue.str();
  940. const char * curMax = maxValue.str();
  941. StringBuffer name;
  942. id.getScopeText(name);
  943. if (!curMin || !*curMin || strcmp(name.str(), curMin) < 0)
  944. minValue.set(name.str());
  945. if (!curMax || strcmp(name.str(), curMax) > 0)
  946. maxValue.set(name.str());
  947. }
  948. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  949. for (iter.first(); iter.isValid(); iter.next())
  950. iter.query().getMinMaxScope(minValue, maxValue, searchScopeType);
  951. }
  952. virtual void getMinMaxActivity(unsigned & minValue, unsigned & maxValue) const
  953. {
  954. unsigned activityId = id.queryActivity();
  955. if (activityId)
  956. {
  957. if ((minValue == 0) || (activityId < minValue))
  958. minValue = activityId;
  959. if (activityId > maxValue)
  960. maxValue = activityId;
  961. }
  962. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  963. for (iter.first(); iter.isValid(); iter.next())
  964. iter.query().getMinMaxActivity(minValue, maxValue);
  965. }
  966. //other public interface functions
  967. void addStatistic(StatisticKind kind, unsigned __int64 value)
  968. {
  969. stats.append(*new Statistic(kind, value));
  970. }
  971. CStatisticCollection * ensureSubScope(const StatsScopeId & search, bool hasChildren)
  972. {
  973. //MORE: Implement hasChildren
  974. return resolveSubScope(search, true, false);
  975. }
  976. CStatisticCollection * resolveSubScope(const StatsScopeId & search, bool create, bool replace)
  977. {
  978. if (!replace)
  979. {
  980. CStatisticCollection * match = children.find(&search);
  981. if (match)
  982. return LINK(match);
  983. }
  984. if (create)
  985. {
  986. CStatisticCollection * ret = new CStatisticCollection(this, search);
  987. children.add(*ret);
  988. return LINK(ret);
  989. }
  990. return NULL;
  991. }
  992. virtual void serialize(MemoryBuffer & out) const
  993. {
  994. out.append(getCollectionType());
  995. id.serialize(out);
  996. out.append(stats.ordinality());
  997. ForEachItemIn(iStat, stats)
  998. stats.item(iStat).serialize(out);
  999. out.append(children.ordinality());
  1000. SuperHashIteratorOf<CStatisticCollection> iter(children, false);
  1001. for (iter.first(); iter.isValid(); iter.next())
  1002. iter.query().serialize(out);
  1003. }
  1004. private:
  1005. StatsScopeId id;
  1006. CStatisticCollection * parent;
  1007. CollectionHashTable children;
  1008. StatsArray stats;
  1009. };
  1010. //---------------------------------------------------------------------------------------------------------------------
  1011. void CollectionHashTable::onAdd(void *et)
  1012. {
  1013. }
  1014. void CollectionHashTable::onRemove(void *et)
  1015. {
  1016. CStatisticCollection * elem = reinterpret_cast<CStatisticCollection *>(et);
  1017. elem->Release();
  1018. }
  1019. unsigned CollectionHashTable::getHashFromElement(const void *et) const
  1020. {
  1021. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1022. return elem->id.getHash();
  1023. }
  1024. unsigned CollectionHashTable::getHashFromFindParam(const void *fp) const
  1025. {
  1026. const StatsScopeId * search = reinterpret_cast<const StatsScopeId *>(fp);
  1027. return search->getHash();
  1028. }
  1029. const void * CollectionHashTable::getFindParam(const void *et) const
  1030. {
  1031. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1032. return &elem->id;
  1033. }
  1034. bool CollectionHashTable::matchesFindParam(const void *et, const void *key, unsigned fphash) const
  1035. {
  1036. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1037. const StatsScopeId * search = reinterpret_cast<const StatsScopeId *>(key);
  1038. return elem->id.matches(*search);
  1039. }
  1040. bool CollectionHashTable::matchesElement(const void *et, const void *searchET) const
  1041. {
  1042. const CStatisticCollection * elem = reinterpret_cast<const CStatisticCollection *>(et);
  1043. const CStatisticCollection * searchElem = reinterpret_cast<const CStatisticCollection *>(searchET);
  1044. return elem->id.matches(searchElem->id);
  1045. }
  1046. //---------------------------------------------------------------------------------------------------------------------
  1047. class CRootStatisticCollection : public CStatisticCollection
  1048. {
  1049. public:
  1050. CRootStatisticCollection(StatisticCreatorType _creatorType, const char * _creator, const StatsScopeId & _id)
  1051. : CStatisticCollection(NULL, _id), creatorType(_creatorType), creator(_creator)
  1052. {
  1053. whenCreated = getTimeStampNowValue();
  1054. }
  1055. CRootStatisticCollection(MemoryBuffer & in, unsigned version) : CStatisticCollection(NULL, in, version)
  1056. {
  1057. byte creatorTypeByte;
  1058. in.read(creatorTypeByte);
  1059. creatorType = (StatisticCreatorType)creatorTypeByte;
  1060. in.read(creator);
  1061. in.read(whenCreated);
  1062. }
  1063. virtual byte getCollectionType() const { return SCroot; }
  1064. virtual unsigned __int64 queryWhenCreated() const
  1065. {
  1066. return whenCreated;
  1067. }
  1068. virtual void serialize(MemoryBuffer & out) const
  1069. {
  1070. CStatisticCollection::serialize(out);
  1071. out.append((byte)creatorType);
  1072. out.append(creator);
  1073. out.append(whenCreated);
  1074. }
  1075. public:
  1076. StatisticCreatorType creatorType;
  1077. StringAttr creator;
  1078. unsigned __int64 whenCreated;
  1079. };
  1080. //---------------------------------------------------------------------------------------------------------------------
  1081. void serializeStatisticCollection(MemoryBuffer & out, IStatisticCollection * collection)
  1082. {
  1083. unsigned currentStatisticsVersion = 1;
  1084. out.append(currentStatisticsVersion);
  1085. collection->serialize(out);
  1086. }
  1087. static CStatisticCollection * deserializeCollection(CStatisticCollection * parent, MemoryBuffer & in, unsigned version)
  1088. {
  1089. byte kind;
  1090. in.read(kind);
  1091. switch (kind)
  1092. {
  1093. case SCroot:
  1094. assertex(!parent);
  1095. return new CRootStatisticCollection(in, version);
  1096. case SCintermediate:
  1097. return new CStatisticCollection(parent, in, version);
  1098. default:
  1099. UNIMPLEMENTED;
  1100. }
  1101. }
  1102. IStatisticCollection * createStatisticCollection(MemoryBuffer & in)
  1103. {
  1104. unsigned version;
  1105. in.read(version);
  1106. return deserializeCollection(NULL, in, version);
  1107. }
  1108. //--------------------------------------------------------------------------------------------------------------------
  1109. class StatisticGatherer : implements CInterfaceOf<IStatisticGatherer>
  1110. {
  1111. public:
  1112. StatisticGatherer(CStatisticCollection * scope) : rootScope(scope)
  1113. {
  1114. scopes.append(*scope);
  1115. }
  1116. virtual void beginScope(const StatsScopeId & id)
  1117. {
  1118. CStatisticCollection & tos = scopes.tos();
  1119. scopes.append(*tos.ensureSubScope(id, true));
  1120. }
  1121. virtual void beginActivityScope(unsigned id)
  1122. {
  1123. StatsScopeId scopeId(SSTactivity, id);
  1124. CStatisticCollection & tos = scopes.tos();
  1125. scopes.append(*tos.ensureSubScope(scopeId, false));
  1126. }
  1127. virtual void beginSubGraphScope(unsigned id)
  1128. {
  1129. StatsScopeId scopeId(SSTsubgraph, id);
  1130. CStatisticCollection & tos = scopes.tos();
  1131. scopes.append(*tos.ensureSubScope(scopeId, true));
  1132. }
  1133. virtual void beginEdgeScope(unsigned id, unsigned oid)
  1134. {
  1135. StatsScopeId scopeId(SSTedge, id, oid);
  1136. CStatisticCollection & tos = scopes.tos();
  1137. scopes.append(*tos.ensureSubScope(scopeId, false));
  1138. }
  1139. virtual void endScope()
  1140. {
  1141. scopes.pop();
  1142. }
  1143. virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
  1144. {
  1145. CStatisticCollection & tos = scopes.tos();
  1146. tos.addStatistic(kind, value);
  1147. }
  1148. virtual IStatisticCollection * getResult()
  1149. {
  1150. return LINK(rootScope);
  1151. }
  1152. protected:
  1153. ICopyArrayOf<CStatisticCollection> scopes;
  1154. Linked<CStatisticCollection> rootScope;
  1155. };
  1156. extern IStatisticGatherer * createStatisticsGatherer(StatisticCreatorType creatorType, const char * creator, const StatsScopeId & rootScope)
  1157. {
  1158. //creator unused at the moment.
  1159. Owned<CStatisticCollection> rootCollection = new CRootStatisticCollection(creatorType, creator, rootScope);
  1160. return new StatisticGatherer(rootCollection);
  1161. }
  1162. //--------------------------------------------------------------------------------------------------------------------
  1163. //This class is used to gather statistics for an activity.
  1164. class CRuntimeStatisticCollection : public IStatisticCollection
  1165. {
  1166. public:
  1167. CRuntimeStatisticCollection(const StatisticsMapping & _mapping) : mapping(_mapping)
  1168. {
  1169. unsigned num = mapping.numStatistics();
  1170. values = new unsigned __int64[num];
  1171. reset();
  1172. }
  1173. ~CRuntimeStatisticCollection()
  1174. {
  1175. delete [] values;
  1176. }
  1177. virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
  1178. {
  1179. unsigned index = queryMapping().getIndex(kind);
  1180. values[index] += value;
  1181. }
  1182. virtual void setStatistic(StatisticKind kind, unsigned __int64 value)
  1183. {
  1184. unsigned index = queryMapping().getIndex(kind);
  1185. values[index] = value;
  1186. }
  1187. virtual unsigned __int64 getStatisticValue(StatisticKind kind) const
  1188. {
  1189. unsigned index = queryMapping().getIndex(kind);
  1190. return values[index];
  1191. }
  1192. void reset()
  1193. {
  1194. unsigned num = mapping.numStatistics();
  1195. memset(values, 0, sizeof(unsigned __int64) * num);
  1196. }
  1197. inline const StatisticsMapping & queryMapping() const { return mapping; };
  1198. inline unsigned ordinality() const { return mapping.numStatistics(); }
  1199. inline StatisticKind getKind(unsigned i) const { return mapping.getKind(i); }
  1200. private:
  1201. const StatisticsMapping & mapping;
  1202. unsigned __int64 * values;
  1203. };
  1204. void processStatistics(IStatisticGatherer & target, const CRuntimeStatisticCollection & stats)
  1205. {
  1206. ForEachItemIn(i, stats)
  1207. {
  1208. StatisticKind kind = stats.getKind(i);
  1209. target.addStatistic(kind, stats.getStatisticValue(kind));
  1210. }
  1211. }
  1212. // ------------------------- old code -------------------------
  1213. extern jlib_decl StatisticKind mapRoxieStatKind(unsigned i)
  1214. {
  1215. switch (i)
  1216. {
  1217. case STATS_INDEX_SEEKS: return StNumIndexSeeks;
  1218. case STATS_INDEX_SCANS: return StNumIndexScans;
  1219. case STATS_INDEX_WILDSEEKS: return StNumIndexWildSeeks;
  1220. case STATS_INDEX_SKIPS: return StNumIndexSkips;
  1221. case STATS_INDEX_NULLSKIPS: return StNumIndexNullSkips;
  1222. case STATS_INDEX_MERGES: return StNumIndexMerges;
  1223. case STATS_BLOBCACHEHIT: return StNumBlobCacheHits;
  1224. case STATS_LEAFCACHEHIT: return StNumLeafCacheHits;
  1225. case STATS_NODECACHEHIT: return StNumNodeCacheHits;
  1226. case STATS_PRELOADCACHEHIT: return StNumPreloadCacheHits;
  1227. case STATS_BLOBCACHEADD: return StNumBlobCacheAdds;
  1228. case STATS_LEAFCACHEADD: return StNumLeafCacheAdds;
  1229. case STATS_NODECACHEADD: return StNumNodeCacheAdds;
  1230. case STATS_PRELOADCACHEADD: return StNumPreloadCacheAdds;
  1231. case STATS_INDEX_MERGECOMPARES: return StNumIndexMergeCompares;
  1232. case STATS_SERVERCACHEHIT: return StNumServerCacheHits;
  1233. case STATS_ACCEPTED: return StNumIndexAccepted;
  1234. case STATS_REJECTED: return StNumIndexRejected;
  1235. case STATS_ATMOST: return StNumAtmostTriggered;
  1236. case STATS_DISK_SEEKS: return StNumDiskSeeks;
  1237. case STATS_SOAPCALL_LATENCY: return StTimeSoapcall;
  1238. default:
  1239. throwUnexpected();
  1240. }
  1241. }
  1242. extern jlib_decl StatisticMeasure getStatMeasure(unsigned i)
  1243. {
  1244. return SMeasureCount;
  1245. }
  1246. //---------------------------------------------------
  1247. bool ScopedItemFilter::matchDepth(unsigned low, unsigned high) const
  1248. {
  1249. if (maxDepth && low && maxDepth < low)
  1250. return false;
  1251. if (minDepth && high && minDepth > high)
  1252. return false;
  1253. return true;
  1254. }
  1255. bool ScopedItemFilter::match(const char * search) const
  1256. {
  1257. if (search)
  1258. {
  1259. if (value)
  1260. {
  1261. if (hasWildcard)
  1262. {
  1263. //MORE: If wildcarding ends up being used a lot then this should be replaced with something that creates a DFA
  1264. if (!WildMatch(search, value, false))
  1265. return false;
  1266. }
  1267. else
  1268. {
  1269. return streq(search, value);
  1270. }
  1271. }
  1272. if (minDepth || maxDepth)
  1273. {
  1274. unsigned searchDepth = queryStatisticsDepth(search);
  1275. if (searchDepth < minDepth)
  1276. return false;
  1277. if (maxDepth && searchDepth > maxDepth)
  1278. return false;
  1279. }
  1280. }
  1281. return true;
  1282. }
  1283. void ScopedItemFilter::set(const char * _value)
  1284. {
  1285. if (_value && *_value && !streq(_value, "*") )
  1286. {
  1287. value.set(_value);
  1288. minDepth = queryStatisticsDepth(_value);
  1289. if (!strchr(_value, '*'))
  1290. {
  1291. maxDepth = minDepth;
  1292. hasWildcard = strchr(_value, '?') != NULL;
  1293. }
  1294. else
  1295. hasWildcard = true;
  1296. }
  1297. else
  1298. value.clear();
  1299. }
  1300. void ScopedItemFilter::setDepth(unsigned _depth)
  1301. {
  1302. minDepth = _depth;
  1303. maxDepth = _depth;
  1304. }
  1305. void ScopedItemFilter::setDepth(unsigned _minDepth, unsigned _maxDepth)
  1306. {
  1307. minDepth = _minDepth;
  1308. maxDepth = _maxDepth;
  1309. }
  1310. StatisticsFilter::StatisticsFilter()
  1311. {
  1312. init();
  1313. }
  1314. StatisticsFilter::StatisticsFilter(const char * filter)
  1315. {
  1316. init();
  1317. setFilter(filter);
  1318. }
  1319. StatisticsFilter::StatisticsFilter(StatisticCreatorType _creatorType, StatisticScopeType _scopeType, StatisticMeasure _measure, StatisticKind _kind)
  1320. {
  1321. init();
  1322. creatorType = _creatorType;
  1323. scopeType = _scopeType;
  1324. measure = _measure;
  1325. kind = _kind;
  1326. }
  1327. StatisticsFilter::StatisticsFilter(const char * _creatorType, const char * _scopeType, const char * _kind)
  1328. {
  1329. init();
  1330. set(_creatorType, _scopeType, _kind);
  1331. }
  1332. StatisticsFilter::StatisticsFilter(const char * _creatorTypeText, const char * _creator, const char * _scopeTypeText, const char * _scope, const char * _measureText, const char * _kindText)
  1333. {
  1334. init();
  1335. set(_creatorTypeText, _creator, _scopeTypeText, _scope, _measureText, _kindText);
  1336. }
  1337. StatisticsFilter::StatisticsFilter(StatisticCreatorType _creatorType, const char * _creator, StatisticScopeType _scopeType, const char * _scope, StatisticMeasure _measure, StatisticKind _kind)
  1338. {
  1339. init();
  1340. creatorType = _creatorType;
  1341. setCreator(_creator);
  1342. scopeType = _scopeType;
  1343. setScope(_scope);
  1344. measure = _measure;
  1345. kind = _kind;
  1346. }
  1347. void StatisticsFilter::init()
  1348. {
  1349. mergeSources = true;
  1350. creatorType = SCTall;
  1351. scopeType = SSTall;
  1352. measure = SMeasureAll;
  1353. kind = StKindAll;
  1354. }
  1355. bool StatisticsFilter::matches(StatisticCreatorType curCreatorType, const char * curCreator, StatisticScopeType curScopeType, const char * curScope, StatisticMeasure curMeasure, StatisticKind curKind) const
  1356. {
  1357. if ((curCreatorType != SCTall) && (creatorType != SCTall) && (creatorType != curCreatorType))
  1358. return false;
  1359. if ((curScopeType != SSTall) && (scopeType != SSTall) && (scopeType != curScopeType))
  1360. return false;
  1361. if ((curMeasure != SMeasureAll) && (measure != SMeasureAll) && (measure != curMeasure))
  1362. return false;
  1363. if ((curKind!= StKindAll) && (kind != StKindAll) && (kind != curKind))
  1364. return false;
  1365. if (!creatorFilter.match(curCreator))
  1366. return false;
  1367. if (!scopeFilter.match(curScope))
  1368. return false;
  1369. return true;
  1370. }
  1371. void StatisticsFilter::set(const char * creatorTypeText, const char * scopeTypeText, const char * kindText)
  1372. {
  1373. StatisticCreatorType creatorType = queryCreatorType(creatorTypeText);
  1374. StatisticScopeType scopeType = queryScopeType(scopeTypeText);
  1375. if (creatorType != SCTnone)
  1376. setCreatorType(creatorType);
  1377. if (scopeType != SSTnone)
  1378. setScopeType(scopeType);
  1379. setKind(kindText);
  1380. }
  1381. void StatisticsFilter::set(const char * _creatorTypeText, const char * _creator, const char * _scopeTypeText, const char * _scope, const char * _measureText, const char * _kindText)
  1382. {
  1383. StatisticMeasure newMeasure = queryMeasure(_measureText);
  1384. if (measure != SMeasureNone)
  1385. setMeasure(measure);
  1386. set(_creatorTypeText, _scopeTypeText, _kindText);
  1387. setCreator(_creator);
  1388. setScope(_scope);
  1389. }
  1390. void StatisticsFilter::setCreatorDepth(unsigned _minCreatorDepth, unsigned _maxCreatorDepth)
  1391. {
  1392. creatorFilter.setDepth(_minCreatorDepth, _maxCreatorDepth);
  1393. }
  1394. void StatisticsFilter::setCreator(const char * _creator)
  1395. {
  1396. creatorFilter.set(_creator);
  1397. }
  1398. void StatisticsFilter::setCreatorType(StatisticCreatorType _creatorType)
  1399. {
  1400. creatorType = _creatorType;
  1401. }
  1402. void StatisticsFilter::setFilter(const char * filter)
  1403. {
  1404. //MORE: Process the filter from a text representation
  1405. }
  1406. void StatisticsFilter::setScopeDepth(unsigned _scopeDepth)
  1407. {
  1408. scopeFilter.setDepth(_scopeDepth);
  1409. }
  1410. void StatisticsFilter::setScopeDepth(unsigned _minScopeDepth, unsigned _maxScopeDepth)
  1411. {
  1412. scopeFilter.setDepth(_minScopeDepth, _maxScopeDepth);
  1413. }
  1414. void StatisticsFilter::setScope(const char * _scope)
  1415. {
  1416. scopeFilter.set(_scope);
  1417. }
  1418. void StatisticsFilter::setScopeType(StatisticScopeType _scopeType)
  1419. {
  1420. scopeType = _scopeType;
  1421. }
  1422. void StatisticsFilter::setMeasure(StatisticMeasure _measure)
  1423. {
  1424. measure = _measure;
  1425. }
  1426. void StatisticsFilter::setMergeSources(bool _value)
  1427. {
  1428. mergeSources = _value;
  1429. }
  1430. void StatisticsFilter::setKind(StatisticKind _kind)
  1431. {
  1432. kind = _kind;
  1433. if (measure == SMeasureAll)
  1434. measure = queryMeasure(kind);
  1435. }
  1436. void StatisticsFilter::setKind(const char * _kind)
  1437. {
  1438. if (!_kind || !*_kind || streq(_kind, "*"))
  1439. {
  1440. measure = SMeasureAll;
  1441. kind = StKindAll;
  1442. return;
  1443. }
  1444. //Convert a kind wildcard to a measure
  1445. for (unsigned i1=SMeasureAll+1; i1 < SMeasureMax; i1++)
  1446. {
  1447. const char * prefix = queryMeasurePrefix((StatisticMeasure)i1);
  1448. size_t len = strlen(prefix);
  1449. if (strnicmp(_kind, prefix, len) == 0)
  1450. {
  1451. setMeasure((StatisticMeasure)i1);
  1452. //Treat When* and When as filters on times.
  1453. if (streq(_kind + len, "*") || !_kind[len])
  1454. return;
  1455. }
  1456. }
  1457. //Other wildcards not currently supported
  1458. kind = queryStatisticKind(_kind);
  1459. }
  1460. //---------------------------------------------------
  1461. class CStatsCategory : public CInterface
  1462. {
  1463. public:
  1464. StringAttr longName;
  1465. StringAttr shortName;
  1466. CStatsCategory(const char *_longName, const char *_shortName)
  1467. : longName(_longName), shortName(_shortName)
  1468. {
  1469. }
  1470. bool match(const char *_longName, const char *_shortName)
  1471. {
  1472. bool lm = stricmp(_longName, longName)==0;
  1473. bool sm = stricmp(_shortName, shortName)==0;
  1474. if (lm || sm)
  1475. {
  1476. if (lm && sm)
  1477. return true;
  1478. throw MakeStringException(0, "A stats category %s (%s) is already registered", shortName.get(), longName.get());
  1479. }
  1480. return false;
  1481. }
  1482. };
  1483. static CIArrayOf<CStatsCategory> statsCategories;
  1484. static CriticalSection statsCategoriesCrit;
  1485. extern int registerStatsCategory(const char *longName, const char *shortName)
  1486. {
  1487. CriticalBlock b(statsCategoriesCrit);
  1488. ForEachItemIn(idx, statsCategories)
  1489. {
  1490. if (statsCategories.item(idx).match(longName, shortName))
  1491. return idx;
  1492. }
  1493. statsCategories.append(*new CStatsCategory(longName, shortName));
  1494. return statsCategories.ordinality()-1;
  1495. }
  1496. static void checkKind(StatisticKind kind)
  1497. {
  1498. StatisticMeasure measure = queryMeasure(kind);
  1499. const char * shortName = queryStatisticName(kind);
  1500. StringBuffer longName;
  1501. queryLongStatisticName(longName, kind);
  1502. const char * tagName = queryTreeTag(kind);
  1503. const char * prefix = queryMeasurePrefix(measure);
  1504. //Check short names are all correctly prefixed.
  1505. assertex(strncmp(shortName, prefix, strlen(prefix)) == 0);
  1506. }
  1507. static void checkDistributedKind(StatisticKind kind)
  1508. {
  1509. checkKind(kind);
  1510. checkKind((StatisticKind)(kind|StMinX));
  1511. checkKind((StatisticKind)(kind|StMaxX));
  1512. checkKind((StatisticKind)(kind|StAvgX));
  1513. checkKind((StatisticKind)(kind|StSkew));
  1514. checkKind((StatisticKind)(kind|StSkewMin));
  1515. checkKind((StatisticKind)(kind|StSkewMax));
  1516. checkKind((StatisticKind)(kind|StNodeMin));
  1517. checkKind((StatisticKind)(kind|StNodeMax));
  1518. checkKind((StatisticKind)(kind|StDeltaX));
  1519. }
  1520. void verifyStatisticFunctions()
  1521. {
  1522. assertex(_elements_in(measureNames) == SMeasureMax+1 && !measureNames[SMeasureMax]);
  1523. assertex(_elements_in(creatorTypeNames) == SCTmax+1 && !creatorTypeNames[SCTmax]);
  1524. assertex(_elements_in(scopeTypeNames) == SSTmax+1 && !scopeTypeNames[SSTmax]);
  1525. //Check the various functions return values for all possible values.
  1526. for (unsigned i1=SMeasureAll; i1 < SMeasureMax; i1++)
  1527. {
  1528. const char * prefix = queryMeasurePrefix((StatisticMeasure)i1);
  1529. const char * name = queryMeasureName((StatisticMeasure)i1);
  1530. assertex(queryMeasure(name) == i1);
  1531. }
  1532. for (StatisticScopeType sst = SSTnone; sst < SSTmax; sst = (StatisticScopeType)(sst+1))
  1533. {
  1534. const char * name = queryScopeTypeName(sst);
  1535. assertex(queryScopeType(name) == sst);
  1536. }
  1537. for (StatisticCreatorType sct = SCTnone; sct < SCTmax; sct = (StatisticCreatorType)(sct+1))
  1538. {
  1539. const char * name = queryCreatorTypeName(sct);
  1540. assertex(queryCreatorType(name) == sct);
  1541. }
  1542. for (unsigned i2=StKindAll+1; i2 < StMax; i2++)
  1543. {
  1544. checkDistributedKind((StatisticKind)i2);
  1545. }
  1546. }
  1547. #if 0
  1548. MODULE_INIT(INIT_PRIORITY_STANDARD)
  1549. {
  1550. verifyStatisticFunctions();
  1551. return true;
  1552. }
  1553. #endif