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