Bug 1468789 - Part 2: Collect inner window id information for js interpreter frames and add a mechanism to get that for jit frames r=gerald,jandem,mstange
authorNazım Can Altınova <canaltinova@gmail.com>
Fri, 15 Nov 2019 08:01:30 +0000
changeset 502164 1c3c775faf994b0cb12be28fc5afca9f444569af
parent 502163 0c93e34bc1f3b2e56d53a48ff6d6c09b083c8bba
child 502165 e4ed5d091e3dd498dbeb416ece8399ffcf13d52e
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald, jandem, mstange
bugs1468789
milestone72.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
Bug 1468789 - Part 2: Collect inner window id information for js interpreter frames and add a mechanism to get that for jit frames r=gerald,jandem,mstange Differential Revision: https://phabricator.services.mozilla.com/D51860
js/public/ProfilingFrameIterator.h
js/public/ProfilingStack.h
js/src/jit/JSJitFrameIter.cpp
js/src/jit/JSJitFrameIter.h
js/src/jit/JitcodeMap.cpp
js/src/jit/JitcodeMap.h
js/src/vm/GeckoProfiler-inl.h
js/src/vm/GeckoProfiler.cpp
js/src/vm/Stack.cpp
mozglue/baseprofiler/core/ProfileBuffer.cpp
mozglue/baseprofiler/core/ProfileBuffer.h
mozglue/baseprofiler/core/ProfileBufferEntry.cpp
mozglue/baseprofiler/core/ProfileBufferEntry.h
mozglue/baseprofiler/public/BaseProfilingStack.h
tools/profiler/core/ProfileBuffer.cpp
tools/profiler/core/ProfileBuffer.h
tools/profiler/core/ProfileBufferEntry.cpp
tools/profiler/core/ProfileBufferEntry.h
tools/profiler/core/platform.cpp
--- 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