/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 "mozilla/Move.h" #include "jsapi.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 // - 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" } // } // // we would build the following tree of CountType subclasses: // // ByCoarseType // objects: ByObjectClass // each class: SimpleCount // scripts: SimpleCount // strings: SimpleCount // other: ByUbinodeType // 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 { void operator()(CountBase*); }; using CountBasePtr = UniquePtr; // Abstract base class for CountType nodes. struct CountType { explicit CountType(Census& census) : census(census) { } virtual ~CountType() { } // 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. virtual bool count(CountBase& count, const Node& node) = 0; // Implement the 'report' method for counts returned by this CountType // instance's 'newCount' method. virtual bool report(CountBase& count, MutableHandleValue report) = 0; protected: Census& census; }; using CountTypePtr = UniquePtr>; // 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() { } public: explicit CountBase(CountType& type) : type(type), total_(0) { } // Categorize and count |node| as appropriate for this count's type. bool count(const Node& node) { return type.count(*this, node); } // Construct a JavaScript object reporting the counts recorded in this // count, and store it in |report|. Return true on success, or false on // failure. bool report(MutableHandleValue report) { return type.report(*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_; }; class RootedCount : JS::CustomAutoRooter { CountBasePtr count; void trace(JSTracer* trc) override { count->trace(trc); } public: RootedCount(JSContext* cx, CountBasePtr&& count) : CustomAutoRooter(cx), count(Move(count)) { } CountBase* operator->() const { return count.get(); } explicit operator bool() const { return count.get(); } operator CountBasePtr&() { return count; } }; // 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; Zone* atomsZone; explicit Census(JSContext* cx) : cx(cx), atomsZone(nullptr) { } bool init(); // A 'new' work-alike that behaves like TempAllocPolicy: report OOM on this // census's context, but don't charge the memory allocated to our context's // GC pressure counters. template T* new_(Args&&... args) MOZ_HEAP_ALLOCATOR { void* memory = js_malloc(sizeof(T)); if (MOZ_UNLIKELY(!memory)) { return nullptr; } return new(memory) T(mozilla::Forward(args)...); } }; // A BreadthFirst handler type that conducts a census, using a CountBase to // categorize and count each node. class CensusHandler { Census& census; CountBasePtr& rootCount; public: CensusHandler(Census& census, CountBasePtr& rootCount) : census(census), rootCount(rootCount) { } bool report(MutableHandleValue report) { return rootCount->report(report); } // This class needs to retain no per-node data. class NodeData { }; bool operator() (BreadthFirst& traversal, Node origin, const Edge& edge, NodeData* referentData, bool first); }; using CensusTraversal = BreadthFirst; // Examine the census options supplied by the API consumer, and use that to // build a CountType tree. bool ParseCensusOptions(JSContext* cx, Census& census, HandleObject options, CountTypePtr& outResult); } // namespace ubi } // namespace JS #endif // js_UbiNodeCensus_h