зеркало из https://github.com/mozilla/gecko-dev.git
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)
This commit is contained in:
Родитель
1ee9c4343b
Коммит
0c554f1795
154
js/src/ds/Fifo.h
154
js/src/ds/Fifo.h
|
@ -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 */
|
|
@ -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
|
|
@ -5,7 +5,6 @@
|
|||
* 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"
|
||||
|
@ -277,47 +276,6 @@ receiveMutableHandleToShapeVector(JS::MutableHandle<TraceableVector<Shape*>> han
|
|||
}
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -357,15 +357,15 @@ 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()),
|
||||
|
@ -390,8 +390,8 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg)
|
|||
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
|
||||
|
@ -1701,7 +1701,7 @@ Debugger::isDebuggee(const JSCompartment* compartment) const
|
|||
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)),
|
||||
|
@ -1712,17 +1712,43 @@ Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSOb
|
|||
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)
|
||||
|
@ -1734,33 +1760,38 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame
|
|||
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);
|
||||
AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj);
|
||||
if (!allocSite)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allocationsLog.length() > maxAllocationsLogLength) {
|
||||
if (!allocationsLog.popFront()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
|
||||
allocationsLog.insertBack(allocSite);
|
||||
|
||||
if (allocationsLogLength >= maxAllocationsLogLength) {
|
||||
js_delete(allocationsLog.popFirst());
|
||||
allocationsLogOverflowed = 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)
|
||||
{
|
||||
|
@ -2301,7 +2332,7 @@ Debugger::removeAllocationsTrackingForAllDebuggees()
|
|||
for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
|
||||
Debugger::removeAllocationsTracking(*r.front().get());
|
||||
}
|
||||
allocationsLog.clear();
|
||||
emptyAllocationsLog();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2320,7 +2351,19 @@ Debugger::markCrossCompartmentEdges(JSTracer* trc)
|
|||
// `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");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2501,8 +2544,17 @@ Debugger::trace(JSTracer* trc)
|
|||
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);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "jsweakmap.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "ds/TraceableFifo.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "js/Debug.h"
|
||||
#include "js/HashTable.h"
|
||||
|
@ -284,72 +283,63 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
|
||||
static JSObject* getObjectAllocationSite(JSObject& obj);
|
||||
|
||||
struct TenurePromotionsLogEntry : public JS::Traceable
|
||||
{
|
||||
TenurePromotionsLogEntry(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
|
||||
{
|
||||
AllocationsLogEntry(HandleObject frame, double when, const char* className,
|
||||
HandleAtom ctorName, size_t size)
|
||||
: frame(frame),
|
||||
when(when),
|
||||
className(className),
|
||||
ctorName(ctorName),
|
||||
size(size)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
|
||||
};
|
||||
|
||||
RelocatablePtrObject frame;
|
||||
double when;
|
||||
const char* className;
|
||||
RelocatablePtrAtom ctorName;
|
||||
size_t size;
|
||||
|
||||
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>;
|
||||
struct TenurePromotionsEntry : public mozilla::LinkedListElement<TenurePromotionsEntry>
|
||||
{
|
||||
TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when);
|
||||
|
||||
const char* className;
|
||||
double when;
|
||||
RelocatablePtrObject frame;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
using TenurePromotionsLog = mozilla::LinkedList<TenurePromotionsEntry>;
|
||||
TenurePromotionsLog tenurePromotionsLog;
|
||||
bool trackingTenurePromotions;
|
||||
size_t tenurePromotionsLogLength;
|
||||
size_t maxTenurePromotionsLogLength;
|
||||
bool tenurePromotionsLogOverflowed;
|
||||
|
||||
using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
|
||||
struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
|
||||
{
|
||||
AllocationSite(HandleObject frame, double when)
|
||||
: frame(frame),
|
||||
when(when),
|
||||
className(nullptr),
|
||||
ctorName(nullptr),
|
||||
size(0)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
|
||||
};
|
||||
|
||||
AllocationsLog allocationsLog;
|
||||
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;
|
||||
|
||||
bool allowUnobservedAsmJS;
|
||||
bool trackingAllocationSites;
|
||||
double allocationSamplingProbability;
|
||||
AllocationSiteList allocationsLog;
|
||||
size_t allocationsLogLength;
|
||||
size_t maxAllocationsLogLength;
|
||||
bool allocationsLogOverflowed;
|
||||
|
||||
|
@ -357,6 +347,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
|
||||
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.
|
||||
|
@ -511,6 +503,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
void trace(JSTracer* trc);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
void markCrossCompartmentEdges(JSTracer* tracer);
|
||||
void traceTenurePromotionsLog(JSTracer* trc);
|
||||
|
||||
static const Class jsclass;
|
||||
|
||||
|
@ -927,20 +920,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
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;
|
||||
|
|
|
@ -185,7 +185,7 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t length = dbg->allocationsLog.length();
|
||||
size_t length = dbg->allocationsLogLength;
|
||||
|
||||
RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
|
||||
if (!result)
|
||||
|
@ -197,21 +197,21 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
|
|||
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));
|
||||
|
@ -219,27 +219,26 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
|
|||
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;
|
||||
}
|
||||
|
@ -273,11 +272,9 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value*
|
|||
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();
|
||||
|
@ -355,7 +352,7 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t length = dbg->tenurePromotionsLog.length();
|
||||
size_t length = dbg->tenurePromotionsLogLength;
|
||||
|
||||
RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
|
||||
if (!result)
|
||||
|
@ -371,42 +368,41 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp
|
|||
// 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;
|
||||
}
|
||||
|
@ -440,11 +436,9 @@ DebuggerMemory::setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Va
|
|||
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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче