UbiNodeCensus.h 8.2 KB

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