Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/MemoryMetrics.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_MemoryMetrics_h
8
#define js_MemoryMetrics_h
9
10
// These declarations are highly likely to change in the future. Depend on them
11
// at your own risk.
12
13
#include "mozilla/MemoryReporting.h"
14
#include "mozilla/TypeTraits.h"
15
16
#include <string.h>
17
18
#include "jspubtd.h"
19
20
#include "js/AllocPolicy.h"
21
#include "js/HashTable.h"
22
#include "js/TracingAPI.h"
23
#include "js/Utility.h"
24
#include "js/Vector.h"
25
26
class nsISupports;      // Needed for ObjectPrivateVisitor.
27
28
namespace JS {
29
30
struct TabSizes
31
{
32
    enum Kind {
33
        Objects,
34
        Strings,
35
        Private,
36
        Other
37
    };
38
39
    TabSizes()
40
      : objects(0)
41
      , strings(0)
42
      , private_(0)
43
      , other(0)
44
0
    {
45
0
    }
46
47
0
    void add(Kind kind, size_t n) {
48
0
        switch (kind) {
49
0
            case Objects: objects  += n; break;
50
0
            case Strings: strings  += n; break;
51
0
            case Private: private_ += n; break;
52
0
            case Other:   other    += n; break;
53
0
            default:      MOZ_CRASH("bad TabSizes kind");
54
0
        }
55
0
    }
56
57
    size_t objects;
58
    size_t strings;
59
    size_t private_;
60
    size_t other;
61
};
62
63
/** These are the measurements used by Servo. */
64
struct ServoSizes
65
{
66
    enum Kind {
67
        GCHeapUsed,
68
        GCHeapUnused,
69
        GCHeapAdmin,
70
        GCHeapDecommitted,
71
        MallocHeap,
72
        NonHeap,
73
        Ignore
74
    };
75
76
    ServoSizes() = default;
77
78
0
    void add(Kind kind, size_t n) {
79
0
        switch (kind) {
80
0
            case GCHeapUsed:        gcHeapUsed        += n; break;
81
0
            case GCHeapUnused:      gcHeapUnused      += n; break;
82
0
            case GCHeapAdmin:       gcHeapAdmin       += n; break;
83
0
            case GCHeapDecommitted: gcHeapDecommitted += n; break;
84
0
            case MallocHeap:        mallocHeap        += n; break;
85
0
            case NonHeap:           nonHeap           += n; break;
86
0
            case Ignore:            /* do nothing */        break;
87
0
            default:                MOZ_CRASH("bad ServoSizes kind");
88
0
        }
89
0
    }
90
91
    size_t gcHeapUsed = 0;
92
    size_t gcHeapUnused = 0;
93
    size_t gcHeapAdmin = 0;
94
    size_t gcHeapDecommitted = 0;
95
    size_t mallocHeap = 0;
96
    size_t nonHeap = 0;
97
};
98
99
} // namespace JS
100
101
namespace js {
102
103
/**
104
 * In memory reporting, we have concept of "sundries", line items which are too
105
 * small to be worth reporting individually.  Under some circumstances, a memory
106
 * reporter gets tossed into the sundries bucket if it's smaller than
107
 * MemoryReportingSundriesThreshold() bytes.
108
 *
109
 * We need to define this value here, rather than in the code which actually
110
 * generates the memory reports, because NotableStringInfo uses this value.
111
 */
112
JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
113
114
/**
115
 * This hash policy avoids flattening ropes (which perturbs the site being
116
 * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
117
 * on every hash and match! Beware.
118
 */
119
struct InefficientNonFlatteningStringHashPolicy
120
{
121
    typedef JSString* Lookup;
122
    static HashNumber hash(const Lookup& l);
123
    static bool match(const JSString* const& k, const Lookup& l);
124
};
125
126
// This file features many classes with numerous size_t fields, and each such
127
// class has one or more methods that need to operate on all of these fields.
128
// Writing these individually is error-prone -- it's easy to add a new field
129
// without updating all the required methods.  So we define a single macro list
130
// in each class to name the fields (and notable characteristics of them), and
131
// then use the following macros to transform those lists into the required
132
// methods.
133
//
134
// - The |tabKind| value is used when measuring TabSizes.
135
//
136
// - The |servoKind| value is used when measuring ServoSizes and also for
137
//   the various sizeOfLiveGCThings() methods.
138
//
139
// In some classes, one or more of the macro arguments aren't used.  We use '_'
140
// for those.
141
//
142
#define DECL_SIZE(tabKind, servoKind, mSize)        size_t mSize;
143
#define ZERO_SIZE(tabKind, servoKind, mSize)        mSize(0),
144
#define COPY_OTHER_SIZE(tabKind, servoKind, mSize)  mSize(other.mSize),
145
0
#define ADD_OTHER_SIZE(tabKind, servoKind, mSize)   mSize += other.mSize;
146
#define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \
147
0
    MOZ_ASSERT(mSize >= other.mSize); \
148
0
    mSize -= other.mSize;
149
0
#define ADD_SIZE_TO_N(tabKind, servoKind, mSize)                  n += mSize;
150
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \
151
    /* Avoid self-comparison warnings by comparing enums indirectly. */ \
152
0
    n += (mozilla::IsSame<int[ServoSizes::servoKind], int[ServoSizes::GCHeapUsed]>::value) \
153
0
         ? mSize \
154
0
         : 0;
155
0
#define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize)               sizes->add(JS::TabSizes::tabKind, mSize);
156
0
#define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize)             sizes->add(JS::ServoSizes::servoKind, mSize);
157
158
} // namespace js
159
160
namespace JS {
161
162
struct ClassInfo
163
{
164
#define FOR_EACH_SIZE(macro) \
165
0
    macro(Objects, GCHeapUsed, objectsGCHeap) \
166
0
    macro(Objects, MallocHeap, objectsMallocHeapSlots) \
167
0
    macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
168
0
    macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
169
0
    macro(Objects, MallocHeap, objectsMallocHeapMisc) \
170
0
    macro(Objects, NonHeap,    objectsNonHeapElementsNormal) \
171
0
    macro(Objects, NonHeap,    objectsNonHeapElementsShared) \
172
0
    macro(Objects, NonHeap,    objectsNonHeapElementsWasm) \
173
0
    macro(Objects, NonHeap,    objectsNonHeapCodeWasm)
174
175
    ClassInfo()
176
      : FOR_EACH_SIZE(ZERO_SIZE)
177
        wasmGuardPages(0)
178
0
    {}
179
180
0
    void add(const ClassInfo& other) {
181
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
182
0
    }
183
184
0
    void subtract(const ClassInfo& other) {
185
0
        FOR_EACH_SIZE(SUB_OTHER_SIZE)
186
0
    }
187
188
0
    size_t sizeOfAllThings() const {
189
0
        size_t n = 0;
190
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N)
191
0
        return n;
192
0
    }
193
194
0
    bool isNotable() const {
195
0
        static const size_t NotabilityThreshold = 16 * 1024;
196
0
        return sizeOfAllThings() >= NotabilityThreshold;
197
0
    }
198
199
0
    size_t sizeOfLiveGCThings() const {
200
0
        size_t n = 0;
201
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
202
0
        return n;
203
0
    }
204
205
0
    void addToTabSizes(TabSizes* sizes) const {
206
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
207
0
    }
208
209
0
    void addToServoSizes(ServoSizes *sizes) const {
210
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
211
0
    }
212
213
    FOR_EACH_SIZE(DECL_SIZE)
214
    size_t wasmGuardPages;
215
216
#undef FOR_EACH_SIZE
217
};
218
219
struct ShapeInfo
220
{
221
#define FOR_EACH_SIZE(macro) \
222
0
    macro(Other,   GCHeapUsed, shapesGCHeapTree) \
223
0
    macro(Other,   GCHeapUsed, shapesGCHeapDict) \
224
0
    macro(Other,   GCHeapUsed, shapesGCHeapBase) \
225
0
    macro(Other,   MallocHeap, shapesMallocHeapTreeTables) \
226
0
    macro(Other,   MallocHeap, shapesMallocHeapDictTables) \
227
0
    macro(Other,   MallocHeap, shapesMallocHeapTreeKids)
228
229
    ShapeInfo()
230
      : FOR_EACH_SIZE(ZERO_SIZE)
231
        dummy()
232
0
    {}
233
234
0
    void add(const ShapeInfo& other) {
235
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
236
0
    }
237
238
0
    void subtract(const ShapeInfo& other) {
239
0
        FOR_EACH_SIZE(SUB_OTHER_SIZE)
240
0
    }
241
242
0
    size_t sizeOfAllThings() const {
243
0
        size_t n = 0;
244
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N)
245
0
        return n;
246
0
    }
247
248
0
    size_t sizeOfLiveGCThings() const {
249
0
        size_t n = 0;
250
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
251
0
        return n;
252
0
    }
253
254
0
    void addToTabSizes(TabSizes* sizes) const {
255
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
256
0
    }
257
258
0
    void addToServoSizes(ServoSizes *sizes) const {
259
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
260
0
    }
261
262
    FOR_EACH_SIZE(DECL_SIZE)
263
    int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
264
265
#undef FOR_EACH_SIZE
266
};
267
268
/**
269
 * Holds data about a notable class (one whose combined object and shape
270
 * instances use more than a certain amount of memory) so we can report it
271
 * individually.
272
 *
273
 * The only difference between this class and ClassInfo is that this class
274
 * holds a copy of the filename.
275
 */
276
struct NotableClassInfo : public ClassInfo
277
{
278
    NotableClassInfo();
279
    NotableClassInfo(const char* className, const ClassInfo& info);
280
    NotableClassInfo(NotableClassInfo&& info);
281
    NotableClassInfo& operator=(NotableClassInfo&& info);
282
283
0
    ~NotableClassInfo() {
284
0
        js_free(className_);
285
0
    }
286
287
    char* className_;
288
289
  private:
290
    NotableClassInfo(const NotableClassInfo& info) = delete;
291
};
292
293
/** Data for tracking JIT-code memory usage. */
294
struct CodeSizes
295
{
296
#define FOR_EACH_SIZE(macro) \
297
0
    macro(_, NonHeap, ion) \
298
0
    macro(_, NonHeap, baseline) \
299
0
    macro(_, NonHeap, regexp) \
300
0
    macro(_, NonHeap, other) \
301
0
    macro(_, NonHeap, unused)
302
303
    CodeSizes()
304
      : FOR_EACH_SIZE(ZERO_SIZE)
305
        dummy()
306
0
    {}
307
308
0
    void addToServoSizes(ServoSizes *sizes) const {
309
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
310
0
    }
311
312
    FOR_EACH_SIZE(DECL_SIZE)
313
    int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
314
315
#undef FOR_EACH_SIZE
316
};
317
318
/** Data for tracking GC memory usage. */
319
struct GCSizes
320
{
321
    // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
322
    // because we don't consider the nursery to be part of the GC heap.
323
#define FOR_EACH_SIZE(macro) \
324
0
    macro(_, MallocHeap, marker) \
325
0
    macro(_, NonHeap,    nurseryCommitted) \
326
0
    macro(_, MallocHeap, nurseryMallocedBuffers) \
327
0
    macro(_, MallocHeap, storeBufferVals) \
328
0
    macro(_, MallocHeap, storeBufferCells) \
329
0
    macro(_, MallocHeap, storeBufferSlots) \
330
0
    macro(_, MallocHeap, storeBufferWholeCells) \
331
0
    macro(_, MallocHeap, storeBufferGenerics)
332
333
    GCSizes()
334
      : FOR_EACH_SIZE(ZERO_SIZE)
335
        dummy()
336
0
    {}
337
338
0
    void addToServoSizes(ServoSizes *sizes) const {
339
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
340
0
    }
341
342
    FOR_EACH_SIZE(DECL_SIZE)
343
    int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
344
345
#undef FOR_EACH_SIZE
346
};
347
348
/**
349
 * This class holds information about the memory taken up by identical copies of
350
 * a particular string.  Multiple JSStrings may have their sizes aggregated
351
 * together into one StringInfo object.  Note that two strings with identical
352
 * chars will not be aggregated together if one is a short string and the other
353
 * is not.
354
 */
355
struct StringInfo
356
{
357
#define FOR_EACH_SIZE(macro) \
358
0
    macro(Strings, GCHeapUsed, gcHeapLatin1) \
359
0
    macro(Strings, GCHeapUsed, gcHeapTwoByte) \
360
0
    macro(Strings, MallocHeap, mallocHeapLatin1) \
361
0
    macro(Strings, MallocHeap, mallocHeapTwoByte)
362
363
    StringInfo()
364
      : FOR_EACH_SIZE(ZERO_SIZE)
365
        numCopies(0)
366
0
    {}
367
368
0
    void add(const StringInfo& other) {
369
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE);
370
0
        numCopies++;
371
0
    }
372
373
0
    void subtract(const StringInfo& other) {
374
0
        FOR_EACH_SIZE(SUB_OTHER_SIZE);
375
0
        numCopies--;
376
0
    }
377
378
0
    bool isNotable() const {
379
0
        static const size_t NotabilityThreshold = 16 * 1024;
380
0
        size_t n = 0;
381
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N)
382
0
        return n >= NotabilityThreshold;
383
0
    }
384
385
0
    size_t sizeOfLiveGCThings() const {
386
0
        size_t n = 0;
387
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
388
0
        return n;
389
0
    }
390
391
0
    void addToTabSizes(TabSizes* sizes) const {
392
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
393
0
    }
394
395
0
    void addToServoSizes(ServoSizes *sizes) const {
396
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
397
0
    }
398
399
    FOR_EACH_SIZE(DECL_SIZE)
400
    uint32_t numCopies;     // How many copies of the string have we seen?
401
402
#undef FOR_EACH_SIZE
403
};
404
405
/**
406
 * Holds data about a notable string (one which, counting all duplicates, uses
407
 * more than a certain amount of memory) so we can report it individually.
408
 *
409
 * The only difference between this class and StringInfo is that
410
 * NotableStringInfo holds a copy of some or all of the string's chars.
411
 */
412
struct NotableStringInfo : public StringInfo
413
{
414
    static const size_t MAX_SAVED_CHARS = 1024;
415
416
    NotableStringInfo();
417
    NotableStringInfo(JSString* str, const StringInfo& info);
418
    NotableStringInfo(NotableStringInfo&& info);
419
    NotableStringInfo& operator=(NotableStringInfo&& info);
420
421
0
    ~NotableStringInfo() {
422
0
        js_free(buffer);
423
0
    }
424
425
    char* buffer;
426
    size_t length;
427
428
  private:
429
    NotableStringInfo(const NotableStringInfo& info) = delete;
430
};
431
432
/**
433
 * This class holds information about the memory taken up by script sources
434
 * from a particular file.
435
 */
436
struct ScriptSourceInfo
437
{
438
#define FOR_EACH_SIZE(macro) \
439
0
    macro(_, MallocHeap, misc)
440
441
    ScriptSourceInfo()
442
      : FOR_EACH_SIZE(ZERO_SIZE)
443
        numScripts(0)
444
0
    {}
445
446
0
    void add(const ScriptSourceInfo& other) {
447
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
448
0
        numScripts++;
449
0
    }
450
451
0
    void subtract(const ScriptSourceInfo& other) {
452
0
        FOR_EACH_SIZE(SUB_OTHER_SIZE)
453
0
        numScripts--;
454
0
    }
455
456
0
    void addToServoSizes(ServoSizes *sizes) const {
457
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
458
0
    }
459
460
0
    bool isNotable() const {
461
0
        static const size_t NotabilityThreshold = 16 * 1024;
462
0
        size_t n = 0;
463
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N)
464
0
        return n >= NotabilityThreshold;
465
0
    }
466
467
    FOR_EACH_SIZE(DECL_SIZE)
468
    uint32_t numScripts;    // How many ScriptSources come from this file? (It
469
                            // can be more than one in XML files that have
470
                            // multiple scripts in CDATA sections.)
471
#undef FOR_EACH_SIZE
472
};
473
474
/**
475
 * Holds data about a notable script source file (one whose combined
476
 * script sources use more than a certain amount of memory) so we can report it
477
 * individually.
478
 *
479
 * The only difference between this class and ScriptSourceInfo is that this
480
 * class holds a copy of the filename.
481
 */
482
struct NotableScriptSourceInfo : public ScriptSourceInfo
483
{
484
    NotableScriptSourceInfo();
485
    NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
486
    NotableScriptSourceInfo(NotableScriptSourceInfo&& info);
487
    NotableScriptSourceInfo& operator=(NotableScriptSourceInfo&& info);
488
489
0
    ~NotableScriptSourceInfo() {
490
0
        js_free(filename_);
491
0
    }
492
493
    char* filename_;
494
495
  private:
496
    NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
497
};
498
499
struct HelperThreadStats
500
{
501
#define FOR_EACH_SIZE(macro) \
502
    macro(_, MallocHeap, stateData) \
503
    macro(_, MallocHeap, parseTask) \
504
    macro(_, MallocHeap, ionBuilder) \
505
    macro(_, MallocHeap, wasmCompile)
506
507
    explicit HelperThreadStats()
508
      : FOR_EACH_SIZE(ZERO_SIZE)
509
        idleThreadCount(0),
510
        activeThreadCount(0)
511
0
    { }
512
513
    FOR_EACH_SIZE(DECL_SIZE)
514
515
    unsigned idleThreadCount;
516
    unsigned activeThreadCount;
517
518
#undef FOR_EACH_SIZE
519
};
520
521
/**
522
 * Measurements that not associated with any individual runtime.
523
 */
524
struct GlobalStats
525
{
526
#define FOR_EACH_SIZE(macro) \
527
    macro(_, MallocHeap, tracelogger)
528
529
    explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
530
      : FOR_EACH_SIZE(ZERO_SIZE)
531
        mallocSizeOf_(mallocSizeOf)
532
0
    { }
533
534
    FOR_EACH_SIZE(DECL_SIZE)
535
536
    HelperThreadStats helperThread;
537
538
    mozilla::MallocSizeOf mallocSizeOf_;
539
540
#undef FOR_EACH_SIZE
541
};
542
543
/**
544
 * These measurements relate directly to the JSRuntime, and not to zones,
545
 * compartments, and realms within it.
546
 */
547
struct RuntimeSizes
548
{
549
#define FOR_EACH_SIZE(macro) \
550
0
    macro(_, MallocHeap, object) \
551
0
    macro(_, MallocHeap, atomsTable) \
552
0
    macro(_, MallocHeap, atomsMarkBitmaps) \
553
0
    macro(_, MallocHeap, contexts) \
554
0
    macro(_, MallocHeap, temporary) \
555
0
    macro(_, MallocHeap, interpreterStack) \
556
0
    macro(_, MallocHeap, sharedImmutableStringsCache) \
557
0
    macro(_, MallocHeap, sharedIntlData) \
558
0
    macro(_, MallocHeap, uncompressedSourceCache) \
559
0
    macro(_, MallocHeap, scriptData) \
560
0
    macro(_, MallocHeap, tracelogger) \
561
0
    macro(_, MallocHeap, wasmRuntime) \
562
0
    macro(_, MallocHeap, jitLazyLink)
563
564
    RuntimeSizes()
565
      : FOR_EACH_SIZE(ZERO_SIZE)
566
        scriptSourceInfo(),
567
        code(),
568
        gc(),
569
        notableScriptSources()
570
0
    {
571
0
        allScriptSources = js_new<ScriptSourcesHashMap>();
572
0
        if (!allScriptSources) {
573
0
            MOZ_CRASH("oom");
574
0
        }
575
0
    }
576
577
0
    ~RuntimeSizes() {
578
0
        // |allScriptSources| is usually deleted and set to nullptr before this
579
0
        // destructor runs. But there are failure cases due to OOMs that may
580
0
        // prevent that, so it doesn't hurt to try again here.
581
0
        js_delete(allScriptSources);
582
0
    }
583
584
0
    void addToServoSizes(ServoSizes *sizes) const {
585
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
586
0
        scriptSourceInfo.addToServoSizes(sizes);
587
0
        code.addToServoSizes(sizes);
588
0
        gc.addToServoSizes(sizes);
589
0
    }
590
591
    // The script source measurements in |scriptSourceInfo| are initially for
592
    // all script sources.  At the end, if the measurement granularity is
593
    // FineGrained, we subtract the measurements of the notable script sources
594
    // and move them into |notableScriptSources|.
595
    FOR_EACH_SIZE(DECL_SIZE)
596
    ScriptSourceInfo scriptSourceInfo;
597
    CodeSizes code;
598
    GCSizes gc;
599
600
    typedef js::HashMap<const char*, ScriptSourceInfo,
601
                        mozilla::CStringHasher,
602
                        js::SystemAllocPolicy> ScriptSourcesHashMap;
603
604
    // |allScriptSources| is only used transiently.  During the reporting phase
605
    // it is filled with info about every script source in the runtime.  It's
606
    // then used to fill in |notableScriptSources| (which actually gets
607
    // reported), and immediately discarded afterwards.
608
    ScriptSourcesHashMap* allScriptSources;
609
    js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
610
611
#undef FOR_EACH_SIZE
612
};
613
614
struct UnusedGCThingSizes
615
{
616
#define FOR_EACH_SIZE(macro) \
617
0
    macro(Other, GCHeapUnused, object) \
618
0
    macro(Other, GCHeapUnused, script) \
619
0
    macro(Other, GCHeapUnused, lazyScript) \
620
0
    macro(Other, GCHeapUnused, shape) \
621
0
    macro(Other, GCHeapUnused, baseShape) \
622
0
    macro(Other, GCHeapUnused, objectGroup) \
623
0
    macro(Other, GCHeapUnused, string) \
624
0
    macro(Other, GCHeapUnused, symbol) \
625
0
    IF_BIGINT(macro(Other, GCHeapUnused, bigInt),) \
626
0
    macro(Other, GCHeapUnused, jitcode) \
627
0
    macro(Other, GCHeapUnused, scope) \
628
0
    macro(Other, GCHeapUnused, regExpShared)
629
630
    UnusedGCThingSizes()
631
      : FOR_EACH_SIZE(ZERO_SIZE)
632
        dummy()
633
0
    {}
634
635
    UnusedGCThingSizes(UnusedGCThingSizes&& other)
636
      : FOR_EACH_SIZE(COPY_OTHER_SIZE)
637
        dummy()
638
0
    {}
639
640
0
    void addToKind(JS::TraceKind kind, intptr_t n) {
641
0
        switch (kind) {
642
0
          case JS::TraceKind::Object:       object += n;       break;
643
0
          case JS::TraceKind::String:       string += n;       break;
644
0
          case JS::TraceKind::Symbol:       symbol += n;       break;
645
#ifdef ENABLE_BIGINT
646
          case JS::TraceKind::BigInt:       bigInt += n;       break;
647
#endif
648
0
          case JS::TraceKind::Script:       script += n;       break;
649
0
          case JS::TraceKind::Shape:        shape += n;        break;
650
0
          case JS::TraceKind::BaseShape:    baseShape += n;    break;
651
0
          case JS::TraceKind::JitCode:      jitcode += n;      break;
652
0
          case JS::TraceKind::LazyScript:   lazyScript += n;   break;
653
0
          case JS::TraceKind::ObjectGroup:  objectGroup += n;  break;
654
0
          case JS::TraceKind::Scope:        scope += n;        break;
655
0
          case JS::TraceKind::RegExpShared: regExpShared += n; break;
656
0
          default:
657
0
            MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
658
0
        }
659
0
    }
660
661
0
    void addSizes(const UnusedGCThingSizes& other) {
662
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
663
0
    }
664
665
0
    size_t totalSize() const {
666
0
        size_t n = 0;
667
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N)
668
0
        return n;
669
0
    }
670
671
0
    void addToTabSizes(JS::TabSizes *sizes) const {
672
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
673
0
    }
674
675
0
    void addToServoSizes(JS::ServoSizes *sizes) const {
676
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
677
0
    }
678
679
    FOR_EACH_SIZE(DECL_SIZE)
680
    int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
681
682
#undef FOR_EACH_SIZE
683
};
684
685
struct ZoneStats
686
{
687
#define FOR_EACH_SIZE(macro) \
688
0
    macro(Other,   GCHeapUsed,  symbolsGCHeap) \
689
0
    IF_BIGINT(macro(Other,   GCHeapUsed,  bigIntsGCHeap),) \
690
0
    IF_BIGINT(macro(Other,   MallocHeap,  bigIntsMallocHeap),) \
691
0
    macro(Other,   GCHeapAdmin, gcHeapArenaAdmin) \
692
0
    macro(Other,   GCHeapUsed,  lazyScriptsGCHeap) \
693
0
    macro(Other,   MallocHeap,  lazyScriptsMallocHeap) \
694
0
    macro(Other,   GCHeapUsed,  jitCodesGCHeap) \
695
0
    macro(Other,   GCHeapUsed,  objectGroupsGCHeap) \
696
0
    macro(Other,   MallocHeap,  objectGroupsMallocHeap) \
697
0
    macro(Other,   GCHeapUsed,  scopesGCHeap) \
698
0
    macro(Other,   MallocHeap,  scopesMallocHeap) \
699
0
    macro(Other,   GCHeapUsed,  regExpSharedsGCHeap) \
700
0
    macro(Other,   MallocHeap,  regExpSharedsMallocHeap) \
701
0
    macro(Other,   MallocHeap,  typePool) \
702
0
    macro(Other,   MallocHeap,  regexpZone) \
703
0
    macro(Other,   MallocHeap,  jitZone) \
704
0
    macro(Other,   MallocHeap,  baselineStubsOptimized) \
705
0
    macro(Other,   MallocHeap,  cachedCFG) \
706
0
    macro(Other,   MallocHeap,  uniqueIdMap) \
707
0
    macro(Other,   MallocHeap,  shapeTables) \
708
0
    macro(Other,   MallocHeap,  compartmentObjects) \
709
0
    macro(Other,   MallocHeap,  crossCompartmentWrappersTables) \
710
0
    macro(Other,   MallocHeap,  compartmentsPrivateData)
711
712
    ZoneStats()
713
      : FOR_EACH_SIZE(ZERO_SIZE)
714
        unusedGCThings(),
715
        stringInfo(),
716
        shapeInfo(),
717
        extra(),
718
        allStrings(nullptr),
719
        notableStrings(),
720
        isTotals(true)
721
0
    {}
722
723
    ZoneStats(ZoneStats&& other)
724
      : FOR_EACH_SIZE(COPY_OTHER_SIZE)
725
        unusedGCThings(std::move(other.unusedGCThings)),
726
        stringInfo(std::move(other.stringInfo)),
727
        shapeInfo(std::move(other.shapeInfo)),
728
        extra(other.extra),
729
        allStrings(other.allStrings),
730
        notableStrings(std::move(other.notableStrings)),
731
        isTotals(other.isTotals)
732
0
    {
733
0
        other.allStrings = nullptr;
734
0
        MOZ_ASSERT(!other.isTotals);
735
0
    }
736
737
0
    ~ZoneStats() {
738
0
        // |allStrings| is usually deleted and set to nullptr before this
739
0
        // destructor runs. But there are failure cases due to OOMs that may
740
0
        // prevent that, so it doesn't hurt to try again here.
741
0
        js_delete(allStrings);
742
0
    }
743
744
    bool initStrings();
745
746
0
    void addSizes(const ZoneStats& other) {
747
0
        MOZ_ASSERT(isTotals);
748
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
749
0
        unusedGCThings.addSizes(other.unusedGCThings);
750
0
        stringInfo.add(other.stringInfo);
751
0
        shapeInfo.add(other.shapeInfo);
752
0
    }
753
754
0
    size_t sizeOfLiveGCThings() const {
755
0
        MOZ_ASSERT(isTotals);
756
0
        size_t n = 0;
757
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
758
0
        n += stringInfo.sizeOfLiveGCThings();
759
0
        n += shapeInfo.sizeOfLiveGCThings();
760
0
        return n;
761
0
    }
762
763
0
    void addToTabSizes(JS::TabSizes* sizes) const {
764
0
        MOZ_ASSERT(isTotals);
765
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
766
0
        unusedGCThings.addToTabSizes(sizes);
767
0
        stringInfo.addToTabSizes(sizes);
768
0
        shapeInfo.addToTabSizes(sizes);
769
0
    }
770
771
0
    void addToServoSizes(JS::ServoSizes *sizes) const {
772
0
        MOZ_ASSERT(isTotals);
773
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
774
0
        unusedGCThings.addToServoSizes(sizes);
775
0
        stringInfo.addToServoSizes(sizes);
776
0
        shapeInfo.addToServoSizes(sizes);
777
0
    }
778
779
    // These string measurements are initially for all strings.  At the end,
780
    // if the measurement granularity is FineGrained, we subtract the
781
    // measurements of the notable script sources and move them into
782
    // |notableStrings|.
783
    FOR_EACH_SIZE(DECL_SIZE)
784
    UnusedGCThingSizes unusedGCThings;
785
    StringInfo stringInfo;
786
    ShapeInfo shapeInfo;
787
    void* extra;    // This field can be used by embedders.
788
789
    typedef js::HashMap<JSString*, StringInfo,
790
                        js::InefficientNonFlatteningStringHashPolicy,
791
                        js::SystemAllocPolicy> StringsHashMap;
792
793
    // |allStrings| is only used transiently.  During the zone traversal it is
794
    // filled with info about every string in the zone.  It's then used to fill
795
    // in |notableStrings| (which actually gets reported), and immediately
796
    // discarded afterwards.
797
    StringsHashMap* allStrings;
798
    js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
799
    bool isTotals;
800
801
#undef FOR_EACH_SIZE
802
};
803
804
struct RealmStats
805
{
806
    // We assume that |objectsPrivate| is on the malloc heap, but it's not
807
    // actually guaranteed. But for Servo, at least, it's a moot point because
808
    // it doesn't provide an ObjectPrivateVisitor so the value will always be
809
    // zero.
810
#define FOR_EACH_SIZE(macro) \
811
0
    macro(Private, MallocHeap, objectsPrivate) \
812
0
    macro(Other,   GCHeapUsed, scriptsGCHeap) \
813
0
    macro(Other,   MallocHeap, scriptsMallocHeapData) \
814
0
    macro(Other,   MallocHeap, baselineData) \
815
0
    macro(Other,   MallocHeap, baselineStubsFallback) \
816
0
    macro(Other,   MallocHeap, ionData) \
817
0
    macro(Other,   MallocHeap, typeInferenceTypeScripts) \
818
0
    macro(Other,   MallocHeap, typeInferenceAllocationSiteTables) \
819
0
    macro(Other,   MallocHeap, typeInferenceArrayTypeTables) \
820
0
    macro(Other,   MallocHeap, typeInferenceObjectTypeTables) \
821
0
    macro(Other,   MallocHeap, realmObject) \
822
0
    macro(Other,   MallocHeap, realmTables) \
823
0
    macro(Other,   MallocHeap, innerViewsTable) \
824
0
    macro(Other,   MallocHeap, lazyArrayBuffersTable) \
825
0
    macro(Other,   MallocHeap, objectMetadataTable) \
826
0
    macro(Other,   MallocHeap, savedStacksSet) \
827
0
    macro(Other,   MallocHeap, varNamesSet) \
828
0
    macro(Other,   MallocHeap, nonSyntacticLexicalScopesTable) \
829
0
    macro(Other,   MallocHeap, jitRealm) \
830
0
    macro(Other,   MallocHeap, scriptCountsMap)
831
832
    RealmStats()
833
      : FOR_EACH_SIZE(ZERO_SIZE)
834
        classInfo(),
835
        extra(),
836
        allClasses(nullptr),
837
        notableClasses(),
838
        isTotals(true)
839
0
    {}
840
841
    RealmStats(RealmStats&& other)
842
      : FOR_EACH_SIZE(COPY_OTHER_SIZE)
843
        classInfo(std::move(other.classInfo)),
844
        extra(other.extra),
845
        allClasses(other.allClasses),
846
        notableClasses(std::move(other.notableClasses)),
847
        isTotals(other.isTotals)
848
0
    {
849
0
        other.allClasses = nullptr;
850
0
        MOZ_ASSERT(!other.isTotals);
851
0
    }
852
853
    RealmStats(const RealmStats&) = delete; // disallow copying
854
855
0
    ~RealmStats() {
856
0
        // |allClasses| is usually deleted and set to nullptr before this
857
0
        // destructor runs. But there are failure cases due to OOMs that may
858
0
        // prevent that, so it doesn't hurt to try again here.
859
0
        js_delete(allClasses);
860
0
    }
861
862
    bool initClasses();
863
864
0
    void addSizes(const RealmStats& other) {
865
0
        MOZ_ASSERT(isTotals);
866
0
        FOR_EACH_SIZE(ADD_OTHER_SIZE)
867
0
        classInfo.add(other.classInfo);
868
0
    }
869
870
0
    size_t sizeOfLiveGCThings() const {
871
0
        MOZ_ASSERT(isTotals);
872
0
        size_t n = 0;
873
0
        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
874
0
        n += classInfo.sizeOfLiveGCThings();
875
0
        return n;
876
0
    }
877
878
0
    void addToTabSizes(TabSizes* sizes) const {
879
0
        MOZ_ASSERT(isTotals);
880
0
        FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
881
0
        classInfo.addToTabSizes(sizes);
882
0
    }
883
884
0
    void addToServoSizes(ServoSizes *sizes) const {
885
0
        MOZ_ASSERT(isTotals);
886
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
887
0
        classInfo.addToServoSizes(sizes);
888
0
    }
889
890
    // The class measurements in |classInfo| are initially for all classes.  At
891
    // the end, if the measurement granularity is FineGrained, we subtract the
892
    // measurements of the notable classes and move them into |notableClasses|.
893
    FOR_EACH_SIZE(DECL_SIZE)
894
    ClassInfo classInfo;
895
    void* extra;            // This field can be used by embedders.
896
897
    typedef js::HashMap<const char*, ClassInfo,
898
                        mozilla::CStringHasher,
899
                        js::SystemAllocPolicy> ClassesHashMap;
900
901
    // These are similar to |allStrings| and |notableStrings| in ZoneStats.
902
    ClassesHashMap* allClasses;
903
    js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
904
    bool isTotals;
905
906
#undef FOR_EACH_SIZE
907
};
908
909
typedef js::Vector<RealmStats, 0, js::SystemAllocPolicy> RealmStatsVector;
910
typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
911
912
struct RuntimeStats
913
{
914
    // |gcHeapChunkTotal| is ignored because it's the sum of all the other
915
    // values. |gcHeapGCThings| is ignored because it's the sum of some of the
916
    // values from the zones and compartments. Both of those values are not
917
    // reported directly, but are just present for sanity-checking other
918
    // values.
919
#define FOR_EACH_SIZE(macro) \
920
0
    macro(_, Ignore,            gcHeapChunkTotal) \
921
0
    macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
922
0
    macro(_, GCHeapUnused,      gcHeapUnusedChunks) \
923
0
    macro(_, GCHeapUnused,      gcHeapUnusedArenas) \
924
0
    macro(_, GCHeapAdmin,       gcHeapChunkAdmin) \
925
0
    macro(_, Ignore,            gcHeapGCThings)
926
927
    explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
928
      : FOR_EACH_SIZE(ZERO_SIZE)
929
        runtime(),
930
        realmTotals(),
931
        zTotals(),
932
        realmStatsVector(),
933
        zoneStatsVector(),
934
        currZoneStats(nullptr),
935
        mallocSizeOf_(mallocSizeOf)
936
0
    {}
937
938
    // Here's a useful breakdown of the GC heap.
939
    //
940
    // - rtStats.gcHeapChunkTotal
941
    //   - decommitted bytes
942
    //     - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
943
    //   - unused bytes
944
    //     - rtStats.gcHeapUnusedChunks (empty chunks)
945
    //     - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
946
    //     - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas)
947
    //   - used bytes
948
    //     - rtStats.gcHeapChunkAdmin
949
    //     - rtStats.zTotals.gcHeapArenaAdmin
950
    //     - rtStats.gcHeapGCThings (in-use GC things)
951
    //       == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings()
952
    //
953
    // It's possible that some arenas in empty chunks may be decommitted, but
954
    // we don't count those under rtStats.gcHeapDecommittedArenas because (a)
955
    // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
956
    // multiple of the chunk size, which is good.
957
958
0
    void addToServoSizes(ServoSizes *sizes) const {
959
0
        FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
960
0
        runtime.addToServoSizes(sizes);
961
0
    }
962
963
    FOR_EACH_SIZE(DECL_SIZE)
964
965
    RuntimeSizes runtime;
966
967
    RealmStats realmTotals;     // The sum of this runtime's realms' measurements.
968
    ZoneStats zTotals;          // The sum of this runtime's zones' measurements.
969
970
    RealmStatsVector realmStatsVector;
971
    ZoneStatsVector zoneStatsVector;
972
973
    ZoneStats* currZoneStats;
974
975
    mozilla::MallocSizeOf mallocSizeOf_;
976
977
    virtual void initExtraRealmStats(JS::Handle<JS::Realm*> realm, RealmStats* rstats) = 0;
978
    virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats) = 0;
979
980
#undef FOR_EACH_SIZE
981
};
982
983
class ObjectPrivateVisitor
984
{
985
  public:
986
    // Within CollectRuntimeStats, this method is called for each JS object
987
    // that has an nsISupports pointer.
988
    virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
989
990
    // A callback that gets a JSObject's nsISupports pointer, if it has one.
991
    // Note: this function does *not* addref |iface|.
992
    typedef bool(*GetISupportsFun)(JSObject* obj, nsISupports** iface);
993
    GetISupportsFun getISupports_;
994
995
    explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
996
      : getISupports_(getISupports)
997
0
    {}
998
};
999
1000
extern JS_PUBLIC_API(bool)
1001
CollectGlobalStats(GlobalStats* gStats);
1002
1003
extern JS_PUBLIC_API(bool)
1004
CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize);
1005
1006
extern JS_PUBLIC_API(size_t)
1007
SystemRealmCount(JSContext* cx);
1008
1009
extern JS_PUBLIC_API(size_t)
1010
UserRealmCount(JSContext* cx);
1011
1012
extern JS_PUBLIC_API(size_t)
1013
PeakSizeOfTemporary(const JSContext* cx);
1014
1015
extern JS_PUBLIC_API(bool)
1016
AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
1017
             ObjectPrivateVisitor* opv, TabSizes* sizes);
1018
1019
extern JS_PUBLIC_API(bool)
1020
AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf,
1021
               ObjectPrivateVisitor* opv, ServoSizes* sizes);
1022
1023
} // namespace JS
1024
1025
#undef DECL_SIZE
1026
#undef ZERO_SIZE
1027
#undef COPY_OTHER_SIZE
1028
#undef ADD_OTHER_SIZE
1029
#undef SUB_OTHER_SIZE
1030
#undef ADD_SIZE_TO_N
1031
#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
1032
#undef ADD_TO_TAB_SIZES
1033
1034
#endif /* js_MemoryMetrics_h */