tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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