Back out 4 changesets (bug 1189490) for Linux32 Jit2 failures
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 08 Aug 2015 23:30:50 -0700
changeset 288637 af32792fe260da69f7b2db7d061c2278a0f4e8df
parent 288636 86bd23d0e40387fc2190d55ca01af84ae8ea6f77
child 288638 f34fe55e4b75e95d3a6c417eda0822c1d1a8cc4d
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1189490
milestone42.0a1
backs out1ec4867e4e8c0c5acb59a8d8d9b1f85b1b6def90
8bf626d3a647f94a8512c831f7f634e8c98acee1
1a1ad27321de3383031cc3fdf38d042ada6c34ee
858b4f682c5a5367ee10d001c27badb1de887373
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
Back out 4 changesets (bug 1189490) for Linux32 Jit2 failures Backed out changeset 1ec4867e4e8c (bug 1189490) Backed out changeset 8bf626d3a647 (bug 1189490) Backed out changeset 1a1ad27321de (bug 1189490) Backed out changeset 858b4f682c5a (bug 1189490)
js/src/ds/Fifo.h
js/src/ds/TraceableFifo.h
js/src/jsapi-tests/testGCExactRooting.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/DebuggerMemory.cpp
deleted file mode 100644
--- a/js/src/ds/Fifo.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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_Fifo_h
-#define js_Fifo_h
-
-#include "mozilla/Move.h"
-
-#include "js/Vector.h"
-
-namespace js {
-
-// A first-in-first-out queue container type. Fifo calls constructors and
-// destructors of all elements added so non-PODs may be used safely. |Fifo|
-// stores the first |MinInlineCapacity| elements in-place before resorting to
-// dynamic allocation.
-//
-// T requirements:
-//  - Either movable or copyable.
-// MinInlineCapacity requirements:
-//  - Must be even.
-// AllocPolicy:
-//  - see "Allocation policies" in AllocPolicy.h
-template <typename T,
-          size_t MinInlineCapacity = 0,
-          class AllocPolicy = TempAllocPolicy>
-class Fifo
-{
-    static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!");
-
-  protected:
-    // An element A is "younger" than an element B if B was inserted into the
-    // |Fifo| before A was.
-    //
-    // Invariant 1: Every element within |front_| is younger than every element
-    // within |rear_|.
-    // Invariant 2: Entries within |front_| are sorted from younger to older.
-    // Invariant 3: Entries within |rear_| are sorted from older to younger.
-    // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty.
-    Vector<T, MinInlineCapacity / 2, AllocPolicy> front_;
-    Vector<T, MinInlineCapacity / 2, AllocPolicy> rear_;
-
-  private:
-    // Maintain invariants after adding or removing entries.
-    bool fixup() {
-        if (!front_.empty())
-            return true;
-
-        if (!front_.reserve(rear_.length()))
-            return false;
-
-        while (!rear_.empty()) {
-            front_.infallibleAppend(mozilla::Move(rear_.back()));
-            rear_.popBack();
-        }
-
-        return true;
-    }
-
-  public:
-    explicit Fifo(AllocPolicy alloc = AllocPolicy())
-        : front_(alloc)
-        , rear_(alloc)
-    { }
-
-    Fifo(Fifo&& rhs)
-        : front_(mozilla::Move(rhs.front_))
-        , rear_(mozilla::Move(rhs.rear_))
-    { }
-
-    Fifo& operator=(Fifo&& rhs) {
-        MOZ_ASSERT(&rhs != this, "self-move disallowed");
-        this->~Fifo();
-        new (this) Fifo(mozilla::Move(rhs));
-        return *this;
-    }
-
-    Fifo(const Fifo&) = delete;
-    Fifo& operator=(const Fifo&) = delete;
-
-    size_t length() const {
-        MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4.
-        return front_.length() + rear_.length();
-    }
-
-    bool empty() const {
-        MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4.
-        return front_.empty();
-    }
-
-    // Push an element to the back of the queue. This method can take either a
-    // |const T&| or a |T&&|.
-    template <typename U>
-    bool pushBack(U&& u) {
-        if (!rear_.append(mozilla::Forward<U>(u)))
-            return false;
-        if (!fixup()) {
-            rear_.popBack();
-            return false;
-        }
-        return true;
-    }
-
-    // Construct a T in-place at the back of the queue.
-    template <typename... Args>
-    bool emplaceBack(Args&&... args) {
-        if (!rear_.emplaceBack(mozilla::Forward<Args>(args)...))
-            return false;
-        if (!fixup()) {
-            rear_.popBack();
-            return false;
-        }
-        return true;
-    }
-
-    // Access the element at the front of the queue.
-    T& front() {
-        MOZ_ASSERT(!empty());
-        return front_.back();
-    }
-    const T& front() const {
-        MOZ_ASSERT(!empty());
-        return front_.back();
-    }
-
-    // Remove the front element from the queue.
-    bool popFront() {
-        MOZ_ASSERT(!empty());
-        T t(mozilla::Move(front()));
-        front_.popBack();
-        if (!fixup()) {
-            // Attempt to remain in a valid state by reinserting the element
-            // back at the front. If we can't remain in a valid state in the
-            // face of OOMs, crash.
-            if (!front_.append(mozilla::Move(t)))
-                CrashAtUnhandlableOOM("js::Fifo::popFront");
-            return false;
-        }
-        return true;
-    }
-
-    // Clear all elements from the queue.
-    void clear() {
-        front_.clear();
-        rear_.clear();
-    }
-};
-
-} // namespace js
-
-#endif /* js_Fifo_h */
deleted file mode 100644
--- a/js/src/ds/TraceableFifo.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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_TraceableFifo_h
-#define js_TraceableFifo_h
-
-#include "ds/Fifo.h"
-#include "js/RootingAPI.h"
-
-namespace js {
-
-template <typename, typename> struct DefaultTracer;
-
-// A TraceableFifo is a Fifo with an additional trace method that knows how to
-// visit all of the items stored in the Fifo. For Fifos that contain GC things,
-// this is usually more convenient than manually iterating and marking the
-// contents.
-//
-// Most types of GC pointers as keys and values can be traced with no extra
-// infrastructure. For structs and non-gc-pointer members, ensure that there is
-// a specialization of DefaultTracer<T> with an appropriate trace method
-// available to handle the custom type.
-//
-// Note that although this Fifo's trace will deal correctly with moved items, it
-// does not itself know when to barrier or trace items. To function properly it
-// must either be used with Rooted, or barriered and traced manually.
-template <typename T,
-          size_t MinInlineCapacity = 0,
-          typename AllocPolicy = TempAllocPolicy,
-          typename TraceFunc = DefaultTracer<T>>
-class TraceableFifo
-  : public js::Fifo<T, MinInlineCapacity, AllocPolicy>,
-    public JS::Traceable
-{
-    using Base = js::Fifo<T, MinInlineCapacity, AllocPolicy>;
-
-  public:
-    explicit TraceableFifo(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {}
-
-    TraceableFifo(TraceableFifo&& rhs) : Base(mozilla::Move(rhs)) { }
-    TraceableFifo& operator=(TraceableFifo&& rhs) { return Base::operator=(mozilla::Move(rhs)); }
-
-    TraceableFifo(const TraceableFifo&) = delete;
-    TraceableFifo& operator=(const TraceableFifo&) = delete;
-
-    static void trace(TraceableFifo* tf, JSTracer* trc) {
-        for (size_t i = 0; i < tf->front_.length(); ++i)
-            TraceFunc::trace(trc, &tf->front_[i], "fifo element");
-        for (size_t i = 0; i < tf->rear_.length(); ++i)
-            TraceFunc::trace(trc, &tf->rear_[i], "fifo element");
-    }
-};
-
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
-class TraceableFifoOperations
-{
-    using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
-    const TF& fifo() const { return static_cast<const Outer*>(this)->extract(); }
-
-  public:
-    size_t length() const { return fifo().length(); }
-    bool empty() const { return fifo().empty(); }
-    const T& front() const { return fifo().front(); }
-};
-
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
-class MutableTraceableFifoOperations
-  : public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy, TraceFunc>
-{
-    using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
-    TF& fifo() { return static_cast<Outer*>(this)->extract(); }
-
-  public:
-    T& front() { return fifo().front(); }
-
-    template<typename U> bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward<U>(u)); }
-    template<typename... Args> bool emplaceBack(Args&&... args) {
-        return fifo().emplaceBack(mozilla::Forward<Args...>(args...));
-    }
-
-    bool popFront() { return fifo().popFront(); }
-    void clear() { fifo().clear(); }
-};
-
-template <typename A, size_t B, typename C, typename D>
-class RootedBase<TraceableFifo<A,B,C,D>>
-  : public MutableTraceableFifoOperations<JS::Rooted<TraceableFifo<A,B,C,D>>, A,B,C,D>
-{
-    using TF = TraceableFifo<A,B,C,D>;
-
-    friend class TraceableFifoOperations<JS::Rooted<TF>, A,B,C,D>;
-    const TF& extract() const { return *static_cast<const JS::Rooted<TF>*>(this)->address(); }
-
-    friend class MutableTraceableFifoOperations<JS::Rooted<TF>, A,B,C,D>;
-    TF& extract() { return *static_cast<JS::Rooted<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C, typename D>
-class MutableHandleBase<TraceableFifo<A,B,C,D>>
-  : public MutableTraceableFifoOperations<JS::MutableHandle<TraceableFifo<A,B,C,D>>, A,B,C,D>
-{
-    using TF = TraceableFifo<A,B,C,D>;
-
-    friend class TraceableFifoOperations<JS::MutableHandle<TF>, A,B,C,D>;
-    const TF& extract() const {
-        return *static_cast<const JS::MutableHandle<TF>*>(this)->address();
-    }
-
-    friend class MutableTraceableFifoOperations<JS::MutableHandle<TF>, A,B,C,D>;
-    TF& extract() { return *static_cast<JS::MutableHandle<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C, typename D>
-class HandleBase<TraceableFifo<A,B,C,D>>
-  : public TraceableFifoOperations<JS::Handle<TraceableFifo<A,B,C,D>>, A,B,C,D>
-{
-    using TF = TraceableFifo<A,B,C,D>;
-
-    friend class TraceableFifoOperations<JS::Handle<TF>, A,B,C,D>;
-    const TF& extract() const {
-        return *static_cast<const JS::Handle<TF>*>(this)->address();
-    }
-};
-
-} // namespace js
-
-#endif // js_TraceableFifo_h
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* 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/. */
 
-#include "ds/TraceableFifo.h"
 #include "js/RootingAPI.h"
 #include "js/TraceableHashTable.h"
 #include "js/TraceableVector.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testGCExactRooting)
 {
@@ -272,57 +271,16 @@ receiveMutableHandleToShapeVector(JS::Mu
     // Ensure range enumeration works through the handle.
     for (auto shape : handle) {
         CHECK(shape);
     }
     return true;
 }
 END_TEST(testGCRootedVector)
 
-BEGIN_TEST(testTraceableFifo)
-{
-    using ShapeFifo = TraceableFifo<Shape*>;
-    JS::Rooted<ShapeFifo> shapes(cx, ShapeFifo(cx));
-    CHECK(shapes.empty());
-
-    for (size_t i = 0; i < 10; ++i) {
-        RootedObject obj(cx, JS_NewObject(cx, nullptr));
-        RootedValue val(cx, UndefinedValue());
-        // Construct a unique property name to ensure that the object creates a
-        // new shape.
-        char buffer[2];
-        buffer[0] = 'a' + i;
-        buffer[1] = '\0';
-        CHECK(JS_SetProperty(cx, obj, buffer, val));
-        CHECK(shapes.pushBack(obj->as<NativeObject>().lastProperty()));
-    }
-
-    CHECK(shapes.length() == 10);
-
-    JS_GC(rt);
-    JS_GC(rt);
-
-    for (size_t i = 0; i < 10; ++i) {
-        // Check the shape to ensure it did not get collected.
-        char buffer[2];
-        buffer[0] = 'a' + i;
-        buffer[1] = '\0';
-        bool match;
-        CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match));
-        CHECK(match);
-        CHECK(shapes.popFront());
-    }
-
-    CHECK(shapes.empty());
-    return true;
-}
-END_TEST(testTraceableFifo)
-
-using ShapeVec = TraceableVector<Shape*>;
-
 static bool
 FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes)
 {
     for (size_t i = 0; i < 10; ++i) {
         RootedObject obj(cx, JS_NewObject(cx, nullptr));
         RootedValue val(cx, UndefinedValue());
         // Construct a unique property name to ensure that the object creates a
         // new shape.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -352,25 +352,25 @@ Breakpoint::nextInSite()
 
 
 /*** Debugger hook dispatch **********************************************************************/
 
 Debugger::Debugger(JSContext* cx, NativeObject* dbg)
   : object(dbg),
     uncaughtExceptionHook(nullptr),
     enabled(true),
-    allowUnobservedAsmJS(false),
     observedGCs(cx),
-    tenurePromotionsLog(cx),
     trackingTenurePromotions(false),
+    tenurePromotionsLogLength(0),
     maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH),
     tenurePromotionsLogOverflowed(false),
-    allocationsLog(cx),
+    allowUnobservedAsmJS(false),
     trackingAllocationSites(false),
     allocationSamplingProbability(1.0),
+    allocationsLogLength(0),
     maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
     allocationsLogOverflowed(false),
     frames(cx->runtime()),
     scripts(cx),
     sources(cx),
     objects(cx),
     environments(cx),
 #ifdef NIGHTLY_BUILD
@@ -385,18 +385,18 @@ Debugger::Debugger(JSContext* cx, Native
     cx->runtime()->debuggerList.insertBack(this);
     JS_INIT_CLIST(&breakpoints);
     JS_INIT_CLIST(&onNewGlobalObjectWatchersLink);
 }
 
 Debugger::~Debugger()
 {
     MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
-    allocationsLog.clear();
-    tenurePromotionsLog.clear();
+    emptyAllocationsLog();
+    emptyTenurePromotionsLog();
 
     /*
      * Since the inactive state for this link is a singleton cycle, it's always
      * safe to apply JS_REMOVE_LINK to it, regardless of whether we're in the list or not.
      *
      * We don't have to worry about locking here since Debugger is not
      * background finalized.
      */
@@ -1696,74 +1696,105 @@ Debugger::slowPathOnIonCompilation(JSCon
 
 bool
 Debugger::isDebuggee(const JSCompartment* compartment) const
 {
     MOZ_ASSERT(compartment);
     return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
 }
 
-Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when)
+Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when)
   : className(obj.getClass()->name),
     when(when),
     frame(getObjectAllocationSite(obj)),
     size(JS::ubi::Node(&obj).size(rt->debuggerMallocSizeOf))
 { }
 
 
 void
 Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when)
 {
-    if (!tenurePromotionsLog.emplaceBack(rt, obj, when))
+    auto* entry = js_new<TenurePromotionsEntry>(rt, obj, when);
+    if (!entry)
         CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
 
-    if (tenurePromotionsLog.length() > maxTenurePromotionsLogLength) {
-        if (!tenurePromotionsLog.popFront())
-            CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
-        MOZ_ASSERT(tenurePromotionsLog.length() == maxTenurePromotionsLogLength);
+    tenurePromotionsLog.insertBack(entry);
+    if (tenurePromotionsLogLength >= maxTenurePromotionsLogLength) {
+        js_delete(tenurePromotionsLog.popFirst());
         tenurePromotionsLogOverflowed = true;
-    }
-}
+    } else {
+        tenurePromotionsLogLength++;
+    }
+}
+
+/* static */ Debugger::AllocationSite*
+Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, double when, HandleObject obj)
+{
+    assertSameCompartment(cx, frame);
+
+    RootedAtom ctorName(cx);
+    {
+        AutoCompartment ac(cx, obj);
+        if (!obj->constructorDisplayAtom(cx, &ctorName))
+            return nullptr;
+    }
+
+    AllocationSite* allocSite = cx->new_<AllocationSite>(frame, when);
+    if (!allocSite)
+        return nullptr;
+
+    allocSite->className = obj->getClass()->name;
+    allocSite->ctorName = ctorName.get();
+    allocSite->size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
+
+    return allocSite;
+}
+
 
 bool
 Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                                double when)
 {
     MOZ_ASSERT(trackingAllocationSites);
 
     AutoCompartment ac(cx, object);
     RootedObject wrappedFrame(cx, frame);
     if (!cx->compartment()->wrap(cx, &wrappedFrame))
         return false;
 
-    RootedAtom ctorName(cx);
-    {
-        AutoCompartment ac(cx, obj);
-        if (!obj->constructorDisplayAtom(cx, &ctorName))
-            return false;
-    }
-
-    auto className = obj->getClass()->name;
-    auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
-
-    if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size)) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    if (allocationsLog.length() > maxAllocationsLogLength) {
-        if (!allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
+    AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj);
+    if (!allocSite)
+        return false;
+
+    allocationsLog.insertBack(allocSite);
+
+    if (allocationsLogLength >= maxAllocationsLogLength) {
+        js_delete(allocationsLog.popFirst());
         allocationsLogOverflowed = true;
-    }
-
-    return true;
+    } else {
+        allocationsLogLength++;
+    }
+
+    return true;
+}
+
+void
+Debugger::emptyAllocationsLog()
+{
+    while (!allocationsLog.isEmpty())
+        js_delete(allocationsLog.popFirst());
+    allocationsLogLength = 0;
+}
+
+void
+Debugger::emptyTenurePromotionsLog()
+{
+    while (!tenurePromotionsLog.isEmpty())
+        js_delete(tenurePromotionsLog.popFirst());
+    tenurePromotionsLogLength = 0;
 }
 
 JSTrapStatus
 Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp)
 {
     MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
 
     RootedObject hookObj(cx, getHook(hook));
@@ -2296,17 +2327,17 @@ Debugger::addAllocationsTrackingForAllDe
 }
 
 void
 Debugger::removeAllocationsTrackingForAllDebuggees()
 {
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         Debugger::removeAllocationsTracking(*r.front().get());
     }
-    allocationsLog.clear();
+    emptyAllocationsLog();
 }
 
 
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
 Debugger::markCrossCompartmentEdges(JSTracer* trc)
@@ -2315,17 +2346,29 @@ Debugger::markCrossCompartmentEdges(JSTr
     environments.markCrossCompartmentEdges<DebuggerEnv_trace>(trc);
     scripts.markCrossCompartmentEdges<DebuggerScript_trace>(trc);
     sources.markCrossCompartmentEdges<DebuggerSource_trace>(trc);
 
     // Because we don't have access to a `cx` inside
     // `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log,
     // and instead have unwrapped cross-compartment edges. We need to be sure to
     // mark those here.
-    TenurePromotionsLog::trace(&tenurePromotionsLog, trc);
+    traceTenurePromotionsLog(trc);
+}
+
+/*
+ * Trace every entry in the promoted to tenured heap log.
+ */
+void
+Debugger::traceTenurePromotionsLog(JSTracer* trc)
+{
+    for (TenurePromotionsEntry* e = tenurePromotionsLog.getFirst(); e; e = e->getNext()) {
+        if (e->frame)
+            TraceEdge(trc, &e->frame, "Debugger::tenurePromotionsLog SavedFrame");
+    }
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
  * However, during zone GC, we have to do something about cross-compartment
@@ -2496,18 +2539,27 @@ Debugger::trace(JSTracer* trc)
      * frames.)
      */
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         RelocatablePtrNativeObject& frameobj = r.front().value();
         MOZ_ASSERT(MaybeForwarded(frameobj.get())->getPrivate());
         TraceEdge(trc, &frameobj, "live Debugger.Frame");
     }
 
-    AllocationsLog::trace(&allocationsLog, trc);
-    TenurePromotionsLog::trace(&tenurePromotionsLog, trc);
+    /*
+     * Mark every allocation site in our allocation log.
+     */
+    for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) {
+        if (s->frame)
+            TraceEdge(trc, &s->frame, "allocation log SavedFrame");
+        if (s->ctorName)
+            TraceEdge(trc, &s->ctorName, "allocation log constructor name");
+    }
+
+    traceTenurePromotionsLog(trc);
 
     /* Trace the weak map from JSScript instances to Debugger.Script objects. */
     scripts.trace(trc);
 
     /* Trace the referent ->Debugger.Source weak map */
     sources.trace(trc);
 
     /* Trace the referent -> Debugger.Object weak map. */
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -14,17 +14,16 @@
 #include "mozilla/Vector.h"
 
 #include "jsclist.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
-#include "ds/TraceableFifo.h"
 #include "gc/Barrier.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "vm/GlobalObject.h"
 #include "vm/SavedStacks.h"
 
 enum JSTrapStatus {
     JSTRAP_ERROR,
@@ -279,89 +278,82 @@ class Debugger : private mozilla::Linked
 
     bool isEnabled() const {
         return enabled;
     }
 
     void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
     static JSObject* getObjectAllocationSite(JSObject& obj);
 
-    struct TenurePromotionsLogEntry : public JS::Traceable
+  private:
+    HeapPtrNativeObject object;         /* The Debugger object. Strong reference. */
+    WeakGlobalObjectSet debuggees;      /* Debuggee globals. Cross-compartment weak references. */
+    JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
+    js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
+    bool enabled;
+    JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
+
+    // The set of GC numbers for which one or more of this Debugger's observed
+    // debuggees participated in.
+    js::HashSet<uint64_t> observedGCs;
+
+    struct TenurePromotionsEntry : public mozilla::LinkedListElement<TenurePromotionsEntry>
     {
-        TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when);
+        TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when);
 
         const char* className;
         double when;
         RelocatablePtrObject frame;
         size_t size;
-
-        static void trace(TenurePromotionsLogEntry* e, JSTracer* trc) {
-            if (e->frame)
-                TraceEdge(trc, &e->frame, "Debugger::TenurePromotionsLogEntry::frame");
-        }
     };
 
-    struct AllocationsLogEntry : public JS::Traceable
+    using TenurePromotionsLog = mozilla::LinkedList<TenurePromotionsEntry>;
+    TenurePromotionsLog tenurePromotionsLog;
+    bool trackingTenurePromotions;
+    size_t tenurePromotionsLogLength;
+    size_t maxTenurePromotionsLogLength;
+    bool tenurePromotionsLogOverflowed;
+
+    struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
     {
-        AllocationsLogEntry(HandleObject frame, double when, const char* className,
-                            HandleAtom ctorName, size_t size)
+        AllocationSite(HandleObject frame, double when)
             : frame(frame),
               when(when),
-              className(className),
-              ctorName(ctorName),
-              size(size)
+              className(nullptr),
+              ctorName(nullptr),
+              size(0)
         {
             MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
         };
 
+        static AllocationSite* create(JSContext* cx, HandleObject frame, double when,
+                                      HandleObject obj);
+
         RelocatablePtrObject frame;
         double when;
         const char* className;
         RelocatablePtrAtom ctorName;
         size_t size;
+    };
+    typedef mozilla::LinkedList<AllocationSite> AllocationSiteList;
 
-        static void trace(AllocationsLogEntry* e, JSTracer* trc) {
-            if (e->frame)
-                TraceEdge(trc, &e->frame, "Debugger::AllocationsLogEntry::frame");
-            if (e->ctorName)
-                TraceEdge(trc, &e->ctorName, "Debugger::AllocationsLogEntry::ctorName");
-        }
-    };
-
-  private:
-    HeapPtrNativeObject object;         /* The Debugger object. Strong reference. */
-    WeakGlobalObjectSet debuggees;      /* Debuggee globals. Cross-compartment weak references. */
-    JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
-    js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
-    bool enabled;
     bool allowUnobservedAsmJS;
-    JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
-
-    // The set of GC numbers for which one or more of this Debugger's observed
-    // debuggees participated in.
-    js::HashSet<uint64_t> observedGCs;
-
-    using TenurePromotionsLog = js::TraceableFifo<TenurePromotionsLogEntry>;
-    TenurePromotionsLog tenurePromotionsLog;
-    bool trackingTenurePromotions;
-    size_t maxTenurePromotionsLogLength;
-    bool tenurePromotionsLogOverflowed;
-
-    using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
-
-    AllocationsLog allocationsLog;
     bool trackingAllocationSites;
     double allocationSamplingProbability;
+    AllocationSiteList allocationsLog;
+    size_t allocationsLogLength;
     size_t maxAllocationsLogLength;
     bool allocationsLogOverflowed;
 
     static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
 
     bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                               double when);
+    void emptyAllocationsLog();
+    void emptyTenurePromotionsLog();
 
     /*
      * Recompute the set of debuggee zones based on the set of debuggee globals.
      */
     bool recomputeDebuggeeZoneSet();
 
     /*
      * Return true if there is an existing object metadata callback for the
@@ -506,16 +498,17 @@ class Debugger : private mozilla::Linked
                                       MutableHandleValue vp, bool callHook = true);
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     static void finalize(FreeOp* fop, JSObject* obj);
     void markCrossCompartmentEdges(JSTracer* tracer);
+    void traceTenurePromotionsLog(JSTracer* trc);
 
     static const Class jsclass;
 
     static bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
     static bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
 
     static Debugger* fromThisValue(JSContext* cx, const CallArgs& ca, const char* fnname);
     static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
@@ -922,30 +915,16 @@ class Debugger : private mozilla::Linked
      */
     JSObject* wrapSource(JSContext* cx, js::HandleScriptSource source);
 
   private:
     Debugger(const Debugger&) = delete;
     Debugger & operator=(const Debugger&) = delete;
 };
 
-template<>
-struct DefaultTracer<Debugger::TenurePromotionsLogEntry> {
-    static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) {
-        Debugger::TenurePromotionsLogEntry::trace(e, trc);
-    }
-};
-
-template<>
-struct DefaultTracer<Debugger::AllocationsLogEntry> {
-    static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) {
-        Debugger::AllocationsLogEntry::trace(e, trc);
-    }
-};
-
 class BreakpointSite {
     friend class Breakpoint;
     friend struct ::JSCompartment;
     friend class ::JSScript;
     friend class Debugger;
 
   public:
     JSScript* script;
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -180,71 +180,70 @@ DebuggerMemory::drainAllocationsLog(JSCo
     Debugger* dbg = memory->getDebugger();
 
     if (!dbg->trackingAllocationSites) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_TRACKING_ALLOCATIONS,
                              "drainAllocationsLog");
         return false;
     }
 
-    size_t length = dbg->allocationsLog.length();
+    size_t length = dbg->allocationsLogLength;
 
     RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
     if (!result)
         return false;
     result->ensureDenseInitializedLength(cx, 0, length);
 
     for (size_t i = 0; i < length; i++) {
         RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
 
-        // Don't pop the AllocationsLogEntry yet. The queue's links are followed
-        // by the GC to find the AllocationsLogEntry, but are not barriered, so
-        // we must edit them with great care. Use the queue entry in place, and
-        // then pop and delete together.
-        Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front();
+        // Don't pop the AllocationSite yet. The queue's links are followed by
+        // the GC to find the AllocationSite, but are not barriered, so we must
+        // edit them with great care. Use the queue entry in place, and then
+        // pop and delete together.
+        Debugger::AllocationSite* allocSite = dbg->allocationsLog.getFirst();
 
-        RootedValue frame(cx, ObjectOrNullValue(entry.frame));
+        RootedValue frame(cx, ObjectOrNullValue(allocSite->frame));
         if (!DefineProperty(cx, obj, cx->names().frame, frame))
             return false;
 
-        RootedValue timestampValue(cx, NumberValue(entry.when));
+        RootedValue timestampValue(cx, NumberValue(allocSite->when));
         if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
             return false;
 
-        RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
+        RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className)));
         if (!className)
             return false;
         RootedValue classNameValue(cx, StringValue(className));
         if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
             return false;
 
         RootedValue ctorName(cx, NullValue());
-        if (entry.ctorName)
-            ctorName.setString(entry.ctorName);
+        if (allocSite->ctorName)
+            ctorName.setString(allocSite->ctorName);
         if (!DefineProperty(cx, obj, cx->names().constructor, ctorName))
             return false;
 
-        RootedValue size(cx, NumberValue(entry.size));
+        RootedValue size(cx, NumberValue(allocSite->size));
         if (!DefineProperty(cx, obj, cx->names().size, size))
             return false;
 
         result->setDenseElement(i, ObjectValue(*obj));
 
-        // Pop the front queue entry, and delete it immediately, so that the GC
-        // sees the AllocationsLogEntry's RelocatablePtr barriers run atomically
-        // with the change to the graph (the queeue link).
-        if (!dbg->allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+        // Pop the front queue entry, and delete it immediately, so that
+        // the GC sees the AllocationSite's RelocatablePtr barriers run
+        // atomically with the change to the graph (the queue link).
+        MOZ_ALWAYS_TRUE(dbg->allocationsLog.popFirst() == allocSite);
+        js_delete(allocSite);
     }
 
     dbg->allocationsLogOverflowed = false;
+    dbg->allocationsLogLength = 0;
     args.rval().setObject(*result);
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get maxAllocationsLogLength)", args, memory);
@@ -268,21 +267,19 @@ DebuggerMemory::setMaxAllocationsLogLeng
                              "(set maxAllocationsLogLength)'s parameter",
                              "not a positive integer");
         return false;
     }
 
     Debugger* dbg = memory->getDebugger();
     dbg->maxAllocationsLogLength = max;
 
-    while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) {
-        if (!dbg->allocationsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+    while (dbg->allocationsLogLength > dbg->maxAllocationsLogLength) {
+        js_delete(dbg->allocationsLog.getFirst());
+        dbg->allocationsLogLength--;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getAllocationSamplingProbability(JSContext* cx, unsigned argc, Value* vp)
@@ -350,68 +347,67 @@ DebuggerMemory::drainTenurePromotionsLog
     Debugger* dbg = memory->getDebugger();
 
     if (!dbg->trackingTenurePromotions) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_TRACKING_TENURINGS,
                              "drainTenurePromotionsLog");
         return false;
     }
 
-    size_t length = dbg->tenurePromotionsLog.length();
+    size_t length = dbg->tenurePromotionsLogLength;
 
     RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
     if (!result)
         return false;
     result->ensureDenseInitializedLength(cx, 0, length);
 
     for (size_t i = 0; i < length; i++) {
         RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
 
         // Don't pop the TenurePromotionsEntry yet. The queue's links are
         // followed by the GC to find the TenurePromotionsEntry, but are not
         // barriered, so we must edit them with great care. Use the queue entry
         // in place, and then pop and delete together.
-        auto& entry = dbg->tenurePromotionsLog.front();
+        auto* entry = dbg->tenurePromotionsLog.getFirst();
 
-        RootedValue frame(cx, ObjectOrNullValue(entry.frame));
+        RootedValue frame(cx, ObjectOrNullValue(entry->frame));
         if (!cx->compartment()->wrap(cx, &frame) ||
             !DefineProperty(cx, obj, cx->names().frame, frame))
         {
             return false;
         }
 
-        RootedValue timestampValue(cx, NumberValue(entry.when));
+        RootedValue timestampValue(cx, NumberValue(entry->when));
         if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
             return false;
 
-        RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
+        RootedString className(cx, Atomize(cx, entry->className, strlen(entry->className)));
         if (!className)
             return false;
         RootedValue classNameValue(cx, StringValue(className));
         if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
             return false;
 
-        RootedValue sizeValue(cx, NumberValue(entry.size));
+        RootedValue sizeValue(cx, NumberValue(entry->size));
         if (!DefineProperty(cx, obj, cx->names().size, sizeValue))
             return false;
 
         result->setDenseElement(i, ObjectValue(*obj));
 
-        // Pop the front queue entry, and delete it immediately, so that the GC
-        // sees the TenurePromotionsEntry's RelocatablePtr barriers run
+        // Pop the front queue entry, and delete it immediately, so that
+        // the GC sees the TenurePromotionsEntry's RelocatablePtr barriers run
         // atomically with the change to the graph (the queue link).
-        if (!dbg->tenurePromotionsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+        MOZ_ALWAYS_TRUE(dbg->tenurePromotionsLog.popFirst() == entry);
+        js_delete(entry);
     }
 
     dbg->tenurePromotionsLogOverflowed = false;
+    dbg->tenurePromotionsLogLength = 0;
     args.rval().setObject(*result);
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get maxTenurePromotionsLogLength)", args, memory);
@@ -435,21 +431,19 @@ DebuggerMemory::setMaxTenurePromotionsLo
                              "(set maxTenurePromotionsLogLength)'s parameter",
                              "not a positive integer");
         return false;
     }
 
     Debugger* dbg = memory->getDebugger();
     dbg->maxTenurePromotionsLogLength = max;
 
-    while (dbg->tenurePromotionsLog.length() > dbg->maxAllocationsLogLength) {
-        if (!dbg->tenurePromotionsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+    while (dbg->tenurePromotionsLogLength > dbg->maxAllocationsLogLength) {
+        js_delete(dbg->tenurePromotionsLog.getFirst());
+        dbg->tenurePromotionsLogLength--;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getTenurePromotionsLogOverflowed(JSContext* cx, unsigned argc, Value* vp)