1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef js_UbiNodeCensus_h
8 #define js_UbiNodeCensus_h
10 #include "js/UbiNode.h"
11 #include "js/UbiNodeBreadthFirst.h"
13 // A census is a ubi::Node traversal that assigns each node to one or more
14 // buckets, and returns a report with the size of each bucket.
16 // We summarize the results of a census with counts broken down according to
17 // criteria selected by the API consumer code that is requesting the census. For
18 // example, the following breakdown might give an interesting overview of the
23 // - objects with a specific [[Class]] *
27 // - nsINodes with a specific name (found in nsINode::NodeName()) *
28 // - all other Node types
29 // - nodes with a specific ubi::Node::typeName *
31 // Obviously, the parts of this tree marked with * represent many separate
32 // counts, depending on how many distinct [[Class]] values and ubi::Node type
33 // names we encounter.
35 // The supported types of breakdowns are documented in
36 // js/src/doc/Debugger/Debugger.Memory.md.
38 // When we parse the 'breakdown' argument to takeCensus, we build a tree of
39 // CountType nodes. For example, for the breakdown shown in the
40 // Debugger.Memory.prototype.takeCensus, documentation:
44 // objects: { by: "objectClass" },
45 // other: { by: "internalType" },
46 // domNode: { by: "descriptiveType" }
49 // we would build the following tree of CountType subclasses:
52 // objects: ByObjectClass
53 // each class: SimpleCount
54 // scripts: SimpleCount
55 // strings: SimpleCount
56 // other: ByUbinodeType
57 // each type: SimpleCount
58 // domNode: ByDomObjectClass
59 // each type: SimpleCount
61 // The interior nodes are all breakdown types that categorize nodes according to
62 // one characteristic or another; and the leaf nodes are all SimpleType.
64 // Each CountType has its own concrete C++ type that holds the counts it
65 // produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
66 // hash table whose keys are object class names and whose values are counts of
67 // some other type (in the example above, SimpleCount).
69 // To keep actual count nodes small, they have no vtable. Instead, each count
70 // points to its CountType, which knows how to carry out all the operations we
71 // need on a Count. A CountType can produce new count nodes; process nodes as we
72 // visit them; build a JS object reporting the results; and destruct count
83 JS_PUBLIC_API
void operator()(CountBase
*);
86 using CountBasePtr
= js::UniquePtr
<CountBase
, CountDeleter
>;
88 // Abstract base class for CountType nodes.
90 explicit CountType() = default;
91 virtual ~CountType() = default;
93 // Destruct a count tree node that this type instance constructed.
94 virtual void destructCount(CountBase
& count
) = 0;
96 // Return a fresh node for the count tree that categorizes nodes according
97 // to this type. Return a nullptr on OOM.
98 virtual CountBasePtr
makeCount() = 0;
100 // Trace |count| and all its children, for garbage collection.
101 virtual void traceCount(CountBase
& count
, JSTracer
* trc
) = 0;
103 // Implement the 'count' method for counts returned by this CountType
104 // instance's 'newCount' method.
105 [[nodiscard
]] virtual bool count(CountBase
& count
,
106 mozilla::MallocSizeOf mallocSizeOf
,
107 const Node
& node
) = 0;
109 // Implement the 'report' method for counts returned by this CountType
110 // instance's 'newCount' method.
111 [[nodiscard
]] virtual bool report(JSContext
* cx
, CountBase
& count
,
112 MutableHandleValue report
) = 0;
115 using CountTypePtr
= js::UniquePtr
<CountType
>;
117 // An abstract base class for count tree nodes.
119 // In lieu of a vtable, each CountBase points to its type, which
120 // carries not only the implementations of the CountBase methods, but also
121 // additional parameters for the type's behavior, as specified in the
122 // breakdown argument passed to takeCensus.
126 ~CountBase() = default;
129 explicit CountBase(CountType
& type
)
130 : type(type
), total_(0), smallestNodeIdCounted_(SIZE_MAX
) {}
132 // Categorize and count |node| as appropriate for this count's type.
133 [[nodiscard
]] bool count(mozilla::MallocSizeOf mallocSizeOf
,
137 auto id
= node
.identifier();
138 if (id
< smallestNodeIdCounted_
) {
139 smallestNodeIdCounted_
= id
;
143 size_t oldTotal
= total_
;
146 bool ret
= type
.count(*this, mallocSizeOf
, node
);
148 MOZ_ASSERT(total_
== oldTotal
,
149 "CountType::count should not increment total_, CountBase::count "
155 // Construct a JavaScript object reporting the counts recorded in this
156 // count, and store it in |report|. Return true on success, or false on
158 [[nodiscard
]] bool report(JSContext
* cx
, MutableHandleValue report
) {
159 return type
.report(cx
, *this, report
);
162 // Down-cast this CountBase to its true type, based on its 'type' member,
163 // and run its destructor.
164 void destruct() { return type
.destructCount(*this); }
166 // Trace this count for garbage collection.
167 void trace(JSTracer
* trc
) { type
.traceCount(*this, trc
); }
171 // The smallest JS::ubi::Node::identifier() passed to this instance's
172 // count() method. This provides a stable way to sort sets.
173 Node::Id smallestNodeIdCounted_
;
176 using RootedCount
= JS::Rooted
<CountBasePtr
>;
178 // Common data for a census traversal, shared across all CountType nodes.
181 // If the targetZones set is non-empty, then only consider nodes whose zone
182 // is an element of the set. If the targetZones set is empty, then nodes in
183 // all zones are considered.
184 JS::ZoneSet targetZones
;
186 explicit Census(JSContext
* cx
) : cx(cx
) {}
189 // A BreadthFirst handler type that conducts a census, using a CountBase to
190 // categorize and count each node.
191 class CensusHandler
{
193 JS::Handle
<CountBasePtr
> rootCount
;
194 mozilla::MallocSizeOf mallocSizeOf
;
197 CensusHandler(Census
& census
, JS::Handle
<CountBasePtr
> rootCount
,
198 mozilla::MallocSizeOf mallocSizeOf
)
199 : census(census
), rootCount(rootCount
), mallocSizeOf(mallocSizeOf
) {}
201 [[nodiscard
]] bool report(JSContext
* cx
, MutableHandleValue report
) {
202 return rootCount
->report(cx
, report
);
205 // This class needs to retain no per-node data.
208 [[nodiscard
]] JS_PUBLIC_API
bool operator()(
209 BreadthFirst
<CensusHandler
>& traversal
, Node origin
, const Edge
& edge
,
210 NodeData
* referentData
, bool first
);
213 using CensusTraversal
= BreadthFirst
<CensusHandler
>;
215 // Examine the census options supplied by the API consumer, and (among other
216 // things) use that to build a CountType tree.
217 [[nodiscard
]] JS_PUBLIC_API
bool ParseCensusOptions(JSContext
* cx
,
219 HandleObject options
,
220 CountTypePtr
& outResult
);
222 // Parse the breakdown language (as described in
223 // js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
224 // is returned on error and is reported to the cx.
225 JS_PUBLIC_API CountTypePtr
ParseBreakdown(JSContext
* cx
,
226 HandleValue breakdownValue
);
231 #endif // js_UbiNodeCensus_h