зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1474383 - JSRuntime hook to construct ubi::Nodes specialized for nsINode instances. r=sfink,jimb,bz
Created a runtime hook to handle DOM nodes. Specialized ubi::Concrete for nsINode-inheriting objects. Displayed outgoing nsISupports* edges on reflector JSObjects. Generated outgoing child edges from nsINodes by examining their children. Updated the UbiNodeCensus to ignore zone checks if there is no zone to be found in a node. --HG-- extra : rebase_source : 319dccb3277a39e51a79588eac9c8f2b4ff97c2f
This commit is contained in:
Родитель
a923510bfa
Коммит
82fd1509ba
|
@ -26,6 +26,7 @@
|
|||
#include "nsTextNode.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "NodeUbiReporting.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -308,5 +309,11 @@ Attr::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return Attr_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
Attr::ConstructUbiNode(void* storage)
|
||||
{
|
||||
JS::ubi::Concrete<Attr>::construct(storage, this);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -51,6 +51,8 @@ public:
|
|||
|
||||
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
|
||||
|
||||
void ConstructUbiNode(void* storage) override;
|
||||
|
||||
nsDOMAttributeMap* GetMap()
|
||||
{
|
||||
return mAttrMap;
|
||||
|
|
|
@ -125,6 +125,8 @@
|
|||
#include "nsChildContentList.h"
|
||||
#include "mozilla/BloomFilter.h"
|
||||
|
||||
#include "NodeUbiReporting.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -445,6 +447,12 @@ nsIContent::GetURLDataForStyleAttr(nsIPrincipal* aSubjectPrincipal) const
|
|||
return do_AddRef(OwnerDoc()->DefaultStyleAttrURLData());
|
||||
}
|
||||
|
||||
void
|
||||
nsIContent::ConstructUbiNode(void* storage)
|
||||
{
|
||||
JS::ubi::Concrete<nsIContent>::construct(storage, this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static inline JSObject*
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- 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 "NodeUbiReporting.h"
|
||||
#include "js/UbiNodeUtils.h"
|
||||
#include "nsWindowSizes.h"
|
||||
|
||||
using JS::ubi::SimpleEdgeRange;
|
||||
using JS::ubi::EdgeRange;
|
||||
|
||||
const char16_t JS::ubi::Concrete<nsIDocument>::concreteTypeName[] = u"nsIDocument";
|
||||
const char16_t JS::ubi::Concrete<nsIContent>::concreteTypeName[] = u"nsIContent";
|
||||
const char16_t JS::ubi::Concrete<Attr>::concreteTypeName[] = u"Attr";
|
||||
|
||||
void
|
||||
JS::ubi::Concrete<nsINode>::construct(void* storage, nsINode* ptr)
|
||||
{
|
||||
// nsINode is abstract, and all of its inherited instances have
|
||||
// an overridden function with instructions to construct ubi::Nodes.
|
||||
// We actually want to call that function and construct from those instances.
|
||||
ptr->ConstructUbiNode(storage);
|
||||
}
|
||||
|
||||
js::UniquePtr<EdgeRange>
|
||||
JS::ubi::Concrete<nsINode>::edges(JSContext* cx, bool wantNames) const
|
||||
{
|
||||
AutoSuppressGCAnalysis suppress;
|
||||
auto range = js::MakeUnique<SimpleEdgeRange>();
|
||||
if (!range) {
|
||||
return nullptr;
|
||||
}
|
||||
if (get().GetParent()) {
|
||||
char16_t* edgeName = nullptr;
|
||||
if (wantNames) {
|
||||
edgeName = NS_strdup(u"Parent Node");
|
||||
}
|
||||
if (!range->addEdge(JS::ubi::Edge(edgeName, get().GetParent()))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
for (auto curr = get().GetFirstChild(); curr; curr = curr->GetNextSibling()) {
|
||||
char16_t* edgeName = nullptr;
|
||||
if (wantNames) {
|
||||
edgeName = NS_strdup(u"Child Node");
|
||||
}
|
||||
if (!range->addEdge(JS::ubi::Edge(edgeName, curr))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
JS::ubi::Node::Size
|
||||
JS::ubi::Concrete<nsINode>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
AutoSuppressGCAnalysis suppress;
|
||||
mozilla::SizeOfState sz(mallocSizeOf);
|
||||
nsWindowSizes wn(sz);
|
||||
size_t n = 0;
|
||||
get().AddSizeOfIncludingThis(wn, &n);
|
||||
return n;
|
||||
}
|
||||
|
||||
JS::ubi::Node::Size
|
||||
JS::ubi::Concrete<nsIDocument>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
AutoSuppressGCAnalysis suppress;
|
||||
mozilla::SizeOfState sz(mallocSizeOf);
|
||||
nsWindowSizes wn(sz);
|
||||
getDoc().DocAddSizeOfIncludingThis(wn);
|
||||
return wn.getTotalSize();
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* -*- 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 dom_NodeUbiReporting_h
|
||||
#define dom_NodeUbiReporting_h
|
||||
|
||||
#include "nsINode.h"
|
||||
#include "js/UbiNode.h"
|
||||
|
||||
/*
|
||||
* This file defines specializations of JS::ubi::Concrete for DOM nodes
|
||||
* so that the JS memory tools, which operate on the UbiNode graph, can
|
||||
* define subclasses of JS::ubi::Base that represent DOM nodes and
|
||||
* yield the outgoing edges in a DOM node graph for reporting.
|
||||
*/
|
||||
|
||||
using mozilla::dom::Attr;
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
// The DOM node base class.
|
||||
// This is an abstract class and therefore does not require a concreteTypeName.
|
||||
template<>
|
||||
class Concrete<nsINode> : public Base
|
||||
{
|
||||
protected:
|
||||
explicit Concrete(nsINode *ptr) : Base(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void *storage, nsINode *ptr);
|
||||
Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
|
||||
js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
|
||||
|
||||
nsINode& get() const { return *static_cast<nsINode*>(ptr); }
|
||||
CoarseType coarseType() const final { return CoarseType::Other; }
|
||||
};
|
||||
|
||||
template<>
|
||||
class Concrete<nsIContent> : public Concrete<nsINode>
|
||||
{
|
||||
protected:
|
||||
explicit Concrete(nsIContent *ptr) : Concrete<nsINode>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void *storage, nsIContent *ptr) { new (storage) Concrete(ptr); }
|
||||
const char16_t* typeName() const override { return concreteTypeName; };
|
||||
static const char16_t concreteTypeName[];
|
||||
};
|
||||
|
||||
template<>
|
||||
class Concrete<nsIDocument> : public Concrete<nsINode>
|
||||
{
|
||||
protected:
|
||||
explicit Concrete(nsIDocument *ptr) : Concrete<nsINode>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void *storage, nsIDocument *ptr) { new (storage) Concrete(ptr); }
|
||||
Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
|
||||
|
||||
nsIDocument& getDoc() const { return *static_cast<nsIDocument*>(ptr); }
|
||||
const char16_t* typeName() const override { return concreteTypeName; };
|
||||
static const char16_t concreteTypeName[];
|
||||
};
|
||||
|
||||
template<>
|
||||
class Concrete<Attr> : public Concrete<nsINode>
|
||||
{
|
||||
protected:
|
||||
explicit Concrete(Attr *ptr) : Concrete<nsINode>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void *storage, Attr *ptr) { new (storage) Concrete(ptr); }
|
||||
const char16_t* typeName() const override { return concreteTypeName; };
|
||||
static const char16_t concreteTypeName[];
|
||||
};
|
||||
|
||||
} //namespace ubi
|
||||
} //namespace JS
|
||||
|
||||
#endif
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsScreen;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ EXPORTS += [
|
|||
'HTMLSplitOnSpacesTokenizer.h',
|
||||
'IframeSandboxKeywordList.h',
|
||||
'mozAutoDocUpdate.h',
|
||||
'NodeUbiReporting.h',
|
||||
'nsAttrAndChildArray.h',
|
||||
'nsAttrName.h',
|
||||
'nsAttrValue.h',
|
||||
|
@ -287,6 +288,7 @@ UNIFIED_SOURCES += [
|
|||
'Navigator.cpp',
|
||||
'NodeInfo.cpp',
|
||||
'NodeIterator.cpp',
|
||||
'NodeUbiReporting.cpp',
|
||||
'nsAttrAndChildArray.cpp',
|
||||
'nsAttrValue.cpp',
|
||||
'nsAttrValueOrString.cpp',
|
||||
|
|
|
@ -275,6 +275,7 @@
|
|||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsHTMLTags.h"
|
||||
#include "NodeUbiReporting.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -1576,6 +1577,13 @@ nsIDocument::IsAboutPage() const
|
|||
return isAboutScheme;
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::ConstructUbiNode(void* storage)
|
||||
{
|
||||
JS::ubi::Concrete<nsIDocument>::construct(storage, this);
|
||||
}
|
||||
|
||||
|
||||
nsDocument::~nsDocument()
|
||||
{
|
||||
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
|
||||
|
|
|
@ -55,6 +55,8 @@ class nsIContent : public nsINode {
|
|||
public:
|
||||
typedef mozilla::widget::IMEState IMEState;
|
||||
|
||||
void ConstructUbiNode(void* storage) override;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// If you're using the external API, the only thing you can know about
|
||||
// nsIContent is that it exists with an IID
|
||||
|
|
|
@ -3042,6 +3042,8 @@ public:
|
|||
// declaration of nsINode::SizeOfIncludingThis.
|
||||
virtual void DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
|
||||
|
||||
void ConstructUbiNode(void* storage) override;
|
||||
|
||||
bool MayHaveDOMMutationObservers()
|
||||
{
|
||||
return mMayHaveDOMMutationObservers;
|
||||
|
|
|
@ -455,6 +455,12 @@ public:
|
|||
|
||||
virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
/**
|
||||
* Hook for constructing JS::ubi::Concrete specializations for memory reporting.
|
||||
* Specializations are defined in NodeUbiReporting.h.
|
||||
*/
|
||||
virtual void ConstructUbiNode(void* storage) = 0;
|
||||
|
||||
/**
|
||||
* returns true if we are in priviliged code or
|
||||
* layout.css.getBoxQuads.enabled == true.
|
||||
|
|
|
@ -1110,9 +1110,7 @@ class JS_PUBLIC_API(Concrete<JSObject>) : public TracerConcrete<JSObject> {
|
|||
explicit Concrete(JSObject* ptr) : TracerConcrete<JSObject>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void* storage, JSObject* ptr) {
|
||||
new (storage) Concrete(ptr);
|
||||
}
|
||||
static void construct(void* storage, JSObject* ptr);
|
||||
|
||||
JS::Compartment* compartment() const override;
|
||||
JS::Realm* realm() const override;
|
||||
|
@ -1165,6 +1163,15 @@ class JS_PUBLIC_API(Concrete<void>) : public Base {
|
|||
static void construct(void* storage, void* ptr) { new (storage) Concrete(ptr); }
|
||||
};
|
||||
|
||||
// The |callback| callback is much like the |Concrete<T>::construct| method: a call to
|
||||
// |callback| should construct an instance of the most appropriate JS::ubi::Base subclass
|
||||
// for |obj| in |storage|. The callback may assume that
|
||||
// |obj->getClass()->isDOMClass()|, and that |storage| refers to the
|
||||
// sizeof(JS::ubi::Base) bytes of space that all ubi::Base implementations should
|
||||
// require.
|
||||
|
||||
// Set |cx|'s runtime hook for constructing ubi::Nodes for DOM classes to |callback|.
|
||||
void SetConstructUbiNodeForDOMObjectCallback(JSContext* cx, void (*callback)(void*, JSObject*));
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
|
|
@ -35,7 +35,7 @@ class SimpleEdgeRange : public EdgeRange {
|
|||
bool addTracerEdges(JSRuntime* rt, void* thing, JS::TraceKind kind, bool wantNames);
|
||||
|
||||
bool addEdge(Edge edge) {
|
||||
if (!edge.name || !edges.append(std::move(edge)))
|
||||
if(!edges.append(std::move(edge)))
|
||||
return false;
|
||||
settle();
|
||||
return true;
|
||||
|
|
|
@ -360,6 +360,11 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
/* Compartment memory reporting callback. */
|
||||
js::MainThreadData<JSSizeOfIncludingThisCompartmentCallback> sizeOfIncludingThisCompartmentCallback;
|
||||
|
||||
/* Callback for creating ubi::Nodes representing DOM node objects. Set by
|
||||
* JS::ubi::SetConstructUbiNodeForDOMObjectCallback. Refer to js/public/UbiNode.h.
|
||||
*/
|
||||
void (*constructUbiNodeForDOMObjectCallback) (void*, JSObject*) = nullptr;
|
||||
|
||||
/* Realm destroy callback. */
|
||||
js::MainThreadData<JS::DestroyRealmCallback> destroyRealmCallback;
|
||||
|
||||
|
|
|
@ -545,5 +545,25 @@ SimpleEdgeRange::addTracerEdges(JSRuntime* rt, void* thing, JS::TraceKind kind,
|
|||
return tracer.okay;
|
||||
}
|
||||
|
||||
void
|
||||
Concrete<JSObject>::construct(void* storage, JSObject* ptr) {
|
||||
if (ptr) {
|
||||
auto clasp = ptr->getClass();
|
||||
auto callback = ptr->compartment()->
|
||||
runtimeFromMainThread()->constructUbiNodeForDOMObjectCallback;
|
||||
if (clasp->isDOMClass() && callback) {
|
||||
AutoSuppressGCAnalysis suppress;
|
||||
callback(storage, ptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
new (storage) Concrete(ptr);
|
||||
}
|
||||
|
||||
void
|
||||
SetConstructUbiNodeForDOMObjectCallback(JSContext* cx, void (*callback)(void*, JSObject*)) {
|
||||
cx->runtime()->constructUbiNodeForDOMObjectCallback = callback;
|
||||
}
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
|
|
@ -940,7 +940,7 @@ CensusHandler::operator() (BreadthFirst<CensusHandler>& traversal,
|
|||
if (census.targetZones.count() == 0 || census.targetZones.has(zone))
|
||||
return rootCount->count(mallocSizeOf, referent);
|
||||
|
||||
if (zone->isAtomsZone()) {
|
||||
if (zone && zone->isAtomsZone()) {
|
||||
traversal.abandonReferent();
|
||||
return rootCount->count(mallocSizeOf, referent);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include "nsCycleCollector.h"
|
||||
#include "jsapi.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/UbiNodeUtils.h"
|
||||
#include "mozilla/dom/GeneratedAtomList.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
@ -57,6 +59,7 @@
|
|||
#include "nsAboutProtocolUtils.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "NodeUbiReporting.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
|
@ -124,7 +127,7 @@ public:
|
|||
NS_IMETHOD Run() override
|
||||
{
|
||||
AUTO_PROFILER_LABEL("AsyncFreeSnowWhite::Run", GCCC);
|
||||
|
||||
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
|
||||
|
@ -2848,6 +2851,67 @@ XPCJSRuntime::Get()
|
|||
return nsXPConnect::GetRuntimeInstance();
|
||||
}
|
||||
|
||||
// Subclass of JS::ubi::Base for DOM reflector objects for the JS::ubi::Node memory
|
||||
// analysis framework; see js/public/UbiNode.h.
|
||||
// In XPCJSRuntime::Initialize, we register the ConstructUbiNode function as a
|
||||
// hook with the SpiderMonkey runtime for it to use to construct ubi::Nodes
|
||||
// of this class for JSObjects whose class has the JSCLASS_IS_DOMJSCLASS flag set.
|
||||
// ReflectorNode specializes Concrete<JSObject> for DOM reflector nodes, reporting
|
||||
// the edge from the JSObject to the nsINode it represents, in addition to the
|
||||
// usual edges departing any normal JSObject.
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
class ReflectorNode : public Concrete<JSObject>
|
||||
{
|
||||
protected:
|
||||
explicit ReflectorNode(JSObject *ptr) : Concrete<JSObject>(ptr) { }
|
||||
|
||||
public:
|
||||
static void construct(void *storage, JSObject *ptr)
|
||||
{
|
||||
new (storage) ReflectorNode(ptr);
|
||||
}
|
||||
js::UniquePtr<JS::ubi::EdgeRange> edges(JSContext* cx, bool wantNames) const override;
|
||||
};
|
||||
|
||||
js::UniquePtr<EdgeRange>
|
||||
ReflectorNode::edges(JSContext* cx, bool wantNames) const
|
||||
{
|
||||
js::UniquePtr<SimpleEdgeRange> range(
|
||||
static_cast<SimpleEdgeRange*>(Concrete<JSObject>::edges(cx, wantNames).release()));
|
||||
if (!range) {
|
||||
return nullptr;
|
||||
}
|
||||
// UNWRAP_OBJECT assumes the object is completely initialized, but ours
|
||||
// may not be. Luckily, UnwrapDOMObjectToISupports checks for the
|
||||
// uninitialized case (and returns null if uninitialized), so we can use
|
||||
// that to guard against uninitialized objects.
|
||||
nsISupports* supp = UnwrapDOMObjectToISupports(&get());
|
||||
if (supp) {
|
||||
nsCOMPtr<nsINode> node;
|
||||
UNWRAP_OBJECT(Node, &get(), node);
|
||||
if (node) {
|
||||
char16_t* edgeName = nullptr;
|
||||
if (wantNames) {
|
||||
edgeName = NS_strdup(u"Reflected Node");
|
||||
}
|
||||
if (!range->addEdge(Edge(edgeName, node.get()))){
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
} // Namespace ubi
|
||||
} // Namespace JS
|
||||
|
||||
void
|
||||
ConstructUbiNode(void* storage, JSObject* ptr)
|
||||
{
|
||||
JS::ubi::ReflectorNode::construct(storage, ptr);
|
||||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::Initialize(JSContext* cx)
|
||||
{
|
||||
|
@ -2912,6 +2976,9 @@ XPCJSRuntime::Initialize(JSContext* cx)
|
|||
RegisterJSMainRuntimeRealmsUserDistinguishedAmount(JSMainRuntimeRealmsUserDistinguishedAmount);
|
||||
mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
|
||||
|
||||
// Set the callback for reporting memory to ubi::Node.
|
||||
JS::ubi::SetConstructUbiNodeForDOMObjectCallback(cx, &ConstructUbiNode);
|
||||
|
||||
xpc_LocalizeRuntime(JS_GetRuntime(cx));
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче