Bug 783154 - Refactor the chrome hang code to use the same class as write poisoning. r=taras,benwa,vladan.

This commit is contained in:
Rafael Ávila de Espíndola
2012-08-21 17:14:38 -04:00
parent 833f632ca4
commit 2da5ebd162
8 changed files with 325 additions and 240 deletions

View File

@@ -25,6 +25,7 @@ EXPORTS_NAMESPACES = mozilla
EXPORTS_mozilla = \
Telemetry.h \
ProcessedStack.h \
TelemetryHistograms.h \
$(NULL)

View File

@@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ProcessedStack_h__
#define ProcessedStack_h__
#include <string>
#include <vector>
namespace mozilla {
namespace Telemetry {
// This class represents a stack trace and the modules referenced in that trace.
// It is designed to be easy to read and write to disk or network and doesn't
// include any logic on how to collect or read the information it stores.
class ProcessedStack
{
public:
ProcessedStack();
size_t GetStackSize() const;
size_t GetNumModules() const;
struct Frame
{
// The offset of this program counter in its module or an absolute pc.
uintptr_t mOffset;
// The index to pass to GetModule to get the module this program counter
// was in.
uint16_t mModIndex;
};
struct Module
{
// The file name, /foo/bar/libxul.so for example.
std::string mName;
// The address it was loaded to.
// FIXME: remove this once chrome hang has switched to using offsets.
uintptr_t mStart;
// The size of this mapping. May or may not be the entire file.
// FIXME: remove this. It was only used as a sanity check.
size_t mMappingSize;
// Windows specific fields. On other platforms they are 0/empty.
int mPdbAge;
std::string mPdbSignature;
std::string mPdbName;
bool operator==(const Module& other) const;
};
const Frame &GetFrame(unsigned aIndex) const;
void AddFrame(const Frame& aFrame);
const Module &GetModule(unsigned aIndex) const;
void AddModule(const Module& aFrame);
void Clear();
// FIXME: remove these once chrome hang has switched to using offsets.
bool HasModule(const Module &aModule) const;
void RemoveModule(unsigned aIndex);
private:
std::vector<Module> mModules;
std::vector<Frame> mStack;
};
// Get the current list of loaded modules, filter and pair it to the provided
// stack. We let the caller collect the stack since different callers have
// different needs (current thread X main thread, stopping the thread, etc).
// FIXME: remove the aRelative option once chrome hang has switched to using
// offsets.
ProcessedStack
GetStackAndModules(const std::vector<uintptr_t> &aPCs, bool aRelative);
} // namespace Telemetry
} // namespace mozilla
#endif // ProcessedStack_h__

View File

@@ -3,6 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <algorithm>
#include "base/histogram.h"
#include "base/pickle.h"
#include "nsIComponentManager.h"
@@ -22,6 +23,7 @@
#include "nsBaseHashtable.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Mutex.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Preferences.h"
@@ -102,8 +104,7 @@ public:
uint32_t delay);
#if defined(MOZ_ENABLE_PROFILER_SPS)
static void RecordChromeHang(uint32_t duration,
const Telemetry::HangStack &callStack,
SharedLibraryInfo &moduleMap);
Telemetry::ProcessedStack &aStack);
#endif
static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
struct Stat {
@@ -117,10 +118,7 @@ public:
typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
struct HangReport {
uint32_t duration;
Telemetry::HangStack callStack;
#if defined(MOZ_ENABLE_PROFILER_SPS)
SharedLibraryInfo moduleMap;
#endif
Telemetry::ProcessedStack mStack;
};
private:
@@ -1021,6 +1019,7 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
// Each hang report is an object in the 'chromeHangs' array
for (size_t i = 0; i < mHangReports.Length(); ++i) {
Telemetry::ProcessedStack &stack = mHangReports[i].mStack;
JSObject *reportObj = JS_NewObject(cx, NULL, NULL, NULL);
if (!reportObj) {
return NS_ERROR_FAILURE;
@@ -1050,10 +1049,11 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
return NS_ERROR_FAILURE;
}
const uint32_t pcCount = mHangReports[i].callStack.Length();
const uint32_t pcCount = stack.GetStackSize();
for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) {
nsCAutoString pcString;
pcString.AppendPrintf("0x%p", mHangReports[i].callStack[pcIndex]);
const Telemetry::ProcessedStack::Frame &Frame = stack.GetFrame(pcIndex);
pcString.AppendPrintf("0x%p", Frame.mOffset);
JSString *str = JS_NewStringCopyZ(cx, pcString.get());
if (!str) {
return NS_ERROR_FAILURE;
@@ -1076,12 +1076,11 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
return NS_ERROR_FAILURE;
}
#if defined(MOZ_ENABLE_PROFILER_SPS)
const uint32_t moduleCount = mHangReports[i].moduleMap.GetSize();
const uint32_t moduleCount = stack.GetNumModules();
for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
// Current module
const SharedLibrary &module =
mHangReports[i].moduleMap.GetEntry(moduleIndex);
const Telemetry::ProcessedStack::Module &module =
stack.GetModule(moduleIndex);
JSObject *moduleInfoArray = JS_NewArrayObject(cx, 0, nullptr);
if (!moduleInfoArray) {
@@ -1094,7 +1093,7 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
// Start address
nsCAutoString addressString;
addressString.AppendPrintf("0x%p", module.GetStart());
addressString.AppendPrintf("0x%p", module.mStart);
JSString *str = JS_NewStringCopyZ(cx, addressString.get());
if (!str) {
return NS_ERROR_FAILURE;
@@ -1105,7 +1104,7 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
}
// Module name
str = JS_NewStringCopyZ(cx, module.GetName());
str = JS_NewStringCopyZ(cx, module.mName.c_str());
if (!str) {
return NS_ERROR_FAILURE;
}
@@ -1115,26 +1114,19 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
}
// Module size in memory
val = INT_TO_JSVAL(int32_t(module.GetEnd() - module.GetStart()));
val = INT_TO_JSVAL(int32_t(module.mMappingSize));
if (!JS_SetElement(cx, moduleInfoArray, 2, &val)) {
return NS_ERROR_FAILURE;
}
// "PDB Age" identifier
val = INT_TO_JSVAL(0);
#if defined(MOZ_PROFILING) && defined(XP_WIN)
val = INT_TO_JSVAL(module.GetPdbAge());
#endif
val = INT_TO_JSVAL(module.mPdbAge);
if (!JS_SetElement(cx, moduleInfoArray, 3, &val)) {
return NS_ERROR_FAILURE;
}
// "PDB Signature" GUID
char guidString[NSID_LENGTH] = { 0 };
#if defined(MOZ_PROFILING) && defined(XP_WIN)
module.GetPdbSignature().ToProvidedString(guidString);
#endif
str = JS_NewStringCopyZ(cx, guidString);
str = JS_NewStringCopyZ(cx, module.mPdbSignature.c_str());
if (!str) {
return NS_ERROR_FAILURE;
}
@@ -1144,11 +1136,7 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
}
// Name of associated PDB file
const char *pdbName = "";
#if defined(MOZ_PROFILING) && defined(XP_WIN)
pdbName = module.GetPdbName();
#endif
str = JS_NewStringCopyZ(cx, pdbName);
str = JS_NewStringCopyZ(cx, module.mPdbName.c_str());
if (!str) {
return NS_ERROR_FAILURE;
}
@@ -1157,7 +1145,6 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret)
return NS_ERROR_FAILURE;
}
}
#endif
}
return NS_OK;
@@ -1424,8 +1411,7 @@ TelemetryImpl::RecordSlowStatement(const nsACString &sql,
#if defined(MOZ_ENABLE_PROFILER_SPS)
void
TelemetryImpl::RecordChromeHang(uint32_t duration,
const Telemetry::HangStack &callStack,
SharedLibraryInfo &moduleMap)
Telemetry::ProcessedStack &aStack)
{
MOZ_ASSERT(sTelemetry);
if (!sTelemetry->mCanRecord) {
@@ -1436,17 +1422,19 @@ TelemetryImpl::RecordChromeHang(uint32_t duration,
// 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);
Telemetry::ProcessedStack &firstStack =
sTelemetry->mHangReports[0].mStack;
const uint32_t moduleCount = aStack.GetNumModules();
for (size_t i = 0; i < moduleCount; ++i) {
const Telemetry::ProcessedStack::Module &module = aStack.GetModule(i);
if (firstStack.HasModule(module)) {
aStack.RemoveModule(i);
--i;
}
}
}
HangReport newReport = { duration, callStack, moduleMap };
HangReport newReport = { duration, aStack };
sTelemetry->mHangReports.AppendElement(newReport);
}
#endif
@@ -1534,13 +1522,195 @@ void Init()
#if defined(MOZ_ENABLE_PROFILER_SPS)
void RecordChromeHang(uint32_t duration,
const Telemetry::HangStack &callStack,
SharedLibraryInfo &moduleMap)
ProcessedStack &aStack)
{
TelemetryImpl::RecordChromeHang(duration, callStack, moduleMap);
TelemetryImpl::RecordChromeHang(duration, aStack);
}
#endif
ProcessedStack::ProcessedStack()
{
}
size_t ProcessedStack::GetStackSize() const
{
return mStack.size();
}
const ProcessedStack::Frame &ProcessedStack::GetFrame(unsigned aIndex) const
{
MOZ_ASSERT(aIndex < mStack.size());
return mStack[aIndex];
}
void ProcessedStack::AddFrame(const Frame &aFrame)
{
mStack.push_back(aFrame);
}
size_t ProcessedStack::GetNumModules() const
{
return mModules.size();
}
const ProcessedStack::Module &ProcessedStack::GetModule(unsigned aIndex) const
{
MOZ_ASSERT(aIndex < mModules.size());
return mModules[aIndex];
}
bool ProcessedStack::HasModule(const Module &aModule) const {
return mModules.end() !=
std::find(mModules.begin(), mModules.end(), aModule);
}
void ProcessedStack::RemoveModule(unsigned aIndex) {
mModules.erase(mModules.begin() + aIndex);
}
void ProcessedStack::AddModule(const Module &aModule)
{
mModules.push_back(aModule);
}
void ProcessedStack::Clear() {
mModules.clear();
mStack.clear();
}
bool ProcessedStack::Module::operator==(const Module& aOther) const {
return mName == aOther.mName &&
mStart == aOther.mStart &&
mMappingSize == aOther.mMappingSize &&
mPdbAge == aOther.mPdbAge &&
mPdbSignature == aOther.mPdbSignature &&
mPdbName == aOther.mPdbName;
}
struct StackFrame
{
uintptr_t mPC; // The program counter at this position in the call stack.
uint16_t mIndex; // The number of this frame in the call stack.
uint16_t mModIndex; // The index of module that has this program counter.
};
#ifdef MOZ_ENABLE_PROFILER_SPS
static bool CompareByPC(const StackFrame &a, const StackFrame &b)
{
return a.mPC < b.mPC;
}
static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
{
return a.mIndex < b.mIndex;
}
#endif
ProcessedStack GetStackAndModules(const std::vector<uintptr_t> &aPCs, bool aRelative)
{
std::vector<StackFrame> rawStack;
for (std::vector<uintptr_t>::const_iterator i = aPCs.begin(),
e = aPCs.end(); i != e; ++i) {
uintptr_t aPC = *i;
StackFrame Frame = {aPC, static_cast<uint16_t>(rawStack.size()),
std::numeric_limits<uint16_t>::max()};
rawStack.push_back(Frame);
}
#ifdef MOZ_ENABLE_PROFILER_SPS
// Remove all modules not referenced by a PC on the stack
std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
size_t moduleIndex = 0;
size_t stackIndex = 0;
size_t stackSize = rawStack.size();
SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
rawModules.SortByAddress();
while (moduleIndex < rawModules.GetSize()) {
const SharedLibrary& module = rawModules.GetEntry(moduleIndex);
uintptr_t moduleStart = module.GetStart();
uintptr_t moduleEnd = module.GetEnd() - 1;
// the interval is [moduleStart, moduleEnd)
bool moduleReferenced = false;
for (;stackIndex < stackSize; ++stackIndex) {
uintptr_t pc = rawStack[stackIndex].mPC;
if (pc >= moduleEnd)
break;
if (pc >= moduleStart) {
// If the current PC is within the current module, mark
// module as used
moduleReferenced = true;
if (aRelative)
rawStack[stackIndex].mPC -= moduleStart;
rawStack[stackIndex].mModIndex = moduleIndex;
} else {
// PC does not belong to any module. It is probably from
// the JIT. Use a fixed mPC so that we don't get different
// stacks on different runs.
rawStack[stackIndex].mPC =
std::numeric_limits<uintptr_t>::max();
}
}
if (moduleReferenced) {
++moduleIndex;
} else {
// Remove module if no PCs within its address range
rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
}
}
for (;stackIndex < stackSize; ++stackIndex) {
// These PCs are past the last module.
rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
}
std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
#endif
// Copy the information to the return value.
ProcessedStack Ret;
for (std::vector<StackFrame>::iterator i = rawStack.begin(),
e = rawStack.end(); i != e; ++i) {
const StackFrame &rawFrame = *i;
ProcessedStack::Frame frame = { rawFrame.mPC, rawFrame.mModIndex };
Ret.AddFrame(frame);
}
#ifdef MOZ_ENABLE_PROFILER_SPS
for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
const SharedLibrary &info = rawModules.GetEntry(i);
ProcessedStack::Module module = {
info.GetName(),
info.GetStart(),
info.GetEnd() - info.GetStart(),
#ifdef XP_WIN
info.GetPdbAge(),
"", // mPdbSignature
info.GetPdbName(),
#else
0, // mPdbAge
"", // mPdbSignature
"" // mPdbName
#endif
};
#ifdef XP_WIN
char guidString[NSID_LENGTH] = { 0 };
info.GetPdbSignature().ToProvidedString(guidString);
module.mPdbSignature = guidString;
#endif
Ret.AddModule(module);
}
#endif
return Ret;
}
} // namespace Telemetry
} // namespace mozilla

View File

@@ -127,10 +127,7 @@ void RecordSlowSQLStatement(const nsACString &statement,
*/
const PRUint32 kSlowStatementThreshold = 100;
/**
* nsTArray of pointers representing PCs on a call stack
*/
typedef nsTArray<uintptr_t> HangStack;
class ProcessedStack;
/**
* Record the main thread's call stack after it hangs.
@@ -141,8 +138,7 @@ typedef nsTArray<uintptr_t> HangStack;
*/
#if defined(MOZ_ENABLE_PROFILER_SPS)
void RecordChromeHang(PRUint32 duration,
const HangStack &callStack,
SharedLibraryInfo &moduleMap);
ProcessedStack &aStack);
#endif
} // namespace Telemetry

View File

@@ -59,7 +59,7 @@ nsProfiler::GetProfile(char **aProfile)
}
static void
AddSharedLibraryInfoToStream(std::ostream& aStream, SharedLibrary& aLib)
AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
{
aStream << "{";
aStream << "\"start\":" << aLib.GetStart();

View File

@@ -140,7 +140,7 @@ public:
mEntries.push_back(entry);
}
SharedLibrary& GetEntry(size_t i)
const SharedLibrary& GetEntry(size_t i) const
{
return mEntries[i];
}

View File

@@ -11,6 +11,7 @@
#include "mozilla/Scoped.h"
#include "mozilla/Mutex.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ProcessedStack.h"
#include "nsStackWalk.h"
#include "nsPrintfCString.h"
#include "mach_override.h"
@@ -38,131 +39,11 @@ struct FuncData {
// 'Function' after it has been replaced.
};
// FIXME: duplicated code. The HangMonitor could also report processed addresses,
// this class should be moved somewhere it can be shared.
class ProcessedStack
{
public:
ProcessedStack() : mProcessed(false)
{
}
void Reserve(unsigned int n)
{
mStack.reserve(n);
}
size_t GetStackSize()
{
return mStack.size();
}
struct ProcessedStackFrame
{
uintptr_t mOffset;
uint16_t mModIndex;
};
ProcessedStackFrame GetFrame(unsigned aIndex)
{
const StackFrame &Frame = mStack[aIndex];
ProcessedStackFrame Ret = { Frame.mPC, Frame.mModIndex };
return Ret;
}
size_t GetNumModules()
{
MOZ_ASSERT(mProcessed);
return mModules.GetSize();
}
const char *GetModuleName(unsigned aIndex)
{
MOZ_ASSERT(mProcessed);
return mModules.GetEntry(aIndex).GetName();
}
void AddStackFrame(uintptr_t aPC)
{
MOZ_ASSERT(!mProcessed);
StackFrame Frame = {aPC, static_cast<uint16_t>(mStack.size()),
std::numeric_limits<uint16_t>::max()};
mStack.push_back(Frame);
}
void Process()
{
mProcessed = true;
mModules = SharedLibraryInfo::GetInfoForSelf();
mModules.SortByAddress();
// Remove all modules not referenced by a PC on the stack
std::sort(mStack.begin(), mStack.end(), CompareByPC);
size_t moduleIndex = 0;
size_t stackIndex = 0;
size_t stackSize = mStack.size();
while (moduleIndex < mModules.GetSize()) {
SharedLibrary& module = mModules.GetEntry(moduleIndex);
uintptr_t moduleStart = module.GetStart();
uintptr_t moduleEnd = module.GetEnd() - 1;
// the interval is [moduleStart, moduleEnd)
bool moduleReferenced = false;
for (;stackIndex < stackSize; ++stackIndex) {
uintptr_t pc = mStack[stackIndex].mPC;
if (pc >= moduleEnd)
break;
if (pc >= moduleStart) {
// If the current PC is within the current module, mark
// module as used
moduleReferenced = true;
mStack[stackIndex].mPC -= moduleStart;
mStack[stackIndex].mModIndex = moduleIndex;
} else {
// PC does not belong to any module. It is probably from
// the JIT. Use a fixed mPC so that we don't get different
// stacks on different runs.
mStack[stackIndex].mPC =
std::numeric_limits<uintptr_t>::max();
}
}
if (moduleReferenced) {
++moduleIndex;
} else {
// Remove module if no PCs within its address range
mModules.RemoveEntries(moduleIndex, moduleIndex + 1);
}
}
for (;stackIndex < stackSize; ++stackIndex) {
// These PCs are past the last module.
mStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
}
std::sort(mStack.begin(), mStack.end(), CompareByIndex);
}
private:
struct StackFrame
{
uintptr_t mPC; // The program counter at this position in the call stack.
uint16_t mIndex; // The number of this frame in the call stack.
uint16_t mModIndex; // The index of module that has this program counter.
};
static bool CompareByPC(const StackFrame &a, const StackFrame &b)
{
return a.mPC < b.mPC;
}
static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
{
return a.mIndex < b.mIndex;
}
SharedLibraryInfo mModules;
std::vector<StackFrame> mStack;
bool mProcessed;
};
void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
{
ProcessedStack *stack = static_cast<ProcessedStack*>(aClosure);
stack->AddStackFrame(reinterpret_cast<uintptr_t>(aPC));
std::vector<uintptr_t> *stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
}
char *sProfileDirectory = NULL;
@@ -177,9 +58,10 @@ bool ValidWriteAssert(bool ok)
// Write the stack and loaded libraries to a file. We can get here
// concurrently from many writes, so we use multiple temporary files.
ProcessedStack stack;
NS_StackWalk(RecordStackWalker, 0, reinterpret_cast<void*>(&stack), 0);
stack.Process();
std::vector<uintptr_t> rawStack;
NS_StackWalk(RecordStackWalker, 0, reinterpret_cast<void*>(&rawStack), 0);
Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack, true);
nsPrintfCString nameAux("%s%s", sProfileDirectory,
"/Telemetry.LateWriteTmpXXXXXX");
@@ -192,14 +74,15 @@ bool ValidWriteAssert(bool ok)
size_t numModules = stack.GetNumModules();
fprintf(f, "%zu\n", numModules);
for (int i = 0; i < numModules; ++i) {
const char *name = stack.GetModuleName(i);
fprintf(f, "%s\n", name ? name : "");
Telemetry::ProcessedStack::Module module = stack.GetModule(i);
fprintf(f, "%s\n", module.mName.c_str());
}
size_t numFrames = stack.GetStackSize();
fprintf(f, "%zu\n", numFrames);
for (size_t i = 0; i < numFrames; ++i) {
const ProcessedStack::ProcessedStackFrame &frame = stack.GetFrame(i);
const Telemetry::ProcessedStack::Frame &frame =
stack.GetFrame(i);
// NOTE: We write the offsets, while the atos tool expects a value with
// the virtual address added. For example, running otool -l on the the firefox
// binary shows

View File

@@ -7,6 +7,7 @@
#include "mozilla/Monitor.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ProcessedStack.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "nsStackWalk.h"
@@ -112,75 +113,32 @@ static void
ChromeStackWalker(void *aPC, void *aSP, void *aClosure)
{
MOZ_ASSERT(aClosure);
Telemetry::HangStack *callStack =
reinterpret_cast< Telemetry::HangStack* >(aClosure);
if (callStack->Length() < MAX_CALL_STACK_PCS)
callStack->AppendElement(reinterpret_cast<uintptr_t>(aPC));
std::vector<uintptr_t> *stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
if (stack->size() == MAX_CALL_STACK_PCS)
return;
MOZ_ASSERT(stack->size() < MAX_CALL_STACK_PCS);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
}
static void
GetChromeHangReport(Telemetry::HangStack &callStack, SharedLibraryInfo &moduleMap)
GetChromeHangReport(Telemetry::ProcessedStack &aStack)
{
MOZ_ASSERT(winMainThreadHandle);
// The thread we're about to suspend might have the alloc lock
// so allocate ahead of time
callStack.SetCapacity(MAX_CALL_STACK_PCS);
std::vector<uintptr_t> rawStack;
rawStack.reserve(MAX_CALL_STACK_PCS);
DWORD ret = ::SuspendThread(winMainThreadHandle);
if (ret == -1) {
callStack.Clear();
moduleMap.Clear();
if (ret == -1)
return;
}
NS_StackWalk(ChromeStackWalker, 0, &callStack,
NS_StackWalk(ChromeStackWalker, 0, reinterpret_cast<void*>(&rawStack),
reinterpret_cast<uintptr_t>(winMainThreadHandle));
ret = ::ResumeThread(winMainThreadHandle);
if (ret == -1) {
callStack.Clear();
moduleMap.Clear();
if (ret == -1)
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());
}
aStack = Telemetry::GetStackAndModules(rawStack, false);
}
#endif
@@ -198,8 +156,7 @@ ThreadMain(void*)
int waitCount = 0;
#ifdef REPORT_CHROME_HANGS
Telemetry::HangStack hangStack;
SharedLibraryInfo hangModuleMap;
Telemetry::ProcessedStack stack;
#endif
while (true) {
@@ -224,7 +181,7 @@ ThreadMain(void*)
++waitCount;
if (waitCount == 2) {
#ifdef REPORT_CHROME_HANGS
GetChromeHangReport(hangStack, hangModuleMap);
GetChromeHangReport(stack);
#else
PRInt32 delay =
PRInt32(PR_IntervalToSeconds(now - timestamp));
@@ -239,9 +196,8 @@ ThreadMain(void*)
#ifdef REPORT_CHROME_HANGS
if (waitCount >= 2) {
PRUint32 hangDuration = PR_IntervalToSeconds(now - lastTimestamp);
Telemetry::RecordChromeHang(hangDuration, hangStack, hangModuleMap);
hangStack.Clear();
hangModuleMap.Clear();
Telemetry::RecordChromeHang(hangDuration, stack);
stack.Clear();
}
#endif
lastTimestamp = timestamp;