Bug 1369644 - Remove use of |volatile| from ProfileEntry. r=mstange,shu,jseward,froydnj.
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 02 Jun 2017 17:16:56 +1000
changeset 410583 a325d838180b07692bbb0e04094ad5e1dc06cca9
parent 410582 2128f5860eb4774a5e3ab85eda1a0383e652afe0
child 410584 e3cf285f484a04e665f1f42d9df50de0c65dc1ea
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, shu, jseward, froydnj
bugs1369644
milestone55.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 1369644 - Remove use of |volatile| from ProfileEntry. r=mstange,shu,jseward,froydnj. These annotations aren't doing anything useful. The important thing with the PseudoStack is that, during pushes, the stack pointer incrementing happens after the new entry is written, and this is ensured by the stack pointer being Atomic. The patch also improves the comments on PseudoStack.
js/public/ProfilingStack.h
js/src/vm/GeckoProfiler.cpp
js/src/vm/GeckoProfiler.h
tools/profiler/core/platform.cpp
xpcom/threads/ThreadStackHelper.cpp
xpcom/threads/ThreadStackHelper.h
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -24,45 +24,36 @@ namespace js {
 
 // A call stack can be specified to the JS engine such that all JS entry/exits
 // to functions push/pop an entry to/from the specified stack.
 //
 // For more detailed information, see vm/GeckoProfiler.h.
 //
 class ProfileEntry
 {
-    // All fields are marked volatile to prevent the compiler from re-ordering
-    // instructions. Namely this sequence:
-    //
-    //    entry[size] = ...;
-    //    size++;
-    //
-    // If the size modification were somehow reordered before the stores, then
-    // if a sample were taken it would be examining bogus information.
-    //
-    // A ProfileEntry represents both a C++ profile entry and a JS one.
+    // A ProfileEntry represents either a C++ profile entry or a JS one.
 
     // Descriptive label for this entry. Must be a static string! Can be an
     // empty string, but not a null pointer.
-    const char * volatile label_;
+    const char* label_;
 
     // An additional descriptive string of this entry which is combined with
     // |label_| in profiler output. Need not be (and usually isn't) static. Can
     // be null.
-    const char * volatile dynamicString_;
+    const char* dynamicString_;
 
     // Stack pointer for non-JS entries, the script pointer otherwise.
-    void * volatile spOrScript;
+    void* spOrScript;
 
     // Line number for non-JS entries, the bytecode offset otherwise.
-    int32_t volatile lineOrPcOffset;
+    int32_t lineOrPcOffset;
 
     // Bits 0..1 hold the Kind. Bits 2..3 are unused. Bits 4..12 hold the
     // Category.
-    uint32_t volatile kindAndCategory_;
+    uint32_t kindAndCategory_;
 
     static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
 
   public:
     enum class Kind : uint32_t {
         // A normal C++ frame.
         CPP_NORMAL = 0,
 
@@ -98,95 +89,90 @@ class ProfileEntry
         LAST     = EVENTS,
 
         CATEGORY_MASK = ~uint32_t(Kind::KIND_MASK),
     };
 
     static_assert((uint32_t(Category::FIRST) & uint32_t(Kind::KIND_MASK)) == 0,
                   "Category overlaps with Kind");
 
-    // All of these methods are marked with the 'volatile' keyword because the
-    // Gecko Profiler's representation of the stack is stored such that all
-    // ProfileEntry instances are volatile. These methods would not be
-    // available unless they were marked as volatile as well.
-
-    bool isCpp() const volatile
+    bool isCpp() const
     {
         Kind k = kind();
         return k == Kind::CPP_NORMAL || k == Kind::CPP_MARKER_FOR_JS;
     }
 
-    bool isJs() const volatile
+    bool isJs() const
     {
         Kind k = kind();
         return k == Kind::JS_NORMAL || k == Kind::JS_OSR;
     }
 
-    void setLabel(const char* aLabel) volatile { label_ = aLabel; }
-    const char* label() const volatile { return label_; }
+    void setLabel(const char* aLabel) { label_ = aLabel; }
+    const char* label() const { return label_; }
 
-    const char* dynamicString() const volatile { return dynamicString_; }
+    const char* dynamicString() const { return dynamicString_; }
 
     void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
-                      Kind aKind, Category aCategory) volatile
+                      Kind aKind, Category aCategory)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = sp;
         lineOrPcOffset = static_cast<int32_t>(aLine);
         kindAndCategory_ = uint32_t(aKind) | uint32_t(aCategory);
         MOZ_ASSERT(isCpp());
     }
 
     void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
-                     jsbytecode* aPc) volatile
+                     jsbytecode* aPc)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = aScript;
         lineOrPcOffset = pcToOffset(aScript, aPc);
         kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | uint32_t(Category::JS);
         MOZ_ASSERT(isJs());
     }
 
-    void setKind(Kind aKind) volatile {
+    void setKind(Kind aKind) {
         kindAndCategory_ = uint32_t(aKind) | uint32_t(category());
     }
 
-    Kind kind() const volatile {
+    Kind kind() const {
         return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK));
     }
 
-    Category category() const volatile {
+    Category category() const {
         return Category(kindAndCategory_ & uint32_t(Category::CATEGORY_MASK));
     }
 
-    void* stackAddress() const volatile {
+    void* stackAddress() const {
         MOZ_ASSERT(!isJs());
         return spOrScript;
     }
 
-    JS_PUBLIC_API(JSScript*) script() const volatile;
+    JS_PUBLIC_API(JSScript*) script() const;
 
-    uint32_t line() const volatile {
+    uint32_t line() const {
         MOZ_ASSERT(!isJs());
         return static_cast<uint32_t>(lineOrPcOffset);
     }
 
     // Note that the pointer returned might be invalid.
-    JSScript* rawScript() const volatile {
+    JSScript* rawScript() const {
         MOZ_ASSERT(isJs());
         return (JSScript*)spOrScript;
     }
 
     // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
-    JS_FRIEND_API(jsbytecode*) pc() const volatile;
-    void setPC(jsbytecode* pc) volatile;
+    JS_FRIEND_API(jsbytecode*) pc() const;
+    void setPC(jsbytecode* pc);
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
 
     // The offset of a pc into a script's code can actually be 0, so to
     // signify a nullptr pc, use a -1 index. This is checked against in
     // pc() and setPC() to set/get the right pc.
     static const int32_t NullPCOffset = -1;
 };
 
 JS_FRIEND_API(void)
@@ -195,19 +181,34 @@ SetContextProfilingStack(JSContext* cx, 
 JS_FRIEND_API(void)
 EnableContextProfilingStack(JSContext* cx, bool enabled);
 
 JS_FRIEND_API(void)
 RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
 
 } // namespace js
 
-// The PseudoStack members are accessed in parallel by multiple threads: the
-// profiler's sampler thread reads these members while other threads modify
-// them.
+// Each thread has its own PseudoStack. That thread modifies the PseudoStack,
+// pushing and popping elements as necessary.
+//
+// The PseudoStack is also read periodically by the profiler's sampler thread.
+// This happens only when the thread that owns the PseudoStack is suspended. So
+// there are no genuine parallel accesses.
+//
+// However, it is possible for pushing/popping to be interrupted by a periodic
+// sample. Because of this, we need pushing/popping to be effectively atomic.
+//
+// - When pushing a new entry, we increment the stack pointer -- making the new
+//   entry visible to the sampler thread -- only after the new entry has been
+//   fully written. The stack pointer is Atomic<> (with SequentiallyConsistent
+//   semantics) to ensure the incrementing is not reordered before the writes.
+//
+// - When popping an old entry, the only operation is the decrementing of the
+//   stack pointer, which is obviously atomic.
+//
 class PseudoStack
 {
   public:
     PseudoStack()
       : stackPointer(0)
     {}
 
     ~PseudoStack() {
@@ -250,18 +251,18 @@ class PseudoStack
     // No copying.
     PseudoStack(const PseudoStack&) = delete;
     void operator=(const PseudoStack&) = delete;
 
   public:
     static const uint32_t MaxEntries = 1024;
 
     // The stack entries.
-    js::ProfileEntry volatile entries[MaxEntries];
+    js::ProfileEntry entries[MaxEntries];
 
     // This may exceed MaxEntries, so instead use the stackSize() method to
     // determine the number of valid samples in entries. When this is less
     // than MaxEntries, it refers to the first free entry past the top of the
     // in-use stack (i.e. entries[stackPointer - 1] is the top stack entry).
-    mozilla::Atomic<uint32_t> stackPointer;
+    mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> stackPointer;
 };
 
 #endif  /* js_ProfilingStack_h */
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -234,25 +234,25 @@ GeckoProfiler::exit(JSScript* script, JS
         // Bug 822041
         if (!pseudoStack_->entries[sp].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
             fprintf(stderr, " entries=%p size=%u/%u\n",
                             (void*) pseudoStack_->entries,
                             uint32_t(pseudoStack_->stackPointer),
                             PseudoStack::MaxEntries);
             for (int32_t i = sp; i >= 0; i--) {
-                volatile ProfileEntry& entry = pseudoStack_->entries[i];
+                ProfileEntry& entry = pseudoStack_->entries[i];
                 if (entry.isJs())
                     fprintf(stderr, "  [%d] JS %s\n", i, entry.dynamicString());
                 else
                     fprintf(stderr, "  [%d] C line %d %s\n", i, entry.line(), entry.dynamicString());
             }
         }
 
-        volatile ProfileEntry& entry = pseudoStack_->entries[sp];
+        ProfileEntry& entry = pseudoStack_->entries[sp];
         MOZ_ASSERT(entry.isJs());
         MOZ_ASSERT(entry.script() == script);
         MOZ_ASSERT(strcmp((const char*) entry.dynamicString(), dynamicString) == 0);
     }
 #endif
 }
 
 /*
@@ -305,17 +305,17 @@ GeckoProfiler::allocProfileString(JSScri
     }
 
     MOZ_ASSERT(ret == len, "Computed length should match actual length!");
 
     return cstr;
 }
 
 void
-GeckoProfiler::trace(JSTracer* trc) volatile
+GeckoProfiler::trace(JSTracer* trc)
 {
     if (pseudoStack_) {
         size_t size = pseudoStack_->stackSize();
         for (size_t i = 0; i < size; i++)
             pseudoStack_->entries[i].trace(trc);
     }
 }
 
@@ -348,17 +348,17 @@ GeckoProfiler::checkStringsMapAfterMovin
         CheckGCThingAfterMovingGC(script);
         auto ptr = locked->lookup(script);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
     }
 }
 #endif
 
 void
-ProfileEntry::trace(JSTracer* trc) volatile
+ProfileEntry::trace(JSTracer* trc)
 {
     if (isJs()) {
         JSScript* s = rawScript();
         TraceNullableRoot(trc, &s, "ProfileEntry script");
         spOrScript = s;
     }
 }
 
@@ -435,38 +435,38 @@ GeckoProfilerBaselineOSRMarker::GeckoPro
         profiler = nullptr;
         return;
     }
 
     spBefore_ = sp;
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
+    ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_NORMAL);
     entry.setKind(ProfileEntry::Kind::JS_OSR);
 }
 
 GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
 {
     if (profiler == nullptr)
         return;
 
     uint32_t sp = profiler->stackPointer();
     MOZ_ASSERT(spBefore_ == sp);
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->stack()[sp - 1];
+    ProfileEntry& entry = profiler->stack()[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_OSR);
     entry.setKind(ProfileEntry::Kind::JS_NORMAL);
 }
 
 JS_PUBLIC_API(JSScript*)
-ProfileEntry::script() const volatile
+ProfileEntry::script() const
 {
     MOZ_ASSERT(isJs());
     auto script = reinterpret_cast<JSScript*>(spOrScript);
     if (!script)
         return nullptr;
 
     // If profiling is supressed then we can't trust the script pointers to be
     // valid as they could be in the process of being moved by a compacting GC
@@ -479,33 +479,33 @@ ProfileEntry::script() const volatile
     if (!cx->isProfilerSamplingEnabled())
         return nullptr;
 
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
-ProfileEntry::pc() const volatile
+ProfileEntry::pc() const
 {
     MOZ_ASSERT(isJs());
     if (lineOrPcOffset == NullPCOffset)
         return nullptr;
 
     JSScript* script = this->script();
     return script ? script->offsetToPC(lineOrPcOffset) : nullptr;
 }
 
 /* static */ int32_t
 ProfileEntry::pcToOffset(JSScript* aScript, jsbytecode* aPc) {
     return aPc ? aScript->pcToOffset(aPc) : NullPCOffset;
 }
 
 void
-ProfileEntry::setPC(jsbytecode* pc) volatile
+ProfileEntry::setPC(jsbytecode* pc)
 {
     MOZ_ASSERT(isJs());
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
     lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -137,17 +137,17 @@ class GeckoProfiler
     UniqueChars allocProfileString(JSScript* script, JSFunction* function);
 
   public:
     explicit GeckoProfiler(JSRuntime* rt);
 
     bool init();
 
     uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
-    volatile ProfileEntry* stack() { return pseudoStack_->entries; }
+    ProfileEntry* stack() { return pseudoStack_->entries; }
 
     /* management of whether instrumentation is on or off */
     bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return pseudoStack_ != nullptr; }
     MOZ_MUST_USE bool enable(bool enabled);
     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     bool slowAssertionsEnabled() { return slowAssertions; }
 
@@ -184,17 +184,17 @@ class GeckoProfiler
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount();
     void stringsReset();
 
     uint32_t* addressOfEnabled() {
         return &enabled_;
     }
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
 inline size_t
 GeckoProfiler::stringsCount()
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -719,18 +719,17 @@ AddDynamicCodeLocationTag(ProfileBuffer*
 
     // Cast to *((void**) to pass the text data to a void*.
     aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static void
 AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
-               volatile js::ProfileEntry& entry,
-               NotNull<RacyThreadInfo*> aRacyInfo)
+               js::ProfileEntry& entry, NotNull<RacyThreadInfo*> aRacyInfo)
 {
   MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
              entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
 
   int lineno = -1;
 
   // First entry has kind CodeLocation.
   const char* label = entry.label();
@@ -822,17 +821,17 @@ struct AutoWalkJSStack
   }
 };
 
 static void
 MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
                        const TickSample& aSample, NativeStack& aNativeStack)
 {
   NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo;
-  volatile js::ProfileEntry* pseudoEntries = racyInfo->entries;
+  js::ProfileEntry* pseudoEntries = racyInfo->entries;
   uint32_t pseudoCount = racyInfo->stackSize();
   JSContext* context = aSample.mJSContext;
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
@@ -897,17 +896,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
   // Iterate as long as there is at least one frame remaining.
   while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
     // There are 1 to 3 frames available. Find and add the oldest.
     uint8_t* pseudoStackAddr = nullptr;
     uint8_t* jsStackAddr = nullptr;
     uint8_t* nativeStackAddr = nullptr;
 
     if (pseudoIndex != pseudoCount) {
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       if (pseudoEntry.isCpp()) {
         lastPseudoCppStackAddr = (uint8_t*) pseudoEntry.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 pseudoframe and jit frame being recorded (and
@@ -947,17 +946,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
     MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       // Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
       // should not be recorded in the profile.
       if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
         AddPseudoEntry(aLock, aBuffer, pseudoEntry, racyInfo);
       }
       pseudoIndex++;
       continue;
@@ -1095,17 +1094,17 @@ DoNativeBacktrace(PSLockRef aLock, Profi
 
   // The pseudostack contains an "EnterJIT" frame whenever we enter
   // JIT code with profiling enabled; the stack pointer value points
   // the saved registers.  We use this to unwind resume unwinding
   // after encounting JIT code.
   for (uint32_t i = racyInfo->stackSize(); i > 0; --i) {
     // The pseudostack grows towards higher indices, so we iterate
     // backwards (from callee to caller).
-    volatile js::ProfileEntry& entry = racyInfo->entries[i - 1];
+    js::ProfileEntry& entry = racyInfo->entries[i - 1];
     if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
       // Found JIT entry frame.  Unwind up to that point (i.e., force
       // the stack walk to stop before the block of saved registers;
       // note that it yields nondecreasing stack pointers), then restore
       // the saved state.
       uint32_t* vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
 
       nativeStack.count += EHABIStackWalk(*mcontext,
@@ -2852,17 +2851,17 @@ profiler_get_backtrace_noalloc(char *out
 
   PseudoStack* pseudoStack = TLSInfo::Stack();
   if (!pseudoStack) {
     return;
   }
 
   bool includeDynamicString = !ActivePS::FeaturePrivacy(lock);
 
-  volatile js::ProfileEntry* pseudoEntries = pseudoStack->entries;
+  js::ProfileEntry* pseudoEntries = pseudoStack->entries;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   for (uint32_t i = 0; i < pseudoCount; i++) {
     const char* label = pseudoEntries[i].label();
     const char* dynamicString =
       includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr;
     size_t labelLength = strlen(label);
     if (dynamicString) {
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -400,17 +400,17 @@ GetPathAfterComponent(const char* filena
     next = strstr(found - 1, component);
   }
   return found;
 }
 
 } // namespace
 
 const char*
-ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+ThreadStackHelper::AppendJSEntry(const js::ProfileEntry* aEntry,
                                  intptr_t& aAvailableBufferSize,
                                  const char* aPrevLabel)
 {
   // May be called from another thread or inside a signal handler.
   // We assume querying the script is safe but we must not manupulate it.
   // Also we must not allocate any memory from heap.
   MOZ_ASSERT(aEntry->isJs());
 
@@ -487,18 +487,18 @@ ThreadStackHelper::FillStackBuffer()
   MOZ_ASSERT(mStackToFill->empty());
 
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
   size_t reservedSize = mStackToFill->capacity();
   size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
   intptr_t availableBufferSize = intptr_t(reservedBufferSize);
 
   // Go from front to back
-  const volatile js::ProfileEntry* entry = mPseudoStack->entries;
-  const volatile js::ProfileEntry* end = entry + mPseudoStack->stackSize();
+  const js::ProfileEntry* entry = mPseudoStack->entries;
+  const js::ProfileEntry* end = entry + mPseudoStack->stackSize();
   // Deduplicate identical, consecutive frames
   const char* prevLabel = nullptr;
   for (; reservedSize-- && entry != end; entry++) {
     if (entry->isJs()) {
       prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
       continue;
     }
     const char* const label = entry->label();
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -72,17 +72,17 @@ private:
   const PseudoStack* const mPseudoStack;
   size_t mMaxStackSize;
   size_t mMaxBufferSize;
 #endif
 
   bool PrepareStackBuffer(Stack& aStack);
   void FillStackBuffer();
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
-  const char* AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+  const char* AppendJSEntry(const js::ProfileEntry* aEntry,
                             intptr_t& aAvailableBufferSize,
                             const char* aPrevLabel);
 #endif
 
 public:
   /**
    * Initialize ThreadStackHelper. Must be called from main thread.
    */