2015-08-19 23:21:08 +03:00
|
|
|
/* -*- 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
|
|
|
|
|
2016-06-17 15:52:43 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2015-08-19 23:21:08 +03:00
|
|
|
#include "mozilla/Move.h"
|
|
|
|
|
2016-04-08 23:21:00 +03:00
|
|
|
#include <algorithm>
|
|
|
|
|
2015-09-14 14:59:00 +03:00
|
|
|
#include "jsapi.h"
|
|
|
|
|
2015-08-19 23:21:08 +03:00
|
|
|
#include "js/UbiNode.h"
|
2015-09-25 00:01:22 +03:00
|
|
|
#include "js/UbiNodeBreadthFirst.h"
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// 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 {
|
2017-03-11 07:52:55 +03:00
|
|
|
JS_PUBLIC_API(void) operator()(CountBase*);
|
2015-08-19 23:21:08 +03:00
|
|
|
};
|
|
|
|
|
2016-01-16 03:26:06 +03:00
|
|
|
using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// Abstract base class for CountType nodes.
|
|
|
|
struct CountType {
|
2015-12-16 11:19:00 +03:00
|
|
|
explicit CountType() { }
|
2015-08-19 23:21:08 +03:00
|
|
|
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.
|
2016-06-17 15:52:43 +03:00
|
|
|
virtual MOZ_MUST_USE bool count(CountBase& count,
|
|
|
|
mozilla::MallocSizeOf mallocSizeOf,
|
|
|
|
const Node& node) = 0;
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// Implement the 'report' method for counts returned by this CountType
|
|
|
|
// instance's 'newCount' method.
|
2016-06-17 15:52:43 +03:00
|
|
|
virtual MOZ_MUST_USE bool report(JSContext* cx, CountBase& count,
|
|
|
|
MutableHandleValue report) = 0;
|
2015-08-19 23:21:08 +03:00
|
|
|
};
|
|
|
|
|
2016-01-16 03:26:06 +03:00
|
|
|
using CountTypePtr = js::UniquePtr<CountType>;
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// 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:
|
2016-04-08 23:21:00 +03:00
|
|
|
explicit CountBase(CountType& type)
|
|
|
|
: type(type)
|
|
|
|
, total_(0)
|
|
|
|
, smallestNodeIdCounted_(SIZE_MAX)
|
|
|
|
{ }
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// Categorize and count |node| as appropriate for this count's type.
|
2016-06-17 15:52:43 +03:00
|
|
|
MOZ_MUST_USE bool count(mozilla::MallocSizeOf mallocSizeOf, const Node& node) {
|
2016-04-08 23:21:00 +03:00
|
|
|
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;
|
2015-12-16 11:19:00 +03:00
|
|
|
}
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// Construct a JavaScript object reporting the counts recorded in this
|
|
|
|
// count, and store it in |report|. Return true on success, or false on
|
|
|
|
// failure.
|
2016-06-17 15:52:43 +03:00
|
|
|
MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
|
2015-12-16 11:19:00 +03:00
|
|
|
return type.report(cx, *this, report);
|
|
|
|
}
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
// 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_;
|
2016-04-08 23:21:00 +03:00
|
|
|
|
|
|
|
// The smallest JS::ubi::Node::identifier() passed to this instance's
|
|
|
|
// count() method. This provides a stable way to sort sets.
|
|
|
|
Node::Id smallestNodeIdCounted_;
|
2015-08-19 23:21:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
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) { }
|
|
|
|
|
2017-03-11 07:52:55 +03:00
|
|
|
MOZ_MUST_USE JS_PUBLIC_API(bool) init();
|
2015-08-19 23:21:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// A BreadthFirst handler type that conducts a census, using a CountBase to
|
|
|
|
// categorize and count each node.
|
|
|
|
class CensusHandler {
|
|
|
|
Census& census;
|
|
|
|
CountBasePtr& rootCount;
|
2015-12-16 11:19:00 +03:00
|
|
|
mozilla::MallocSizeOf mallocSizeOf;
|
2015-08-19 23:21:08 +03:00
|
|
|
|
|
|
|
public:
|
2015-12-16 11:19:00 +03:00
|
|
|
CensusHandler(Census& census, CountBasePtr& rootCount, mozilla::MallocSizeOf mallocSizeOf)
|
2015-08-19 23:21:08 +03:00
|
|
|
: census(census),
|
2015-12-16 11:19:00 +03:00
|
|
|
rootCount(rootCount),
|
|
|
|
mallocSizeOf(mallocSizeOf)
|
2015-08-19 23:21:08 +03:00
|
|
|
{ }
|
|
|
|
|
2016-06-17 15:52:43 +03:00
|
|
|
MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
|
2015-12-16 11:19:00 +03:00
|
|
|
return rootCount->report(cx, report);
|
2015-08-19 23:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// This class needs to retain no per-node data.
|
|
|
|
class NodeData { };
|
|
|
|
|
2017-03-11 07:52:55 +03:00
|
|
|
MOZ_MUST_USE JS_PUBLIC_API(bool) operator() (BreadthFirst<CensusHandler>& traversal,
|
|
|
|
Node origin, const Edge& edge,
|
|
|
|
NodeData* referentData, bool first);
|
2015-08-19 23:21:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
using CensusTraversal = BreadthFirst<CensusHandler>;
|
|
|
|
|
2015-12-16 11:19:00 +03:00
|
|
|
// Examine the census options supplied by the API consumer, and (among other
|
|
|
|
// things) use that to build a CountType tree.
|
2017-03-11 07:52:55 +03:00
|
|
|
MOZ_MUST_USE JS_PUBLIC_API(bool) ParseCensusOptions(JSContext* cx,
|
|
|
|
Census& census, HandleObject options,
|
|
|
|
CountTypePtr& outResult);
|
2015-08-19 23:21:08 +03:00
|
|
|
|
2015-12-16 11:19:00 +03:00
|
|
|
// 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.
|
2017-03-11 07:52:55 +03:00
|
|
|
JS_PUBLIC_API(CountTypePtr) ParseBreakdown(JSContext* cx, HandleValue breakdownValue);
|
2015-12-16 11:19:00 +03:00
|
|
|
|
|
|
|
|
2015-08-19 23:21:08 +03:00
|
|
|
} // namespace ubi
|
|
|
|
} // namespace JS
|
|
|
|
|
|
|
|
#endif // js_UbiNodeCensus_h
|