ProfileInstrument.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "CommonCorePch.h"
  6. #ifdef PROFILE_EXEC
  7. #include "Core/ProfileInstrument.h"
  8. #define HIRES_PROFILER
  9. namespace Js
  10. {
  11. ///----------------------------------------------------------------------------
  12. ///----------------------------------------------------------------------------
  13. ///
  14. /// class Profiler::UnitData
  15. ///
  16. ///----------------------------------------------------------------------------
  17. ///----------------------------------------------------------------------------
  18. ///----------------------------------------------------------------------------
  19. ///
  20. /// UnitData::UnitData
  21. ///
  22. /// Constructor
  23. ///
  24. ///----------------------------------------------------------------------------
  25. UnitData::UnitData()
  26. {
  27. this->incl = 0;
  28. this->excl = 0;
  29. this->max = 0;
  30. this->count = 0;
  31. }
  32. ///----------------------------------------------------------------------------
  33. ///
  34. /// UnitData::Add
  35. ///
  36. ///----------------------------------------------------------------------------
  37. void
  38. UnitData::Add(TimeStamp incl, TimeStamp excl)
  39. {
  40. this->incl += incl;
  41. this->excl += excl;
  42. this->count++;
  43. if (incl > this->max)
  44. {
  45. this->max = incl;
  46. }
  47. }
  48. ///----------------------------------------------------------------------------
  49. ///----------------------------------------------------------------------------
  50. ///
  51. /// class Profiler
  52. ///
  53. ///----------------------------------------------------------------------------
  54. ///----------------------------------------------------------------------------
  55. ///----------------------------------------------------------------------------
  56. ///
  57. /// Profiler::Profiler
  58. ///
  59. /// Constructor
  60. ///
  61. ///----------------------------------------------------------------------------
  62. Profiler::Profiler(ArenaAllocator * allocator) :
  63. alloc(allocator),
  64. rootNode(NULL)
  65. {
  66. this->curNode = &this->rootNode;
  67. for(int i = 0; i < PhaseCount; i++)
  68. {
  69. this->inclSumAtLevel[i] = 0;
  70. }
  71. }
  72. ///----------------------------------------------------------------------------
  73. ///
  74. /// Profiler::Begin
  75. ///
  76. ///----------------------------------------------------------------------------
  77. void
  78. Profiler::Begin(Phase tag)
  79. {
  80. Push(TimeEntry(tag, GetTime()));
  81. }
  82. ///----------------------------------------------------------------------------
  83. ///
  84. /// Profiler::End
  85. ///
  86. ///----------------------------------------------------------------------------
  87. void
  88. Profiler::End(Phase tag)
  89. {
  90. Pop(TimeEntry(tag, GetTime()));
  91. }
  92. void
  93. Profiler::Suspend(Phase tag, SuspendRecord * suspendRecord)
  94. {
  95. suspendRecord->count = 0;
  96. Phase topTag;
  97. do
  98. {
  99. topTag = timeStack.Peek()->tag;
  100. Pop(TimeEntry(topTag, GetTime()));
  101. suspendRecord->phase[suspendRecord->count++] = topTag;
  102. } while(topTag != tag);
  103. }
  104. void
  105. Profiler::Resume(SuspendRecord * suspendRecord)
  106. {
  107. while (suspendRecord->count)
  108. {
  109. suspendRecord->count--;
  110. Begin(suspendRecord->phase[suspendRecord->count]);
  111. }
  112. }
  113. ///----------------------------------------------------------------------------
  114. ///
  115. /// Profiler::EndAllUpTo
  116. ///
  117. /// Ends all phases up to the specified phase. Useful for catching exceptions
  118. /// after a phase was started, and ending all intermediate phases until the
  119. /// first phase that was started.
  120. ///
  121. ///----------------------------------------------------------------------------
  122. void
  123. Profiler::EndAllUpTo(Phase tag)
  124. {
  125. Phase topTag;
  126. do
  127. {
  128. topTag = timeStack.Peek()->tag;
  129. Pop(TimeEntry(topTag, GetTime()));
  130. } while(topTag != tag);
  131. }
  132. ///----------------------------------------------------------------------------
  133. ///
  134. /// Profiler::Push
  135. ///
  136. /// 1. Push entry on stack.
  137. /// 2. Update curNode
  138. ///
  139. ///----------------------------------------------------------------------------
  140. void
  141. Profiler::Push(TimeEntry entry)
  142. {
  143. AssertMsg(NULL != curNode, "Profiler Stack Corruption");
  144. this->timeStack.Push(entry);
  145. if(!curNode->ChildExistsAt(entry.tag))
  146. {
  147. TypeNode * node = AnewNoThrow(this->alloc, TypeNode, curNode);
  148. // We crash if we run out of memory here and we don't care
  149. curNode->SetChildAt(entry.tag, node);
  150. }
  151. curNode = curNode->GetChildAt(entry.tag);
  152. }
  153. ///----------------------------------------------------------------------------
  154. ///
  155. /// Profiler::Pop
  156. ///
  157. /// Core logic for the timer. Calculated the exclusive, inclusive times.
  158. /// There is a list inclSumAtLevel which stores accumulates the inclusive sum
  159. /// of all the tags that where 'pushed' after this tag.
  160. ///
  161. /// Consider the following calls. fx indicates Push and fx', the corresponding Pop
  162. ///
  163. /// f1
  164. /// f2
  165. /// f3
  166. /// f3'
  167. /// f2'
  168. /// f4
  169. /// f5
  170. /// f5'
  171. /// f4'
  172. /// f1'
  173. ///
  174. /// calculating the inclusive times are trivial. Let us calculate the exclusive
  175. /// time for f1. That would be
  176. /// excl(f1) = incl(f1) - [incl(f2) + incl(f4)]
  177. ///
  178. /// Basically if a function is at level 'x' then we need to deduct from its
  179. /// exclusive times, the inclusive times of all the functions at level 'x + 1'
  180. /// We don't care about deeper levels. Hence 'inclSumAtLevel' array which accumulates
  181. /// the sum of variables at different levels.
  182. ///
  183. /// Reseting the next level is also required. In the above example, f3 and f5 are
  184. /// at the same level. if we don't reset level 3 when popping f2, then we will
  185. /// have wrong sums for f4. So once a tag has been popped, all sums at its higher
  186. /// levels is set to zero. (Of course we just need to reset the next level and
  187. /// all above levels will invariably remain zero)
  188. ///
  189. ///----------------------------------------------------------------------------
  190. void
  191. Profiler::Pop(TimeEntry curEntry)
  192. {
  193. int curLevel = this->timeStack.Count();
  194. TimeEntry *entry = this->timeStack.Pop();
  195. AssertMsg(entry->tag == curEntry.tag, "Profiler Stack corruption, push pop entries do not correspond to the same tag");
  196. TimeStamp inclusive = curEntry.time - entry->time;
  197. TimeStamp exclusive = inclusive - this->inclSumAtLevel[curLevel +1];
  198. Assert(inclusive >= 0);
  199. Assert(exclusive >= 0);
  200. this->inclSumAtLevel[curLevel + 1] = 0;
  201. this->inclSumAtLevel[curLevel] += inclusive;
  202. curNode->GetValue()->Add(inclusive, exclusive);
  203. curNode = curNode->GetParent();
  204. AssertMsg(curNode != NULL, "Profiler stack corruption");
  205. }
  206. void
  207. Profiler::Merge(Profiler * profiler)
  208. {
  209. MergeTree(&rootNode, &profiler->rootNode);
  210. if (profiler->timeStack.Count() > 1)
  211. {
  212. FixedStack<TimeEntry, MaxStackDepth> reverseStack;
  213. do
  214. {
  215. reverseStack.Push(*profiler->timeStack.Pop());
  216. }
  217. while (profiler->timeStack.Count() > 1);
  218. do
  219. {
  220. TimeEntry * entry = reverseStack.Pop();
  221. this->Push(*entry);
  222. profiler->timeStack.Push(*entry);
  223. }
  224. while (reverseStack.Count() != 0);
  225. }
  226. }
  227. void
  228. Profiler::MergeTree(TypeNode * toNode, TypeNode * fromNode)
  229. {
  230. UnitData * toData = toNode->GetValue();
  231. const UnitData * fromData = fromNode->GetValue();
  232. toData->count += fromData->count;
  233. toData->incl += fromData->incl;
  234. toData->excl += fromData->excl;
  235. if (fromData->max > toData->max)
  236. {
  237. toData->max = fromData->max;
  238. }
  239. for (int i = 0; i < PhaseCount; i++)
  240. {
  241. if (fromNode->ChildExistsAt(i))
  242. {
  243. TypeNode * fromChild = fromNode->GetChildAt(i);
  244. TypeNode * toChild;
  245. if (!toNode->ChildExistsAt(i))
  246. {
  247. toChild = Anew(this->alloc, TypeNode, toNode);
  248. toNode->SetChildAt(i, toChild);
  249. }
  250. else
  251. {
  252. toChild = toNode->GetChildAt(i);
  253. }
  254. MergeTree(toChild, fromChild);
  255. }
  256. }
  257. }
  258. ///----------------------------------------------------------------------------
  259. ///
  260. /// Profiler::PrintTree
  261. ///
  262. /// Private method that walks the tree and prints it recursively.
  263. ///
  264. ///----------------------------------------------------------------------------
  265. void
  266. Profiler::PrintTree(TypeNode *node, TypeNode *baseNode, int column, TimeStamp freq)
  267. {
  268. const UnitData *base = baseNode->GetValue();
  269. for(int i = 0; i < PhaseCount; i++)
  270. {
  271. if(node->ChildExistsAt(i))
  272. {
  273. UnitData *data = node->GetChildAt(i)->GetValue();
  274. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  275. if( int(data->incl * 100 / base->incl) >= Configuration::Global.flags.ProfileThreshold) // threshold
  276. #endif
  277. {
  278. Output::SkipToColumn(column);
  279. Output::Print(_u("%-*s %7.1f %5d %7.1f %5d %7.1f %7.1f %5d\n"),
  280. (Profiler::PhaseNameWidth-column), PhaseNames[i],
  281. (double)data->incl / freq , // incl
  282. int(data->incl * 100 / base->incl ), // incl %
  283. (double)data->excl / freq , // excl
  284. int(data->excl * 100 / base->incl ), // excl %
  285. (double)data->max / freq , // max
  286. (double)data->incl / ( freq * data->count ), // mean
  287. int(data->count) // count
  288. );
  289. }
  290. PrintTree(node->GetChildAt(i), baseNode, column + Profiler::TabWidth, freq);
  291. }
  292. }
  293. }
  294. ///----------------------------------------------------------------------------
  295. ///
  296. /// Profiler::Print
  297. ///
  298. /// Pretty printer
  299. ///
  300. ///----------------------------------------------------------------------------
  301. void
  302. Profiler::Print(Phase baseTag)
  303. {
  304. if (baseTag == InvalidPhase)
  305. {
  306. baseTag = AllPhase; // default to all phase
  307. }
  308. const TimeStamp freq = this->GetFrequency();
  309. bool foundNode = false;
  310. ForEachNode(baseTag, &rootNode, [&](TypeNode *const baseNode, const Phase parentTag)
  311. {
  312. if(!foundNode)
  313. {
  314. foundNode = true;
  315. Output::Print(_u("%-*s:%7s %5s %7s %5s %7s %7s %5s\n"),
  316. (Profiler::PhaseNameWidth-0),
  317. _u("Profiler Report"),
  318. _u("Incl"),
  319. _u("(%)"),
  320. _u("Excl"),
  321. _u("(%)"),
  322. _u("Max"),
  323. _u("Mean"),
  324. _u("Count")
  325. );
  326. Output::Print(_u("-------------------------------------------------------------------------------\n"));
  327. }
  328. UnitData *data = baseNode->GetValue();
  329. if(0 == data->count)
  330. {
  331. Output::Print(_u("The phase : %s was never started"), PhaseNames[baseTag]);
  332. return;
  333. }
  334. int indent = 0;
  335. if(parentTag != InvalidPhase)
  336. {
  337. TypeNode *const parentNode = baseNode->GetParent();
  338. Assert(parentNode);
  339. Output::Print(_u("%-*s\n"), (Profiler::PhaseNameWidth-0), PhaseNames[parentTag]);
  340. indent += Profiler::TabWidth;
  341. }
  342. if(indent)
  343. {
  344. Output::SkipToColumn(indent);
  345. }
  346. Output::Print(_u("%-*s %7.1f %5d %7.1f %5d %7.1f %7.1f %5d\n"),
  347. (Profiler::PhaseNameWidth-indent),
  348. PhaseNames[baseTag],
  349. (double)data->incl / freq , // incl
  350. int(100), // incl %
  351. (double)data->excl / freq , // excl
  352. int(data->excl * 100 / data->incl ), // excl %
  353. (double)data->max / freq , // max
  354. (double)data->incl / ( freq * data->count ),// mean
  355. int(data->count) // count
  356. );
  357. indent += Profiler::TabWidth;
  358. PrintTree(baseNode, baseNode, indent, freq);
  359. });
  360. if(foundNode)
  361. {
  362. Output::Print(_u("-------------------------------------------------------------------------------\n"));
  363. Output::Flush();
  364. }
  365. }
  366. ///----------------------------------------------------------------------------
  367. ///
  368. /// Profiler::FindNode
  369. ///
  370. /// Does a tree traversal(DFS) and finds the first occurrence of the 'tag'
  371. ///
  372. ///----------------------------------------------------------------------------
  373. template<class FVisit>
  374. void
  375. Profiler::ForEachNode(Phase tag, TypeNode *node, FVisit visit, Phase parentTag)
  376. {
  377. AssertMsg(node != NULL, "Invalid usage: node must always be non null");
  378. for(int i = 0; i < PhaseCount; i++)
  379. {
  380. if(node->ChildExistsAt(i))
  381. {
  382. TypeNode * child = node->GetChildAt(i);
  383. if(i == tag)
  384. {
  385. visit(child, parentTag);
  386. }
  387. else
  388. {
  389. ForEachNode(tag, child, visit, static_cast<Phase>(i));
  390. }
  391. }
  392. }
  393. }
  394. ///----------------------------------------------------------------------------
  395. ///
  396. /// Profiler::GetTime
  397. ///
  398. ///----------------------------------------------------------------------------
  399. TimeStamp
  400. Profiler::GetTime()
  401. {
  402. #if !defined HIRES_PROFILER && (defined(_M_IX86) || defined(_M_X64))
  403. return __rdtsc();
  404. #else
  405. LARGE_INTEGER tmp;
  406. if(QueryPerformanceCounter(&tmp))
  407. {
  408. return tmp.QuadPart;
  409. }
  410. else
  411. {
  412. AssertMsg(0, "Could not get time. Don't know what to do");
  413. return 0;
  414. }
  415. #endif
  416. }
  417. ///----------------------------------------------------------------------------
  418. ///
  419. /// Profiler::GetFrequency
  420. ///
  421. ///----------------------------------------------------------------------------
  422. TimeStamp
  423. Profiler::GetFrequency()
  424. {
  425. #if !defined HIRES_PROFILER && (defined(_M_IX86) || defined(_M_X64))
  426. long long start, end;
  427. int CPUInfo[4];
  428. // Flush pipeline
  429. __cpuid(CPUInfo, 0);
  430. // Measure 1 second / 5
  431. start = GetTime();
  432. Sleep(1000/5);
  433. end = GetTime();
  434. return ((end - start) * 5) / FrequencyScale;
  435. #else
  436. LARGE_INTEGER tmp;
  437. if(QueryPerformanceFrequency(&tmp))
  438. {
  439. return tmp.QuadPart / FrequencyScale;
  440. }
  441. else
  442. {
  443. AssertMsg(0, "Could not get time. Don't know what to do");
  444. return 0;
  445. }
  446. #endif
  447. }
  448. } //namespace Js
  449. #endif