author | Nazım Can Altınova <canaltinova@gmail.com> |
Fri, 15 Nov 2019 08:01:30 +0000 | |
changeset 502181 | 1c3c775faf994b0cb12be28fc5afca9f444569af |
parent 502180 | 0c93e34bc1f3b2e56d53a48ff6d6c09b083c8bba |
child 502182 | e4ed5d091e3dd498dbeb416ece8399ffcf13d52e |
push id | 100654 |
push user | canaltinova@gmail.com |
push date | Fri, 15 Nov 2019 13:29:30 +0000 |
treeherder | autoland@e4ed5d091e3d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gerald, jandem, mstange |
bugs | 1468789 |
milestone | 72.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/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -118,16 +118,17 @@ class MOZ_NON_PARAM JS_PUBLIC_API Profil union { void* returnAddress_; jsbytecode* interpreterPC_; }; void* activation; void* endStackAddress; const char* label; JSScript* interpreterScript; + uint64_t realmID; public: void* returnAddress() const { MOZ_ASSERT(kind != Frame_BaselineInterpreter); return returnAddress_; } jsbytecode* interpreterPC() const { MOZ_ASSERT(kind == Frame_BaselineInterpreter); @@ -193,16 +194,18 @@ class MOZ_STACK_CLASS ProfiledFrameHandl JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const; JS_PUBLIC_API void forEachOptimizationAttempt( ForEachTrackedOptimizationAttemptOp& op, JSScript** scriptOut, jsbytecode** pcOut) const; JS_PUBLIC_API void forEachOptimizationTypeInfo( ForEachTrackedOptimizationTypeInfoOp& op) const; + + JS_PUBLIC_API uint64_t realmID() const; }; class ProfiledFrameRange { public: class Iter final { public: Iter(const ProfiledFrameRange& range, uint32_t index) : range_(range), index_(index) {}
--- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -142,32 +142,42 @@ class ProfilingStackFrame { // The bytecode offset for JS stack frames. // Must not be used on non-JS frames; it'll contain either the default 0, // or a leftover value from a previous JS stack frame that was using this // ProfilingStackFrame object. mozilla::Atomic<int32_t, mozilla::ReleaseAcquire, mozilla::recordreplay::Behavior::DontPreserve> pcOffsetIfJS_; + // ID of the JS Realm for JS stack frames. + // Must not be used on non-JS frames; it'll contain either the default 0, + // or a leftover value from a previous JS stack frame that was using this + // ProfilingStackFrame object. + mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire, + mozilla::recordreplay::Behavior::DontPreserve> + realmID_; + // Bits 0...8 hold the Flags. Bits 9...31 hold the category pair. mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire, mozilla::recordreplay::Behavior::DontPreserve> flagsAndCategoryPair_; static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc); public: ProfilingStackFrame() = default; ProfilingStackFrame& operator=(const ProfilingStackFrame& other) { label_ = other.label(); dynamicString_ = other.dynamicString(); void* spScript = other.spOrScript; spOrScript = spScript; int32_t offsetIfJS = other.pcOffsetIfJS_; pcOffsetIfJS_ = offsetIfJS; + uint64_t realmID = other.realmID_; + realmID_ = realmID; uint32_t flagsAndCategory = other.flagsAndCategoryPair_; flagsAndCategoryPair_ = flagsAndCategory; return *this; } // 9 bits for the flags. // That leaves 32 - 9 = 23 bits for the category pair. enum class Flags : uint32_t { @@ -287,36 +297,39 @@ class ProfilingStackFrame { // pcOffsetIfJS_ is not set and must not be used on sp marker frames. flagsAndCategoryPair_ = uint32_t(Flags::IS_SP_MARKER_FRAME) | (uint32_t(JS::ProfilingCategoryPair::OTHER) << uint32_t(Flags::FLAGS_BITCOUNT)); MOZ_ASSERT(isSpMarkerFrame()); } void initJsFrame(const char* aLabel, const char* aDynamicString, - JSScript* aScript, jsbytecode* aPc) { + JSScript* aScript, jsbytecode* aPc, uint64_t aRealmID) { label_ = aLabel; dynamicString_ = aDynamicString; spOrScript = aScript; pcOffsetIfJS_ = pcToOffset(aScript, aPc); + realmID_ = aRealmID; flagsAndCategoryPair_ = uint32_t(Flags::IS_JS_FRAME) | (uint32_t(JS::ProfilingCategoryPair::JS) << uint32_t(Flags::FLAGS_BITCOUNT)); MOZ_ASSERT(isJsFrame()); } uint32_t flags() const { return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::FLAGS_MASK); } JS::ProfilingCategoryPair categoryPair() const { return JS::ProfilingCategoryPair(flagsAndCategoryPair_ >> uint32_t(Flags::FLAGS_BITCOUNT)); } + uint64_t realmID() const { return realmID_; } + void* stackAddress() const { MOZ_ASSERT(!isJsFrame()); return spOrScript; } JS_PUBLIC_API JSScript* script() const; // Note that the pointer returned might be invalid. @@ -424,25 +437,26 @@ class ProfilingStack final { } frames[oldStackPointer].initSpMarkerFrame(sp); // This must happen at the end, see the comment in pushLabelFrame. stackPointer = oldStackPointer + 1; } void pushJsFrame(const char* label, const char* dynamicString, - JSScript* script, jsbytecode* pc) { + JSScript* script, jsbytecode* pc, uint64_t aRealmID) { // This thread is the only one that ever changes the value of // stackPointer. Only load the atomic once. uint32_t oldStackPointer = stackPointer; if (MOZ_UNLIKELY(oldStackPointer >= capacity)) { ensureCapacitySlow(); } - frames[oldStackPointer].initJsFrame(label, dynamicString, script, pc); + frames[oldStackPointer].initJsFrame(label, dynamicString, script, pc, + aRealmID); // This must happen at the end, see the comment in pushLabelFrame. stackPointer = stackPointer + 1; } void pop() { MOZ_ASSERT(stackPointer > 0); // Do the read and the write as two separate statements, in order to
--- a/js/src/jit/JSJitFrameIter.cpp +++ b/js/src/jit/JSJitFrameIter.cpp @@ -639,30 +639,32 @@ bool JSJitProfilingFrameIterator::tryIni } const char* JSJitProfilingFrameIterator::baselineInterpreterLabel() const { MOZ_ASSERT(type_ == FrameType::BaselineJS); return frameScript()->jitScript()->profileString(); } void JSJitProfilingFrameIterator::baselineInterpreterScriptPC( - JSScript** script, jsbytecode** pc) const { + JSScript** script, jsbytecode** pc, uint64_t* realmID) const { MOZ_ASSERT(type_ == FrameType::BaselineJS); BaselineFrame* blFrame = (BaselineFrame*)(fp_ - BaselineFrame::FramePointerOffset - BaselineFrame::Size()); *script = frameScript(); *pc = (*script)->code(); if (blFrame->runningInInterpreter() && blFrame->interpreterScript() == *script) { jsbytecode* interpPC = blFrame->interpreterPC(); if ((*script)->containsPC(interpPC)) { *pc = interpPC; } + + *realmID = (*script)->realm()->creationOptions().profilerRealmID(); } } void JSJitProfilingFrameIterator::operator++() { JitFrameLayout* frame = framePtr(); moveToNextFrame(frame); }
--- a/js/src/jit/JSJitFrameIter.h +++ b/js/src/jit/JSJitFrameIter.h @@ -295,17 +295,18 @@ class JSJitProfilingFrameIterator { public: JSJitProfilingFrameIterator(JSContext* cx, void* pc); explicit JSJitProfilingFrameIterator(CommonFrameLayout* exitFP); void operator++(); bool done() const { return fp_ == nullptr; } const char* baselineInterpreterLabel() const; - void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc) const; + void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc, + uint64_t* realmID) const; void* fp() const { MOZ_ASSERT(!done()); return fp_; } void* stackAddress() const { return fp(); } FrameType frameType() const { MOZ_ASSERT(!done());
--- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -110,16 +110,28 @@ void JitcodeGlobalEntry::IonEntry::young uint32_t scriptIdx, pcOffset; locationIter.readNext(&scriptIdx, &pcOffset); pcOffset = region.findPcOffset(ptrOffset, pcOffset); *script = getScript(scriptIdx); *pc = (*script)->offsetToPC(pcOffset); } +uint64_t JitcodeGlobalEntry::IonEntry::lookupRealmID(void* ptr) const { + uint32_t ptrOffset; + JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset); + JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator(); + MOZ_ASSERT(locationIter.hasMore()); + uint32_t scriptIdx, pcOffset; + locationIter.readNext(&scriptIdx, &pcOffset); + + JSScript* script = getScript(scriptIdx); + return script->realm()->creationOptions().profilerRealmID(); +} + void JitcodeGlobalEntry::IonEntry::destroy() { // The region table is stored at the tail of the compacted data, // which means the start of the region table is a pointer to // the _middle_ of the memory space allocated for it. // // When freeing it, obtain the payload start pointer first. if (regionTable_) { js_free((void*)(regionTable_->payloadStart())); @@ -185,16 +197,20 @@ uint32_t JitcodeGlobalEntry::BaselineEnt void JitcodeGlobalEntry::BaselineEntry::youngestFrameLocationAtAddr( void* ptr, JSScript** script, jsbytecode** pc) const { uint8_t* addr = reinterpret_cast<uint8_t*>(ptr); *script = script_; *pc = script_->baselineScript()->approximatePcForNativeAddress(script_, addr); } +uint64_t JitcodeGlobalEntry::BaselineEntry::lookupRealmID() const { + return script_->realm()->creationOptions().profilerRealmID(); +} + void JitcodeGlobalEntry::BaselineEntry::destroy() { if (!str_) { return; } js_free((void*)str_); str_ = nullptr; } @@ -213,16 +229,20 @@ uint32_t JitcodeGlobalEntry::BaselineInt MOZ_CRASH("shouldn't be called for BaselineInterpreter entries"); } void JitcodeGlobalEntry::BaselineInterpreterEntry::youngestFrameLocationAtAddr( void* ptr, JSScript** script, jsbytecode** pc) const { MOZ_CRASH("shouldn't be called for BaselineInterpreter entries"); } +uint64_t JitcodeGlobalEntry::BaselineInterpreterEntry::lookupRealmID() const { + MOZ_CRASH("shouldn't be called for BaselineInterpreter entries"); +} + static inline JitcodeGlobalEntry& RejoinEntry( JSRuntime* rt, const JitcodeGlobalEntry::IonCacheEntry& cache, void* ptr) { MOZ_ASSERT(cache.containsPointer(ptr)); // There must exist an entry for the rejoin addr if this entry exists. JitRuntime* jitrt = rt->jitRuntime(); JitcodeGlobalEntry& entry = jitrt->getJitcodeGlobalTable()->lookupInfallible(cache.rejoinAddr()); @@ -248,16 +268,22 @@ uint32_t JitcodeGlobalEntry::IonCacheEnt } void JitcodeGlobalEntry::IonCacheEntry::youngestFrameLocationAtAddr( JSRuntime* rt, void* ptr, JSScript** script, jsbytecode** pc) const { const JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr); return entry.youngestFrameLocationAtAddr(rt, rejoinAddr(), script, pc); } +uint64_t JitcodeGlobalEntry::IonCacheEntry::lookupRealmID(JSRuntime* rt, + void* ptr) const { + const JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr); + return entry.lookupRealmID(rt, ptr); +} + static int ComparePointers(const void* a, const void* b) { const uint8_t* a_ptr = reinterpret_cast<const uint8_t*>(a); const uint8_t* b_ptr = reinterpret_cast<const uint8_t*>(b); if (a_ptr < b_ptr) { return -1; } if (a_ptr > b_ptr) { return 1; @@ -1545,16 +1571,20 @@ JS::ProfiledFrameHandle::frameKind() con return JS::ProfilingFrameIterator::Frame_BaselineInterpreter; } if (entry_.isBaseline()) { return JS::ProfilingFrameIterator::Frame_Baseline; } return JS::ProfilingFrameIterator::Frame_Ion; } +JS_PUBLIC_API uint64_t JS::ProfiledFrameHandle::realmID() const { + return entry_.lookupRealmID(rt_, addr_); +} + JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx, void* addr) { JSRuntime* rt = cx->runtime(); js::jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); js::jit::JitcodeGlobalEntry* entry = table->lookup(addr); ProfiledFrameRange result(rt, addr, entry);
--- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -321,16 +321,18 @@ class JitcodeGlobalEntry { uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; void youngestFrameLocationAtAddr(void* ptr, JSScript** script, jsbytecode** pc) const; + uint64_t lookupRealmID(void* ptr) const; + bool hasTrackedOptimizations() const { return !!optsRegionTable_; } const IonTrackedOptimizationsRegionTable* trackedOptimizationsRegionTable() const { MOZ_ASSERT(hasTrackedOptimizations()); return optsRegionTable_; } @@ -411,16 +413,18 @@ class JitcodeGlobalEntry { uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; void youngestFrameLocationAtAddr(void* ptr, JSScript** script, jsbytecode** pc) const; + uint64_t lookupRealmID() const; + template <class ShouldTraceProvider> bool trace(JSTracer* trc); void sweepChildren(); bool isMarkedFromAnyThread(JSRuntime* rt); }; struct BaselineInterpreterEntry : public BaseEntry { void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) { @@ -436,16 +440,18 @@ class JitcodeGlobalEntry { BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; void youngestFrameLocationAtAddr(void* ptr, JSScript** script, jsbytecode** pc) const; + + uint64_t lookupRealmID() const; }; struct IonCacheEntry : public BaseEntry { void* rejoinAddr_; JS::TrackedOutcome trackedOutcome_; void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, void* rejoinAddr, JS::TrackedOutcome trackedOutcome) { @@ -467,16 +473,18 @@ class JitcodeGlobalEntry { uint32_t* depth) const; uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, JSScript** script, jsbytecode** pc) const; + uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const; + bool hasTrackedOptimizations() const { return true; } mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( JSRuntime* rt, void* ptr, uint32_t* entryOffsetOut); void forEachOptimizationAttempt( JSRuntime* rt, uint8_t index, JS::ForEachTrackedOptimizationAttemptOp& op); void forEachOptimizationTypeInfo( JSRuntime* rt, uint8_t index, @@ -513,16 +521,18 @@ class JitcodeGlobalEntry { return 0; } void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, JSScript** script, jsbytecode** pc) const { *script = nullptr; *pc = nullptr; } + + uint64_t lookupRealmID() const { return 0; } }; // QueryEntry is never stored in the table, just used for queries // where an instance of JitcodeGlobalEntry is required to do tree // lookups. struct QueryEntry : public BaseEntry { void init(void* addr) { BaseEntry::init(Query, nullptr, addr, addr); } uint8_t* addr() const { @@ -795,16 +805,31 @@ class JitcodeGlobalEntry { return ionCacheEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); case Dummy: return dummyEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } } + uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const { + switch (kind()) { + case Ion: + return ionEntry().lookupRealmID(ptr); + case Baseline: + return baselineEntry().lookupRealmID(); + case IonCache: + return ionCacheEntry().lookupRealmID(rt, ptr); + case Dummy: + return dummyEntry().lookupRealmID(); + default: + MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); + } + } + // Figure out the number of the (JSScript*, jsbytecode*) pairs that are active // at this location. uint32_t lookupInlineCallDepth(void* ptr); // Compare two global entries. static int compare(const JitcodeGlobalEntry& ent1, const JitcodeGlobalEntry& ent2); int compareTo(const JitcodeGlobalEntry& other) {
--- a/js/src/vm/GeckoProfiler-inl.h +++ b/js/src/vm/GeckoProfiler-inl.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef vm_GeckoProfiler_inl_h #define vm_GeckoProfiler_inl_h #include "vm/GeckoProfiler.h" #include "vm/JSContext.h" +#include "vm/Realm.h" #include "vm/Runtime.h" namespace js { inline void GeckoProfilerThread::updatePC(JSContext* cx, JSScript* script, jsbytecode* pc) { if (!cx->runtime()->geckoProfiler().enabled()) { return; @@ -61,17 +62,19 @@ GeckoProfilerEntryMarker::GeckoProfilerE spBefore_ = profiler_->stackPointer(); #endif // Push an sp marker frame so the profiler can correctly order JS and native // stacks. profiler_->profilingStack_->pushSpMarkerFrame(this); profiler_->profilingStack_->pushJsFrame( - "js::RunScript", /* dynamicString = */ nullptr, script, script->code()); + "js::RunScript", + /* dynamicString = */ nullptr, script, script->code(), + script->realm()->creationOptions().profilerRealmID()); } MOZ_ALWAYS_INLINE GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker() { if (MOZ_LIKELY(profiler_ == nullptr)) { return; }
--- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -219,17 +219,19 @@ bool GeckoProfilerThread::enter(JSContex size_t start = (sp > 4) ? sp - 4 : 0; for (size_t i = start; i < sp - 1; i++) { MOZ_ASSERT_IF(profilingStack_->frames[i].isJsFrame(), profilingStack_->frames[i].pc()); } } #endif - profilingStack_->pushJsFrame("", dynamicString, script, script->code()); + profilingStack_->pushJsFrame( + "", dynamicString, script, script->code(), + script->realm()->creationOptions().profilerRealmID()); return true; } void GeckoProfilerThread::exit(JSContext* cx, JSScript* script) { profilingStack_->pop(); #ifdef DEBUG /* Sanity check to make sure push/pop balanced */
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -651,16 +651,18 @@ JS::ProfilingFrameIterator::getPhysicalF Frame frame; frame.kind = Frame_Wasm; frame.stackAddress = stackAddr; frame.returnAddress_ = nullptr; frame.activation = activation_; frame.label = nullptr; frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP(); frame.interpreterScript = nullptr; + // TODO: get the realm ID of wasm frames. Bug 1596235. + frame.realmID = 0; return mozilla::Some(frame); } MOZ_ASSERT(isJSJit()); // Look up an entry for the return address. void* returnAddr = jsJitIter().resumePCinCurrentFrame(); jit::JitcodeGlobalTable* table = @@ -686,24 +688,25 @@ JS::ProfilingFrameIterator::getPhysicalF } else if (entry->isBaseline()) { frame.kind = Frame_Baseline; } else { frame.kind = Frame_Ion; } frame.stackAddress = stackAddr; if (entry->isBaselineInterpreter()) { frame.label = jsJitIter().baselineInterpreterLabel(); - jsJitIter().baselineInterpreterScriptPC(&frame.interpreterScript, - &frame.interpreterPC_); + jsJitIter().baselineInterpreterScriptPC( + &frame.interpreterScript, &frame.interpreterPC_, &frame.realmID); MOZ_ASSERT(frame.interpreterScript); MOZ_ASSERT(frame.interpreterPC_); } else { frame.interpreterScript = nullptr; frame.returnAddress_ = returnAddr; frame.label = nullptr; + frame.realmID = 0; } frame.activation = activation_; frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP(); return mozilla::Some(frame); } uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames, uint32_t offset,
--- a/mozglue/baseprofiler/core/ProfileBuffer.cpp +++ b/mozglue/baseprofiler/core/ProfileBuffer.cpp @@ -72,17 +72,18 @@ BlocksRingBuffer::BlockIndex ProfileBuff } uint64_t ProfileBuffer::AddThreadIdEntry(int aThreadId) { return AddThreadIdEntry(mEntries, aThreadId).ConvertToU64(); } void ProfileBuffer::CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, + uint64_t aInnerWindowID, const Maybe<uint32_t>& aLineNumber, + const Maybe<uint32_t>& aColumnNumber, const Maybe<ProfilingCategoryPair>& aCategoryPair) { AddEntry(ProfileBufferEntry::Label(aLabel)); AddEntry(ProfileBufferEntry::FrameFlags(uint64_t(aFrameFlags))); if (aStr) { // Store the string using one or more DynamicStringFragment entries. size_t strLen = strlen(aStr) + 1; // +1 for the null terminator for (size_t j = 0; j < strLen;) { @@ -94,16 +95,20 @@ void ProfileBuffer::CollectCodeLocation( } memcpy(chars, &aStr[j], len); j += ProfileBufferEntry::kNumChars; AddEntry(ProfileBufferEntry::DynamicStringFragment(chars)); } } + if (aInnerWindowID) { + AddEntry(ProfileBufferEntry::InnerWindowID(aInnerWindowID)); + } + if (aLineNumber) { AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber)); } if (aColumnNumber) { AddEntry(ProfileBufferEntry::ColumnNumber(*aColumnNumber)); } @@ -200,16 +205,17 @@ void ProfileBufferCollector::CollectProf // Adjust the dynamic string as necessary. if (ProfilerFeature::HasPrivacy(mFeatures) && !isChromeJSEntry) { dynamicString = "(private)"; } else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) { dynamicString = "(too long)"; } } - mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), line, column, + mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), + aFrame.realmID(), line, column, Some(aFrame.categoryPair())); } } // namespace baseprofiler } // namespace mozilla #endif // MOZ_BASE_PROFILER
--- a/mozglue/baseprofiler/core/ProfileBuffer.h +++ b/mozglue/baseprofiler/core/ProfileBuffer.h @@ -45,17 +45,17 @@ class ProfileBuffer final { // Returns the position of the entry. uint64_t AddEntry(const ProfileBufferEntry& aEntry); // Add to the buffer a sample start (ThreadId) entry for aThreadId. // Returns the position of the entry. uint64_t AddThreadIdEntry(int aThreadId); void CollectCodeLocation(const char* aLabel, const char* aStr, - uint32_t aFrameFlags, + uint32_t aFrameFlags, uint64_t aInnerWindowID, const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, const Maybe<ProfilingCategoryPair>& aCategoryPair); // Maximum size of a frameKey string that we'll handle. static const size_t kMaxFrameKeyLength = 512; // Stream JSON for samples in the buffer to aWriter, using the supplied
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp +++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp @@ -235,17 +235,18 @@ UniqueStacks::StackKey UniqueStacks::App const FrameKey& aFrame) { return StackKey(aStack, GetOrAddStackIndex(aStack), GetOrAddFrameIndex(aFrame)); } bool UniqueStacks::FrameKey::NormalFrameData::operator==( const NormalFrameData& aOther) const { return mLocation == aOther.mLocation && - mRelevantForJS == aOther.mRelevantForJS && mLine == aOther.mLine && + mRelevantForJS == aOther.mRelevantForJS && + mInnerWindowID == aOther.mInnerWindowID && mLine == aOther.mLine && mColumn == aOther.mColumn && mCategoryPair == aOther.mCategoryPair; } UniqueStacks::UniqueStacks() : mUniqueStrings(MakeUnique<UniqueJSONStrings>()) { mFrameTableWriter.StartBareList(); mStackTableWriter.StartBareList(); } @@ -735,16 +736,22 @@ void ProfileBuffer::StreamSamplesToJSON( frameLabel += dynStrBuf.get(); } } else if (hasDynamicString) { frameLabel += dynStrBuf.get(); } else { frameLabel += label; } + uint64_t innerWindowID = 0; + if (e.Has() && e.Get().IsInnerWindowID()) { + innerWindowID = uint64_t(e.Get().GetUint64()); + e.Next(); + } + Maybe<unsigned> line; if (e.Has() && e.Get().IsLineNumber()) { line = Some(unsigned(e.Get().GetInt())); e.Next(); } Maybe<unsigned> column; if (e.Has() && e.Get().IsColumnNumber()) { @@ -755,19 +762,19 @@ void ProfileBuffer::StreamSamplesToJSON( Maybe<ProfilingCategoryPair> categoryPair; if (e.Has() && e.Get().IsCategoryPair()) { categoryPair = Some(ProfilingCategoryPair(uint32_t(e.Get().GetInt()))); e.Next(); } stack = aUniqueStacks.AppendFrame( - stack, - UniqueStacks::FrameKey(std::move(frameLabel), relevantForJS, line, - column, categoryPair)); + stack, UniqueStacks::FrameKey(std::move(frameLabel), + relevantForJS, innerWindowID, line, + column, categoryPair)); } else { break; } } if (numFrames == 0) { // It is possible to have empty stacks if native stackwalking is
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.h +++ b/mozglue/baseprofiler/core/ProfileBufferEntry.h @@ -29,16 +29,17 @@ namespace baseprofiler { #define FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(MACRO) \ MACRO(CategoryPair, int, sizeof(int)) \ MACRO(CollectionStart, double, sizeof(double)) \ MACRO(CollectionEnd, double, sizeof(double)) \ MACRO(Label, const char*, sizeof(const char*)) \ MACRO(FrameFlags, uint64_t, sizeof(uint64_t)) \ MACRO(DynamicStringFragment, char*, ProfileBufferEntry::kNumChars) \ MACRO(JitReturnAddr, void*, sizeof(void*)) \ + MACRO(InnerWindowID, uint64_t, sizeof(uint64_t)) \ MACRO(LineNumber, int, sizeof(int)) \ MACRO(ColumnNumber, int, sizeof(int)) \ MACRO(NativeLeafAddr, void*, sizeof(void*)) \ MACRO(Pause, double, sizeof(double)) \ MACRO(Responsiveness, double, sizeof(double)) \ MACRO(Resume, double, sizeof(double)) \ MACRO(ThreadId, int, sizeof(int)) \ MACRO(Time, double, sizeof(double)) \ @@ -158,37 +159,39 @@ class UniqueJSONStrings { SpliceableChunkedJSONWriter mStringTableWriter; HashMap<HashNumber, uint32_t> mStringHashToIndexMap; }; class UniqueStacks { public: struct FrameKey { explicit FrameKey(const char* aLocation) - : mData(NormalFrameData{std::string(aLocation), false, Nothing(), + : mData(NormalFrameData{std::string(aLocation), false, 0, Nothing(), Nothing()}) {} FrameKey(std::string&& aLocation, bool aRelevantForJS, - const Maybe<unsigned>& aLine, const Maybe<unsigned>& aColumn, + uint64_t aInnerWindowID, const Maybe<unsigned>& aLine, + const Maybe<unsigned>& aColumn, const Maybe<ProfilingCategoryPair>& aCategoryPair) - : mData(NormalFrameData{aLocation, aRelevantForJS, aLine, aColumn, - aCategoryPair}) {} + : mData(NormalFrameData{aLocation, aRelevantForJS, aInnerWindowID, + aLine, aColumn, aCategoryPair}) {} FrameKey(const FrameKey& aToCopy) = default; uint32_t Hash() const; bool operator==(const FrameKey& aOther) const { return mData == aOther.mData; } struct NormalFrameData { bool operator==(const NormalFrameData& aOther) const; std::string mLocation; bool mRelevantForJS; + uint64_t mInnerWindowID; Maybe<unsigned> mLine; Maybe<unsigned> mColumn; Maybe<ProfilingCategoryPair> mCategoryPair; }; Variant<NormalFrameData> mData; }; struct FrameKeyHasher { @@ -198,16 +201,17 @@ class UniqueStacks { HashNumber hash = 0; if (aLookup.mData.is<FrameKey::NormalFrameData>()) { const FrameKey::NormalFrameData& data = aLookup.mData.as<FrameKey::NormalFrameData>(); if (!data.mLocation.empty()) { hash = AddToHash(hash, HashString(data.mLocation.c_str())); } hash = AddToHash(hash, data.mRelevantForJS); + hash = mozilla::AddToHash(hash, data.mInnerWindowID); if (data.mLine.isSome()) { hash = AddToHash(hash, *data.mLine); } if (data.mColumn.isSome()) { hash = AddToHash(hash, *data.mColumn); } if (data.mCategoryPair.isSome()) { hash = AddToHash(hash, static_cast<uint32_t>(*data.mCategoryPair));
--- a/mozglue/baseprofiler/public/BaseProfilingStack.h +++ b/mozglue/baseprofiler/public/BaseProfilingStack.h @@ -140,29 +140,39 @@ class ProfilingStackFrame { // The bytecode offset for JS stack frames. // Must not be used on non-JS frames; it'll contain either the default 0, // or a leftover value from a previous JS stack frame that was using this // ProfilingStackFrame object. Atomic<int32_t, ReleaseAcquire, recordreplay::Behavior::DontPreserve> pcOffsetIfJS_; + // ID of the JS Realm for JS stack frames. + // Must not be used on non-JS frames; it'll contain either the default 0, + // or a leftover value from a previous JS stack frame that was using this + // ProfilingStackFrame object. + mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire, + mozilla::recordreplay::Behavior::DontPreserve> + realmID_; + // Bits 0...8 hold the Flags. Bits 9...31 hold the category pair. Atomic<uint32_t, ReleaseAcquire, recordreplay::Behavior::DontPreserve> flagsAndCategoryPair_; public: ProfilingStackFrame() = default; ProfilingStackFrame& operator=(const ProfilingStackFrame& other) { label_ = other.label(); dynamicString_ = other.dynamicString(); void* spScript = other.spOrScript; spOrScript = spScript; int32_t offsetIfJS = other.pcOffsetIfJS_; pcOffsetIfJS_ = offsetIfJS; + int64_t realmID = other.realmID_; + realmID_ = realmID; uint32_t flagsAndCategory = other.flagsAndCategoryPair_; flagsAndCategoryPair_ = flagsAndCategory; return *this; } // 9 bits for the flags. // That leaves 32 - 9 = 23 bits for the category pair. enum class Flags : uint32_t { @@ -275,36 +285,40 @@ class ProfilingStackFrame { // pcOffsetIfJS_ is not set and must not be used on sp marker frames. flagsAndCategoryPair_ = uint32_t(Flags::IS_SP_MARKER_FRAME) | (uint32_t(ProfilingCategoryPair::OTHER) << uint32_t(Flags::FLAGS_BITCOUNT)); MOZ_ASSERT(isSpMarkerFrame()); } void initJsFrame(const char* aLabel, const char* aDynamicString, - void* /* JSScript* */ aScript, int32_t aOffset) { + void* /* JSScript* */ aScript, int32_t aOffset, + uint64_t aRealmID) { label_ = aLabel; dynamicString_ = aDynamicString; spOrScript = aScript; pcOffsetIfJS_ = aOffset; + realmID_ = aRealmID; flagsAndCategoryPair_ = uint32_t(Flags::IS_JS_FRAME) | (uint32_t(ProfilingCategoryPair::JS) << uint32_t(Flags::FLAGS_BITCOUNT)); MOZ_ASSERT(isJsFrame()); } uint32_t flags() const { return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::FLAGS_MASK); } ProfilingCategoryPair categoryPair() const { return ProfilingCategoryPair(flagsAndCategoryPair_ >> uint32_t(Flags::FLAGS_BITCOUNT)); } + uint64_t realmID() const { return realmID_; } + void* stackAddress() const { MOZ_ASSERT(!isJsFrame()); return spOrScript; } // Note that the pointer returned might be invalid. void* rawScript() const { MOZ_ASSERT(isJsFrame()); @@ -391,25 +405,26 @@ class ProfilingStack final { } frames[oldStackPointer].initSpMarkerFrame(sp); // This must happen at the end, see the comment in pushLabelFrame. stackPointer = oldStackPointer + 1; } void pushJsOffsetFrame(const char* label, const char* dynamicString, - void* script, int32_t offset) { + void* script, int32_t offset, uint64_t aRealmID) { // This thread is the only one that ever changes the value of // stackPointer. Only load the atomic once. uint32_t oldStackPointer = stackPointer; if (MOZ_UNLIKELY(oldStackPointer >= capacity)) { ensureCapacitySlow(); } - frames[oldStackPointer].initJsFrame(label, dynamicString, script, offset); + frames[oldStackPointer].initJsFrame(label, dynamicString, script, offset, + aRealmID); // This must happen at the end, see the comment in pushLabelFrame. stackPointer = stackPointer + 1; } void pop() { MOZ_ASSERT(stackPointer > 0); // Do the read and the write as two separate statements, in order to
--- a/tools/profiler/core/ProfileBuffer.cpp +++ b/tools/profiler/core/ProfileBuffer.cpp @@ -71,17 +71,18 @@ BlocksRingBuffer::BlockIndex ProfileBuff } uint64_t ProfileBuffer::AddThreadIdEntry(int aThreadId) { return AddThreadIdEntry(mEntries, aThreadId).ConvertToU64(); } void ProfileBuffer::CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, + uint64_t aInnerWindowID, const Maybe<uint32_t>& aLineNumber, + const Maybe<uint32_t>& aColumnNumber, const Maybe<JS::ProfilingCategoryPair>& aCategoryPair) { AddEntry(ProfileBufferEntry::Label(aLabel)); AddEntry(ProfileBufferEntry::FrameFlags(uint64_t(aFrameFlags))); if (aStr) { // Store the string using one or more DynamicStringFragment entries. size_t strLen = strlen(aStr) + 1; // +1 for the null terminator for (size_t j = 0; j < strLen;) { @@ -93,16 +94,20 @@ void ProfileBuffer::CollectCodeLocation( } memcpy(chars, &aStr[j], len); j += ProfileBufferEntry::kNumChars; AddEntry(ProfileBufferEntry::DynamicStringFragment(chars)); } } + if (aInnerWindowID) { + AddEntry(ProfileBufferEntry::InnerWindowID(aInnerWindowID)); + } + if (aLineNumber) { AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber)); } if (aColumnNumber) { AddEntry(ProfileBufferEntry::ColumnNumber(*aColumnNumber)); } @@ -180,17 +185,17 @@ void ProfileBufferCollector::CollectNati mBuf.AddEntry(ProfileBufferEntry::NativeLeafAddr(aAddr)); } void ProfileBufferCollector::CollectJitReturnAddr(void* aAddr) { mBuf.AddEntry(ProfileBufferEntry::JitReturnAddr(aAddr)); } void ProfileBufferCollector::CollectWasmFrame(const char* aLabel) { - mBuf.CollectCodeLocation("", aLabel, 0, Nothing(), Nothing(), Nothing()); + mBuf.CollectCodeLocation("", aLabel, 0, 0, Nothing(), Nothing(), Nothing()); } void ProfileBufferCollector::CollectProfilingStackFrame( const js::ProfilingStackFrame& aFrame) { // WARNING: this function runs within the profiler's "critical section". MOZ_ASSERT(aFrame.isLabelFrame() || (aFrame.isJsFrame() && !aFrame.isOSRFrame())); @@ -234,11 +239,12 @@ void ProfileBufferCollector::CollectProf // Adjust the dynamic string as necessary. if (ProfilerFeature::HasPrivacy(mFeatures) && !isChromeJSEntry) { dynamicString = "(private)"; } else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) { dynamicString = "(too long)"; } } - mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), line, column, + mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), + aFrame.realmID(), line, column, Some(aFrame.categoryPair())); }
--- a/tools/profiler/core/ProfileBuffer.h +++ b/tools/profiler/core/ProfileBuffer.h @@ -44,17 +44,17 @@ class ProfileBuffer final { uint64_t AddEntry(const ProfileBufferEntry& aEntry); // Add to the buffer a sample start (ThreadId) entry for aThreadId. // Returns the position of the entry. uint64_t AddThreadIdEntry(int aThreadId); void CollectCodeLocation( const char* aLabel, const char* aStr, uint32_t aFrameFlags, - const mozilla::Maybe<uint32_t>& aLineNumber, + uint64_t aInnerWindowID, const mozilla::Maybe<uint32_t>& aLineNumber, const mozilla::Maybe<uint32_t>& aColumnNumber, const mozilla::Maybe<JS::ProfilingCategoryPair>& aCategoryPair); // Maximum size of a frameKey string that we'll handle. static const size_t kMaxFrameKeyLength = 512; // Add JIT frame information to aJITFrameInfo for any JitReturnAddr entries // that are currently in the buffer at or after aRangeStart, in samples
--- a/tools/profiler/core/ProfileBufferEntry.cpp +++ b/tools/profiler/core/ProfileBufferEntry.cpp @@ -338,17 +338,18 @@ JITFrameInfo::JITFrameInfo(const JITFram for (const JITFrameInfoForBufferRange& range : aOther.mRanges) { MOZ_RELEASE_ASSERT(mRanges.append(range.Clone())); } } bool UniqueStacks::FrameKey::NormalFrameData::operator==( const NormalFrameData& aOther) const { return mLocation == aOther.mLocation && - mRelevantForJS == aOther.mRelevantForJS && mLine == aOther.mLine && + mRelevantForJS == aOther.mRelevantForJS && + mInnerWindowID == aOther.mInnerWindowID && mLine == aOther.mLine && mColumn == aOther.mColumn && mCategoryPair == aOther.mCategoryPair; } bool UniqueStacks::FrameKey::JITFrameData::operator==( const JITFrameData& aOther) const { return mCanonicalAddress == aOther.mCanonicalAddress && mDepth == aOther.mDepth && mRangeIndex == aOther.mRangeIndex; } @@ -1059,16 +1060,22 @@ void ProfileBuffer::StreamSamplesToJSON( frameLabel.AppendPrintf("%s %s", label, dynStrBuf.get()); } } else if (hasDynamicString) { frameLabel.Append(dynStrBuf.get()); } else { frameLabel.Append(label); } + uint64_t innerWindowID = 0; + if (e.Has() && e.Get().IsInnerWindowID()) { + innerWindowID = uint64_t(e.Get().GetUint64()); + e.Next(); + } + Maybe<unsigned> line; if (e.Has() && e.Get().IsLineNumber()) { line = Some(unsigned(e.Get().GetInt())); e.Next(); } Maybe<unsigned> column; if (e.Has() && e.Get().IsColumnNumber()) { @@ -1079,19 +1086,19 @@ void ProfileBuffer::StreamSamplesToJSON( Maybe<JS::ProfilingCategoryPair> categoryPair; if (e.Has() && e.Get().IsCategoryPair()) { categoryPair = Some(JS::ProfilingCategoryPair(uint32_t(e.Get().GetInt()))); e.Next(); } stack = aUniqueStacks.AppendFrame( - stack, - UniqueStacks::FrameKey(std::move(frameLabel), relevantForJS, - line, column, categoryPair)); + stack, UniqueStacks::FrameKey(std::move(frameLabel), + relevantForJS, innerWindowID, + line, column, categoryPair)); } else if (e.Get().IsJitReturnAddr()) { numFrames++; // A JIT frame may expand to multiple frames due to inlining. void* pc = e.Get().GetPtr(); const Maybe<Vector<UniqueStacks::FrameKey>>& frameKeys = aUniqueStacks.LookupFramesForJITAddressFromBufferPos(
--- a/tools/profiler/core/ProfileBufferEntry.h +++ b/tools/profiler/core/ProfileBufferEntry.h @@ -29,16 +29,17 @@ class ProfilerCodeAddressService; #define FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(MACRO) \ MACRO(CategoryPair, int, sizeof(int)) \ MACRO(CollectionStart, double, sizeof(double)) \ MACRO(CollectionEnd, double, sizeof(double)) \ MACRO(Label, const char*, sizeof(const char*)) \ MACRO(FrameFlags, uint64_t, sizeof(uint64_t)) \ MACRO(DynamicStringFragment, char*, ProfileBufferEntry::kNumChars) \ MACRO(JitReturnAddr, void*, sizeof(void*)) \ + MACRO(InnerWindowID, uint64_t, sizeof(uint64_t)) \ MACRO(LineNumber, int, sizeof(int)) \ MACRO(ColumnNumber, int, sizeof(int)) \ MACRO(NativeLeafAddr, void*, sizeof(void*)) \ MACRO(Pause, double, sizeof(double)) \ MACRO(Resume, double, sizeof(double)) \ MACRO(ThreadId, int, sizeof(int)) \ MACRO(Time, double, sizeof(double)) \ MACRO(TimeBeforeCompactStack, double, sizeof(double)) \ @@ -262,41 +263,42 @@ struct JITFrameInfo final { // cached in mRanges. mozilla::UniquePtr<UniqueJSONStrings> mUniqueStrings; }; class UniqueStacks { public: struct FrameKey { explicit FrameKey(const char* aLocation) - : mData(NormalFrameData{nsCString(aLocation), false, mozilla::Nothing(), - mozilla::Nothing()}) {} + : mData(NormalFrameData{nsCString(aLocation), false, 0, + mozilla::Nothing(), mozilla::Nothing()}) {} FrameKey(nsCString&& aLocation, bool aRelevantForJS, - const mozilla::Maybe<unsigned>& aLine, + uint64_t aInnerWindowID, const mozilla::Maybe<unsigned>& aLine, const mozilla::Maybe<unsigned>& aColumn, const mozilla::Maybe<JS::ProfilingCategoryPair>& aCategoryPair) - : mData(NormalFrameData{aLocation, aRelevantForJS, aLine, aColumn, - aCategoryPair}) {} + : mData(NormalFrameData{aLocation, aRelevantForJS, aInnerWindowID, + aLine, aColumn, aCategoryPair}) {} FrameKey(void* aJITAddress, uint32_t aJITDepth, uint32_t aRangeIndex) : mData(JITFrameData{aJITAddress, aJITDepth, aRangeIndex}) {} FrameKey(const FrameKey& aToCopy) = default; uint32_t Hash() const; bool operator==(const FrameKey& aOther) const { return mData == aOther.mData; } struct NormalFrameData { bool operator==(const NormalFrameData& aOther) const; nsCString mLocation; bool mRelevantForJS; + uint64_t mInnerWindowID; mozilla::Maybe<unsigned> mLine; mozilla::Maybe<unsigned> mColumn; mozilla::Maybe<JS::ProfilingCategoryPair> mCategoryPair; }; struct JITFrameData { bool operator==(const JITFrameData& aOther) const; void* mCanonicalAddress; @@ -314,16 +316,17 @@ class UniqueStacks { if (aLookup.mData.is<FrameKey::NormalFrameData>()) { const FrameKey::NormalFrameData& data = aLookup.mData.as<FrameKey::NormalFrameData>(); if (!data.mLocation.IsEmpty()) { hash = mozilla::AddToHash(hash, mozilla::HashString(data.mLocation.get())); } hash = mozilla::AddToHash(hash, data.mRelevantForJS); + hash = mozilla::AddToHash(hash, data.mInnerWindowID); if (data.mLine.isSome()) { hash = mozilla::AddToHash(hash, *data.mLine); } if (data.mColumn.isSome()) { hash = mozilla::AddToHash(hash, *data.mColumn); } if (data.mCategoryPair.isSome()) { hash = mozilla::AddToHash(hash,
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -1486,17 +1486,17 @@ static void MergeStacks(uint32_t aFeatur aCollector.CollectWasmFrame(jsFrame.label); } else if (jsFrame.kind == JS::ProfilingFrameIterator::Frame_BaselineInterpreter) { // For now treat this as a C++ Interpreter frame by materializing a // ProfilingStackFrame. JSScript* script = jsFrame.interpreterScript; jsbytecode* pc = jsFrame.interpreterPC(); js::ProfilingStackFrame stackFrame; - stackFrame.initJsFrame("", jsFrame.label, script, pc); + stackFrame.initJsFrame("", jsFrame.label, script, pc, jsFrame.realmID); aCollector.CollectProfilingStackFrame(stackFrame); } else { MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion || jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline); aCollector.CollectJitReturnAddr(jsFrame.returnAddress()); } jsIndex--; @@ -2248,17 +2248,17 @@ static UniquePtr<ProfileBuffer> CollectJ "android.os.MessageQueue.nativePollOnce()")) { categoryPair = Some(JS::ProfilingCategoryPair::IDLE); parentFrameWasIdleFrame = true; } else if (parentFrameWasIdleFrame) { categoryPair = Some(JS::ProfilingCategoryPair::OTHER); parentFrameWasIdleFrame = false; } - buffer->CollectCodeLocation("", frameNameString.get(), 0, Nothing(), + buffer->CollectCodeLocation("", frameNameString.get(), 0, 0, Nothing(), Nothing(), categoryPair); } sampleId++; } return buffer; } #endif