ProfileMemory.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. #include "CommonCorePch.h"
  7. #ifdef PROFILE_MEM
  8. #include "Memory/AutoPtr.h"
  9. #include "Core/ProfileMemory.h"
  10. THREAD_LOCAL MemoryProfiler * MemoryProfiler::Instance = nullptr;
  11. CriticalSection MemoryProfiler::s_cs;
  12. AutoPtr<MemoryProfiler, NoCheckHeapAllocator> MemoryProfiler::profilers(nullptr);
  13. MemoryProfiler::MemoryProfiler() :
  14. pageAllocator(nullptr, Js::Configuration::Global.flags,
  15. PageAllocatorType_Max, 0, false
  16. #if ENABLE_BACKGROUND_PAGE_FREEING
  17. , nullptr
  18. #endif
  19. ),
  20. alloc(_u("MemoryProfiler"), &pageAllocator, Js::Throw::OutOfMemory),
  21. arenaDataMap(&alloc, 10)
  22. {
  23. threadId = ::GetCurrentThreadId();
  24. memset((void*)&pageMemoryData, 0, sizeof(pageMemoryData));
  25. memset((void*)&recyclerMemoryData, 0, sizeof(recyclerMemoryData));
  26. }
  27. MemoryProfiler::~MemoryProfiler()
  28. {
  29. #if DBG
  30. pageAllocator.SetDisableThreadAccessCheck();
  31. #endif
  32. if (next != nullptr)
  33. {
  34. NoCheckHeapDelete(next);
  35. }
  36. }
  37. MemoryProfiler *
  38. MemoryProfiler::EnsureMemoryProfiler()
  39. {
  40. MemoryProfiler * memoryProfiler = MemoryProfiler::Instance;
  41. if (memoryProfiler == nullptr)
  42. {
  43. memoryProfiler = NoCheckHeapNew(MemoryProfiler);
  44. {
  45. AutoCriticalSection autocs(&s_cs);
  46. memoryProfiler->next = MemoryProfiler::profilers.Detach();
  47. MemoryProfiler::profilers = memoryProfiler;
  48. }
  49. MemoryProfiler::Instance = memoryProfiler;
  50. }
  51. return memoryProfiler;
  52. }
  53. PageMemoryData *
  54. MemoryProfiler::GetPageMemoryData(PageAllocatorType type)
  55. {
  56. if (!Js::Configuration::Global.flags.IsEnabled(Js::TraceMemoryFlag))
  57. {
  58. return nullptr;
  59. }
  60. if (type == PageAllocatorType_Max)
  61. {
  62. return nullptr;
  63. }
  64. MemoryProfiler * memoryProfiler = EnsureMemoryProfiler();
  65. return &memoryProfiler->pageMemoryData[type];
  66. }
  67. RecyclerMemoryData *
  68. MemoryProfiler::GetRecyclerMemoryData()
  69. {
  70. if (!Js::Configuration::Global.flags.IsEnabled(Js::TraceMemoryFlag))
  71. {
  72. return nullptr;
  73. }
  74. MemoryProfiler * memoryProfiler = EnsureMemoryProfiler();
  75. return &memoryProfiler->recyclerMemoryData;
  76. }
  77. ArenaMemoryData *
  78. MemoryProfiler::Begin(LPCWSTR name)
  79. {
  80. if (!Js::Configuration::Global.flags.IsEnabled(Js::TraceMemoryFlag))
  81. {
  82. return nullptr;
  83. }
  84. Assert(name != nullptr);
  85. if (wcscmp(name, _u("MemoryProfiler")) == 0)
  86. {
  87. // Don't profile memory profiler itself
  88. return nullptr;
  89. }
  90. // This is debug only code, we don't care if we catch the right exception
  91. AUTO_NESTED_HANDLED_EXCEPTION_TYPE(ExceptionType_DisableCheck);
  92. MemoryProfiler * memoryProfiler = EnsureMemoryProfiler();
  93. ArenaMemoryDataSummary * arenaTotalMemoryData;
  94. if (!memoryProfiler->arenaDataMap.TryGetValue((LPWSTR)name, &arenaTotalMemoryData))
  95. {
  96. arenaTotalMemoryData = AnewStructZ(&memoryProfiler->alloc, ArenaMemoryDataSummary);
  97. memoryProfiler->arenaDataMap.Add((LPWSTR)name, arenaTotalMemoryData);
  98. }
  99. arenaTotalMemoryData->arenaCount++;
  100. ArenaMemoryData * memoryData = AnewStructZ(&memoryProfiler->alloc, ArenaMemoryData);
  101. if (arenaTotalMemoryData->data == nullptr)
  102. {
  103. arenaTotalMemoryData->data = memoryData;
  104. }
  105. else
  106. {
  107. memoryData->next = arenaTotalMemoryData->data;
  108. arenaTotalMemoryData->data->prev = memoryData;
  109. arenaTotalMemoryData->data = memoryData;
  110. }
  111. memoryData->profiler = memoryProfiler;
  112. return memoryData;
  113. }
  114. void
  115. MemoryProfiler::Reset(LPCWSTR name, ArenaMemoryData * memoryData)
  116. {
  117. MemoryProfiler * memoryProfiler = memoryData->profiler;
  118. ArenaMemoryDataSummary * arenaMemoryDataSummary;
  119. bool hasItem = memoryProfiler->arenaDataMap.TryGetValue((LPWSTR)name, &arenaMemoryDataSummary);
  120. Assert(hasItem);
  121. AccumulateData(arenaMemoryDataSummary, memoryData, true);
  122. memoryData->allocatedBytes = 0;
  123. memoryData->alignmentBytes = 0;
  124. memoryData->requestBytes = 0;
  125. memoryData->requestCount = 0;
  126. memoryData->reuseBytes = 0;
  127. memoryData->reuseCount = 0;
  128. memoryData->freelistBytes = 0;
  129. memoryData->freelistCount = 0;
  130. memoryData->resetCount++;
  131. }
  132. void
  133. MemoryProfiler::End(LPCWSTR name, ArenaMemoryData * memoryData)
  134. {
  135. MemoryProfiler * memoryProfiler = memoryData->profiler;
  136. ArenaMemoryDataSummary * arenaMemoryDataSummary;
  137. bool hasItem = memoryProfiler->arenaDataMap.TryGetValue((LPWSTR)name, &arenaMemoryDataSummary);
  138. Assert(hasItem);
  139. if (memoryData->next != nullptr)
  140. {
  141. memoryData->next->prev = memoryData->prev;
  142. }
  143. if (memoryData->prev != nullptr)
  144. {
  145. memoryData->prev->next = memoryData->next;
  146. }
  147. else
  148. {
  149. Assert(arenaMemoryDataSummary->data == memoryData);
  150. arenaMemoryDataSummary->data = memoryData->next;
  151. }
  152. AccumulateData(arenaMemoryDataSummary, memoryData);
  153. }
  154. void
  155. MemoryProfiler::AccumulateData(ArenaMemoryDataSummary * arenaMemoryDataSummary, ArenaMemoryData * memoryData, bool reset)
  156. {
  157. arenaMemoryDataSummary->total.alignmentBytes += memoryData->alignmentBytes;
  158. arenaMemoryDataSummary->total.allocatedBytes += memoryData->allocatedBytes;
  159. arenaMemoryDataSummary->total.freelistBytes += memoryData->freelistBytes;
  160. arenaMemoryDataSummary->total.freelistCount += memoryData->freelistCount;
  161. arenaMemoryDataSummary->total.requestBytes += memoryData->requestBytes;
  162. arenaMemoryDataSummary->total.requestCount += memoryData->requestCount;
  163. arenaMemoryDataSummary->total.reuseCount += memoryData->reuseCount;
  164. arenaMemoryDataSummary->total.reuseBytes += memoryData->reuseBytes;
  165. if (!reset)
  166. {
  167. arenaMemoryDataSummary->total.resetCount += memoryData->resetCount;
  168. }
  169. arenaMemoryDataSummary->max.alignmentBytes = max(arenaMemoryDataSummary->max.alignmentBytes, memoryData->alignmentBytes);
  170. arenaMemoryDataSummary->max.allocatedBytes = max(arenaMemoryDataSummary->max.allocatedBytes, memoryData->allocatedBytes);
  171. arenaMemoryDataSummary->max.freelistBytes = max(arenaMemoryDataSummary->max.freelistBytes, memoryData->freelistBytes);
  172. arenaMemoryDataSummary->max.freelistCount = max(arenaMemoryDataSummary->max.freelistCount, memoryData->freelistCount);
  173. arenaMemoryDataSummary->max.requestBytes = max(arenaMemoryDataSummary->max.requestBytes, memoryData->requestBytes);
  174. arenaMemoryDataSummary->max.requestCount = max(arenaMemoryDataSummary->max.requestCount, memoryData->requestCount);
  175. arenaMemoryDataSummary->max.reuseCount = max(arenaMemoryDataSummary->max.reuseCount, memoryData->reuseCount);
  176. arenaMemoryDataSummary->max.reuseBytes = max(arenaMemoryDataSummary->max.reuseBytes, memoryData->reuseBytes);
  177. if (!reset)
  178. {
  179. arenaMemoryDataSummary->max.resetCount = max(arenaMemoryDataSummary->max.resetCount, memoryData->resetCount);
  180. }
  181. }
  182. void
  183. MemoryProfiler::PrintPageMemoryData(PageMemoryData const& pageMemoryData, char const * title)
  184. {
  185. if (pageMemoryData.allocSegmentCount != 0)
  186. {
  187. Output::Print(_u("%-10S:%9d %10d | %4d %10d | %4d %10d | %10d | %10d | %10d | %10d\n"), title,
  188. pageMemoryData.currentCommittedPageCount * AutoSystemInfo::PageSize, pageMemoryData.peakCommittedPageCount * AutoSystemInfo::PageSize,
  189. pageMemoryData.allocSegmentCount, pageMemoryData.allocSegmentBytes,
  190. pageMemoryData.releaseSegmentCount, pageMemoryData.releaseSegmentBytes,
  191. pageMemoryData.allocPageCount * AutoSystemInfo::PageSize,
  192. pageMemoryData.releasePageCount * AutoSystemInfo::PageSize,
  193. pageMemoryData.decommitPageCount * AutoSystemInfo::PageSize,
  194. pageMemoryData.recommitPageCount * AutoSystemInfo::PageSize);
  195. }
  196. }
  197. void
  198. MemoryProfiler::Print()
  199. {
  200. Output::Print(_u("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"));
  201. Output::Print(_u("Allocation for thread 0x%08X\n"), threadId);
  202. Output::Print(_u("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"));
  203. bool hasData = false;
  204. for (int i = 0; i < PageAllocatorType_Max; i++)
  205. {
  206. if (pageMemoryData[i].allocSegmentCount != 0)
  207. {
  208. hasData = true;
  209. break;
  210. }
  211. }
  212. if (hasData)
  213. {
  214. Output::Print(_u("%-10s:%-20s | %-15s | %-15s | %10s | %10s | %11s | %11s\n"), _u(""), _u(" Current"), _u(" Alloc Seg"), _u(" Free Seg"),
  215. _u("Request"), _u("Released"), _u("Decommitted"), _u("Recommitted"));
  216. Output::Print(_u("%-10s:%9s %10s | %4s %10s | %4s %10s | %10s | %10s | %10s | %10s\n"), _u(""), _u("Bytes"), _u("Peak"), _u("#"), _u("Bytes"), _u("#"), _u("Bytes"),
  217. _u("Bytes"), _u("Bytes"), _u("Bytes"), _u("Bytes"));
  218. Output::Print(_u("------------------------------------------------------------------------------------------------------------------\n"));
  219. #define PAGEALLOCATOR_PRINT(i) PrintPageMemoryData(pageMemoryData[PageAllocatorType_ ## i], STRINGIZE(i));
  220. PAGE_ALLOCATOR_TYPE(PAGEALLOCATOR_PRINT);
  221. Output::Print(_u("------------------------------------------------------------------------------------------------------------------\n"));
  222. }
  223. if (recyclerMemoryData.requestCount != 0)
  224. {
  225. Output::Print(_u("%-10s:%7s %10s %10s %10s\n"),
  226. _u("Recycler"),
  227. _u("#Alloc"),
  228. _u("AllocBytes"),
  229. _u("ReqBytes"),
  230. _u("AlignByte"));
  231. Output::Print(_u("--------------------------------------------------------------------------------------------------------\n"));
  232. Output::Print(_u("%-10s:%7d %10d %10d %10d\n"),
  233. _u(""),
  234. recyclerMemoryData.requestCount,
  235. recyclerMemoryData.requestBytes + recyclerMemoryData.alignmentBytes,
  236. recyclerMemoryData.requestBytes,
  237. recyclerMemoryData.alignmentBytes);
  238. Output::Print(_u("--------------------------------------------------------------------------------------------------------\n"));
  239. }
  240. if (Js::Configuration::Global.flags.TraceMemory.IsEnabled(Js::AllPhase))
  241. {
  242. PrintArena(false);
  243. }
  244. PrintArena(true);
  245. }
  246. void
  247. MemoryProfiler::PrintArenaHeader(char16 const * title)
  248. {
  249. Output::Print(_u("--------------------------------------------------------------------------------------------------------\n"));
  250. Output::Print(_u("%-20s:%7s %9s %9s %9s %6s %9s %6s %9s %5s | %5s\n"),
  251. title,
  252. _u("#Alloc"),
  253. _u("AllocByte"),
  254. _u("ReqBytes"),
  255. _u("AlignByte"),
  256. _u("#Reuse"),
  257. _u("ReuseByte"),
  258. _u("#Free"),
  259. _u("FreeBytes"),
  260. _u("Reset"),
  261. _u("Count"));
  262. Output::Print(_u("--------------------------------------------------------------------------------------------------------\n"));
  263. }
  264. int MemoryProfiler::CreateArenaUsageSummary(ArenaAllocator * alloc, bool liveOnly,
  265. _Outptr_result_buffer_(return) LPWSTR ** name_ptr, _Outptr_result_buffer_(return) ArenaMemoryDataSummary *** summaries_ptr)
  266. {
  267. Assert(alloc);
  268. LPWSTR *& name = *name_ptr;
  269. ArenaMemoryDataSummary **& summaries = *summaries_ptr;
  270. int count = arenaDataMap.Count();
  271. name = AnewArray(alloc, LPWSTR, count);
  272. int i = 0;
  273. arenaDataMap.Map([&i, name](LPWSTR key, ArenaMemoryDataSummary*)
  274. {
  275. name[i++] = key;
  276. });
  277. qsort_s(name, count, sizeof(LPWSTR), [](void*, const void* a, const void* b) { return DefaultComparer<LPWSTR>::Compare(*(LPWSTR*)a, *(LPWSTR*)b); }, nullptr);
  278. summaries = AnewArray(alloc, ArenaMemoryDataSummary *, count);
  279. for (int j = 0; j < count; j++)
  280. {
  281. ArenaMemoryDataSummary * summary = arenaDataMap.Item(name[j]);
  282. ArenaMemoryData * data = summary->data;
  283. ArenaMemoryDataSummary * localSummary;
  284. if (liveOnly)
  285. {
  286. if (data == nullptr)
  287. {
  288. summaries[j] = nullptr;
  289. continue;
  290. }
  291. localSummary = AnewStructZ(alloc, ArenaMemoryDataSummary);
  292. }
  293. else
  294. {
  295. localSummary = Anew(alloc, ArenaMemoryDataSummary, *summary);
  296. }
  297. while (data != nullptr)
  298. {
  299. localSummary->outstandingCount++;
  300. AccumulateData(localSummary, data);
  301. data = data->next;
  302. }
  303. if (liveOnly)
  304. {
  305. localSummary->arenaCount = localSummary->outstandingCount;
  306. }
  307. summaries[j] = localSummary;
  308. }
  309. return count;
  310. }
  311. void
  312. MemoryProfiler::PrintArena(bool liveOnly)
  313. {
  314. WithArenaUsageSummary(liveOnly, [&] (int count, _In_reads_(count) LPWSTR * name, _In_reads_(count) ArenaMemoryDataSummary ** summaries)
  315. {
  316. int i = 0;
  317. if (liveOnly)
  318. {
  319. Output::Print(_u("Arena usage summary (live)\n"));
  320. }
  321. else
  322. {
  323. Output::Print(_u("Arena usage summary (all)\n"));
  324. }
  325. bool header = false;
  326. for (i = 0; i < count; i++)
  327. {
  328. ArenaMemoryDataSummary * data = summaries[i];
  329. if (data == nullptr)
  330. {
  331. continue;
  332. }
  333. if (!header)
  334. {
  335. header = true;
  336. PrintArenaHeader(_u("Arena Size"));
  337. }
  338. Output::Print(_u("%-20s %7d %9d %9d %9d %6d %9d %6d %9d %5d | %5d\n"),
  339. name[i],
  340. data->total.requestCount,
  341. data->total.allocatedBytes,
  342. data->total.requestBytes,
  343. data->total.alignmentBytes,
  344. data->total.reuseCount,
  345. data->total.reuseBytes,
  346. data->total.freelistCount,
  347. data->total.freelistBytes,
  348. data->total.resetCount,
  349. data->arenaCount);
  350. }
  351. header = false;
  352. for (i = 0; i < count; i++)
  353. {
  354. ArenaMemoryDataSummary * data = summaries[i];
  355. if (data == nullptr)
  356. {
  357. continue;
  358. }
  359. if (!header)
  360. {
  361. header = true;
  362. PrintArenaHeader(_u("Arena Max"));
  363. }
  364. Output::Print(_u("%-20s %7d %9d %9d %9d %6d %9d %6d %9d %5d | %5d\n"),
  365. name[i],
  366. data->max.requestCount,
  367. data->max.allocatedBytes,
  368. data->max.requestBytes,
  369. data->max.alignmentBytes,
  370. data->max.reuseCount,
  371. data->max.reuseBytes,
  372. data->max.freelistCount, data->max.freelistBytes,
  373. data->max.resetCount, data->outstandingCount);
  374. }
  375. header = false;
  376. for (i = 0; i < count; i++)
  377. {
  378. ArenaMemoryDataSummary * data = summaries[i];
  379. if (data == nullptr)
  380. {
  381. continue;
  382. }
  383. if (!header)
  384. {
  385. header = true;
  386. PrintArenaHeader(_u("Arena Average"));
  387. }
  388. Output::Print(_u("%-20s %7d %9d %9d %9d %6d %9d %6d %9d %5d\n"), name[i],
  389. data->total.requestCount / data->arenaCount,
  390. data->total.allocatedBytes / data->arenaCount,
  391. data->total.requestBytes / data->arenaCount,
  392. data->total.alignmentBytes / data->arenaCount,
  393. data->total.reuseCount / data->arenaCount,
  394. data->total.reuseBytes / data->arenaCount,
  395. data->total.freelistCount / data->arenaCount,
  396. data->total.freelistBytes / data->arenaCount,
  397. data->total.resetCount / data->arenaCount);
  398. }
  399. Output::Print(_u("--------------------------------------------------------------------------------------------------------\n"));
  400. });
  401. }
  402. void
  403. MemoryProfiler::PrintCurrentThread()
  404. {
  405. MemoryProfiler* instance = NULL;
  406. instance = MemoryProfiler::Instance;
  407. Output::Print(_u("========================================================================================================\n"));
  408. Output::Print(_u("Memory Profile (Current thread)\n"));
  409. if (instance != nullptr)
  410. {
  411. instance->Print();
  412. }
  413. Output::Flush();
  414. }
  415. void
  416. MemoryProfiler::PrintAll()
  417. {
  418. Output::Print(_u("========================================================================================================\n"));
  419. Output::Print(_u("Memory Profile (All threads)\n"));
  420. ForEachProfiler([] (MemoryProfiler * memoryProfiler)
  421. {
  422. memoryProfiler->Print();
  423. });
  424. Output::Flush();
  425. }
  426. bool
  427. MemoryProfiler::IsTraceEnabled(bool isRecycler)
  428. {
  429. if (!Js::Configuration::Global.flags.IsEnabled(Js::TraceMemoryFlag))
  430. {
  431. return false;
  432. }
  433. if (Js::Configuration::Global.flags.TraceMemory.IsEnabled(Js::AllPhase))
  434. {
  435. return true;
  436. }
  437. if (!isRecycler)
  438. {
  439. return (Js::Configuration::Global.flags.TraceMemory.IsEnabled(Js::RunPhase)
  440. || Js::Configuration::Global.flags.TraceMemory.GetFirstPhase() == Js::InvalidPhase);
  441. }
  442. return Js::Configuration::Global.flags.TraceMemory.IsEnabled(Js::RecyclerPhase);
  443. }
  444. bool
  445. MemoryProfiler::IsEnabled()
  446. {
  447. return Js::Configuration::Global.flags.IsEnabled(Js::ProfileMemoryFlag);
  448. }
  449. bool
  450. MemoryProfiler::DoTrackRecyclerAllocation()
  451. {
  452. return MemoryProfiler::IsEnabled() || MemoryProfiler::IsTraceEnabled(true) || MemoryProfiler::IsTraceEnabled(false);
  453. }
  454. #endif