Backed out 9 changesets (bug 1499507) for bustages on ProfileBuffer.cpp . CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Mon, 05 Nov 2018 21:48:11 +0200
changeset 444463 848152c22f8bd814a564a2306bd249b88099aba8
parent 444462 1e0f0a17143f07a323d4f166327ae2c9d164d393
child 444464 6e528251fad42087432100835b1e3fe8c59f5fec
push id34996
push userrgurzau@mozilla.com
push dateTue, 06 Nov 2018 09:53:23 +0000
treeherdermozilla-central@e160f0a60e4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1499507
milestone65.0a1
backs out541186291b888899e895b8aa92d3823cf3475905
8a3f4acbad3b6b573330a785e5a0e8a1c95f17bb
f427afc392b07914bf0d46635c6e6b39501613c7
58dc19fb2b76c829104030ee5414ec83373d32a8
9225e9aea37715f55aa76e1fcf20c70bfc46d0ef
ca23a517da632d88c47cb8a0a351a63ad36595f9
16d6c90333de0a829854f0d381a93fbbf36cf94f
bc134fe1722a5fee0ba7f3e663ef26c7c629ad65
4a9c9a91182cbf3055d94055d728cc0888459092
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
Backed out 9 changesets (bug 1499507) for bustages on ProfileBuffer.cpp . CLOSED TREE Backed out changeset 541186291b88 (bug 1499507) Backed out changeset 8a3f4acbad3b (bug 1499507) Backed out changeset f427afc392b0 (bug 1499507) Backed out changeset 58dc19fb2b76 (bug 1499507) Backed out changeset 9225e9aea377 (bug 1499507) Backed out changeset ca23a517da63 (bug 1499507) Backed out changeset 16d6c90333de (bug 1499507) Backed out changeset bc134fe1722a (bug 1499507) Backed out changeset 4a9c9a91182c (bug 1499507)
dom/base/nsJSUtils.cpp
dom/bindings/Codegen.py
dom/script/ScriptSettings.cpp
js/public/ProfilingStack.h
js/public/RootingAPI.h
js/src/vm/GeckoProfiler-inl.h
js/src/vm/GeckoProfiler.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/ProfilingStack.cpp
mozglue/build/WindowsDllBlocklist.cpp
mozglue/misc/AutoProfilerLabel.cpp
mozglue/misc/AutoProfilerLabel.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
tools/profiler/public/GeckoProfiler.h
tools/profiler/tests/gtest/GeckoProfiler.cpp
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -120,17 +120,17 @@ EvaluationExceptionToNSResult(JSContext*
   return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
 }
 
 nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
                                               JS::Handle<JSObject*> aGlobal)
   :
 #ifdef MOZ_GECKO_PROFILER
     mAutoProfilerLabel("nsJSUtils::ExecutionContext", /* dynamicStr */ nullptr,
-                       js::ProfilingStackFrame::Category::JS),
+                       __LINE__, js::ProfilingStackFrame::Category::JS),
 #endif
     mCx(aCx)
   , mRealm(aCx, aGlobal)
   , mRetValue(aCx)
   , mScopeChain(aCx)
   , mRv(NS_OK)
   , mSkip(false)
   , mCoerceToString(false)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1581,16 +1581,22 @@ class CGAbstractMethod(CGThing):
         elif self.inline:
             decorators.append('inline')
         if self.static:
             decorators.append('static')
         decorators.append(self.returnType)
         maybeNewline = " " if self.inline else "\n"
         return ' '.join(decorators) + maybeNewline
 
+    def _auto_profiler_label(self):
+        profiler_label_and_jscontext = self.profiler_label_and_jscontext()
+        if profiler_label_and_jscontext:
+            return 'AUTO_PROFILER_LABEL_FAST("%s", DOM, %s);' % profiler_label_and_jscontext
+        return None
+
     def declare(self):
         if self.inline:
             return self._define(True)
         return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
 
     def indent_body(self, body):
         """
         Indent the code returned by self.definition_body(). Most classes
@@ -1605,33 +1611,33 @@ class CGAbstractMethod(CGThing):
                 self.definition_epilogue())
 
     def define(self):
         return "" if self.inline else self._define()
 
     def definition_prologue(self, fromDeclare):
         prologue = "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
                                         self.name, self._argstring(fromDeclare))
-        profiler_label = self.auto_profiler_label()
+        profiler_label = self._auto_profiler_label()
         if profiler_label:
-            prologue += indent(profiler_label) + "\n"
+            prologue += "  %s\n\n" % profiler_label
 
         return prologue
 
     def definition_epilogue(self):
         return "}\n"
 
     def definition_body(self):
         assert False  # Override me!
 
     """
     Override this method to return a pair of (descriptive string, name of a
     JSContext* variable) in order to generate a profiler label for this method.
     """
-    def auto_profiler_label(self):
+    def profiler_label_and_jscontext(self):
         return None # Override me!
 
 class CGAbstractStaticMethod(CGAbstractMethod):
     """
     Abstract base class for codegen of implementation-only (no
     declaration) static methods.
     """
     def __init__(self, descriptor, name, returnType, args, canRunScript=False):
@@ -1864,27 +1870,23 @@ class CGClassConstructor(CGAbstractStati
 
         name = self._ctor.identifier.name
         nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
         callGenerator = CGMethodCall(nativeName, True, self.descriptor,
                                      self._ctor, isConstructor=True,
                                      constructorName=ctorName)
         return preamble + "\n" + callGenerator.define()
 
-    def auto_profiler_label(self):
+    def profiler_label_and_jscontext(self):
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST("${ctorName}", "constructor", DOM, cx, 0);
-            """,
-            ctorName=ctorName)
+        return ("%s constructor" % ctorName, "cx")
 
 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
 class CGConstructNavigatorObject(CGAbstractMethod):
     """
     Construct a new JS-implemented WebIDL DOM object, for use on navigator.
     """
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
@@ -8664,19 +8666,27 @@ class CGAbstractStaticBindingMethod(CGAb
         # later use it to wrap return values.
         unwrap = dedent("""
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             JS::Rooted<JSObject*> obj(cx, &args.callee());
 
             """)
         return unwrap + self.generate_code().define()
 
+    def profiler_label_and_jscontext(self):
+        # Our args are JSNativeArguments() which contain a "JSContext* cx"
+        # argument. We let our subclasses choose the label.
+        return (self.profiler_label(), "cx")
+
     def generate_code(self):
         assert False  # Override me
 
+    def profiler_label(self):
+        assert False # Override me
+
 
 def MakeNativeName(name):
     return name[0].upper() + IDLToCIdentifier(name[1:])
 
 
 class CGSpecializedMethod(CGAbstractStaticMethod):
     """
     A class for generating the C++ code for a specialized method that the JIT
@@ -8693,27 +8703,20 @@ class CGSpecializedMethod(CGAbstractStat
                                         canRunScript=True)
 
     def definition_body(self):
         nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
                                                         self.method)
         return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
                             self.method).define()
 
-    def auto_profiler_label(self):
+    def profiler_label_and_jscontext(self):
         interface_name = self.descriptor.interface.identifier.name
         method_name = self.method.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${method_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD));
-            """,
-            interface_name=interface_name,
-            method_name=method_name)
+        return ("%s.%s" % (interface_name, method_name), "cx")
 
     @staticmethod
     def makeNativeName(descriptor, method):
         name = method.identifier.name
         return MakeNativeName(descriptor.binaryNameFor(name))
 
 
 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
@@ -8960,27 +8963,20 @@ class CGStaticMethod(CGAbstractStaticBin
         name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
         CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
 
     def generate_code(self):
         nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
                                                         self.method)
         return CGMethodCall(nativeName, True, self.descriptor, self.method)
 
-    def auto_profiler_label(self):
+    def profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         method_name = self.method.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${method_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD));
-            """,
-            interface_name=interface_name,
-            method_name=method_name)
+        return "%s.%s" % (interface_name, method_name)
 
 
 class CGSpecializedGetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute getter
     that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
@@ -9074,27 +9070,20 @@ class CGSpecializedGetter(CGAbstractStat
         if self.attr.navigatorObjectGetter:
             cgGetterCall = CGNavigatorGetterCall
         else:
             cgGetterCall = CGGetterCall
         return (prefix +
                 cgGetterCall(self.attr.type, nativeName,
                              self.descriptor, self.attr).define())
 
-    def auto_profiler_label(self):
+    def profiler_label_and_jscontext(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${attr_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER));
-            """,
-            interface_name=interface_name,
-            attr_name=attr_name)
+        return ("get %s.%s" % (interface_name, attr_name), "cx")
 
     @staticmethod
     def makeNativeName(descriptor, attr):
         name = attr.identifier.name
         nativeName = MakeNativeName(descriptor.binaryNameFor(name))
         _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type,
                                                                  descriptor)
         extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
@@ -9144,27 +9133,20 @@ class CGStaticGetter(CGAbstractStaticBin
         CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
 
     def generate_code(self):
         nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
                                                         self.attr)
         return CGGetterCall(self.attr.type, nativeName, self.descriptor,
                             self.attr)
 
-    def auto_profiler_label(self):
+    def profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${attr_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER));
-            """,
-            interface_name=interface_name,
-            attr_name=attr_name)
+        return "get %s.%s" % (interface_name, attr_name)
 
 
 class CGSpecializedSetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute setter
     that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
@@ -9178,27 +9160,20 @@ class CGSpecializedSetter(CGAbstractStat
                                         canRunScript=True)
 
     def definition_body(self):
         nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
                                                         self.attr)
         return CGSetterCall(self.attr.type, nativeName, self.descriptor,
                             self.attr).define()
 
-    def auto_profiler_label(self):
+    def profiler_label_and_jscontext(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${attr_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER));
-            """,
-            interface_name=interface_name,
-            attr_name=attr_name)
+        return ("set %s.%s" % (interface_name, attr_name), "cx")
 
     @staticmethod
     def makeNativeName(descriptor, attr):
         name = attr.identifier.name
         return "Set" + MakeNativeName(descriptor.binaryNameFor(name))
 
 
 class CGStaticSetter(CGAbstractStaticBindingMethod):
@@ -9219,27 +9194,20 @@ class CGStaticSetter(CGAbstractStaticBin
               return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
             }
             """,
             name=self.attr.identifier.name))
         call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
                             self.attr)
         return CGList([checkForArg, call])
 
-    def auto_profiler_label(self):
+    def profiler_label(self):
         interface_name = self.descriptor.interface.identifier.name
         attr_name = self.attr.identifier.name
-        return fill(
-            """
-            AUTO_PROFILER_LABEL_DYNAMIC_FAST(
-              "${interface_name}", "${attr_name}", DOM, cx,
-              uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER));
-            """,
-            interface_name=interface_name,
-            attr_name=attr_name)
+        return "set %s.%s" % (interface_name, attr_name)
 
 
 class CGSpecializedForwardingSetter(CGSpecializedSetter):
     """
     A class for generating the code for a specialized attribute setter with
     PutForwards that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -640,17 +640,17 @@ AutoEntryScript::AutoEntryScript(nsIGlob
                                  const char* aReason,
                                  bool aIsMainThread)
   : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript)
   , mWebIDLCallerPrincipal(nullptr)
   // This relies on us having a cx() because the AutoJSAPI constructor already
   // ran.
   , mCallerOverride(cx())
 #ifdef MOZ_GECKO_PROFILER
-  , mAutoProfilerLabel("AutoEntryScript", aReason,
+  , mAutoProfilerLabel("AutoEntryScript", aReason, __LINE__,
                        js::ProfilingStackFrame::Category::JS)
 #endif
 {
   MOZ_ASSERT(aGlobalObject);
 
   if (aIsMainThread) {
     if (gRunToCompletionListeners > 0) {
       mDocShellEntryMonitor.emplace(cx(), aReason);
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -140,80 +140,62 @@ class ProfilingStackFrame
     // be null.
     mozilla::Atomic<const char*, mozilla::ReleaseAcquire,
                     mozilla::recordreplay::Behavior::DontPreserve> dynamicString_;
 
     // Stack pointer for non-JS stack frames, the script pointer otherwise.
     mozilla::Atomic<void*, mozilla::ReleaseAcquire,
                     mozilla::recordreplay::Behavior::DontPreserve> spOrScript;
 
-    // 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.
+    // Line number for non-JS stack frames, the bytecode offset otherwise.
     mozilla::Atomic<int32_t, mozilla::ReleaseAcquire,
-                    mozilla::recordreplay::Behavior::DontPreserve> pcOffsetIfJS_;
+                    mozilla::recordreplay::Behavior::DontPreserve> lineOrPcOffset;
 
-    // Bits 0...6 hold the Flags. Bits 7...31 hold the category.
+    // Bits 0...1 hold the Kind. Bits 2...31 hold the category.
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
-                    mozilla::recordreplay::Behavior::DontPreserve> flagsAndCategory_;
+                    mozilla::recordreplay::Behavior::DontPreserve> kindAndCategory_;
 
     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;
-        uint32_t flagsAndCategory = other.flagsAndCategory_;
-        flagsAndCategory_ = flagsAndCategory;
+        int32_t offset = other.lineOrPcOffset;
+        lineOrPcOffset = offset;
+        uint32_t kindAndCategory = other.kindAndCategory_;
+        kindAndCategory_ = kindAndCategory;
         return *this;
     }
 
-    // 7 bits for the flags.
-    // That leaves 32 - 7 = 25 bits for the category.
-    enum class Flags : uint32_t {
-        // The first three flags describe the kind of the frame and are
-        // mutually exclusive. (We still give them individual bits for
-        // simplicity.)
-
+    enum class Kind : uint32_t {
         // A regular label frame. These usually come from AutoProfilerLabel.
-        IS_LABEL_FRAME = 1 << 0,
+        LABEL = 0,
 
         // A special frame indicating the start of a run of JS profiling stack
-        // frames. IS_SP_MARKER_FRAME frames are ignored, except for the sp
-        // field. These frames are needed to get correct ordering between JS
-        // and LABEL frames because JS frames don't carry sp information.
+        // frames. SP_MARKER frames are ignored, except for the sp field.
+        // These frames are needed to get correct ordering between JS and LABEL
+        // frames because JS frames don't carry sp information.
         // SP is short for "stack pointer".
-        IS_SP_MARKER_FRAME = 1 << 1,
-
-        // A JS frame.
-        IS_JS_FRAME = 1 << 2,
+        SP_MARKER = 1,
 
-        // An interpreter JS frame that has OSR-ed into baseline. IS_JS_FRAME
-        // frames can have this flag set and unset during their lifetime.
-        // JS_OSR frames are ignored.
-        JS_OSR = 1 << 3,
+        // A normal JS frame.
+        JS_NORMAL = 2,
 
-        // The next three are mutually exclusive.
-        // By default, for profiling stack frames that have both a label and a
-        // dynamic string, the two strings are combined into one string of the
-        // form "<label> <dynamicString>" during JSON serialization. The
-        // following flags can be used to change this preset.
-        STRING_TEMPLATE_METHOD = 1 << 4, // "<label>.<dynamicString>"
-        STRING_TEMPLATE_GETTER = 1 << 5, // "get <label>.<dynamicString>"
-        STRING_TEMPLATE_SETTER = 1 << 6, // "set <label>.<dynamicString>"
+        // An interpreter JS frame that has OSR-ed into baseline. JS_NORMAL
+        // frames can be converted to JS_OSR and back. JS_OSR frames are
+        // ignored.
+        JS_OSR = 3,
 
-        FLAGS_BITCOUNT = 7,
-        FLAGS_MASK = (1 << FLAGS_BITCOUNT) - 1
+        KIND_BITCOUNT = 2,
+        KIND_MASK = (1 << KIND_BITCOUNT) - 1
     };
 
     // Keep these in sync with devtools/client/performance/modules/categories.js
     enum class Category : uint32_t {
         IDLE,
         OTHER,
         LAYOUT,
         JS,
@@ -221,107 +203,96 @@ class ProfilingStackFrame
         NETWORK,
         GRAPHICS,
         DOM,
 
         FIRST    = OTHER,
         LAST     = DOM,
     };
 
-    static_assert(uint32_t(Category::LAST) <= (UINT32_MAX >> uint32_t(Flags::FLAGS_BITCOUNT)),
-                  "Too many categories to fit into u32 with together with the reserved bits for the flags");
+    static_assert(uint32_t(Category::LAST) <= (UINT32_MAX >> uint32_t(Kind::KIND_BITCOUNT)),
+                  "Too many categories to fit into u32 with two bits reserved for the kind");
 
     bool isLabelFrame() const
     {
-        return uint32_t(flagsAndCategory_) & uint32_t(Flags::IS_LABEL_FRAME);
+        return kind() == Kind::LABEL;
     }
 
     bool isSpMarkerFrame() const
     {
-        return uint32_t(flagsAndCategory_) & uint32_t(Flags::IS_SP_MARKER_FRAME);
+        return kind() == Kind::SP_MARKER;
     }
 
     bool isJsFrame() const
     {
-        return uint32_t(flagsAndCategory_) & uint32_t(Flags::IS_JS_FRAME);
-    }
-
-    bool isOSRFrame() const {
-        return uint32_t(flagsAndCategory_) & uint32_t(Flags::JS_OSR);
-    }
-
-    void setIsOSRFrame(bool isOSR) {
-        if (isOSR) {
-            flagsAndCategory_ =
-                uint32_t(flagsAndCategory_) | uint32_t(Flags::JS_OSR);
-        } else {
-            flagsAndCategory_ =
-                uint32_t(flagsAndCategory_) & ~uint32_t(Flags::JS_OSR);
-        }
+        Kind k = kind();
+        return k == Kind::JS_NORMAL || k == Kind::JS_OSR;
     }
 
     void setLabel(const char* aLabel) { label_ = aLabel; }
     const char* label() const { return label_; }
 
     const char* dynamicString() const { return dynamicString_; }
 
     void initLabelFrame(const char* aLabel, const char* aDynamicString, void* sp,
-                        Category aCategory, uint32_t aFlags)
+                        uint32_t aLine, Category aCategory)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = sp;
-        // pcOffsetIfJS_ is not set and must not be used on label frames.
-        flagsAndCategory_ =
-            uint32_t(Flags::IS_LABEL_FRAME) |
-            (uint32_t(aCategory) << uint32_t(Flags::FLAGS_BITCOUNT)) |
-            aFlags;
+        lineOrPcOffset = static_cast<int32_t>(aLine);
+        kindAndCategory_ = uint32_t(Kind::LABEL) | (uint32_t(aCategory) << uint32_t(Kind::KIND_BITCOUNT));
         MOZ_ASSERT(isLabelFrame());
     }
 
     void initSpMarkerFrame(void* sp)
     {
         label_ = "";
         dynamicString_ = nullptr;
         spOrScript = sp;
-        // pcOffsetIfJS_ is not set and must not be used on sp marker frames.
-        flagsAndCategory_ =
-            uint32_t(Flags::IS_SP_MARKER_FRAME) |
-            (uint32_t(Category::OTHER) << uint32_t(Flags::FLAGS_BITCOUNT));
+        lineOrPcOffset = 0;
+        kindAndCategory_ = uint32_t(Kind::SP_MARKER) | (uint32_t(Category::OTHER) << uint32_t(Kind::KIND_BITCOUNT));
         MOZ_ASSERT(isSpMarkerFrame());
     }
 
     void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
                      jsbytecode* aPc)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = aScript;
-        pcOffsetIfJS_ = pcToOffset(aScript, aPc);
-        flagsAndCategory_ =
-            uint32_t(Flags::IS_JS_FRAME) |
-            (uint32_t(Category::JS) << uint32_t(Flags::FLAGS_BITCOUNT));
+        lineOrPcOffset = pcToOffset(aScript, aPc);
+        kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | (uint32_t(Category::JS) << uint32_t(Kind::KIND_BITCOUNT));
         MOZ_ASSERT(isJsFrame());
     }
 
-    uint32_t flags() const {
-        return uint32_t(flagsAndCategory_) & uint32_t(Flags::FLAGS_MASK);
+    void setKind(Kind aKind) {
+        kindAndCategory_ = uint32_t(aKind) | (uint32_t(category()) << uint32_t(Kind::KIND_BITCOUNT));
+    }
+
+    Kind kind() const {
+        return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK));
     }
 
     Category category() const {
-        return Category(flagsAndCategory_ >> uint32_t(Flags::FLAGS_BITCOUNT));
+        return Category(kindAndCategory_ >> uint32_t(Kind::KIND_BITCOUNT));
     }
 
     void* stackAddress() const {
         MOZ_ASSERT(!isJsFrame());
         return spOrScript;
     }
 
     JS_PUBLIC_API(JSScript*) script() const;
 
+    uint32_t line() const {
+        MOZ_ASSERT(!isJsFrame());
+        return static_cast<uint32_t>(lineOrPcOffset);
+    }
+
     // Note that the pointer returned might be invalid.
     JSScript* rawScript() const {
         MOZ_ASSERT(isJsFrame());
         void* script = spOrScript;
         return static_cast<JSScript*>(script);
     }
 
     // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
@@ -387,88 +358,75 @@ class ProfilingStack final
   public:
     ProfilingStack()
       : stackPointer(0)
     {}
 
     ~ProfilingStack();
 
     void pushLabelFrame(const char* label, const char* dynamicString, void* sp,
-                        js::ProfilingStackFrame::Category category,
-                        uint32_t flags = 0) {
-        // This thread is the only one that ever changes the value of
-        // stackPointer.
-        // Store the value of the atomic in a non-atomic local variable so that
-        // the compiler won't generate two separate loads from the atomic for
-        // the size check and the frames[] array indexing operation.
-        uint32_t stackPointerVal = stackPointer;
+                        uint32_t line, js::ProfilingStackFrame::Category category) {
+        uint32_t oldStackPointer = stackPointer;
 
-        if (MOZ_UNLIKELY(stackPointerVal >= capacity)) {
-            ensureCapacitySlow();
+        if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) {
+            frames[oldStackPointer].initLabelFrame(label, dynamicString, sp, line, category);
         }
-        frames[stackPointerVal].initLabelFrame(label, dynamicString, sp,
-                                               category, flags);
 
         // This must happen at the end! The compiler will not reorder this
         // update because stackPointer is Atomic<..., ReleaseAcquire>, so any
         // the writes above will not be reordered below the stackPointer store.
         // Do the read and the write as two separate statements, in order to
         // make it clear that we don't need an atomic increment, which would be
         // more expensive on x86 than the separate operations done here.
-        // However, don't use stackPointerVal here; instead, allow the compiler
-        // to turn this store into a non-atomic increment instruction which
-        // takes up less code size.
-        stackPointer = stackPointer + 1;
+        // This thread is the only one that ever changes the value of
+        // stackPointer.
+        stackPointer = oldStackPointer + 1;
     }
 
     void pushSpMarkerFrame(void* sp) {
         uint32_t oldStackPointer = stackPointer;
 
-        if (MOZ_UNLIKELY(oldStackPointer >= capacity)) {
-            ensureCapacitySlow();
+        if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) {
+            frames[oldStackPointer].initSpMarkerFrame(sp);
         }
-        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) {
-        // 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();
+        if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) {
+            frames[oldStackPointer].initJsFrame(label, dynamicString, script, pc);
         }
-        frames[oldStackPointer].initJsFrame(label, dynamicString, script, pc);
 
         // This must happen at the end, see the comment in pushLabelFrame.
-        stackPointer = stackPointer + 1;
+        stackPointer = oldStackPointer + 1;
     }
 
     void pop() {
         MOZ_ASSERT(stackPointer > 0);
         // Do the read and the write as two separate statements, in order to
         // make it clear that we don't need an atomic decrement, which would be
         // more expensive on x86 than the separate operations done here.
         // This thread is the only one that ever changes the value of
         // stackPointer.
         uint32_t oldStackPointer = stackPointer;
         stackPointer = oldStackPointer - 1;
     }
 
-    uint32_t stackSize() const { return stackPointer; }
+    uint32_t stackSize() const { return std::min(uint32_t(stackPointer), stackCapacity()); }
     uint32_t stackCapacity() const { return capacity; }
 
   private:
     // Out of line path for expanding the buffer, since otherwise this would get inlined in every
     // DOM WebIDL call.
-    MOZ_COLD void ensureCapacitySlow();
+    MOZ_COLD MOZ_MUST_USE bool ensureCapacitySlow();
 
     // No copying.
     ProfilingStack(const ProfilingStack&) = delete;
     void operator=(const ProfilingStack&) = delete;
 
     // No moving either.
     ProfilingStack(ProfilingStack&&) = delete;
     void operator=(ProfilingStack&&) = delete;
@@ -508,36 +466,31 @@ class GeckoProfilerBaselineOSRMarker;
 class GeckoProfilerThread
 {
     friend class AutoGeckoProfilerEntry;
     friend class GeckoProfilerEntryMarker;
     friend class GeckoProfilerBaselineOSRMarker;
 
     ProfilingStack*         profilingStack_;
 
-    // Same as profilingStack_ if the profiler is currently active, otherwise null.
-    ProfilingStack*         profilingStackIfEnabled_;
-
   public:
     GeckoProfilerThread();
 
     uint32_t stackPointer() { MOZ_ASSERT(infraInstalled()); return profilingStack_->stackPointer; }
     ProfilingStackFrame* stack() { return profilingStack_->frames; }
     ProfilingStack* getProfilingStack() { return profilingStack_; }
-    ProfilingStack* getProfilingStackIfEnabled() { return profilingStackIfEnabled_; }
 
     /*
      * True if the profiler infrastructure is setup.  Should be true in builds
      * that include profiler support except during early startup or late
      * shutdown.  Unrelated to the presence of the Gecko Profiler addon.
      */
     bool infraInstalled() { return profilingStack_ != nullptr; }
 
-    void setProfilingStack(ProfilingStack* profilingStack, bool enabled);
-    void enable(bool enable) { profilingStackIfEnabled_ = enable ? profilingStack_ : nullptr; }
+    void setProfilingStack(ProfilingStack* profilingStack);
     void trace(JSTracer* trc);
 
     /*
      * Functions which are the actual instrumentation to track run information
      *
      *   - enter: a function has started to execute
      *   - updatePC: updates the pc information about where a function
      *               is currently executing
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -861,18 +861,18 @@ class RootingContext
     template <typename T> friend class JS::Rooted;
 
     // Stack GC roots for AutoFooRooter classes.
     JS::AutoGCRooter* autoGCRooters_;
     friend class JS::AutoGCRooter;
 
     // Gecko profiling metadata.
     // This isn't really rooting related. It's only here because we want
-    // GetContextProfilingStackIfEnabled to be inlineable into non-JS code, and
-    // we didn't want to add another superclass of JSContext just for this.
+    // GetContextProfilingStack to be inlineable into non-JS code, and we
+    // didn't want to add another superclass of JSContext just for this.
     js::GeckoProfilerThread geckoProfiler_;
 
   public:
     RootingContext();
 
     void traceStackRoots(JSTracer* trc);
     void checkNoGCRooters();
 
@@ -1088,19 +1088,19 @@ GetContextCompartment(const JSContext* c
 
 inline JS::Zone*
 GetContextZone(const JSContext* cx)
 {
     return JS::RootingContext::get(cx)->zone_;
 }
 
 inline ProfilingStack*
-GetContextProfilingStackIfEnabled(JSContext* cx)
+GetContextProfilingStack(JSContext* cx)
 {
-    return JS::RootingContext::get(cx)->geckoProfiler().getProfilingStackIfEnabled();
+    return JS::RootingContext::get(cx)->geckoProfiler().getProfilingStack();
 }
 
 /**
  * Augment the generic Rooted<T> interface when T = JSObject* with
  * class-querying and downcasting operations.
  *
  * Given a Rooted<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
--- a/js/src/vm/GeckoProfiler-inl.h
+++ b/js/src/vm/GeckoProfiler-inl.h
@@ -93,16 +93,17 @@ AutoGeckoProfilerEntry::AutoGeckoProfile
         return;
     }
 #ifdef DEBUG
     spBefore_ = profiler_->stackPointer();
 #endif
     profiler_->profilingStack_->pushLabelFrame(label,
                                             /* dynamicString = */ nullptr,
                                             /* sp = */ this,
+                                            /* line = */ 0,
                                             category);
 }
 
 MOZ_ALWAYS_INLINE
 AutoGeckoProfilerEntry::~AutoGeckoProfilerEntry()
 {
     if (MOZ_LIKELY(!profiler_)) {
         return;
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -25,35 +25,33 @@
 #include "gc/Marking-inl.h"
 
 using namespace js;
 
 using mozilla::DebugOnly;
 
 GeckoProfilerThread::GeckoProfilerThread()
   : profilingStack_(nullptr)
-  , profilingStackIfEnabled_(nullptr)
 {
 }
 
 GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt)
   : rt(rt),
     strings(mutexid::GeckoProfilerStrings),
     slowAssertions(false),
     enabled_(false),
     eventMarker_(nullptr)
 {
     MOZ_ASSERT(rt != nullptr);
 }
 
 void
-GeckoProfilerThread::setProfilingStack(ProfilingStack* profilingStack, bool enabled)
+GeckoProfilerThread::setProfilingStack(ProfilingStack* profilingStack)
 {
     profilingStack_ = profilingStack;
-    profilingStackIfEnabled_ = enabled ? profilingStack : nullptr;
 }
 
 void
 GeckoProfilerRuntime::setEventMarker(void (*fn)(const char*))
 {
     eventMarker_ = fn;
 }
 
@@ -251,17 +249,17 @@ GeckoProfilerThread::exit(JSScript* scri
                             (void*) profilingStack_->frames,
                             uint32_t(profilingStack_->stackPointer),
                             profilingStack_->stackCapacity());
             for (int32_t i = sp; i >= 0; i--) {
                 ProfilingStackFrame& frame = profilingStack_->frames[i];
                 if (frame.isJsFrame()) {
                     fprintf(stderr, "  [%d] JS %s\n", i, frame.dynamicString());
                 } else {
-                    fprintf(stderr, "  [%d] Label %s\n", i, frame.dynamicString());
+                    fprintf(stderr, "  [%d] C line %d %s\n", i, frame.line(), frame.dynamicString());
                 }
             }
         }
 
         ProfilingStackFrame& frame = profilingStack_->frames[sp];
         MOZ_ASSERT(frame.isJsFrame());
         MOZ_ASSERT(frame.script() == script);
         MOZ_ASSERT(strcmp((const char*) frame.dynamicString(), dynamicString) == 0);
@@ -398,35 +396,35 @@ GeckoProfilerBaselineOSRMarker::GeckoPro
     }
 
     spBefore_ = sp;
     if (sp == 0) {
         return;
     }
 
     ProfilingStackFrame& frame = profiler->profilingStack_->frames[sp - 1];
-    MOZ_ASSERT(!frame.isOSRFrame());
-    frame.setIsOSRFrame(true);
+    MOZ_ASSERT(frame.kind() == ProfilingStackFrame::Kind::JS_NORMAL);
+    frame.setKind(ProfilingStackFrame::Kind::JS_OSR);
 }
 
 GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
 {
     if (profiler == nullptr) {
         return;
     }
 
     uint32_t sp = profiler->stackPointer();
     MOZ_ASSERT(spBefore_ == sp);
     if (sp == 0) {
         return;
     }
 
     ProfilingStackFrame& frame = profiler->stack()[sp - 1];
-    MOZ_ASSERT(frame.isOSRFrame());
-    frame.setIsOSRFrame(false);
+    MOZ_ASSERT(frame.kind() == ProfilingStackFrame::Kind::JS_OSR);
+    frame.setKind(ProfilingStackFrame::Kind::JS_NORMAL);
 }
 
 JS_PUBLIC_API(JSScript*)
 ProfilingStackFrame::script() const
 {
     MOZ_ASSERT(isJsFrame());
     auto script = reinterpret_cast<JSScript*>(spOrScript.operator void*());
     if (!script) {
@@ -444,49 +442,47 @@ ProfilingStackFrame::script() const
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
 ProfilingStackFrame::pc() const
 {
     MOZ_ASSERT(isJsFrame());
-    if (pcOffsetIfJS_ == NullPCOffset) {
+    if (lineOrPcOffset == NullPCOffset) {
         return nullptr;
     }
 
     JSScript* script = this->script();
-    return script ? script->offsetToPC(pcOffsetIfJS_) : nullptr;
+    return script ? script->offsetToPC(lineOrPcOffset) : nullptr;
 }
 
 /* static */ int32_t
 ProfilingStackFrame::pcToOffset(JSScript* aScript, jsbytecode* aPc) {
     return aPc ? aScript->pcToOffset(aPc) : NullPCOffset;
 }
 
 void
 ProfilingStackFrame::setPC(jsbytecode* pc)
 {
     MOZ_ASSERT(isJsFrame());
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
-    pcOffsetIfJS_ = pcToOffset(script, pc);
+    lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
 js::SetContextProfilingStack(JSContext* cx, ProfilingStack* profilingStack)
 {
-    cx->geckoProfiler().setProfilingStack(profilingStack,
-        cx->runtime()->geckoProfiler().enabled());
+    cx->geckoProfiler().setProfilingStack(profilingStack);
 }
 
 JS_FRIEND_API(void)
 js::EnableContextProfilingStack(JSContext* cx, bool enabled)
 {
-    cx->geckoProfiler().enable(enabled);
     cx->runtime()->geckoProfiler().enable(enabled);
 }
 
 JS_FRIEND_API(void)
 js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*))
 {
     MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
     cx->runtime()->geckoProfiler().setEventMarker(fn);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -51,17 +51,17 @@ GlobalHelperThreadState* gHelperThreadSt
 
 // These macros are identical in function to the same-named ones in
 // GeckoProfiler.h, but they are defined separately because SpiderMonkey can't
 // use GeckoProfiler.h.
 #define PROFILER_RAII_PASTE(id, line) id ## line
 #define PROFILER_RAII_EXPAND(id, line) PROFILER_RAII_PASTE(id, line)
 #define PROFILER_RAII PROFILER_RAII_EXPAND(raiiObject, __LINE__)
 #define AUTO_PROFILER_LABEL(label, category) \
-  HelperThread::AutoProfilerLabel PROFILER_RAII(this, label, \
+  HelperThread::AutoProfilerLabel PROFILER_RAII(this, label, __LINE__, \
                                                 js::ProfilingStackFrame::Category::category)
 
 bool
 js::CreateHelperThreadsState()
 {
     MOZ_ASSERT(!gHelperThreadState);
     gHelperThreadState = js_new<GlobalHelperThreadState>();
     return gHelperThreadState != nullptr;
@@ -2578,21 +2578,22 @@ const HelperThread::TaskSpec HelperThrea
         THREAD_TYPE_WASM_TIER2,
         &GlobalHelperThreadState::canStartWasmTier2Generator,
         &HelperThread::handleWasmTier2GeneratorWorkload
     }
 };
 
 HelperThread::AutoProfilerLabel::AutoProfilerLabel(HelperThread* helperThread,
                                                    const char* label,
+                                                   uint32_t line,
                                                    ProfilingStackFrame::Category category)
   : profilingStack(helperThread->profilingStack)
 {
     if (profilingStack) {
-        profilingStack->pushLabelFrame(label, nullptr, this, category);
+        profilingStack->pushLabelFrame(label, nullptr, this, line, category);
     }
 }
 
 HelperThread::AutoProfilerLabel::~AutoProfilerLabel()
 {
     if (profilingStack) {
         profilingStack->pop();
     }
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -416,16 +416,17 @@ struct HelperThread
 
     void ensureRegisteredWithProfiler();
     void unregisterWithProfilerIfNeeded();
 
   private:
     struct AutoProfilerLabel
     {
         AutoProfilerLabel(HelperThread* helperThread, const char* label,
+                          uint32_t line,
                           ProfilingStackFrame::Category category);
         ~AutoProfilerLabel();
 
     private:
         ProfilingStack* profilingStack;
     };
 
     /*
--- a/js/src/vm/ProfilingStack.cpp
+++ b/js/src/vm/ProfilingStack.cpp
@@ -19,30 +19,36 @@ ProfilingStack::~ProfilingStack()
     // The label macros keep a reference to the ProfilingStack to avoid a TLS
     // access. If these are somehow not all cleared we will get a
     // use-after-free so better to crash now.
     MOZ_RELEASE_ASSERT(stackPointer == 0);
 
     delete[] frames;
 }
 
-void
+bool
 ProfilingStack::ensureCapacitySlow()
 {
     MOZ_ASSERT(stackPointer >= capacity);
     const uint32_t kInitialCapacity = 128;
 
     uint32_t sp = stackPointer;
     auto newCapacity = std::max(sp + 1,  capacity ? capacity * 2 : kInitialCapacity);
 
-    auto* newFrames = new js::ProfilingStackFrame[newCapacity];
+    auto* newFrames =
+        new (mozilla::fallible) js::ProfilingStackFrame[newCapacity];
+    if (MOZ_UNLIKELY(!newFrames)) {
+        return false;
+    }
 
     // It's important that `frames` / `capacity` / `stackPointer` remain consistent here at
     // all times.
     for (auto i : mozilla::IntegerRange(capacity)) {
         newFrames[i] = frames[i];
     }
 
     js::ProfilingStackFrame* oldFrames = frames;
     frames = newFrames;
     capacity = newCapacity;
     delete[] oldFrames;
+
+    return true;
 }
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -664,17 +664,18 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
 continue_loading:
 #ifdef DEBUG_very_verbose
     printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
 #endif
 
   // A few DLLs such as xul.dll and nss3.dll get loaded before mozglue's
   // AutoProfilerLabel is initialized, and this is a no-op in those cases. But
   // the vast majority of DLLs do get labelled here.
-  AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName);
+  AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName,
+                          __LINE__);
 
 #ifdef _M_AMD64
   // Prevent the stack walker from suspending this thread when LdrLoadDll
   // holds the RtlLookupFunctionEntry lock.
   AutoSuppressStackWalking suppress;
 #endif
   NTSTATUS ret;
   HANDLE myHandle;
--- a/mozglue/misc/AutoProfilerLabel.cpp
+++ b/mozglue/misc/AutoProfilerLabel.cpp
@@ -15,22 +15,23 @@ void
 RegisterProfilerLabelEnterExit(ProfilerLabelEnter aEnter,
                                ProfilerLabelExit aExit)
 {
   sEnter = aEnter;
   sExit = aExit;
 }
 
 AutoProfilerLabel::AutoProfilerLabel(const char* aLabel,
-                                     const char* aDynamicString
+                                     const char* aDynamicString,
+                                     uint32_t aLine
                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-  mProfilingStack = sEnter ? sEnter(aLabel, aDynamicString, this) : nullptr;
+  mProfilingStack = sEnter ? sEnter(aLabel, aDynamicString, this, aLine) : nullptr;
 }
 
 AutoProfilerLabel::~AutoProfilerLabel()
 {
   if (sExit && mProfilingStack) {
     sExit(mProfilingStack);
   }
 }
--- a/mozglue/misc/AutoProfilerLabel.h
+++ b/mozglue/misc/AutoProfilerLabel.h
@@ -24,31 +24,33 @@
 // the callbacks provided by the profiler use. (Specifying the category in
 // this file would require #including ProfilingStack.h in mozglue, which we
 // don't want to do.)
 
 class ProfilingStack;
 
 namespace mozilla {
 
-typedef ProfilingStack* (*ProfilerLabelEnter)(const char*, const char*, void*);
+typedef ProfilingStack* (*ProfilerLabelEnter)(const char*, const char*, void*,
+                                           uint32_t);
 typedef void (*ProfilerLabelExit)(ProfilingStack*);
 
 // Register callbacks that do the entry/exit work involving sProfilingStack.
 MFBT_API void RegisterProfilerLabelEnterExit(ProfilerLabelEnter aEnter,
                                              ProfilerLabelExit aExit);
 
 // This #ifdef prevents this AutoProfilerLabel from being defined in libxul,
 // which would conflict with the one in the profiler.
 #ifdef IMPL_MFBT
 
 class MOZ_RAII AutoProfilerLabel
 {
 public:
-  AutoProfilerLabel(const char* aLabel, const char* aDynamicString
+  AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
+                    uint32_t aLine
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~AutoProfilerLabel();
 
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   ProfilingStack* mProfilingStack;
 };
 
--- a/tools/profiler/core/ProfileBuffer.cpp
+++ b/tools/profiler/core/ProfileBuffer.cpp
@@ -63,22 +63,21 @@ void
 ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
 {
   aStoredMarker->SetPositionInBuffer(mRangeEnd);
   mStoredMarkers.insert(aStoredMarker);
 }
 
 void
 ProfileBuffer::CollectCodeLocation(
-  const char* aLabel, const char* aStr, uint32_t aFrameFlags,
+  const char* aLabel, const char* aStr,
   const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber,
   const Maybe<js::ProfilingStackFrame::Category>& aCategory)
 {
   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; ) {
       // Store up to kNumChars characters in the entry.
       char chars[ProfileBufferEntry::kNumChars];
       size_t len = ProfileBufferEntry::kNumChars;
@@ -150,27 +149,26 @@ 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, 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()));
+  MOZ_ASSERT(aFrame.kind() == js::ProfilingStackFrame::Kind::LABEL ||
+             aFrame.kind() == js::ProfilingStackFrame::Kind::JS_NORMAL);
 
   const char* label = aFrame.label();
   const char* dynamicString = aFrame.dynamicString();
   bool isChromeJSEntry = false;
   Maybe<uint32_t> line;
   Maybe<uint32_t> column;
 
   if (aFrame.isJsFrame()) {
@@ -195,22 +193,23 @@ ProfileBufferCollector::CollectProfiling
         }
       }
 
     } else {
       MOZ_ASSERT(strcmp(label, "js::RunScript") == 0 && !dynamicString);
     }
   } else {
     MOZ_ASSERT(aFrame.isLabelFrame());
+    line = Some(aFrame.line());
   }
 
   if (dynamicString) {
     // 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, Some(aFrame.category()));
+  mBuf.CollectCodeLocation(label, dynamicString, line, column,
+                           Some(aFrame.category()));
 }
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -39,17 +39,17 @@ public:
   // Add |aEntry| to the buffer, ignoring what kind of entry it is.
   void 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 char* aLabel, const char* aStr,
     const mozilla::Maybe<uint32_t>& aLineNumber,
     const mozilla::Maybe<uint32_t>& aColumnNumber,
     const mozilla::Maybe<js::ProfilingStackFrame::Category>& aCategory);
 
   // 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
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -818,17 +818,17 @@ private:
 // The following grammar shows legal sequences of profile buffer entries.
 // The sequences beginning with a ThreadId entry are known as "samples".
 //
 // (
 //   ( /* Samples */
 //     ThreadId
 //     Time
 //     ( NativeLeafAddr
-//     | Label FrameFlags? DynamicStringFragment* LineNumber? Category?
+//     | Label DynamicStringFragment* LineNumber? Category?
 //     | JitReturnAddr
 //     )+
 //     Marker*
 //     Responsiveness?
 //     ResidentMemory?
 //     UnsharedMemory?
 //   )
 //   | ( ResidentMemory UnsharedMemory? Time)  /* Memory */
@@ -861,82 +861,75 @@ private:
 //
 //     Label("ElementRestyler::ComputeStyleChangeFor")
 //     LineNumber(3003)
 //     Category(ProfilingStackFrame::Category::CSS)
 //
 // - ProfilingStack frames with a dynamic string:
 //
 //     Label("nsObserverService::NotifyObservers")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_LABEL_FRAME))
 //     DynamicStringFragment("domwindo")
 //     DynamicStringFragment("wopened")
 //     LineNumber(291)
 //     Category(ProfilingStackFrame::Category::OTHER)
 //
 //     Label("")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_JS_FRAME))
 //     DynamicStringFragment("closeWin")
 //     DynamicStringFragment("dow (chr")
 //     DynamicStringFragment("ome://gl")
 //     DynamicStringFragment("obal/con")
 //     DynamicStringFragment("tent/glo")
 //     DynamicStringFragment("balOverl")
 //     DynamicStringFragment("ay.js:5)")
 //     DynamicStringFragment("")          # this string holds the closing '\0'
 //     LineNumber(25)
 //     Category(ProfilingStackFrame::Category::JS)
 //
 //     Label("")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_JS_FRAME))
 //     DynamicStringFragment("bound (s")
 //     DynamicStringFragment("elf-host")
 //     DynamicStringFragment("ed:914)")
 //     LineNumber(945)
 //     Category(ProfilingStackFrame::Category::JS)
 //
 // - A profiling stack frame with a dynamic string, but with privacy enabled:
 //
 //     Label("nsObserverService::NotifyObservers")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_LABEL_FRAME))
 //     DynamicStringFragment("(private")
 //     DynamicStringFragment(")")
 //     LineNumber(291)
 //     Category(ProfilingStackFrame::Category::OTHER)
 //
 // - A profiling stack frame with an overly long dynamic string:
 //
 //     Label("")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_LABEL_FRAME))
 //     DynamicStringFragment("(too lon")
 //     DynamicStringFragment("g)")
 //     LineNumber(100)
 //     Category(ProfilingStackFrame::Category::NETWORK)
 //
 // - A wasm JIT frame:
 //
 //     Label("")
-//     FrameFlags(uint64_t(0))
 //     DynamicStringFragment("wasm-fun")
 //     DynamicStringFragment("ction[87")
 //     DynamicStringFragment("36] (blo")
 //     DynamicStringFragment("b:http:/")
 //     DynamicStringFragment("/webasse")
 //     DynamicStringFragment("mbly.org")
 //     DynamicStringFragment("/3dc5759")
 //     DynamicStringFragment("4-ce58-4")
 //     DynamicStringFragment("626-975b")
 //     DynamicStringFragment("-08ad116")
 //     DynamicStringFragment("30bc1:38")
 //     DynamicStringFragment("29856)")
 //
 // - A JS frame in a synchronous sample:
 //
 //     Label("")
-//     FrameFlags(uint64_t(ProfilingStackFrame::Flags::IS_LABEL_FRAME))
 //     DynamicStringFragment("u (https")
 //     DynamicStringFragment("://perf-")
 //     DynamicStringFragment("html.io/")
 //     DynamicStringFragment("ac0da204")
 //     DynamicStringFragment("aaa44d75")
 //     DynamicStringFragment("a800.bun")
 //     DynamicStringFragment("dle.js:2")
 //     DynamicStringFragment("5)")
@@ -951,17 +944,17 @@ private:
     continue; \
   }
 
 void
 ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
                                    double aSinceTime,
                                    UniqueStacks& aUniqueStacks) const
 {
-  UniquePtr<char[]> dynStrBuf = MakeUnique<char[]>(kMaxFrameKeyLength);
+  UniquePtr<char[]> strbuf = MakeUnique<char[]>(kMaxFrameKeyLength);
 
   EntryGetter e(*this);
 
   for (;;) {
     // This block skips entries until we find the start of the next sample.
     // This is useful in three situations.
     //
     // - The circular buffer overwrites old entries, so when we start parsing
@@ -1026,63 +1019,49 @@ ProfileBuffer::StreamSamplesToJSON(Splic
         char buf[20];
         SprintfLiteral(buf, "%#llx", pc);
         stack = aUniqueStacks.AppendFrame(stack, UniqueStacks::FrameKey(buf));
         e.Next();
 
       } else if (e.Get().IsLabel()) {
         numFrames++;
 
+        // Copy the label into strbuf.
         const char* label = e.Get().u.mString;
+        strncpy(strbuf.get(), label, kMaxFrameKeyLength);
+        size_t i = strlen(label);
         e.Next();
 
-        using FrameFlags = js::ProfilingStackFrame::Flags;
-        uint32_t frameFlags = 0;
-        if (e.Has() && e.Get().IsFrameFlags()) {
-          frameFlags = uint32_t(e.Get().u.mUint64);
-          e.Next();
-        }
-
-        // Copy potential dynamic string fragments into dynStrBuf, so that
-        // dynStrBuf will then contain the entire dynamic string.
-        size_t i = 0;
-        dynStrBuf[0] = '\0';
+        bool seenFirstDynamicStringFragment = false;
         while (e.Has()) {
           if (e.Get().IsDynamicStringFragment()) {
+            // If this is the first dynamic string fragment and we have a
+            // non-empty label, insert a ' ' after the label and before the
+            // dynamic string.
+            if (!seenFirstDynamicStringFragment) {
+              if (i > 0 && i < kMaxFrameKeyLength) {
+                strbuf[i] = ' ';
+                i++;
+              }
+              seenFirstDynamicStringFragment = true;
+            }
+
             for (size_t j = 0; j < ProfileBufferEntry::kNumChars; j++) {
               const char* chars = e.Get().u.mChars;
               if (i < kMaxFrameKeyLength) {
-                dynStrBuf[i] = chars[j];
+                strbuf[i] = chars[j];
                 i++;
               }
             }
             e.Next();
           } else {
             break;
           }
         }
-        dynStrBuf[kMaxFrameKeyLength - 1] = '\0';
-        bool hasDynamicString = (i != 0);
-
-        nsCString frameLabel;
-        if (label[0] != '\0' && hasDynamicString) {
-          if (frameFlags & uint32_t(FrameFlags::STRING_TEMPLATE_METHOD)) {
-            frameLabel.AppendPrintf("%s.%s", label, dynStrBuf.get());
-          } else if (frameFlags & uint32_t(FrameFlags::STRING_TEMPLATE_GETTER)) {
-            frameLabel.AppendPrintf("get %s.%s", label, dynStrBuf.get());
-          } else if (frameFlags & uint32_t(FrameFlags::STRING_TEMPLATE_SETTER)) {
-            frameLabel.AppendPrintf("set %s.%s", label, dynStrBuf.get());
-          } else {
-            frameLabel.AppendPrintf("%s %s", label, dynStrBuf.get());
-          }
-        } else if (hasDynamicString) {
-          frameLabel.Append(dynStrBuf.get());
-        } else {
-          frameLabel.Append(label);
-        }
+        strbuf[kMaxFrameKeyLength - 1] = '\0';
 
         Maybe<unsigned> line;
         if (e.Has() && e.Get().IsLineNumber()) {
           line = Some(unsigned(e.Get().u.mInt));
           e.Next();
         }
 
         Maybe<unsigned> column;
@@ -1093,18 +1072,17 @@ ProfileBuffer::StreamSamplesToJSON(Splic
 
         Maybe<unsigned> category;
         if (e.Has() && e.Get().IsCategory()) {
           category = Some(unsigned(e.Get().u.mInt));
           e.Next();
         }
 
         stack = aUniqueStacks.AppendFrame(
-          stack, UniqueStacks::FrameKey(std::move(frameLabel), line, column,
-                                        category));
+          stack, UniqueStacks::FrameKey(strbuf.get(), line, column, category));
 
       } else if (e.Get().IsJitReturnAddr()) {
         numFrames++;
 
         // A JIT frame may expand to multiple frames due to inlining.
         void* pc = e.Get().u.mPtr;
         const Maybe<nsTArray<UniqueStacks::FrameKey>>& frameKeys =
           aUniqueStacks.LookupFramesForJITAddressFromBufferPos(pc, e.CurPos());
--- a/tools/profiler/core/ProfileBufferEntry.h
+++ b/tools/profiler/core/ProfileBufferEntry.h
@@ -32,17 +32,16 @@ class ProfilerMarker;
 
 // NOTE!  If you add entries, you need to verify if they need to be added to the
 // switch statement in DuplicateLastSample!
 #define FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(macro) \
   macro(Category,              int) \
   macro(CollectionStart,       double) \
   macro(CollectionEnd,         double) \
   macro(Label,                 const char*) \
-  macro(FrameFlags,            uint64_t) \
   macro(DynamicStringFragment, char*) /* char[kNumChars], really */ \
   macro(JitReturnAddr,         void*) \
   macro(LineNumber,            int) \
   macro(ColumnNumber,          int) \
   macro(NativeLeafAddr,        void*) \
   macro(Marker,                ProfilerMarker*) \
   macro(Pause,                 double) \
   macro(Responsiveness,        double) \
@@ -231,20 +230,20 @@ class UniqueStacks
 public:
   struct FrameKey {
     explicit FrameKey(const char* aLocation)
       : mData(NormalFrameData{
                 nsCString(aLocation), mozilla::Nothing(), mozilla::Nothing() })
     {
     }
 
-    FrameKey(nsCString&& aLocation, const mozilla::Maybe<unsigned>& aLine,
+    FrameKey(const char* aLocation, const mozilla::Maybe<unsigned>& aLine,
              const mozilla::Maybe<unsigned>& aColumn,
              const mozilla::Maybe<unsigned>& aCategory)
-      : mData(NormalFrameData{ aLocation, aLine, aColumn, aCategory })
+      : mData(NormalFrameData{ nsCString(aLocation), aLine, aColumn, aCategory })
     {
     }
 
     FrameKey(void* aJITAddress, uint32_t aJITDepth, uint32_t aRangeIndex)
       : mData(JITFrameData{ aJITAddress, aJITDepth, aRangeIndex })
     {
     }
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -980,17 +980,17 @@ MergeStacks(uint32_t aFeatures, bool aIs
         lastLabelFrameStackAddr = (uint8_t*) profilingStackFrame.stackAddress();
       }
 
       // Skip any JS_OSR frames. Such frames are used when the JS interpreter
       // enters a jit frame on a loop edge (via on-stack-replacement, or OSR).
       // To avoid both the profiling stack frame and jit frame being recorded
       // (and showing up twice), the interpreter marks the interpreter
       // profiling stack frame as JS_OSR to ensure that it doesn't get counted.
-      if (profilingStackFrame.isOSRFrame()) {
+      if (profilingStackFrame.kind() == js::ProfilingStackFrame::Kind::JS_OSR) {
           profilingStackIndex++;
           continue;
       }
 
       MOZ_ASSERT(lastLabelFrameStackAddr);
       profilingStackAddr = lastLabelFrameStackAddr;
     }
 
@@ -1835,19 +1835,18 @@ CollectJavaThreadProfileData()
       if (frameNameString.EqualsLiteral("android.os.MessageQueue.nativePollOnce()")) {
         category = Some(js::ProfilingStackFrame::Category::IDLE);
         parentFrameWasIdleFrame = true;
       } else if (parentFrameWasIdleFrame) {
         category = Some(js::ProfilingStackFrame::Category::OTHER);
         parentFrameWasIdleFrame = false;
       }
 
-      buffer->CollectCodeLocation("", frameNameString.get(),
-          js::ProfilingStackFrame::StringTemplate::DEFAULT,
-          Nothing(), Nothing(), category);
+      buffer->CollectCodeLocation("", frameNameString.get(), Nothing(),
+          Nothing(), category);
     }
     sampleId++;
   }
   return buffer;
 }
 #endif
 
 static void
@@ -2489,21 +2488,22 @@ NotifyProfilerStarted(const int aCapacit
 
 static void
 locked_profiler_start(PSLockRef aLock, uint32_t aCapacity, double aInterval,
                       uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount);
 
 // This basically duplicates AutoProfilerLabel's constructor.
 ProfilingStack*
-MozGlueLabelEnter(const char* aLabel, const char* aDynamicString, void* aSp)
+MozGlueLabelEnter(const char* aLabel, const char* aDynamicString, void* aSp,
+                  uint32_t aLine)
 {
   ProfilingStack* profilingStack = AutoProfilerLabel::sProfilingStack.get();
   if (profilingStack) {
-    profilingStack->pushLabelFrame(aLabel, aDynamicString, aSp,
+    profilingStack->pushLabelFrame(aLabel, aDynamicString, aSp, aLine,
                                 js::ProfilingStackFrame::Category::OTHER);
   }
   return profilingStack;
 }
 
 // This basically duplicates AutoProfilerLabel's destructor.
 void
 MozGlueLabelExit(ProfilingStack* sProfilingStack)
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -41,17 +41,16 @@
 #define PROFILER_SET_JS_CONTEXT(cx)
 #define PROFILER_CLEAR_JS_CONTEXT()
 
 #define AUTO_PROFILER_LABEL(label, category)
 #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, category, cStr)
 #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, category, nsCStr)
 #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, category, nsStr)
 #define AUTO_PROFILER_LABEL_FAST(label, category, ctx)
-#define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, category, ctx, flags)
 
 #define PROFILER_ADD_MARKER(markerName)
 #define PROFILER_ADD_NETWORK_MARKER(uri, pri, channel, type, start, end, count, timings, redirect)
 
 #define PROFILER_TRACING(category, markerName, kind)
 #define AUTO_PROFILER_TRACING(category, markerName)
 
 #else // !MOZ_GECKO_PROFILER
@@ -469,17 +468,17 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 // form "ClassName::FunctionName". (Ideally we'd use the compiler to provide
 // that for us, but __func__ gives us the function name without the class
 // name.) If the label applies to only part of a function, you can qualify it
 // like this: "ClassName::FunctionName:PartName".
 //
 // Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic
 // information to the label stack frame.
 #define AUTO_PROFILER_LABEL(label, category) \
-  mozilla::AutoProfilerLabel PROFILER_RAII(label, nullptr, \
+  mozilla::AutoProfilerLabel PROFILER_RAII(label, nullptr, __LINE__, \
                                            js::ProfilingStackFrame::Category::category)
 
 // Similar to AUTO_PROFILER_LABEL, but with an additional string. The inserted
 // RAII object stores the cStr pointer in a field; it does not copy the string.
 //
 // WARNING: This means that the string you pass to this macro needs to live at
 // least until the end of the current scope. Be careful using this macro with
 // ns[C]String; the other AUTO_PROFILER_LABEL_DYNAMIC_* macros below are
@@ -493,69 +492,61 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 // Compare this to the plain AUTO_PROFILER_LABEL macro, which only accepts
 // literal strings: When the label stack frames generated by
 // AUTO_PROFILER_LABEL are sampled, no string copy needs to be made because the
 // profile buffer can just store the raw pointers to the literal strings.
 // Consequently, AUTO_PROFILER_LABEL frames take up considerably less space in
 // the profile buffer than AUTO_PROFILER_LABEL_DYNAMIC_* frames.
 #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, category, cStr) \
   mozilla::AutoProfilerLabel \
-    PROFILER_RAII(label, cStr, js::ProfilingStackFrame::Category::category)
+    PROFILER_RAII(label, cStr, __LINE__, js::ProfilingStackFrame::Category::category)
 
 // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsACString.
 //
 // Note: The use of the Maybe<>s ensures the scopes for the dynamic string and
 // the AutoProfilerLabel are appropriate, while also not incurring the runtime
 // cost of the string assignment unless the profiler is active. Therefore,
 // unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro
 // doesn't push/pop a label when the profiler is inactive.
 #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, category, nsCStr) \
   mozilla::Maybe<nsAutoCString> autoCStr; \
   mozilla::Maybe<AutoProfilerLabel> raiiObjectNsCString; \
   if (profiler_is_active()) { \
     autoCStr.emplace(nsCStr); \
-    raiiObjectNsCString.emplace(label, autoCStr->get(), \
+    raiiObjectNsCString.emplace(label, autoCStr->get(), __LINE__, \
                                 js::ProfilingStackFrame::Category::category); \
   }
 
 // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is
 // is lossily converted to an ASCII string.
 //
 // Note: The use of the Maybe<>s ensures the scopes for the converted dynamic
 // string and the AutoProfilerLabel are appropriate, while also not incurring
 // the runtime cost of the string conversion unless the profiler is active.
 // Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR,
 // this macro doesn't push/pop a label when the profiler is inactive.
 #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, category, nsStr) \
   mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; \
   mozilla::Maybe<AutoProfilerLabel> raiiObjectLossyNsString; \
   if (profiler_is_active()) { \
     asciiStr.emplace(nsStr); \
-    raiiObjectLossyNsString.emplace(label, asciiStr->get(), \
+    raiiObjectLossyNsString.emplace(label, asciiStr->get(), __LINE__, \
                                     js::ProfilingStackFrame::Category::category); \
   }
 
 // Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a
 // no-op if the profiler is disabled.
 // Used to annotate functions for which overhead in the range of nanoseconds is
 // noticeable. It avoids overhead from the TLS lookup because it can get the
 // ProfilingStack from the JS context, and avoids almost all overhead in the case
 // where the profiler is disabled.
 #define AUTO_PROFILER_LABEL_FAST(label, category, ctx) \
-  mozilla::AutoProfilerLabel PROFILER_RAII(ctx, label, nullptr, \
+  mozilla::AutoProfilerLabel PROFILER_RAII(ctx, label, nullptr, __LINE__, \
                                            js::ProfilingStackFrame::Category::category)
 
-// Similar to AUTO_PROFILER_LABEL_FAST, but also takes an extra string and an
-// additional set of flags. The flags parameter should carry values from the
-// js::ProfilingStackFrame::Flags enum.
-#define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, category, ctx, flags) \
-  mozilla::AutoProfilerLabel PROFILER_RAII(ctx, label, dynamicString, \
-                                           js::ProfilingStackFrame::Category::category, \
-                                           flags)
-
 // Insert a marker in the profile timeline. This is useful to delimit something
 // important happening such as the first paint. Unlike labels, which are only
 // recorded in the profile buffer if a sample is collected while the label is
 // on the label stack, markers will always be recorded in the profile buffer.
 // aMarkerName is copied, so the caller does not need to ensure it lives for a
 // certain length of time. A no-op if the profiler is inactive or in privacy
 // mode.
 #define PROFILER_ADD_MARKER(markerName) \
@@ -723,49 +714,51 @@ private:
 // are stack-allocated, and so exist within a thread, and are thus bounded by
 // the lifetime of the thread, which ensures that the references held can't be
 // used after the ProfilingStack is destroyed.
 class MOZ_RAII AutoProfilerLabel
 {
 public:
   // This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant.
   AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
-                    js::ProfilingStackFrame::Category aCategory
+                    uint32_t aLine, js::ProfilingStackFrame::Category aCategory
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
     // Get the ProfilingStack from TLS.
-    Push(sProfilingStack.get(), aLabel, aDynamicString, aCategory);
+    Push(sProfilingStack.get(), aLabel, aDynamicString, aLine, aCategory);
   }
 
-  // This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the ProfilingStack
-  // from the JSContext and does nothing if the profiler is inactive.
+  // This is the AUTO_PROFILER_LABEL_FAST variant. It's guarded on
+  // profiler_is_active() and retrieves the ProfilingStack from the JSContext.
   AutoProfilerLabel(JSContext* aJSContext,
                     const char* aLabel, const char* aDynamicString,
-                    js::ProfilingStackFrame::Category aCategory,
-                    uint32_t aFlags
+                    uint32_t aLine, js::ProfilingStackFrame::Category aCategory
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    Push(js::GetContextProfilingStackIfEnabled(aJSContext),
-         aLabel, aDynamicString, aCategory, aFlags);
+    if (profiler_is_active()) {
+      Push(js::GetContextProfilingStack(aJSContext),
+           aLabel, aDynamicString, aLine, aCategory);
+    } else {
+      mProfilingStack = nullptr;
+    }
   }
 
   void Push(ProfilingStack* aProfilingStack,
             const char* aLabel, const char* aDynamicString,
-            js::ProfilingStackFrame::Category aCategory,
-            uint32_t aFlags = 0)
+            uint32_t aLine, js::ProfilingStackFrame::Category aCategory)
   {
     // This function runs both on and off the main thread.
 
     mProfilingStack = aProfilingStack;
     if (mProfilingStack) {
-      mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, aCategory,
-                                      aFlags);
+      mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, aLine,
+                                   aCategory);
     }
   }
 
   ~AutoProfilerLabel()
   {
     // This function runs both on and off the main thread.
 
     if (mProfilingStack) {
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -743,19 +743,19 @@ TEST(GeckoProfiler, ProfilingStack)
       "A::C3", JS, NS_ConvertUTF8toUTF16(dynamic.get()));
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
                    features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_get_backtrace());
   }
 
-  AutoProfilerLabel label1("A", nullptr,
+  AutoProfilerLabel label1("A", nullptr, 888,
                            js::ProfilingStackFrame::Category::DOM);
-  AutoProfilerLabel label2("A", dynamic.get(),
+  AutoProfilerLabel label2("A", dynamic.get(), 888,
                            js::ProfilingStackFrame::Category::NETWORK);
   ASSERT_TRUE(profiler_get_backtrace());
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }