Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/UbiNodeCensus.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "mozilla/Attributes.h"
11
#include "mozilla/Move.h"
12
13
#include <algorithm>
14
15
#include "jsapi.h"
16
17
#include "js/UbiNode.h"
18
#include "js/UbiNodeBreadthFirst.h"
19
20
// A census is a ubi::Node traversal that assigns each node to one or more
21
// buckets, and returns a report with the size of each bucket.
22
//
23
// We summarize the results of a census with counts broken down according to
24
// criteria selected by the API consumer code that is requesting the census. For
25
// example, the following breakdown might give an interesting overview of the
26
// heap:
27
//
28
//   - all nodes
29
//     - objects
30
//       - objects with a specific [[Class]] *
31
//     - strings
32
//     - scripts
33
//     - DOM nodes
34
//       - nsINodes with a specific name (found in nsINode::NodeName()) *
35
//     - all other Node types
36
//       - nodes with a specific ubi::Node::typeName *
37
//
38
// Obviously, the parts of this tree marked with * represent many separate
39
// counts, depending on how many distinct [[Class]] values and ubi::Node type
40
// names we encounter.
41
//
42
// The supported types of breakdowns are documented in
43
// js/src/doc/Debugger/Debugger.Memory.md.
44
//
45
// When we parse the 'breakdown' argument to takeCensus, we build a tree of
46
// CountType nodes. For example, for the breakdown shown in the
47
// Debugger.Memory.prototype.takeCensus, documentation:
48
//
49
//    {
50
//      by: "coarseType",
51
//      objects: { by: "objectClass" },
52
//      other:    { by: "internalType" },
53
//      domNode: { by: "descriptiveType" }
54
//    }
55
//
56
// we would build the following tree of CountType subclasses:
57
//
58
//    ByCoarseType
59
//      objects: ByObjectClass
60
//        each class: SimpleCount
61
//      scripts: SimpleCount
62
//      strings: SimpleCount
63
//      other: ByUbinodeType
64
//        each type: SimpleCount
65
//      domNode: ByDomObjectClass
66
//        each type: SimpleCount
67
//
68
// The interior nodes are all breakdown types that categorize nodes according to
69
// one characteristic or another; and the leaf nodes are all SimpleType.
70
//
71
// Each CountType has its own concrete C++ type that holds the counts it
72
// produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
73
// hash table whose keys are object class names and whose values are counts of
74
// some other type (in the example above, SimpleCount).
75
//
76
// To keep actual count nodes small, they have no vtable. Instead, each count
77
// points to its CountType, which knows how to carry out all the operations we
78
// need on a Count. A CountType can produce new count nodes; process nodes as we
79
// visit them; build a JS object reporting the results; and destruct count
80
// nodes.
81
82
83
namespace JS {
84
namespace ubi {
85
86
struct Census;
87
88
class CountBase;
89
90
struct CountDeleter {
91
    JS_PUBLIC_API(void) operator()(CountBase*);
92
};
93
94
using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
95
96
// Abstract base class for CountType nodes.
97
struct CountType {
98
0
    explicit CountType() { }
99
0
    virtual ~CountType() { }
100
101
    // Destruct a count tree node that this type instance constructed.
102
    virtual void destructCount(CountBase& count) = 0;
103
104
    // Return a fresh node for the count tree that categorizes nodes according
105
    // to this type. Return a nullptr on OOM.
106
    virtual CountBasePtr makeCount() = 0;
107
108
    // Trace |count| and all its children, for garbage collection.
109
    virtual void traceCount(CountBase& count, JSTracer* trc) = 0;
110
111
    // Implement the 'count' method for counts returned by this CountType
112
    // instance's 'newCount' method.
113
    virtual MOZ_MUST_USE bool count(CountBase& count,
114
                                    mozilla::MallocSizeOf mallocSizeOf,
115
                                    const Node& node) = 0;
116
117
    // Implement the 'report' method for counts returned by this CountType
118
    // instance's 'newCount' method.
119
    virtual MOZ_MUST_USE bool report(JSContext* cx, CountBase& count,
120
                                     MutableHandleValue report) = 0;
121
};
122
123
using CountTypePtr = js::UniquePtr<CountType>;
124
125
// An abstract base class for count tree nodes.
126
class CountBase {
127
    // In lieu of a vtable, each CountBase points to its type, which
128
    // carries not only the implementations of the CountBase methods, but also
129
    // additional parameters for the type's behavior, as specified in the
130
    // breakdown argument passed to takeCensus.
131
    CountType& type;
132
133
  protected:
134
0
    ~CountBase() { }
135
136
  public:
137
    explicit CountBase(CountType& type)
138
      : type(type)
139
      , total_(0)
140
      , smallestNodeIdCounted_(SIZE_MAX)
141
0
    { }
142
143
    // Categorize and count |node| as appropriate for this count's type.
144
0
    MOZ_MUST_USE bool count(mozilla::MallocSizeOf mallocSizeOf, const Node& node) {
145
0
        total_++;
146
0
147
0
        auto id = node.identifier();
148
0
        if (id < smallestNodeIdCounted_) {
149
0
            smallestNodeIdCounted_ = id;
150
0
        }
151
0
152
#ifdef DEBUG
153
        size_t oldTotal = total_;
154
#endif
155
156
0
        bool ret = type.count(*this, mallocSizeOf, node);
157
0
158
0
        MOZ_ASSERT(total_ == oldTotal,
159
0
                   "CountType::count should not increment total_, CountBase::count handles that");
160
0
161
0
        return ret;
162
0
    }
163
164
    // Construct a JavaScript object reporting the counts recorded in this
165
    // count, and store it in |report|. Return true on success, or false on
166
    // failure.
167
0
    MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
168
0
        return type.report(cx, *this, report);
169
0
    }
170
171
    // Down-cast this CountBase to its true type, based on its 'type' member,
172
    // and run its destructor.
173
0
    void destruct() { return type.destructCount(*this); }
174
175
    // Trace this count for garbage collection.
176
0
    void trace(JSTracer* trc) { type.traceCount(*this, trc); }
177
178
    size_t total_;
179
180
    // The smallest JS::ubi::Node::identifier() passed to this instance's
181
    // count() method. This provides a stable way to sort sets.
182
    Node::Id smallestNodeIdCounted_;
183
};
184
185
class RootedCount : JS::CustomAutoRooter {
186
    CountBasePtr count;
187
188
0
    void trace(JSTracer* trc) override { count->trace(trc); }
189
190
  public:
191
    RootedCount(JSContext* cx, CountBasePtr&& count)
192
        : CustomAutoRooter(cx),
193
          count(std::move(count))
194
0
          { }
195
0
    CountBase* operator->() const { return count.get(); }
196
0
    explicit operator bool() const { return count.get(); }
197
0
    operator CountBasePtr&() { return count; }
198
};
199
200
// Common data for a census traversal, shared across all CountType nodes.
201
struct Census {
202
    JSContext* const cx;
203
    // If the targetZones set is non-empty, then only consider nodes whose zone
204
    // is an element of the set. If the targetZones set is empty, then nodes in
205
    // all zones are considered.
206
    JS::ZoneSet targetZones;
207
208
0
    explicit Census(JSContext* cx) : cx(cx) { }
209
};
210
211
// A BreadthFirst handler type that conducts a census, using a CountBase to
212
// categorize and count each node.
213
class CensusHandler {
214
    Census& census;
215
    CountBasePtr& rootCount;
216
    mozilla::MallocSizeOf mallocSizeOf;
217
218
  public:
219
    CensusHandler(Census& census, CountBasePtr& rootCount, mozilla::MallocSizeOf mallocSizeOf)
220
      : census(census),
221
        rootCount(rootCount),
222
        mallocSizeOf(mallocSizeOf)
223
0
    { }
224
225
0
    MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
226
0
        return rootCount->report(cx, report);
227
0
    }
228
229
    // This class needs to retain no per-node data.
230
    class NodeData { };
231
232
    MOZ_MUST_USE JS_PUBLIC_API(bool) operator() (BreadthFirst<CensusHandler>& traversal,
233
                                                 Node origin, const Edge& edge,
234
                                                 NodeData* referentData, bool first);
235
};
236
237
using CensusTraversal = BreadthFirst<CensusHandler>;
238
239
// Examine the census options supplied by the API consumer, and (among other
240
// things) use that to build a CountType tree.
241
MOZ_MUST_USE JS_PUBLIC_API(bool) ParseCensusOptions(JSContext* cx,
242
                                                    Census& census, HandleObject options,
243
                                                    CountTypePtr& outResult);
244
245
// Parse the breakdown language (as described in
246
// js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
247
// is returned on error and is reported to the cx.
248
JS_PUBLIC_API(CountTypePtr) ParseBreakdown(JSContext* cx, HandleValue breakdownValue);
249
250
251
} // namespace ubi
252
} // namespace JS
253
254
#endif // js_UbiNodeCensus_h