author | Rafael Ávila de Espíndola <respindola@mozilla.org> |
Tue, 20 Nov 2012 09:45:14 -0500 | |
changeset 113775 | 2b3982c803ec8436db700f8e26e5165f05d4b0e3 |
parent 113774 | b7e492bf7c1386e9847913d91dab00415937a296 |
child 113776 | f0a6c0c3cd8db7ecfd527c418456473291d59493 |
push id | 23890 |
push user | ryanvm@gmail.com |
push date | Wed, 21 Nov 2012 02:43:32 +0000 |
treeherder | mozilla-central@4f19e7fd8bea [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | vladan |
bugs | 808699 |
milestone | 20.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
|
--- a/toolkit/components/telemetry/ProcessedStack.h +++ b/toolkit/components/telemetry/ProcessedStack.h @@ -30,23 +30,16 @@ public: // 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; }; @@ -60,16 +53,14 @@ public: 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); +GetStackAndModules(const std::vector<uintptr_t> &aPCs); } // namespace Telemetry } // namespace mozilla #endif // ProcessedStack_h__
--- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -1216,147 +1216,145 @@ TelemetryImpl::GetDebugSlowSQL(JSContext return NS_OK; return NS_ERROR_FAILURE; } NS_IMETHODIMP TelemetryImpl::GetChromeHangs(JSContext *cx, jsval *ret) { MutexAutoLock hangReportMutex(mHangReportsMutex); + + JSObject *fullReportObj = JS_NewObject(cx, nullptr, nullptr, nullptr); + if (!fullReportObj) { + return NS_ERROR_FAILURE; + } + + *ret = OBJECT_TO_JSVAL(fullReportObj); + + JSObject *moduleArray = JS_NewArrayObject(cx, 0, nullptr); + if (!moduleArray) { + return NS_ERROR_FAILURE; + } + JSBool ok = JS_DefineProperty(cx, fullReportObj, "memoryMap", + OBJECT_TO_JSVAL(moduleArray), + NULL, NULL, JSPROP_ENUMERATE); + if (!ok) { + return NS_ERROR_FAILURE; + } + + const uint32_t moduleCount = mHangReports.GetModuleCount(); + for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { + // Current module + const Telemetry::ProcessedStack::Module& module = + 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; + } + + unsigned index = 0; + + // Module name + JSString *str = JS_NewStringCopyZ(cx, module.mName.c_str()); + if (!str) { + return NS_ERROR_FAILURE; + } + val = STRING_TO_JSVAL(str); + if (!JS_SetElement(cx, moduleInfoArray, index++, &val)) { + return NS_ERROR_FAILURE; + } + + // "PDB Age" identifier + val = INT_TO_JSVAL(module.mPdbAge); + if (!JS_SetElement(cx, moduleInfoArray, index++, &val)) { + return NS_ERROR_FAILURE; + } + + // "PDB Signature" GUID + str = JS_NewStringCopyZ(cx, module.mPdbSignature.c_str()); + if (!str) { + return NS_ERROR_FAILURE; + } + val = STRING_TO_JSVAL(str); + if (!JS_SetElement(cx, moduleInfoArray, index++, &val)) { + return NS_ERROR_FAILURE; + } + + // Name of associated PDB file + str = JS_NewStringCopyZ(cx, module.mPdbName.c_str()); + if (!str) { + return NS_ERROR_FAILURE; + } + val = STRING_TO_JSVAL(str); + if (!JS_SetElement(cx, moduleInfoArray, index++, &val)) { + return NS_ERROR_FAILURE; + } + } + JSObject *reportArray = JS_NewArrayObject(cx, 0, nullptr); if (!reportArray) { return NS_ERROR_FAILURE; } - *ret = OBJECT_TO_JSVAL(reportArray); + ok = JS_DefineProperty(cx, fullReportObj, "stacks", + OBJECT_TO_JSVAL(reportArray), + NULL, NULL, JSPROP_ENUMERATE); + if (!ok) { + return NS_ERROR_FAILURE; + } - // Each hang report is an object in the 'chromeHangs' array - 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)) { + JSObject *durationArray = JS_NewArrayObject(cx, 0, nullptr); + ok = JS_DefineProperty(cx, fullReportObj, "durations", + OBJECT_TO_JSVAL(durationArray), + NULL, NULL, JSPROP_ENUMERATE); + if (!ok) { + return NS_ERROR_FAILURE; + } + + for (size_t i = 0, n = mHangReports.GetStackCount(); i < n; ++i) { + jsval duration = INT_TO_JSVAL(mHangReports.GetDuration(i)); + if (!JS_SetElement(cx, durationArray, i, &duration)) { return NS_ERROR_FAILURE; } - // Record the hang duration (expressed in seconds) - JSBool ok = JS_DefineProperty(cx, reportObj, "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) + // Represent call stack PCs as (module index, offset) pairs. JSObject *pcArray = JS_NewArrayObject(cx, 0, nullptr); if (!pcArray) { 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.size(); - for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) { - nsAutoCString pcString; - 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; - } - } - - // Record memory map info - JSObject *moduleArray = JS_NewArrayObject(cx, 0, nullptr); - if (!moduleArray) { - return NS_ERROR_FAILURE; - } - ok = JS_DefineProperty(cx, reportObj, "memoryMap", - OBJECT_TO_JSVAL(moduleArray), - NULL, NULL, JSPROP_ENUMERATE); - if (!ok) { + jsval pcArrayVal = OBJECT_TO_JSVAL(pcArray); + if (!JS_SetElement(cx, reportArray, i, &pcArrayVal)) { return NS_ERROR_FAILURE; } - const uint32_t moduleCount = (i == 0) ? mHangReports.GetModuleCount() : 0; - for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { - // Current module - const Telemetry::ProcessedStack::Module &module = - 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; - } - - // Start address - nsAutoCString addressString; - addressString.AppendPrintf("0x%p", module.mStart); - JSString *str = JS_NewStringCopyZ(cx, addressString.get()); - if (!str) { - return NS_ERROR_FAILURE; - } - val = STRING_TO_JSVAL(str); - if (!JS_SetElement(cx, moduleInfoArray, 0, &val)) { - return NS_ERROR_FAILURE; - } - - // Module name - str = JS_NewStringCopyZ(cx, module.mName.c_str()); - if (!str) { + const CombinedStacks::Stack& stack = mHangReports.GetStack(i); + const uint32_t pcCount = stack.size(); + for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) { + const Telemetry::ProcessedStack::Frame& frame = stack[pcIndex]; + JSObject *framePair = JS_NewArrayObject(cx, 0, nullptr); + if (!framePair) { return NS_ERROR_FAILURE; } - val = STRING_TO_JSVAL(str); - if (!JS_SetElement(cx, moduleInfoArray, 1, &val)) { - return NS_ERROR_FAILURE; - } - - // Module size in memory - 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(module.mPdbAge); - if (!JS_SetElement(cx, moduleInfoArray, 3, &val)) { + int modIndex = (std::numeric_limits<uint16_t>::max() == frame.mModIndex) ? + -1 : frame.mModIndex; + jsval modIndexVal = INT_TO_JSVAL(modIndex); + if (!JS_SetElement(cx, framePair, 0, &modIndexVal)) { return NS_ERROR_FAILURE; } - - // "PDB Signature" GUID - str = JS_NewStringCopyZ(cx, module.mPdbSignature.c_str()); - if (!str) { + jsval mOffsetVal = INT_TO_JSVAL(frame.mOffset); + if (!JS_SetElement(cx, framePair, 1, &mOffsetVal)) { return NS_ERROR_FAILURE; } - val = STRING_TO_JSVAL(str); - if (!JS_SetElement(cx, moduleInfoArray, 4, &val)) { - return NS_ERROR_FAILURE; - } - - // Name of associated PDB file - str = JS_NewStringCopyZ(cx, module.mPdbName.c_str()); - if (!str) { - return NS_ERROR_FAILURE; - } - val = STRING_TO_JSVAL(str); - if (!JS_SetElement(cx, moduleInfoArray, 5, &val)) { + jsval framePairVal = OBJECT_TO_JSVAL(framePair); + if (!JS_SetElement(cx, pcArray, pcIndex, &framePairVal)) { return NS_ERROR_FAILURE; } } } return NS_OK; } @@ -1758,18 +1756,16 @@ void ProcessedStack::AddModule(const Mod 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. @@ -1785,17 +1781,18 @@ static bool CompareByPC(const StackFrame } 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) +ProcessedStack +GetStackAndModules(const std::vector<uintptr_t>& aPCs) { 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); @@ -1823,18 +1820,17 @@ ProcessedStack GetStackAndModules(const 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].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(); } @@ -1865,18 +1861,16 @@ ProcessedStack GetStackAndModules(const 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
--- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -217,27 +217,29 @@ let ChromeHangs = { */ render: function ChromeHangs_render() { let hangsDiv = document.getElementById("chrome-hangs-data"); this.clearHangData(hangsDiv); document.getElementById("fetch-symbols").classList.remove("hidden"); document.getElementById("hide-symbols").classList.add("hidden"); let hangs = Telemetry.chromeHangs; - if (hangs.length == 0) { + let stacks = hangs.stacks; + if (stacks.length == 0) { showEmptySectionMessage("chrome-hangs-section"); return; } this.renderMemoryMap(hangsDiv); - for (let i = 0; i < hangs.length; ++i) { - let currentHang = hangs[i]; - this.renderHangHeader(hangsDiv, i + 1, currentHang.duration); - this.renderStack(hangsDiv, currentHang.stack) + let durations = hangs.durations; + for (let i = 0; i < stacks.length; ++i) { + let stack = stacks[i]; + this.renderHangHeader(hangsDiv, i + 1, durations[i]); + this.renderStack(hangsDiv, stack) } }, /** * Renders the title of the hang: e.g. "Hang Report #1 (6 seconds)" * * @param aDiv Output div * @param aIndex The number of the hang @@ -274,42 +276,48 @@ let ChromeHangs = { * Outputs the memory map associated with this hang report * * @param aDiv Output div */ renderMemoryMap: function ChromeHangs_renderMemoryMap(aDiv) { aDiv.appendChild(document.createTextNode(this.memoryMapTitle)); aDiv.appendChild(document.createElement("br")); - let singleMemoryMap = Telemetry.chromeHangs[0].memoryMap; - for (let currentModule of singleMemoryMap) { + let hangs = Telemetry.chromeHangs; + let memoryMap = hangs.memoryMap; + for (let currentModule of memoryMap) { aDiv.appendChild(document.createTextNode(currentModule.join(" "))); aDiv.appendChild(document.createElement("br")); } aDiv.appendChild(document.createElement("br")); }, /** * Sends a symbolication request for the recorded hangs */ fetchSymbols: function ChromeHangs_fetchSymbols() { let symbolServerURI = getPref(PREF_SYMBOL_SERVER_URI, DEFAULT_SYMBOL_SERVER_URI); - let chromeHangsJSON = JSON.stringify(Telemetry.chromeHangs); + let hangs = Telemetry.chromeHangs; + let memoryMap = hangs.memoryMap; + let stacks = hangs.stacks; + let request = {"memoryMap" : memoryMap, "stacks" : stacks, + "version" : 2}; + let requestJSON = JSON.stringify(request); this.symbolRequest = XMLHttpRequest(); this.symbolRequest.open("POST", symbolServerURI, true); this.symbolRequest.setRequestHeader("Content-type", "application/json"); - this.symbolRequest.setRequestHeader("Content-length", chromeHangsJSON.length); + this.symbolRequest.setRequestHeader("Content-length", requestJSON.length); this.symbolRequest.setRequestHeader("Connection", "close"); this.symbolRequest.onreadystatechange = this.handleSymbolResponse.bind(this); - this.symbolRequest.send(chromeHangsJSON); + this.symbolRequest.send(requestJSON); }, /** * Called when the 'readyState' of the XMLHttpRequest changes. We only care * about state 4 ("completed") - handling the response data. */ handleSymbolResponse: function ChromeHangs_handleSymbolResponse() { if (this.symbolRequest.readyState != 4) @@ -330,19 +338,21 @@ let ChromeHangs = { try { jsonResponse = JSON.parse(this.symbolRequest.responseText); } catch (e) { hangsDiv.appendChild(document.createTextNode(this.errorMessage)); return; } let hangs = Telemetry.chromeHangs; + let stacks = hangs.stacks; + let durations = hangs.durations; for (let i = 0; i < jsonResponse.length; ++i) { let stack = jsonResponse[i]; - let hangDuration = hangs[i].duration; + let hangDuration = durations[i]; this.renderHangHeader(hangsDiv, i + 1, hangDuration); for (let symbol of stack) { hangsDiv.appendChild(document.createTextNode(symbol)); hangsDiv.appendChild(document.createElement("br")); } hangsDiv.appendChild(document.createElement("br")); }
--- a/xpcom/build/mozPoisonWriteMac.cpp +++ b/xpcom/build/mozPoisonWriteMac.cpp @@ -91,17 +91,17 @@ bool ValidWriteAssert(bool ok) if (ok || !sProfileDirectory || !Telemetry::CanRecord()) return ok; // Write the stack and loaded libraries to a file. We can get here // concurrently from many writes, so we use multiple temporary files. std::vector<uintptr_t> rawStack; NS_StackWalk(RecordStackWalker, 0, reinterpret_cast<void*>(&rawStack), 0); - Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack, true); + Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack); nsPrintfCString nameAux("%s%s", sProfileDirectory, "/Telemetry.LateWriteTmpXXXXXX"); char *name; nameAux.GetMutableData(&name); // We want the sha1 of the entire file, so please don't write to fd // directly; use sha1Stream.
--- a/xpcom/threads/HangMonitor.cpp +++ b/xpcom/threads/HangMonitor.cpp @@ -133,17 +133,17 @@ GetChromeHangReport(Telemetry::Processed DWORD ret = ::SuspendThread(winMainThreadHandle); if (ret == -1) return; NS_StackWalk(ChromeStackWalker, 0, reinterpret_cast<void*>(&rawStack), reinterpret_cast<uintptr_t>(winMainThreadHandle)); ret = ::ResumeThread(winMainThreadHandle); if (ret == -1) return; - aStack = Telemetry::GetStackAndModules(rawStack, false); + aStack = Telemetry::GetStackAndModules(rawStack); } #endif void ThreadMain(void*) { PR_SetCurrentThreadName("Hang Monitor");