void TelemetryImpl::RecordChromeHang(uint32_t duration, const Telemetry::HangStack &callStack, SharedLibraryInfo &moduleMap) { MOZ_ASSERT(sTelemetry); if (!sTelemetry->mCanRecord) { return; } MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex); // Only report the modules which changed since the first hang report if (sTelemetry->mHangReports.Length()) { SharedLibraryInfo &firstModuleMap = sTelemetry->mHangReports[0].moduleMap; for (size_t i = 0; i < moduleMap.GetSize(); ++i) { if (firstModuleMap.Contains(moduleMap.GetEntry(i))) { moduleMap.RemoveEntries(i, i + 1); --i; } } } HangReport newReport = { duration, callStack, moduleMap }; sTelemetry->mHangReports.AppendElement(newReport); }
static void GetChromeHangReport(Telemetry::HangStack &callStack, SharedLibraryInfo &moduleMap) { MOZ_ASSERT(winMainThreadHandle); DWORD ret = ::SuspendThread(winMainThreadHandle); if (ret == -1) { callStack.Clear(); moduleMap.Clear(); return; } NS_StackWalk(ChromeStackWalker, 0, &callStack, reinterpret_cast<uintptr_t>(winMainThreadHandle)); ret = ::ResumeThread(winMainThreadHandle); if (ret == -1) { callStack.Clear(); moduleMap.Clear(); return; } moduleMap = SharedLibraryInfo::GetInfoForSelf(); moduleMap.SortByAddress(); // Remove all modules not referenced by a PC on the stack Telemetry::HangStack sortedStack = callStack; sortedStack.Sort(); size_t moduleIndex = 0; size_t stackIndex = 0; bool unreferencedModule = true; while (stackIndex < sortedStack.Length() && moduleIndex < moduleMap.GetSize()) { uintptr_t pc = sortedStack[stackIndex]; SharedLibrary& module = moduleMap.GetEntry(moduleIndex); uintptr_t moduleStart = module.GetStart(); uintptr_t moduleEnd = module.GetEnd() - 1; if (moduleStart <= pc && pc <= moduleEnd) { // If the current PC is within the current module, mark module as used unreferencedModule = false; ++stackIndex; } else if (pc > moduleEnd) { if (unreferencedModule) { // Remove module if no PCs within its address range moduleMap.RemoveEntries(moduleIndex, moduleIndex + 1); } else { // Module was referenced on stack, but current PC belongs to later module unreferencedModule = true; ++moduleIndex; } } else { // PC does not belong to any module ++stackIndex; } } // Clean up remaining unreferenced modules, i.e. module addresses > max(pc) if (moduleIndex + 1 < moduleMap.GetSize()) { moduleMap.RemoveEntries(moduleIndex + 1, moduleMap.GetSize()); } }
virtual const CodeModule* GetModuleAtIndex(unsigned int aIndex) const { const SharedLibrary& lib = mLibs.GetEntry(aIndex); mModule = new BasicCodeModule(lib.GetStart(), lib.GetEnd() - lib.GetStart(), lib.GetName(), lib.GetBreakpadId(), lib.GetName(), lib.GetBreakpadId(), ""); // Keep mModule valid until the next GetModuleAtIndex call. return mModule; }
std::string GetSharedLibraryInfoString() { SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf(); if (info.GetSize() == 0) return "[]"; std::ostringstream os; os << "["; AddSharedLibraryInfoToStream(os, info.GetEntry(0)); for (size_t i = 1; i < info.GetSize(); i++) { os << ","; AddSharedLibraryInfoToStream(os, info.GetEntry(i)); } os << "]"; return os.str(); }
virtual unsigned int module_count() const { return mLibs.GetSize(); }
void ThreadMain(void*) { PR_SetCurrentThreadName("Hang Monitor"); MonitorAutoLock lock(*gMonitor); // In order to avoid issues with the hang monitor incorrectly triggering // during a general system stop such as sleeping, the monitor thread must // run twice to trigger hang protection. PRIntervalTime lastTimestamp = 0; int waitCount = 0; #ifdef REPORT_CHROME_HANGS Telemetry::HangStack hangStack; SharedLibraryInfo hangModuleMap; #endif while (true) { if (gShutdown) { return; // Exit the thread } // avoid rereading the volatile value in this loop PRIntervalTime timestamp = gTimestamp; PRIntervalTime now = PR_IntervalNow(); if (timestamp != PR_INTERVAL_NO_WAIT && now < timestamp) { // 32-bit overflow, reset for another waiting period timestamp = 1; // lowest legal PRInterval value } if (timestamp != PR_INTERVAL_NO_WAIT && timestamp == lastTimestamp && gTimeout > 0) { ++waitCount; if (waitCount == 2) { #ifdef REPORT_CHROME_HANGS GetChromeHangReport(hangStack, hangModuleMap); #else PRInt32 delay = PRInt32(PR_IntervalToSeconds(now - timestamp)); if (delay > gTimeout) { MonitorAutoUnlock unlock(*gMonitor); Crash(); } #endif } } else { #ifdef REPORT_CHROME_HANGS if (waitCount >= 2) { PRUint32 hangDuration = PR_IntervalToSeconds(now - lastTimestamp); Telemetry::RecordChromeHang(hangDuration, hangStack, hangModuleMap); hangStack.Clear(); hangModuleMap.Clear(); } #endif lastTimestamp = timestamp; waitCount = 0; } PRIntervalTime timeout; if (gTimeout <= 0) { timeout = PR_INTERVAL_NO_TIMEOUT; } else { timeout = PR_MillisecondsToInterval(gTimeout * 500); } lock.Wait(timeout); } }