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:
Kristen Wright 2018-07-06 16:52:42 -07:00
Родитель a923510bfa
Коммит 82fd1509ba
17 изменённых файлов: 302 добавлений и 6 удалений

Просмотреть файл

@ -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));
}