Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #ifndef js_UbiNodeCensus_h
8 : #define js_UbiNodeCensus_h
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/Move.h"
12 :
13 : #include <algorithm>
14 :
15 : #include "jsapi.h"
16 :
17 : #include "js/UbiNode.h"
18 : #include "js/UbiNodeBreadthFirst.h"
19 :
20 : // A census is a ubi::Node traversal that assigns each node to one or more
21 : // buckets, and returns a report with the size of each bucket.
22 : //
23 : // We summarize the results of a census with counts broken down according to
24 : // criteria selected by the API consumer code that is requesting the census. For
25 : // example, the following breakdown might give an interesting overview of the
26 : // heap:
27 : //
28 : // - all nodes
29 : // - objects
30 : // - objects with a specific [[Class]] *
31 : // - strings
32 : // - scripts
33 : // - all other Node types
34 : // - nodes with a specific ubi::Node::typeName *
35 : //
36 : // Obviously, the parts of this tree marked with * represent many separate
37 : // counts, depending on how many distinct [[Class]] values and ubi::Node type
38 : // names we encounter.
39 : //
40 : // The supported types of breakdowns are documented in
41 : // js/src/doc/Debugger/Debugger.Memory.md.
42 : //
43 : // When we parse the 'breakdown' argument to takeCensus, we build a tree of
44 : // CountType nodes. For example, for the breakdown shown in the
45 : // Debugger.Memory.prototype.takeCensus, documentation:
46 : //
47 : // {
48 : // by: "coarseType",
49 : // objects: { by: "objectClass" },
50 : // other: { by: "internalType" }
51 : // }
52 : //
53 : // we would build the following tree of CountType subclasses:
54 : //
55 : // ByCoarseType
56 : // objects: ByObjectClass
57 : // each class: SimpleCount
58 : // scripts: SimpleCount
59 : // strings: SimpleCount
60 : // other: ByUbinodeType
61 : // each type: SimpleCount
62 : //
63 : // The interior nodes are all breakdown types that categorize nodes according to
64 : // one characteristic or another; and the leaf nodes are all SimpleType.
65 : //
66 : // Each CountType has its own concrete C++ type that holds the counts it
67 : // produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
68 : // hash table whose keys are object class names and whose values are counts of
69 : // some other type (in the example above, SimpleCount).
70 : //
71 : // To keep actual count nodes small, they have no vtable. Instead, each count
72 : // points to its CountType, which knows how to carry out all the operations we
73 : // need on a Count. A CountType can produce new count nodes; process nodes as we
74 : // visit them; build a JS object reporting the results; and destruct count
75 : // nodes.
76 :
77 :
78 : namespace JS {
79 : namespace ubi {
80 :
81 : struct Census;
82 :
83 : class CountBase;
84 :
85 : struct CountDeleter {
86 : JS_PUBLIC_API(void) operator()(CountBase*);
87 : };
88 :
89 : using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
90 :
91 : // Abstract base class for CountType nodes.
92 : struct CountType {
93 0 : explicit CountType() { }
94 0 : virtual ~CountType() { }
95 :
96 : // Destruct a count tree node that this type instance constructed.
97 : virtual void destructCount(CountBase& count) = 0;
98 :
99 : // Return a fresh node for the count tree that categorizes nodes according
100 : // to this type. Return a nullptr on OOM.
101 : virtual CountBasePtr makeCount() = 0;
102 :
103 : // Trace |count| and all its children, for garbage collection.
104 : virtual void traceCount(CountBase& count, JSTracer* trc) = 0;
105 :
106 : // Implement the 'count' method for counts returned by this CountType
107 : // instance's 'newCount' method.
108 : virtual MOZ_MUST_USE bool count(CountBase& count,
109 : mozilla::MallocSizeOf mallocSizeOf,
110 : const Node& node) = 0;
111 :
112 : // Implement the 'report' method for counts returned by this CountType
113 : // instance's 'newCount' method.
114 : virtual MOZ_MUST_USE bool report(JSContext* cx, CountBase& count,
115 : MutableHandleValue report) = 0;
116 : };
117 :
118 : using CountTypePtr = js::UniquePtr<CountType>;
119 :
120 : // An abstract base class for count tree nodes.
121 : class CountBase {
122 : // In lieu of a vtable, each CountBase points to its type, which
123 : // carries not only the implementations of the CountBase methods, but also
124 : // additional parameters for the type's behavior, as specified in the
125 : // breakdown argument passed to takeCensus.
126 : CountType& type;
127 :
128 : protected:
129 0 : ~CountBase() { }
130 :
131 : public:
132 0 : explicit CountBase(CountType& type)
133 0 : : type(type)
134 : , total_(0)
135 0 : , smallestNodeIdCounted_(SIZE_MAX)
136 0 : { }
137 :
138 : // Categorize and count |node| as appropriate for this count's type.
139 0 : MOZ_MUST_USE bool count(mozilla::MallocSizeOf mallocSizeOf, const Node& node) {
140 0 : total_++;
141 :
142 0 : auto id = node.identifier();
143 0 : if (id < smallestNodeIdCounted_) {
144 0 : smallestNodeIdCounted_ = id;
145 : }
146 :
147 : #ifdef DEBUG
148 0 : size_t oldTotal = total_;
149 : #endif
150 :
151 0 : bool ret = type.count(*this, mallocSizeOf, node);
152 :
153 0 : MOZ_ASSERT(total_ == oldTotal,
154 : "CountType::count should not increment total_, CountBase::count handles that");
155 :
156 0 : return ret;
157 : }
158 :
159 : // Construct a JavaScript object reporting the counts recorded in this
160 : // count, and store it in |report|. Return true on success, or false on
161 : // failure.
162 0 : MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
163 0 : return type.report(cx, *this, report);
164 : }
165 :
166 : // Down-cast this CountBase to its true type, based on its 'type' member,
167 : // and run its destructor.
168 0 : void destruct() { return type.destructCount(*this); }
169 :
170 : // Trace this count for garbage collection.
171 0 : void trace(JSTracer* trc) { type.traceCount(*this, trc); }
172 :
173 : size_t total_;
174 :
175 : // The smallest JS::ubi::Node::identifier() passed to this instance's
176 : // count() method. This provides a stable way to sort sets.
177 : Node::Id smallestNodeIdCounted_;
178 : };
179 :
180 0 : class RootedCount : JS::CustomAutoRooter {
181 : CountBasePtr count;
182 :
183 0 : void trace(JSTracer* trc) override { count->trace(trc); }
184 :
185 : public:
186 0 : RootedCount(JSContext* cx, CountBasePtr&& count)
187 0 : : CustomAutoRooter(cx),
188 0 : count(Move(count))
189 0 : { }
190 0 : CountBase* operator->() const { return count.get(); }
191 0 : explicit operator bool() const { return count.get(); }
192 0 : operator CountBasePtr&() { return count; }
193 : };
194 :
195 : // Common data for a census traversal, shared across all CountType nodes.
196 0 : struct Census {
197 : JSContext* const cx;
198 : // If the targetZones set is non-empty, then only consider nodes whose zone
199 : // is an element of the set. If the targetZones set is empty, then nodes in
200 : // all zones are considered.
201 : JS::ZoneSet targetZones;
202 : Zone* atomsZone;
203 :
204 0 : explicit Census(JSContext* cx) : cx(cx), atomsZone(nullptr) { }
205 :
206 : MOZ_MUST_USE JS_PUBLIC_API(bool) init();
207 : };
208 :
209 : // A BreadthFirst handler type that conducts a census, using a CountBase to
210 : // categorize and count each node.
211 : class CensusHandler {
212 : Census& census;
213 : CountBasePtr& rootCount;
214 : mozilla::MallocSizeOf mallocSizeOf;
215 :
216 : public:
217 0 : CensusHandler(Census& census, CountBasePtr& rootCount, mozilla::MallocSizeOf mallocSizeOf)
218 0 : : census(census),
219 : rootCount(rootCount),
220 0 : mallocSizeOf(mallocSizeOf)
221 0 : { }
222 :
223 0 : MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
224 0 : return rootCount->report(cx, report);
225 : }
226 :
227 : // This class needs to retain no per-node data.
228 : class NodeData { };
229 :
230 : MOZ_MUST_USE JS_PUBLIC_API(bool) operator() (BreadthFirst<CensusHandler>& traversal,
231 : Node origin, const Edge& edge,
232 : NodeData* referentData, bool first);
233 : };
234 :
235 : using CensusTraversal = BreadthFirst<CensusHandler>;
236 :
237 : // Examine the census options supplied by the API consumer, and (among other
238 : // things) use that to build a CountType tree.
239 : MOZ_MUST_USE JS_PUBLIC_API(bool) ParseCensusOptions(JSContext* cx,
240 : Census& census, HandleObject options,
241 : CountTypePtr& outResult);
242 :
243 : // Parse the breakdown language (as described in
244 : // js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
245 : // is returned on error and is reported to the cx.
246 : JS_PUBLIC_API(CountTypePtr) ParseBreakdown(JSContext* cx, HandleValue breakdownValue);
247 :
248 :
249 : } // namespace ubi
250 : } // namespace JS
251 :
252 : #endif // js_UbiNodeCensus_h
|