UbiNodeCensus.h 8.3 KB

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