author | Rafael Ávila de Espíndola <respindola@mozilla.org> |
Mon, 05 Nov 2012 13:45:19 -0500 | |
changeset 112339 | f8cd6fa12a8bee4f601bea35b7f8be6e94969bce |
parent 112338 | 0a6cb7947e07189eb4d4673c0b15bd584e79e2db |
child 112340 | c69822a9bd0fe015d1696be81c9e92a9203a477a |
push id | 23812 |
push user | emorley@mozilla.com |
push date | Tue, 06 Nov 2012 14:01:34 +0000 |
treeherder | mozilla-central@f4aeed115e54 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | vdjeric |
bugs | 805046 |
milestone | 19.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
toolkit/components/telemetry/ProcessedStack.h | file | annotate | diff | comparison | revisions | |
toolkit/components/telemetry/Telemetry.cpp | file | annotate | diff | comparison | revisions |
--- a/toolkit/components/telemetry/ProcessedStack.h +++ b/toolkit/components/telemetry/ProcessedStack.h @@ -52,20 +52,16 @@ public: 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).
--- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -85,16 +85,155 @@ bool AutoHashtable<EntryType>::ReflectIntoJS(ReflectEntryFunc entryFunc, JSContext *cx, JSObject *obj) { EnumeratorArgs args = { cx, obj, entryFunc }; uint32_t num = this->EnumerateEntries(ReflectEntryStub, static_cast<void*>(&args)); return num == this->Count(); } +// This class is conceptually a list of ProcessedStack objects, but it represents them +// more efficiently by keeping a single global list of modules. +class CombinedStacks { +public: + typedef std::vector<Telemetry::ProcessedStack::Frame> Stack; + const Telemetry::ProcessedStack::Module& GetModule(unsigned aIndex) const; + size_t GetModuleCount() const; + const Stack& GetStack(unsigned aIndex) const; + void AddStack(const Telemetry::ProcessedStack& aStack); + size_t GetStackCount() const; + size_t SizeOfExcludingThis() const; +private: + std::vector<Telemetry::ProcessedStack::Module> mModules; + std::vector<Stack> mStacks; +}; + +size_t +CombinedStacks::GetModuleCount() const { + return mModules.size(); +} + +const Telemetry::ProcessedStack::Module& +CombinedStacks::GetModule(unsigned aIndex) const { + return mModules[aIndex]; +} + +void +CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) { + mStacks.resize(mStacks.size() + 1); + CombinedStacks::Stack& adjustedStack = mStacks.back(); + + size_t stackSize = aStack.GetStackSize(); + for (int i = 0; i < stackSize; ++i) { + const Telemetry::ProcessedStack::Frame& frame = aStack.GetFrame(i); + uint16_t modIndex; + if (frame.mModIndex == std::numeric_limits<uint16_t>::max()) { + modIndex = frame.mModIndex; + } else { + const Telemetry::ProcessedStack::Module& module = + aStack.GetModule(frame.mModIndex); + std::vector<Telemetry::ProcessedStack::Module>::iterator modIterator = + std::find(mModules.begin(), mModules.end(), module); + if (modIterator == mModules.end()) { + mModules.push_back(module); + modIndex = mModules.size() - 1; + } else { + modIndex = modIterator - mModules.begin(); + } + } + Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex }; + adjustedStack.push_back(adjustedFrame); + } +} + +const CombinedStacks::Stack& +CombinedStacks::GetStack(unsigned aIndex) const { + return mStacks[aIndex]; +} + +size_t +CombinedStacks::GetStackCount() const { + return mStacks.size(); +} + +size_t +CombinedStacks::SizeOfExcludingThis() const { + // This is a crude approximation. We would like to do something like + // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call + // malloc_usable_size which is only safe on the pointers returned by malloc. + // While it works on current libstdc++, it is better to be safe and not assume + // that &vec[0] points to one. We could use a custom allocator, but + // it doesn't seem worth it. + size_t n = 0; + n += mModules.capacity() * sizeof(Telemetry::ProcessedStack::Module); + n += mStacks.capacity() * sizeof(Stack); + for (std::vector<Stack>::const_iterator i = mStacks.begin(), + e = mStacks.end(); i != e; ++i) { + const Stack& s = *i; + n += s.capacity() * sizeof(Telemetry::ProcessedStack::Frame); + } + return n; +} + +class HangReports { +public: + size_t SizeOfExcludingThis() const; + void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration); + size_t GetStackCount() const; + const CombinedStacks::Stack& GetStack(unsigned aIndex) const; + uint32_t GetDuration(unsigned aIndex) const; + size_t GetModuleCount() const; + const Telemetry::ProcessedStack::Module& GetModule(unsigned aIndex) const; +private: + CombinedStacks mStacks; + std::vector<uint32_t> mDurations; +}; + +void +HangReports::AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration) { + mStacks.AddStack(aStack); + mDurations.push_back(aDuration); +} + +size_t +HangReports::SizeOfExcludingThis() const { + size_t n = 0; + n += mStacks.SizeOfExcludingThis(); + // This is a crude approximation. See comment on + // CombinedStacks::SizeOfExcludingThis. + n += mDurations.capacity() * sizeof(uint32_t); + return n; +} + +size_t +HangReports::GetModuleCount() const { + return mStacks.GetModuleCount(); +} + +const Telemetry::ProcessedStack::Module& +HangReports::GetModule(unsigned aIndex) const { + return mStacks.GetModule(aIndex); +} + +uint32_t +HangReports::GetDuration(unsigned aIndex) const { + return mDurations[aIndex]; +} + +const CombinedStacks::Stack& +HangReports::GetStack(unsigned aIndex) const { + return mStacks.GetStack(aIndex); +} + +size_t +HangReports::GetStackCount() const { + MOZ_ASSERT(mDurations.size() == mStacks.GetStackCount()); + return mStacks.GetStackCount(); +} + class TelemetryImpl MOZ_FINAL : public nsITelemetry { NS_DECL_ISUPPORTS NS_DECL_NSITELEMETRY public: TelemetryImpl(); ~TelemetryImpl(); @@ -115,20 +254,16 @@ public: uint32_t hitCount; uint32_t totalTime; }; struct StmtStats { struct Stat mainThread; struct Stat otherThreads; }; typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType; - struct HangReport { - uint32_t duration; - Telemetry::ProcessedStack mStack; - }; private: // We don't need to poke inside any of our hashtables for more // information, so we just have One Function To Size Them All. template<typename EntryType> struct impl { static size_t SizeOfEntryExcludingThis(EntryType *, nsMallocSizeOfFun, @@ -186,17 +321,17 @@ private: bool mCanRecord; static TelemetryImpl *sTelemetry; AutoHashtable<SlowSQLEntryType> mPrivateSQL; AutoHashtable<SlowSQLEntryType> mSanitizedSQL; // This gets marked immutable in debug builds, so we can't use // AutoHashtable here. nsTHashtable<nsCStringHashKey> mTrackedDBs; Mutex mHashMutex; - nsTArray<HangReport> mHangReports; + HangReports mHangReports; Mutex mHangReportsMutex; nsIMemoryReporter *mMemoryReporter; }; TelemetryImpl* TelemetryImpl::sTelemetry = NULL; NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(TelemetryMallocSizeOf, "telemetry") @@ -211,17 +346,17 @@ TelemetryImpl::SizeOfIncludingThis(nsMal n += mHistogramMap.SizeOfExcludingThis(impl<CharPtrEntryType>::SizeOfEntryExcludingThis, aMallocSizeOf); n += mPrivateSQL.SizeOfExcludingThis(impl<SlowSQLEntryType>::SizeOfEntryExcludingThis, aMallocSizeOf); n += mSanitizedSQL.SizeOfExcludingThis(impl<SlowSQLEntryType>::SizeOfEntryExcludingThis, aMallocSizeOf); n += mTrackedDBs.SizeOfExcludingThis(impl<nsCStringHashKey>::SizeOfEntryExcludingThis, aMallocSizeOf); - n += mHangReports.SizeOfExcludingThis(aMallocSizeOf); + n += mHangReports.SizeOfExcludingThis(); return n; } int64_t TelemetryImpl::GetTelemetryMemoryUsed() { int64_t n = 0; if (sTelemetry) { @@ -1088,30 +1223,30 @@ TelemetryImpl::GetChromeHangs(JSContext MutexAutoLock hangReportMutex(mHangReportsMutex); JSObject *reportArray = JS_NewArrayObject(cx, 0, nullptr); if (!reportArray) { return NS_ERROR_FAILURE; } *ret = OBJECT_TO_JSVAL(reportArray); // 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; + for (size_t i = 0; i < mHangReports.GetStackCount(); ++i) { + const CombinedStacks::Stack &stack = mHangReports.GetStack(i); JSObject *reportObj = JS_NewObject(cx, NULL, NULL, NULL); if (!reportObj) { return NS_ERROR_FAILURE; } jsval reportObjVal = OBJECT_TO_JSVAL(reportObj); if (!JS_SetElement(cx, reportArray, i, &reportObjVal)) { return NS_ERROR_FAILURE; } // Record the hang duration (expressed in seconds) JSBool ok = JS_DefineProperty(cx, reportObj, "duration", - INT_TO_JSVAL(mHangReports[i].duration), + INT_TO_JSVAL(mHangReports.GetDuration(i)), NULL, NULL, JSPROP_ENUMERATE); if (!ok) { return NS_ERROR_FAILURE; } // Represent call stack PCs as strings // (JS can't represent all 64-bit integer values) JSObject *pcArray = JS_NewArrayObject(cx, 0, nullptr); @@ -1119,20 +1254,20 @@ TelemetryImpl::GetChromeHangs(JSContext return NS_ERROR_FAILURE; } ok = JS_DefineProperty(cx, reportObj, "stack", OBJECT_TO_JSVAL(pcArray), NULL, NULL, JSPROP_ENUMERATE); if (!ok) { return NS_ERROR_FAILURE; } - const uint32_t pcCount = stack.GetStackSize(); + const uint32_t pcCount = stack.size(); for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) { nsAutoCString pcString; - const Telemetry::ProcessedStack::Frame &Frame = stack.GetFrame(pcIndex); + const Telemetry::ProcessedStack::Frame &Frame = stack[pcIndex]; pcString.AppendPrintf("0x%p", Frame.mOffset); JSString *str = JS_NewStringCopyZ(cx, pcString.get()); if (!str) { return NS_ERROR_FAILURE; } jsval v = STRING_TO_JSVAL(str); if (!JS_SetElement(cx, pcArray, pcIndex, &v)) { return NS_ERROR_FAILURE; @@ -1146,21 +1281,21 @@ TelemetryImpl::GetChromeHangs(JSContext } ok = JS_DefineProperty(cx, reportObj, "memoryMap", OBJECT_TO_JSVAL(moduleArray), NULL, NULL, JSPROP_ENUMERATE); if (!ok) { return NS_ERROR_FAILURE; } - const uint32_t moduleCount = stack.GetNumModules(); + const uint32_t moduleCount = (i == 0) ? mHangReports.GetModuleCount() : 0; for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { // Current module const Telemetry::ProcessedStack::Module &module = - stack.GetModule(moduleIndex); + mHangReports.GetModule(moduleIndex); JSObject *moduleInfoArray = JS_NewArrayObject(cx, 0, nullptr); if (!moduleInfoArray) { return NS_ERROR_FAILURE; } jsval val = OBJECT_TO_JSVAL(moduleInfoArray); if (!JS_SetElement(cx, moduleArray, moduleIndex, &val)) { return NS_ERROR_FAILURE; @@ -1487,31 +1622,17 @@ void TelemetryImpl::RecordChromeHang(uint32_t duration, Telemetry::ProcessedStack &aStack) { if (!sTelemetry || !sTelemetry->mCanRecord) return; MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex); - // Only report the modules which changed since the first hang report - if (sTelemetry->mHangReports.Length()) { - Telemetry::ProcessedStack &firstStack = - sTelemetry->mHangReports[0].mStack; - for (size_t i = 0; i < aStack.GetNumModules(); ++i) { - const Telemetry::ProcessedStack::Module &module = aStack.GetModule(i); - if (firstStack.HasModule(module)) { - aStack.RemoveModule(i); - --i; - } - } - } - - HangReport newReport = { duration, aStack }; - sTelemetry->mHangReports.AppendElement(newReport); + sTelemetry->mHangReports.AddHang(aStack, duration); } #endif NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetryImpl, nsITelemetry) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance) #define NS_TELEMETRY_CID \ {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}} @@ -1625,25 +1746,16 @@ size_t ProcessedStack::GetNumModules() c } 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();