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