LeakReport.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 "CommonMemoryPch.h"
  6. #ifdef LEAK_REPORT
  7. // Initialization order
  8. // AB AutoSystemInfo
  9. // AD PerfCounter
  10. // AE PerfCounterSet
  11. // AM Output/Configuration
  12. // AN MemProtectHeap
  13. // AP DbgHelpSymbolManager
  14. // AQ CFGLogger
  15. // AR LeakReport
  16. // AS JavascriptDispatch/RecyclerObjectDumper
  17. // AT HeapAllocator/RecyclerHeuristic
  18. // AU RecyclerWriteBarrierManager
  19. #pragma warning(disable:4075) // initializers put in unrecognized initialization area on purpose
  20. #pragma init_seg(".CRT$XCAR")
  21. CriticalSection LeakReport::s_cs;
  22. DWORD LeakReport::nestedSectionCount = 0;
  23. DWORD LeakReport::nestedRedirectOutputCount = 0;
  24. AutoFILE LeakReport::file;
  25. FILE * oldFile = nullptr;
  26. bool LeakReport::openReportFileFailed = false;
  27. LeakReport::UrlRecord * LeakReport::urlRecordHead = nullptr;
  28. LeakReport::UrlRecord * LeakReport::urlRecordTail = nullptr;
  29. void
  30. LeakReport::StartRedirectOutput()
  31. {
  32. if (!EnsureLeakReportFile())
  33. {
  34. return;
  35. }
  36. s_cs.Enter();
  37. if (nestedRedirectOutputCount == 0)
  38. {
  39. Assert(oldFile == nullptr);
  40. oldFile = Output::SetFile(file);
  41. }
  42. nestedRedirectOutputCount++;
  43. }
  44. void
  45. LeakReport::EndRedirectOutput()
  46. {
  47. if (nestedRedirectOutputCount == 0)
  48. {
  49. return;
  50. }
  51. Assert(file != nullptr);
  52. nestedRedirectOutputCount--;
  53. if (nestedRedirectOutputCount == 0)
  54. {
  55. fflush(file);
  56. FILE * tmpFile = Output::SetFile(oldFile);
  57. Assert(tmpFile == file);
  58. oldFile = nullptr;
  59. }
  60. s_cs.Leave();
  61. }
  62. void
  63. LeakReport::StartSection(wchar_t const * msg, ...)
  64. {
  65. va_list argptr;
  66. va_start(argptr, msg);
  67. StartSection(msg, argptr);
  68. va_end(argptr);
  69. }
  70. void
  71. LeakReport::StartSection(wchar_t const * msg, va_list argptr)
  72. {
  73. s_cs.Enter();
  74. if (!EnsureLeakReportFile())
  75. {
  76. return;
  77. }
  78. nestedSectionCount++;
  79. Print(CH_WSTR("--------------------------------------------------------------------------------\n"));
  80. vfwprintf(file, msg, argptr);
  81. Print(CH_WSTR("\n"));
  82. Print(CH_WSTR("--------------------------------------------------------------------------------\n"));
  83. }
  84. void
  85. LeakReport::EndSection()
  86. {
  87. s_cs.Leave();
  88. if (file == nullptr)
  89. {
  90. return;
  91. }
  92. nestedSectionCount--;
  93. }
  94. void
  95. LeakReport::Print(wchar_t const * msg, ...)
  96. {
  97. AutoCriticalSection autocs(&s_cs);
  98. if (!EnsureLeakReportFile())
  99. {
  100. return;
  101. }
  102. va_list argptr;
  103. va_start(argptr, msg);
  104. vfwprintf(file, msg, argptr);
  105. va_end(argptr);
  106. }
  107. bool
  108. LeakReport::EnsureLeakReportFile()
  109. {
  110. AutoCriticalSection autocs(&s_cs);
  111. if (openReportFileFailed)
  112. {
  113. return false;
  114. }
  115. if (file != nullptr)
  116. {
  117. return true;
  118. }
  119. wchar_t const * filename = Js::Configuration::Global.flags.LeakReport;
  120. wchar_t const * openMode = CH_WSTR("w+");
  121. wchar_t defaultFilename[_MAX_PATH];
  122. if (filename == nullptr)
  123. {
  124. // xplat-todo: Implement swprintf_s in the PAL
  125. #ifdef _MSC_VER
  126. swprintf_s(defaultFilename, L"jsleakreport-%u.txt", ::GetCurrentProcessId());
  127. #else
  128. _snwprintf(defaultFilename, _countof(defaultFilename), CH_WSTR("jsleakreport-%u.txt"), ::GetCurrentProcessId());
  129. #endif
  130. filename = defaultFilename;
  131. openMode = CH_WSTR("a+"); // append mode
  132. }
  133. if (_wfopen_s(&file, filename, openMode) != 0)
  134. {
  135. openReportFileFailed = true;
  136. return false;
  137. }
  138. Print(CH_WSTR("================================================================================\n"));
  139. Print(CH_WSTR("Chakra Leak Report - PID: %d\n"), ::GetCurrentProcessId());
  140. // xplat-todo: Make this code cross-platform
  141. #if _MSC_VER
  142. __time64_t time_value = _time64(NULL);
  143. wchar_t time_string[26];
  144. struct tm local_time;
  145. _localtime64_s(&local_time, &time_value);
  146. _wasctime_s(time_string, &local_time);
  147. Print(time_string);
  148. #endif
  149. Print(CH_WSTR("\n"));
  150. return true;
  151. }
  152. LeakReport::UrlRecord *
  153. LeakReport::LogUrl(wchar_t const * url, void * globalObject)
  154. {
  155. UrlRecord * record = NoCheckHeapNewStruct(UrlRecord);
  156. size_t length = wcslen(url) + 1; // Add 1 for the NULL.
  157. wchar_t* urlCopy = NoCheckHeapNewArray(wchar_t, length);
  158. js_memcpy_s(urlCopy, (length - 1) * sizeof(wchar_t), url, (length - 1) * sizeof(wchar_t));
  159. urlCopy[length - 1] = L'\0';
  160. record->url = urlCopy;
  161. #if _MSC_VER
  162. record->time = _time64(NULL);
  163. #else
  164. record->time = time(NULL);
  165. #endif
  166. record->tid = ::GetCurrentThreadId();
  167. record->next = nullptr;
  168. record->scriptEngine = nullptr;
  169. record->globalObject = globalObject;
  170. AutoCriticalSection autocs(&s_cs);
  171. if (LeakReport::urlRecordHead == nullptr)
  172. {
  173. Assert(LeakReport::urlRecordTail == nullptr);
  174. LeakReport::urlRecordHead = record;
  175. LeakReport::urlRecordTail = record;
  176. }
  177. else
  178. {
  179. LeakReport::urlRecordTail->next = record;
  180. LeakReport::urlRecordTail = record;
  181. }
  182. return record;
  183. }
  184. void
  185. LeakReport::DumpUrl(DWORD tid)
  186. {
  187. AutoCriticalSection autocs(&s_cs);
  188. if (!EnsureLeakReportFile())
  189. {
  190. return;
  191. }
  192. UrlRecord * prev = nullptr;
  193. UrlRecord ** pprev = &LeakReport::urlRecordHead;
  194. UrlRecord * curr = *pprev;
  195. while (curr != nullptr)
  196. {
  197. if (curr->tid == tid)
  198. {
  199. wchar_t timeStr[26] = CH_WSTR("00:00");
  200. // xplat-todo: Need to implement _wasctime_s in the PAL
  201. #if _MSC_VER
  202. struct tm local_time;
  203. _localtime64_s(&local_time, &curr->time);
  204. _wasctime_s(timeStr, &local_time);
  205. #endif
  206. timeStr[wcslen(timeStr) - 1] = 0;
  207. Print(CH_WSTR("%s - (%p, %p) %s\n"), timeStr, curr->scriptEngine, curr->globalObject, curr->url);
  208. *pprev = curr->next;
  209. NoCheckHeapDeleteArray(wcslen(curr->url) + 1, curr->url);
  210. NoCheckHeapDelete(curr);
  211. }
  212. else
  213. {
  214. pprev = &curr->next;
  215. prev = curr;
  216. }
  217. curr = *pprev;
  218. }
  219. if (prev == nullptr)
  220. {
  221. LeakReport::urlRecordTail = nullptr;
  222. }
  223. else if (prev->next == nullptr)
  224. {
  225. LeakReport::urlRecordTail = prev;
  226. }
  227. }
  228. AutoLeakReportSection::AutoLeakReportSection(Js::ConfigFlagsTable& flags, wchar_t const * msg, ...):
  229. m_flags(flags)
  230. {
  231. if (flags.IsEnabled(Js::LeakReportFlag))
  232. {
  233. va_list argptr;
  234. va_start(argptr, msg);
  235. LeakReport::StartSection(msg, argptr);
  236. va_end(argptr);
  237. }
  238. }
  239. AutoLeakReportSection::~AutoLeakReportSection()
  240. {
  241. if (m_flags.IsEnabled(Js::LeakReportFlag))
  242. {
  243. LeakReport::EndSection();
  244. }
  245. }
  246. #endif