Bug 1004726 - Clean up profiler pseudo-stack representation. r=djvj,mrosenberg
authorVictor Porof <vporof@mozilla.com>
Tue, 20 May 2014 15:25:25 -0400
changeset 184182 c2429d6c41fb3b7598136535f4c6fc9428a15e09
parent 184181 54b6736975ed9f3aef9843f40e87d2f1eaec7b69
child 184183 ea2ef5b9ee02f7ea475b95e5c428be8177fc1772
push id7199
push usercbook@mozilla.com
push dateWed, 21 May 2014 12:32:32 +0000
treeherderb2g-inbound@42d5f49f4657 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj, mrosenberg
bugs1004726
milestone32.0a1
Bug 1004726 - Clean up profiler pseudo-stack representation. r=djvj,mrosenberg
CLOBBER
js/public/ProfilingStack.h
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/MacroAssembler-x86.h
js/src/vm/SPSProfiler.cpp
js/src/vm/SPSProfiler.h
tools/profiler/BreakpadSampler.cpp
tools/profiler/PseudoStack.h
tools/profiler/TableTicker.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 994964 apparently requires a clobber, unclear why
+Bug 1004726 requires a clobber for B2G Emulator builds
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -3,17 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ProfilingStack_h
 #define js_ProfilingStack_h
 
 #include "mozilla/NullPtr.h"
- 
+
 #include "jsbytecode.h"
 #include "jstypes.h"
 
 #include "js/Utility.h"
 
 struct JSRuntime;
 
 namespace js {
@@ -29,77 +29,107 @@ class ProfileEntry
     // 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. Both use
-    // the string as a description, but JS uses the sp as nullptr or (void*)1 to
-    // indicate that it is a JS entry. The script_ is then only ever examined for
-    // a JS entry, and the idx is used by both, but with different meanings.
-    //
-    const char * volatile string; // Descriptive string of this entry
-    void * volatile sp;           // Relevant stack pointer for the entry,
-                                  // less than or equal to SCRIPT_OPT_STACKPOINTER for js
-                                  // script entries, greater for non-js entries.
-    JSScript * volatile script_;  // if js(), non-null script which is running - low bit
-                                  // indicates if script is optimized or not.
-    int32_t volatile idx;         // if js(), idx of pc, otherwise line number
+    // A ProfileEntry represents both a C++ profile entry and a JS one.
+
+    // Descriptive string of this entry.
+    const char * volatile string;
+
+    // Stack pointer for non-JS entries, the script pointer otherwise.
+    void * volatile spOrScript;
+
+    // Line number for non-JS entries, the bytecode offset otherwise.
+    int32_t volatile lineOrPc;
+
+    // General purpose storage describing this frame.
+    uint32_t volatile flags;
 
   public:
-    static const uintptr_t SCRIPT_OPT_STACKPOINTER = 0x1;
+    ProfileEntry(void) : flags(0) {}
+
+    // These traits are bit masks. Make sure they're powers of 2.
+    enum Flags {
+        // Indicate whether a profile entry represents a CPP frame. If not set,
+        // a JS frame is assumed by default. You're not allowed to publicly
+        // change the frame type. Instead, call `setJsFrame` or `setCppFrame`.
+        IS_CPP_ENTRY = 0x01,
+
+        // Indicate that copying the frame label is not necessary when taking a
+        // sample of the pseudostack.
+        FRAME_LABEL_COPY = 0x02
+    };
 
     // All of these methods are marked with the 'volatile' keyword because SPS'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 js() const volatile {
-        MOZ_ASSERT_IF(uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER, script_ != nullptr);
-        return uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER;
+    bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); }
+    bool isJs() const volatile { return !isCpp(); }
+
+    bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); };
+
+    void setLabel(const char *aString) volatile { string = aString; }
+    const char *label() const volatile { return string; }
+
+    void setJsFrame(JSScript *aScript, jsbytecode *aPc) volatile {
+        flags &= ~IS_CPP_ENTRY;
+        spOrScript = aScript;
+        setPC(aPc);
+    }
+    void setCppFrame(void *aSp, uint32_t aLine) volatile {
+        flags |= IS_CPP_ENTRY;
+        spOrScript = aSp;
+        lineOrPc = aLine;
     }
 
-    uint32_t line() const volatile { MOZ_ASSERT(!js()); return idx; }
-    JSScript *script() const volatile { MOZ_ASSERT(js()); return script_; }
-    bool scriptIsOptimized() const volatile {
-        MOZ_ASSERT(js());
-        return uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER;
+    void setFlag(Flags flag) volatile {
+        MOZ_ASSERT(flag != IS_CPP_ENTRY);
+        flags |= flag;
     }
+    void unsetFlag(Flags flag) volatile {
+        MOZ_ASSERT(flag != IS_CPP_ENTRY);
+        flags &= ~flag;
+    }
+    bool hasFlag(Flags flag) const volatile {
+        return bool(flags & uint32_t(flag));
+    }
+
     void *stackAddress() const volatile {
-        if (js())
-            return nullptr;
-        return sp;
+        MOZ_ASSERT(!isJs());
+        return spOrScript;
     }
-    const char *label() const volatile { return string; }
-
-    void setLine(uint32_t aLine) volatile { MOZ_ASSERT(!js()); idx = aLine; }
-    void setLabel(const char *aString) volatile { string = aString; }
-    void setStackAddress(void *aSp) volatile { sp = aSp; }
-    void setScript(JSScript *aScript) volatile { script_ = aScript; }
+    JSScript *script() const volatile {
+        MOZ_ASSERT(isJs());
+        return (JSScript *)spOrScript;
+    }
+    uint32_t line() const volatile {
+        MOZ_ASSERT(!isJs());
+        return lineOrPc;
+    }
 
     // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
     JS_FRIEND_API(jsbytecode *) pc() const volatile;
     JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
 
-    static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
-    static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); }
-    static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); }
-    static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); }
+    // 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;
 
-    // The index used in the entry can either be a line number or the offset of
-    // a pc into a script's code. 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 NullPCIndex = -1;
-
-    // This bit is added to the stack address to indicate that copying the
-    // frame label is not necessary when taking a sample of the pseudostack.
-    static const uintptr_t NoCopyBit = 1;
+    static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); }
+    static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); }
+    static size_t offsetOfLineOrPc() { return offsetof(ProfileEntry, lineOrPc); }
+    static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags); }
 };
 
 JS_FRIEND_API(void)
 SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                          uint32_t max);
 
 JS_FRIEND_API(void)
 EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1919,20 +1919,20 @@ MacroAssembler::spsMarkJit(SPSProfiler *
     push(temp); // +4: Did we push an sps frame.
     branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled);
 
     Label stackFull;
     // We always need the "safe" versions, because these are used in trampolines
     // and won't be regenerated when SPS state changes.
     spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
-    storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfString()));
-    storePtr(framePtr,              Address(temp, ProfileEntry::offsetOfStackAddress()));
-    storePtr(ImmWord(uintptr_t(0)), Address(temp, ProfileEntry::offsetOfScript()));
-    store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));
+    storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel()));
+    storePtr(framePtr,              Address(temp, ProfileEntry::offsetOfSpOrScript()));
+    store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
+    store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags()));
 
     /* Always increment the stack size, whether or not we actually pushed. */
     bind(&stackFull);
     loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp);
     add32(Imm32(1), Address(temp, 0));
 
     bind(&spsNotEnabled);
 }
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1006,19 +1006,24 @@ class MacroAssembler : public MacroAssem
                                 Label *full)
     {
         movePtr(ImmPtr(p->sizePointer()), temp);
         load32(Address(temp, 0), temp);
         if (offset != 0)
             add32(Imm32(offset), temp);
         branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);
 
-        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
-        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
-        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
+        JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8);
+        if (sizeof(void *) == 4) {
+            lshiftPtr(Imm32(4), temp);
+        } else {
+            lshiftPtr(Imm32(3), temp);
+            mulBy3(temp, temp);
+        }
+
         addPtr(ImmPtr(p->stack()), temp);
     }
 
     // The safe version of the above method refrains from assuming that the fields
     // of the SPSProfiler class are going to stay the same across different runs of
     // the jitcode.  Ion can use the more efficient unsafe version because ion jitcode
     // will not survive changes to to the profiler settings.  Baseline jitcode, however,
     // can span these changes, so any hardcoded field values will be incorrect afterwards.
@@ -1032,80 +1037,83 @@ class MacroAssembler : public MacroAssem
         // Load size
         load32(Address(temp, 0), temp);
         if (offset != 0)
             add32(Imm32(offset), temp);
 
         // Test against max size.
         branch32(Assembler::LessThanOrEqual, AbsoluteAddress(p->addressOfMaxSize()), temp, full);
 
-        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
-        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
-        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
+        JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8);
+        if (sizeof(void *) == 4) {
+            lshiftPtr(Imm32(4), temp);
+        } else {
+            lshiftPtr(Imm32(3), temp);
+            mulBy3(temp, temp);
+        }
+
         push(temp);
         loadPtr(AbsoluteAddress(p->addressOfStack()), temp);
         addPtr(Address(StackPointer, 0), temp);
         addPtr(Imm32(sizeof(size_t)), StackPointer);
     }
 
   public:
     // These functions are needed by the IonInstrumentation interface defined in
     // vm/SPSProfiler.h.  They will modify the pseudostack provided to SPS to
     // perform the actual instrumentation.
 
     void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, -1, temp, &stackFull);
-        store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
+        store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfLineOrPc()));
         bind(&stackFull);
     }
 
     void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) {
         Label stackFull;
         spsProfileEntryAddressSafe(p, -1, temp, &stackFull);
-        store32(idx, Address(temp, ProfileEntry::offsetOfPCIdx()));
+        store32(idx, Address(temp, ProfileEntry::offsetOfLineOrPc()));
         bind(&stackFull);
     }
 
     // spsPushFrame variant for Ion-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
-        storePtr(ImmPtr(str),  Address(temp, ProfileEntry::offsetOfString()));
-        storePtr(ImmGCPtr(s),  Address(temp, ProfileEntry::offsetOfScript()));
-        storePtr(ImmPtr((void*) ProfileEntry::SCRIPT_OPT_STACKPOINTER),
-                 Address(temp, ProfileEntry::offsetOfStackAddress()));
-        store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));
+        storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel()));
+        storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript()));
+        store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
+        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->sizePointer()), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     // spsPushFrame variant for Baseline-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
                       Register temp, Register temp2)
     {
         Label stackFull;
         spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
         loadPtr(str, temp2);
-        storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));
+        storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel()));
 
         loadPtr(script, temp2);
-        storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));
-
-        storePtr(ImmPtr(nullptr), Address(temp, ProfileEntry::offsetOfStackAddress()));
+        storePtr(temp2, Address(temp, ProfileEntry::offsetOfSpOrScript()));
 
         // Store 0 for PCIdx because that's what interpreter does.
         // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
         //  with 0 pcIdx).
-        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));
+        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
+        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->addressOfSizePointer()), temp);
         loadPtr(Address(temp, 0), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1430,16 +1430,19 @@ class MacroAssemblerARMCompat : public M
     void addPtr(Imm32 imm, const Register dest);
     void addPtr(Imm32 imm, const Address &dest);
     void addPtr(ImmWord imm, const Register dest) {
         addPtr(Imm32(imm.value), dest);
     }
     void addPtr(ImmPtr imm, const Register dest) {
         addPtr(ImmWord(uintptr_t(imm.value)), dest);
     }
+    void mulBy3(const Register &src, const Register &dest) {
+        as_add(dest, src, lsl(src, 1));
+    }
 
     void setStackArg(Register reg, uint32_t arg);
 
     void breakpoint();
     // conditional breakpoint
     void breakpoint(Condition cc);
 
     void compareDouble(FloatRegister lhs, FloatRegister rhs);
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -567,16 +567,19 @@ class MacroAssemblerX64 : public MacroAs
         subq(src, dest);
     }
     void subPtr(const Address &addr, Register dest) {
         subq(Operand(addr), dest);
     }
     void subPtr(Register src, const Address &dest) {
         subq(src, Operand(dest));
     }
+    void mulBy3(const Register &src, const Register &dest) {
+        lea(Operand(src, src, TimesTwo), dest);
+    }
 
     void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label *label) {
         if (JSC::X86Assembler::isAddressImmediate(lhs.addr)) {
             branch32(cond, Operand(lhs), rhs, label);
         } else {
             mov(ImmPtr(lhs.addr), ScratchReg);
             branch32(cond, Address(ScratchReg, 0), rhs, label);
         }
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -576,16 +576,19 @@ class MacroAssemblerX86 : public MacroAs
         subl(src, dest);
     }
     void subPtr(const Address &addr, Register dest) {
         subl(Operand(addr), dest);
     }
     void subPtr(Register src, const Address &dest) {
         subl(src, Operand(dest));
     }
+    void mulBy3(const Register &src, const Register &dest) {
+        lea(Operand(src, src, TimesTwo), dest);
+    }
 
     void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label *label) {
         cmpl(Operand(lhs), rhs);
         j(cond, label);
     }
     void branch32(Condition cond, AbsoluteAddress lhs, Register rhs, Label *label) {
         cmpl(Operand(lhs), rhs);
         j(cond, label);
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -157,49 +157,49 @@ SPSProfiler::enter(JSScript *script, JSF
 
 #ifdef DEBUG
     // In debug builds, assert the JS pseudo frames already on the stack
     // have a non-null pc. Only look at the top frames to avoid quadratic
     // behavior.
     if (*size_ > 0 && *size_ - 1 < max_) {
         size_t start = (*size_ > 4) ? *size_ - 4 : 0;
         for (size_t i = start; i < *size_ - 1; i++)
-            MOZ_ASSERT_IF(stack_[i].js(), stack_[i].pc() != nullptr);
+            MOZ_ASSERT_IF(stack_[i].isJs(), stack_[i].pc() != nullptr);
     }
 #endif
 
-    push(str, nullptr, script, script->code());
+    push(str, nullptr, script, script->code(), /* copy = */ true);
     return true;
 }
 
 void
 SPSProfiler::exit(JSScript *script, JSFunction *maybeFun)
 {
     pop();
 
 #ifdef DEBUG
     /* Sanity check to make sure push/pop balanced */
     if (*size_ < max_) {
         const char *str = profileString(script, maybeFun);
         /* Can't fail lookup because we should already be in the set */
         JS_ASSERT(str != nullptr);
 
         // Bug 822041
-        if (!stack_[*size_].js()) {
+        if (!stack_[*size_].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
             fprintf(stderr, " stack=%p size=%d/%d\n", (void*) stack_, *size_, max_);
             for (int32_t i = *size_; i >= 0; i--) {
-                if (stack_[i].js())
+                if (stack_[i].isJs())
                     fprintf(stderr, "  [%d] JS %s\n", i, stack_[i].label());
                 else
                     fprintf(stderr, "  [%d] C line %d %s\n", i, stack_[i].line(), stack_[i].label());
             }
         }
 
-        JS_ASSERT(stack_[*size_].js());
+        JS_ASSERT(stack_[*size_].isJs());
         JS_ASSERT(stack_[*size_].script() == script);
         JS_ASSERT(strcmp((const char*) stack_[*size_].label(), str) == 0);
         stack_[*size_].setLabel(nullptr);
         stack_[*size_].setPC(nullptr);
     }
 #endif
 }
 
@@ -209,37 +209,47 @@ SPSProfiler::enterNative(const char *str
     /* these operations cannot be re-ordered, so volatile-ize operations */
     volatile ProfileEntry *stack = stack_;
     volatile uint32_t *size = size_;
     uint32_t current = *size;
 
     JS_ASSERT(enabled());
     if (current < max_) {
         stack[current].setLabel(string);
-        stack[current].setStackAddress(sp);
-        stack[current].setScript(nullptr);
-        stack[current].setLine(0);
+        stack[current].setCppFrame(sp, 0);
     }
     *size = current + 1;
 }
 
 void
-SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc)
+SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy)
 {
+    JS_ASSERT_IF(sp != nullptr, script == nullptr && pc == nullptr);
+    JS_ASSERT_IF(sp == nullptr, script != nullptr && pc != nullptr);
+
     /* these operations cannot be re-ordered, so volatile-ize operations */
     volatile ProfileEntry *stack = stack_;
     volatile uint32_t *size = size_;
     uint32_t current = *size;
 
     JS_ASSERT(installed());
     if (current < max_) {
-        stack[current].setLabel(string);
-        stack[current].setStackAddress(sp);
-        stack[current].setScript(script);
-        stack[current].setPC(pc);
+        volatile ProfileEntry &entry = stack[current];
+        entry.setLabel(string);
+
+        if (sp != nullptr)
+            entry.setCppFrame(sp, 0);
+        else
+            entry.setJsFrame(script, pc);
+
+        // Track if mLabel needs a copy.
+        if (copy)
+            entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
+        else
+            entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
     }
     *size = current + 1;
 }
 
 void
 SPSProfiler::pop()
 {
     JS_ASSERT(installed());
@@ -308,37 +318,38 @@ SPSEntryMarker::SPSEntryMarker(JSRuntime
     : profiler(&rt->spsProfiler)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler->installed()) {
         profiler = nullptr;
         return;
     }
     size_before = *profiler->size_;
-    profiler->pushNoCopy("js::RunScript", this, nullptr, nullptr);
+    profiler->push("js::RunScript", this, nullptr, nullptr, /* copy = */ false);
 }
-
 SPSEntryMarker::~SPSEntryMarker()
 {
     if (profiler != nullptr) {
         profiler->pop();
         JS_ASSERT(size_before == *profiler->size_);
     }
 }
 
 JS_FRIEND_API(jsbytecode*)
 ProfileEntry::pc() const volatile
 {
-    return idx == NullPCIndex ? nullptr : script()->offsetToPC(idx);
+    MOZ_ASSERT(isJs());
+    return lineOrPc == NullPCOffset ? nullptr : script()->offsetToPC(lineOrPc);
 }
 
 JS_FRIEND_API(void)
 ProfileEntry::setPC(jsbytecode *pc) volatile
 {
-    idx = pc == nullptr ? NullPCIndex : script()->pcToOffset(pc);
+    MOZ_ASSERT(isJs());
+    lineOrPc = pc == nullptr ? NullPCOffset : script()->pcToOffset(pc);
 }
 
 JS_FRIEND_API(void)
 js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max)
 {
     rt->spsProfiler.setProfilingStack(stack, size, max);
 }
 
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -123,23 +123,17 @@ class SPSProfiler
     uint32_t             *size_;
     uint32_t             max_;
     bool                 slowAssertions;
     uint32_t             enabled_;
     PRLock               *lock_;
     void                (*eventMarker_)(const char *);
 
     const char *allocProfileString(JSScript *script, JSFunction *function);
-    void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
-    void pushNoCopy(const char *string, void *sp,
-                    JSScript *script, jsbytecode *pc) {
-        push(string, reinterpret_cast<void*>(
-            reinterpret_cast<uintptr_t>(sp) | ProfileEntry::NoCopyBit),
-            script, pc);
-    }
+    void push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy);
     void pop();
 
   public:
     SPSProfiler(JSRuntime *rt);
     ~SPSProfiler();
 
     bool init();
 
@@ -432,17 +426,17 @@ class SPSInstrumentation
      */
     void pushManual(JSScript *script, Assembler &masm, Register scratch,
                     bool inlinedFunction = false)
     {
         if (!enabled())
             return;
 
         if (!inlinedFunction)
-            masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
+            masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch);
 
         setPushed(script);
     }
 
     /*
      * Signals that the current function is leaving for a function call. This
      * can happen both on JS function calls and also calls to C++. This
      * internally manages how many leave() calls have been seen, and only the
@@ -473,18 +467,18 @@ class SPSInstrumentation
      * state with leave() to only emit instrumentation at proper times.
      */
     void reenter(Assembler &masm, Register scratch, bool inlinedFunction = false) {
         if (!enabled() || !frame->script || frame->left-- != 1)
             return;
         if (frame->skipNext) {
             frame->skipNext = false;
         } else {
-             if (!inlinedFunction)
-                 masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
+            if (!inlinedFunction)
+                masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch);
         }
     }
 
     /*
      * Signifies exiting a JS frame, popping the SPS entry. Because there can be
      * multiple return sites of a function, this does not cease instrumentation
      * emission.
      */
--- a/tools/profiler/BreakpadSampler.cpp
+++ b/tools/profiler/BreakpadSampler.cpp
@@ -92,17 +92,17 @@ void genProfileEntry(/*MODIFIED*/Unwinde
       char text[sizeof(void*)];
       for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
         text[pos] = sampleLabel[j+pos];
       }
       j += sizeof(void*)/sizeof(char);
       // Cast to *((void**) to pass the text data to a void*
       utb__addEntry( utb, ProfileEntry('d', *((void**)(&text[0]))) );
     }
-    if (entry.js()) {
+    if (entry.isJs()) {
       if (!entry.pc()) {
         // The JIT only allows the top-most entry to have a nullptr pc
         MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
         // If stack-walking was disabled, then that's just unfortunate
         if (lastpc) {
           jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                 lastpc);
           if (jspc) {
@@ -222,17 +222,17 @@ void populateBuffer(UnwinderThreadBuffer
       utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
       genPseudoBacktraceEntries(utb, stack, sample);
       break;
     case UnwINVALID:
     default:
       MOZ_CRASH();
   }
 
-  if (recordSample) {    
+  if (recordSample) {
     // add a "flush now" hint
     utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) );
   }
 
   // Add any extras
   if (!sLastTracerEvent.IsNull() && sample) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     utb__addEntry( utb, ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())) );
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -80,36 +80,16 @@ static inline uint32_t sMin(uint32_t l, 
 // walking is not available on the platform we are running on.
 //
 // Each entry has a descriptive string, a relevant stack address, and some extra
 // information the JS engine might want to inform SPS of. This class inherits
 // from the JS engine's version of the entry to ensure that the size and layout
 // of the two representations are consistent.
 class StackEntry : public js::ProfileEntry
 {
-public:
-
-  bool isCopyLabel() const volatile {
-    return !((uintptr_t)stackAddress() & 0x1);
-  }
-
-  void setStackAddressCopy(void *sparg, bool copy) volatile {
-    // Tagged pointer. Less significant bit used to track if mLabel needs a
-    // copy. Note that we don't need the last bit of the stack address for
-    // proper ordering. This is optimized for encoding within the JS engine's
-    // instrumentation, so we do the extra work here of encoding a bit.
-    // Last bit 1 = Don't copy, Last bit 0 = Copy.
-    if (copy) {
-      setStackAddress(reinterpret_cast<void*>(
-                        reinterpret_cast<uintptr_t>(sparg) & ~NoCopyBit));
-    } else {
-      setStackAddress(reinterpret_cast<void*>(
-                        reinterpret_cast<uintptr_t>(sparg) | NoCopyBit));
-    }
-  }
 };
 
 class ProfilerMarkerPayload;
 template<typename T>
 class ProfilerLinkedList;
 class JSStreamWriter;
 class JSCustomArray;
 class ThreadProfile;
@@ -358,33 +338,35 @@ public:
   }
 
   // called within signal. Function must be reentrant
   ProfilerMarkerLinkedList* getPendingMarkers()
   {
     return mPendingMarkers.getPendingMarkers();
   }
 
-  void push(const char *aName, uint32_t line)
-  {
-    push(aName, nullptr, false, line);
-  }
-
-  void push(const char *aName, void *aStackAddress, bool aCopy, uint32_t line)
+  void push(const char *aName, void *aStackAddress, uint32_t line, bool aCopy)
   {
     if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
       mStackPointer++;
       return;
     }
 
+    volatile StackEntry &entry = mStack[mStackPointer];
+
     // Make sure we increment the pointer after the name has
     // been written such that mStack is always consistent.
-    mStack[mStackPointer].setLabel(aName);
-    mStack[mStackPointer].setStackAddressCopy(aStackAddress, aCopy);
-    mStack[mStackPointer].setLine(line);
+    entry.setLabel(aName);
+    entry.setCppFrame(aStackAddress, line);
+
+    // Track if mLabel needs a copy.
+    if (aCopy)
+      entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
+    else
+      entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
 
     // Prevent the optimizer from re-ordering these instructions
     STORE_SEQUENCER();
     mStackPointer++;
   }
   void pop()
   {
     mStackPointer--;
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -342,17 +342,17 @@ void addProfileEntry(volatile StackEntry
   // First entry has tagName 's' (start)
   // Check for magic pointer bit 1 to indicate copy
   const char* sampleLabel = entry.label();
   if (entry.isCopyLabel()) {
     // Store the string using 1 or more 'd' (dynamic) tags
     // that will happen to the preceding tag
 
     addDynamicTag(aProfile, 'c', sampleLabel);
-    if (entry.js()) {
+    if (entry.isJs()) {
       if (!entry.pc()) {
         // The JIT only allows the top-most entry to have a nullptr pc
         MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
         // If stack-walking was disabled, then that's just unfortunate
         if (lastpc) {
           jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                 lastpc);
           if (jspc) {
@@ -362,17 +362,22 @@ void addProfileEntry(volatile StackEntry
       } else {
         lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
       }
     } else {
       lineno = entry.line();
     }
   } else {
     aProfile.addTag(ProfileEntry('c', sampleLabel));
-    lineno = entry.line();
+
+    // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
+    // line for js entries as well.
+    if (entry.isCpp()) {
+      lineno = entry.line();
+    }
   }
   if (lineno != -1) {
     aProfile.addTag(ProfileEntry('n', lineno));
   }
 }
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
 typedef struct {
@@ -502,17 +507,17 @@ void TableTicker::doNativeBacktrace(Thre
   // 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 = pseudoStack->stackSize(); i > 0; --i) {
     // The pseudostack grows towards higher indices, so we iterate
     // backwards (from callee to caller).
     volatile StackEntry &entry = pseudoStack->mStack[i - 1];
-    if (!entry.js() && strcmp(entry.label(), "EnterJIT") == 0) {
+    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());
 
       array.count += EHABIStackWalk(*mcontext,
                                     /* stackBase = */ vSP,