summaryrefslogtreecommitdiffstats
path: root/js/public/UbiNodeCensus.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/public/UbiNodeCensus.h')
-rw-r--r--js/public/UbiNodeCensus.h231
1 files changed, 231 insertions, 0 deletions
diff --git a/js/public/UbiNodeCensus.h b/js/public/UbiNodeCensus.h
new file mode 100644
index 0000000000..4086300580
--- /dev/null
+++ b/js/public/UbiNodeCensus.h
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeCensus_h
+#define js_UbiNodeCensus_h
+
+#include "js/UbiNode.h"
+#include "js/UbiNodeBreadthFirst.h"
+
+// A census is a ubi::Node traversal that assigns each node to one or more
+// buckets, and returns a report with the size of each bucket.
+//
+// We summarize the results of a census with counts broken down according to
+// criteria selected by the API consumer code that is requesting the census. For
+// example, the following breakdown might give an interesting overview of the
+// heap:
+//
+// - all nodes
+// - objects
+// - objects with a specific [[Class]] *
+// - strings
+// - scripts
+// - DOM nodes
+// - nsINodes with a specific name (found in nsINode::NodeName()) *
+// - all other Node types
+// - nodes with a specific ubi::Node::typeName *
+//
+// Obviously, the parts of this tree marked with * represent many separate
+// counts, depending on how many distinct [[Class]] values and ubi::Node type
+// names we encounter.
+//
+// The supported types of breakdowns are documented in
+// js/src/doc/Debugger/Debugger.Memory.md.
+//
+// When we parse the 'breakdown' argument to takeCensus, we build a tree of
+// CountType nodes. For example, for the breakdown shown in the
+// Debugger.Memory.prototype.takeCensus, documentation:
+//
+// {
+// by: "coarseType",
+// objects: { by: "objectClass" },
+// other: { by: "internalType" },
+// domNode: { by: "descriptiveType" }
+// }
+//
+// we would build the following tree of CountType subclasses:
+//
+// ByCoarseType
+// objects: ByObjectClass
+// each class: SimpleCount
+// scripts: SimpleCount
+// strings: SimpleCount
+// other: ByUbinodeType
+// each type: SimpleCount
+// domNode: ByDomObjectClass
+// each type: SimpleCount
+//
+// The interior nodes are all breakdown types that categorize nodes according to
+// one characteristic or another; and the leaf nodes are all SimpleType.
+//
+// Each CountType has its own concrete C++ type that holds the counts it
+// produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
+// hash table whose keys are object class names and whose values are counts of
+// some other type (in the example above, SimpleCount).
+//
+// To keep actual count nodes small, they have no vtable. Instead, each count
+// points to its CountType, which knows how to carry out all the operations we
+// need on a Count. A CountType can produce new count nodes; process nodes as we
+// visit them; build a JS object reporting the results; and destruct count
+// nodes.
+
+namespace JS {
+namespace ubi {
+
+struct Census;
+
+class CountBase;
+
+struct CountDeleter {
+ JS_PUBLIC_API void operator()(CountBase*);
+};
+
+using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
+
+// Abstract base class for CountType nodes.
+struct CountType {
+ explicit CountType() = default;
+ virtual ~CountType() = default;
+
+ // Destruct a count tree node that this type instance constructed.
+ virtual void destructCount(CountBase& count) = 0;
+
+ // Return a fresh node for the count tree that categorizes nodes according
+ // to this type. Return a nullptr on OOM.
+ virtual CountBasePtr makeCount() = 0;
+
+ // Trace |count| and all its children, for garbage collection.
+ virtual void traceCount(CountBase& count, JSTracer* trc) = 0;
+
+ // Implement the 'count' method for counts returned by this CountType
+ // instance's 'newCount' method.
+ [[nodiscard]] virtual bool count(CountBase& count,
+ mozilla::MallocSizeOf mallocSizeOf,
+ const Node& node) = 0;
+
+ // Implement the 'report' method for counts returned by this CountType
+ // instance's 'newCount' method.
+ [[nodiscard]] virtual bool report(JSContext* cx, CountBase& count,
+ MutableHandleValue report) = 0;
+};
+
+using CountTypePtr = js::UniquePtr<CountType>;
+
+// An abstract base class for count tree nodes.
+class CountBase {
+ // In lieu of a vtable, each CountBase points to its type, which
+ // carries not only the implementations of the CountBase methods, but also
+ // additional parameters for the type's behavior, as specified in the
+ // breakdown argument passed to takeCensus.
+ CountType& type;
+
+ protected:
+ ~CountBase() = default;
+
+ public:
+ explicit CountBase(CountType& type)
+ : type(type), total_(0), smallestNodeIdCounted_(SIZE_MAX) {}
+
+ // Categorize and count |node| as appropriate for this count's type.
+ [[nodiscard]] bool count(mozilla::MallocSizeOf mallocSizeOf,
+ const Node& node) {
+ total_++;
+
+ auto id = node.identifier();
+ if (id < smallestNodeIdCounted_) {
+ smallestNodeIdCounted_ = id;
+ }
+
+#ifdef DEBUG
+ size_t oldTotal = total_;
+#endif
+
+ bool ret = type.count(*this, mallocSizeOf, node);
+
+ MOZ_ASSERT(total_ == oldTotal,
+ "CountType::count should not increment total_, CountBase::count "
+ "handles that");
+
+ return ret;
+ }
+
+ // Construct a JavaScript object reporting the counts recorded in this
+ // count, and store it in |report|. Return true on success, or false on
+ // failure.
+ [[nodiscard]] bool report(JSContext* cx, MutableHandleValue report) {
+ return type.report(cx, *this, report);
+ }
+
+ // Down-cast this CountBase to its true type, based on its 'type' member,
+ // and run its destructor.
+ void destruct() { return type.destructCount(*this); }
+
+ // Trace this count for garbage collection.
+ void trace(JSTracer* trc) { type.traceCount(*this, trc); }
+
+ size_t total_;
+
+ // The smallest JS::ubi::Node::identifier() passed to this instance's
+ // count() method. This provides a stable way to sort sets.
+ Node::Id smallestNodeIdCounted_;
+};
+
+using RootedCount = JS::Rooted<CountBasePtr>;
+
+// Common data for a census traversal, shared across all CountType nodes.
+struct Census {
+ JSContext* const cx;
+ // If the targetZones set is non-empty, then only consider nodes whose zone
+ // is an element of the set. If the targetZones set is empty, then nodes in
+ // all zones are considered.
+ JS::ZoneSet targetZones;
+
+ explicit Census(JSContext* cx) : cx(cx) {}
+};
+
+// A BreadthFirst handler type that conducts a census, using a CountBase to
+// categorize and count each node.
+class CensusHandler {
+ Census& census;
+ JS::Handle<CountBasePtr> rootCount;
+ mozilla::MallocSizeOf mallocSizeOf;
+
+ public:
+ CensusHandler(Census& census, JS::Handle<CountBasePtr> rootCount,
+ mozilla::MallocSizeOf mallocSizeOf)
+ : census(census), rootCount(rootCount), mallocSizeOf(mallocSizeOf) {}
+
+ [[nodiscard]] bool report(JSContext* cx, MutableHandleValue report) {
+ return rootCount->report(cx, report);
+ }
+
+ // This class needs to retain no per-node data.
+ class NodeData {};
+
+ [[nodiscard]] JS_PUBLIC_API bool operator()(
+ BreadthFirst<CensusHandler>& traversal, Node origin, const Edge& edge,
+ NodeData* referentData, bool first);
+};
+
+using CensusTraversal = BreadthFirst<CensusHandler>;
+
+// Examine the census options supplied by the API consumer, and (among other
+// things) use that to build a CountType tree.
+[[nodiscard]] JS_PUBLIC_API bool ParseCensusOptions(JSContext* cx,
+ Census& census,
+ HandleObject options,
+ CountTypePtr& outResult);
+
+// Parse the breakdown language (as described in
+// js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
+// is returned on error and is reported to the cx.
+JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx,
+ HandleValue breakdownValue);
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeCensus_h