| /* -*- 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<CountBase, CountDeleter>; |
| |
| // 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<CountType, JS::DeletePolicy<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() { } |
| |
| 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<typename T, typename... Args> |
| 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>(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<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 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 |