| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "CommonMemoryPch.h"
- #ifdef LEAK_REPORT
- // Initialization order
- // AB AutoSystemInfo
- // AD PerfCounter
- // AE PerfCounterSet
- // AM Output/Configuration
- // AN MemProtectHeap
- // AP DbgHelpSymbolManager
- // AQ CFGLogger
- // AR LeakReport
- // AS JavascriptDispatch/RecyclerObjectDumper
- // AT HeapAllocator/RecyclerHeuristic
- // AU RecyclerWriteBarrierManager
- #pragma warning(disable:4075) // initializers put in unrecognized initialization area on purpose
- #pragma init_seg(".CRT$XCAR")
- CriticalSection LeakReport::s_cs;
- DWORD LeakReport::nestedSectionCount = 0;
- DWORD LeakReport::nestedRedirectOutputCount = 0;
- AutoFILE LeakReport::file;
- FILE * oldFile = nullptr;
- bool LeakReport::openReportFileFailed = false;
- LeakReport::UrlRecord * LeakReport::urlRecordHead = nullptr;
- LeakReport::UrlRecord * LeakReport::urlRecordTail = nullptr;
- void
- LeakReport::StartRedirectOutput()
- {
- if (!EnsureLeakReportFile())
- {
- return;
- }
- s_cs.Enter();
- if (nestedRedirectOutputCount == 0)
- {
- Assert(oldFile == nullptr);
- oldFile = Output::SetFile(file);
- }
- nestedRedirectOutputCount++;
- }
- void
- LeakReport::EndRedirectOutput()
- {
- if (nestedRedirectOutputCount == 0)
- {
- return;
- }
- Assert(file != nullptr);
- nestedRedirectOutputCount--;
- if (nestedRedirectOutputCount == 0)
- {
- fflush(file);
- FILE * tmpFile = Output::SetFile(oldFile);
- Assert(tmpFile == file);
- oldFile = nullptr;
- }
- s_cs.Leave();
- }
- void
- LeakReport::StartSection(wchar_t const * msg, ...)
- {
- va_list argptr;
- va_start(argptr, msg);
- StartSection(msg, argptr);
- va_end(argptr);
- }
- void
- LeakReport::StartSection(wchar_t const * msg, va_list argptr)
- {
- s_cs.Enter();
- if (!EnsureLeakReportFile())
- {
- return;
- }
- nestedSectionCount++;
- Print(CH_WSTR("--------------------------------------------------------------------------------\n"));
- vfwprintf(file, msg, argptr);
- Print(CH_WSTR("\n"));
- Print(CH_WSTR("--------------------------------------------------------------------------------\n"));
- }
- void
- LeakReport::EndSection()
- {
- s_cs.Leave();
- if (file == nullptr)
- {
- return;
- }
- nestedSectionCount--;
- }
- void
- LeakReport::Print(wchar_t const * msg, ...)
- {
- AutoCriticalSection autocs(&s_cs);
- if (!EnsureLeakReportFile())
- {
- return;
- }
- va_list argptr;
- va_start(argptr, msg);
- vfwprintf(file, msg, argptr);
- va_end(argptr);
- }
- bool
- LeakReport::EnsureLeakReportFile()
- {
- AutoCriticalSection autocs(&s_cs);
- if (openReportFileFailed)
- {
- return false;
- }
- if (file != nullptr)
- {
- return true;
- }
- wchar_t const * filename = Js::Configuration::Global.flags.LeakReport;
- wchar_t const * openMode = CH_WSTR("w+");
- wchar_t defaultFilename[_MAX_PATH];
- if (filename == nullptr)
- {
- // xplat-todo: Implement swprintf_s in the PAL
- #ifdef _MSC_VER
- swprintf_s(defaultFilename, L"jsleakreport-%u.txt", ::GetCurrentProcessId());
- #else
- _snwprintf(defaultFilename, _countof(defaultFilename), CH_WSTR("jsleakreport-%u.txt"), ::GetCurrentProcessId());
- #endif
- filename = defaultFilename;
- openMode = CH_WSTR("a+"); // append mode
- }
- if (_wfopen_s(&file, filename, openMode) != 0)
- {
- openReportFileFailed = true;
- return false;
- }
- Print(CH_WSTR("================================================================================\n"));
- Print(CH_WSTR("Chakra Leak Report - PID: %d\n"), ::GetCurrentProcessId());
- // xplat-todo: Make this code cross-platform
- #if _MSC_VER
- __time64_t time_value = _time64(NULL);
- wchar_t time_string[26];
- struct tm local_time;
- _localtime64_s(&local_time, &time_value);
- _wasctime_s(time_string, &local_time);
- Print(time_string);
- #endif
-
- Print(CH_WSTR("\n"));
- return true;
- }
- LeakReport::UrlRecord *
- LeakReport::LogUrl(wchar_t const * url, void * globalObject)
- {
- UrlRecord * record = NoCheckHeapNewStruct(UrlRecord);
- size_t length = wcslen(url) + 1; // Add 1 for the NULL.
- wchar_t* urlCopy = NoCheckHeapNewArray(wchar_t, length);
- js_memcpy_s(urlCopy, (length - 1) * sizeof(wchar_t), url, (length - 1) * sizeof(wchar_t));
- urlCopy[length - 1] = L'\0';
- record->url = urlCopy;
- #if _MSC_VER
- record->time = _time64(NULL);
- #else
- record->time = time(NULL);
- #endif
- record->tid = ::GetCurrentThreadId();
- record->next = nullptr;
- record->scriptEngine = nullptr;
- record->globalObject = globalObject;
- AutoCriticalSection autocs(&s_cs);
- if (LeakReport::urlRecordHead == nullptr)
- {
- Assert(LeakReport::urlRecordTail == nullptr);
- LeakReport::urlRecordHead = record;
- LeakReport::urlRecordTail = record;
- }
- else
- {
- LeakReport::urlRecordTail->next = record;
- LeakReport::urlRecordTail = record;
- }
- return record;
- }
- void
- LeakReport::DumpUrl(DWORD tid)
- {
- AutoCriticalSection autocs(&s_cs);
- if (!EnsureLeakReportFile())
- {
- return;
- }
- UrlRecord * prev = nullptr;
- UrlRecord ** pprev = &LeakReport::urlRecordHead;
- UrlRecord * curr = *pprev;
- while (curr != nullptr)
- {
- if (curr->tid == tid)
- {
- wchar_t timeStr[26] = CH_WSTR("00:00");
-
- // xplat-todo: Need to implement _wasctime_s in the PAL
- #if _MSC_VER
- struct tm local_time;
- _localtime64_s(&local_time, &curr->time);
- _wasctime_s(timeStr, &local_time);
- #endif
- timeStr[wcslen(timeStr) - 1] = 0;
- Print(CH_WSTR("%s - (%p, %p) %s\n"), timeStr, curr->scriptEngine, curr->globalObject, curr->url);
- *pprev = curr->next;
- NoCheckHeapDeleteArray(wcslen(curr->url) + 1, curr->url);
- NoCheckHeapDelete(curr);
- }
- else
- {
- pprev = &curr->next;
- prev = curr;
- }
- curr = *pprev;
- }
- if (prev == nullptr)
- {
- LeakReport::urlRecordTail = nullptr;
- }
- else if (prev->next == nullptr)
- {
- LeakReport::urlRecordTail = prev;
- }
- }
- AutoLeakReportSection::AutoLeakReportSection(Js::ConfigFlagsTable& flags, wchar_t const * msg, ...):
- m_flags(flags)
- {
- if (flags.IsEnabled(Js::LeakReportFlag))
- {
- va_list argptr;
- va_start(argptr, msg);
- LeakReport::StartSection(msg, argptr);
- va_end(argptr);
- }
- }
- AutoLeakReportSection::~AutoLeakReportSection()
- {
- if (m_flags.IsEnabled(Js::LeakReportFlag))
- {
- LeakReport::EndSection();
- }
- }
- #endif
|