|
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/NullPtr.h" |
|
15 #include "mozilla/PodOperations.h" |
|
16 |
|
17 #include <string.h> |
|
18 |
|
19 #include "jsalloc.h" |
|
20 #include "jspubtd.h" |
|
21 |
|
22 #include "js/HashTable.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() { mozilla::PodZero(this); } |
|
40 |
|
41 void add(Kind kind, size_t n) { |
|
42 switch (kind) { |
|
43 case Objects: objects += n; break; |
|
44 case Strings: strings += n; break; |
|
45 case Private: private_ += n; break; |
|
46 case Other: other += n; break; |
|
47 default: MOZ_CRASH("bad TabSizes kind"); |
|
48 } |
|
49 } |
|
50 |
|
51 size_t objects; |
|
52 size_t strings; |
|
53 size_t private_; |
|
54 size_t other; |
|
55 }; |
|
56 |
|
57 } // namespace JS |
|
58 |
|
59 namespace js { |
|
60 |
|
61 // In memory reporting, we have concept of "sundries", line items which are too |
|
62 // small to be worth reporting individually. Under some circumstances, a memory |
|
63 // reporter gets tossed into the sundries bucket if it's smaller than |
|
64 // MemoryReportingSundriesThreshold() bytes. |
|
65 // |
|
66 // We need to define this value here, rather than in the code which actually |
|
67 // generates the memory reports, because NotableStringInfo uses this value. |
|
68 JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold(); |
|
69 |
|
70 // This hash policy avoids flattening ropes (which perturbs the site being |
|
71 // measured and requires a JSContext) at the expense of doing a FULL ROPE COPY |
|
72 // on every hash and match! Beware. |
|
73 struct InefficientNonFlatteningStringHashPolicy |
|
74 { |
|
75 typedef JSString *Lookup; |
|
76 static HashNumber hash(const Lookup &l); |
|
77 static bool match(const JSString *const &k, const Lookup &l); |
|
78 }; |
|
79 |
|
80 struct CStringHashPolicy |
|
81 { |
|
82 typedef const char *Lookup; |
|
83 static HashNumber hash(const Lookup &l); |
|
84 static bool match(const char *const &k, const Lookup &l); |
|
85 }; |
|
86 |
|
87 // This file features many classes with numerous size_t fields, and each such |
|
88 // class has one or more methods that need to operate on all of these fields. |
|
89 // Writing these individually is error-prone -- it's easy to add a new field |
|
90 // without updating all the required methods. So we define a single macro list |
|
91 // in each class to name the fields (and notable characteristics of them), and |
|
92 // then use the following macros to transform those lists into the required |
|
93 // methods. |
|
94 // |
|
95 // In some classes, one or more of the macro arguments aren't used. We use '_' |
|
96 // for those. |
|
97 // |
|
98 #define DECL_SIZE(kind, gc, mSize) size_t mSize; |
|
99 #define ZERO_SIZE(kind, gc, mSize) mSize(0), |
|
100 #define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize), |
|
101 #define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize; |
|
102 #define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \ |
|
103 mSize -= other.mSize; |
|
104 #define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize; |
|
105 #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0; |
|
106 #define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize); |
|
107 |
|
108 // Used to annotate which size_t fields measure live GC things and which don't. |
|
109 enum { |
|
110 NotLiveGCThing = false, |
|
111 IsLiveGCThing = true |
|
112 }; |
|
113 |
|
114 } // namespace js |
|
115 |
|
116 namespace JS { |
|
117 |
|
118 // Data for tracking memory usage of things hanging off objects. |
|
119 struct ObjectsExtraSizes |
|
120 { |
|
121 #define FOR_EACH_SIZE(macro) \ |
|
122 macro(Objects, NotLiveGCThing, mallocHeapSlots) \ |
|
123 macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \ |
|
124 macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \ |
|
125 macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \ |
|
126 macro(Objects, NotLiveGCThing, nonHeapElementsMapped) \ |
|
127 macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \ |
|
128 macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \ |
|
129 macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \ |
|
130 macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \ |
|
131 macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \ |
|
132 macro(Objects, NotLiveGCThing, mallocHeapCtypesData) |
|
133 |
|
134 ObjectsExtraSizes() |
|
135 : FOR_EACH_SIZE(ZERO_SIZE) |
|
136 dummy() |
|
137 {} |
|
138 |
|
139 void add(const ObjectsExtraSizes &other) { |
|
140 FOR_EACH_SIZE(ADD_OTHER_SIZE) |
|
141 } |
|
142 |
|
143 size_t sizeOfLiveGCThings() const { |
|
144 size_t n = 0; |
|
145 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) |
|
146 return n; |
|
147 } |
|
148 |
|
149 void addToTabSizes(TabSizes *sizes) const { |
|
150 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) |
|
151 } |
|
152 |
|
153 FOR_EACH_SIZE(DECL_SIZE) |
|
154 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) |
|
155 |
|
156 #undef FOR_EACH_SIZE |
|
157 }; |
|
158 |
|
159 // Data for tracking JIT-code memory usage. |
|
160 struct CodeSizes |
|
161 { |
|
162 #define FOR_EACH_SIZE(macro) \ |
|
163 macro(_, _, ion) \ |
|
164 macro(_, _, baseline) \ |
|
165 macro(_, _, regexp) \ |
|
166 macro(_, _, other) \ |
|
167 macro(_, _, unused) |
|
168 |
|
169 CodeSizes() |
|
170 : FOR_EACH_SIZE(ZERO_SIZE) |
|
171 dummy() |
|
172 {} |
|
173 |
|
174 FOR_EACH_SIZE(DECL_SIZE) |
|
175 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) |
|
176 |
|
177 #undef FOR_EACH_SIZE |
|
178 }; |
|
179 |
|
180 // Data for tracking GC memory usage. |
|
181 struct GCSizes |
|
182 { |
|
183 #define FOR_EACH_SIZE(macro) \ |
|
184 macro(_, _, marker) \ |
|
185 macro(_, _, nurseryCommitted) \ |
|
186 macro(_, _, nurseryDecommitted) \ |
|
187 macro(_, _, nurseryHugeSlots) \ |
|
188 macro(_, _, storeBufferVals) \ |
|
189 macro(_, _, storeBufferCells) \ |
|
190 macro(_, _, storeBufferSlots) \ |
|
191 macro(_, _, storeBufferWholeCells) \ |
|
192 macro(_, _, storeBufferRelocVals) \ |
|
193 macro(_, _, storeBufferRelocCells) \ |
|
194 macro(_, _, storeBufferGenerics) |
|
195 |
|
196 GCSizes() |
|
197 : FOR_EACH_SIZE(ZERO_SIZE) |
|
198 dummy() |
|
199 {} |
|
200 |
|
201 FOR_EACH_SIZE(DECL_SIZE) |
|
202 int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) |
|
203 |
|
204 #undef FOR_EACH_SIZE |
|
205 }; |
|
206 |
|
207 // This class holds information about the memory taken up by identical copies of |
|
208 // a particular string. Multiple JSStrings may have their sizes aggregated |
|
209 // together into one StringInfo object. Note that two strings with identical |
|
210 // chars will not be aggregated together if one is a short string and the other |
|
211 // is not. |
|
212 struct StringInfo |
|
213 { |
|
214 #define FOR_EACH_SIZE(macro) \ |
|
215 macro(Strings, IsLiveGCThing, gcHeap) \ |
|
216 macro(Strings, NotLiveGCThing, mallocHeap) \ |
|
217 |
|
218 StringInfo() |
|
219 : FOR_EACH_SIZE(ZERO_SIZE) |
|
220 numCopies(0) |
|
221 {} |
|
222 |
|
223 void add(const StringInfo &other) { |
|
224 FOR_EACH_SIZE(ADD_OTHER_SIZE); |
|
225 numCopies++; |
|
226 } |
|
227 |
|
228 void subtract(const StringInfo &other) { |
|
229 FOR_EACH_SIZE(SUB_OTHER_SIZE); |
|
230 numCopies--; |
|
231 } |
|
232 |
|
233 bool isNotable() const { |
|
234 static const size_t NotabilityThreshold = 16 * 1024; |
|
235 size_t n = 0; |
|
236 FOR_EACH_SIZE(ADD_SIZE_TO_N) |
|
237 return n >= NotabilityThreshold; |
|
238 } |
|
239 |
|
240 size_t sizeOfLiveGCThings() const { |
|
241 size_t n = 0; |
|
242 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) |
|
243 return n; |
|
244 } |
|
245 |
|
246 void addToTabSizes(TabSizes *sizes) const { |
|
247 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) |
|
248 } |
|
249 |
|
250 FOR_EACH_SIZE(DECL_SIZE) |
|
251 uint32_t numCopies; // How many copies of the string have we seen? |
|
252 |
|
253 #undef FOR_EACH_SIZE |
|
254 }; |
|
255 |
|
256 // Holds data about a notable string (one which, counting all duplicates, uses |
|
257 // more than a certain amount of memory) so we can report it individually. |
|
258 // |
|
259 // The only difference between this class and StringInfo is that |
|
260 // NotableStringInfo holds a copy of some or all of the string's chars. |
|
261 struct NotableStringInfo : public StringInfo |
|
262 { |
|
263 static const size_t MAX_SAVED_CHARS = 1024; |
|
264 |
|
265 NotableStringInfo(); |
|
266 NotableStringInfo(JSString *str, const StringInfo &info); |
|
267 NotableStringInfo(NotableStringInfo &&info); |
|
268 NotableStringInfo &operator=(NotableStringInfo &&info); |
|
269 |
|
270 ~NotableStringInfo() { |
|
271 js_free(buffer); |
|
272 } |
|
273 |
|
274 char *buffer; |
|
275 size_t length; |
|
276 |
|
277 private: |
|
278 NotableStringInfo(const NotableStringInfo& info) MOZ_DELETE; |
|
279 }; |
|
280 |
|
281 // This class holds information about the memory taken up by script sources |
|
282 // from a particular file. |
|
283 struct ScriptSourceInfo |
|
284 { |
|
285 #define FOR_EACH_SIZE(macro) \ |
|
286 macro(_, _, compressed) \ |
|
287 macro(_, _, uncompressed) \ |
|
288 macro(_, _, misc) |
|
289 |
|
290 ScriptSourceInfo() |
|
291 : FOR_EACH_SIZE(ZERO_SIZE) |
|
292 numScripts(0) |
|
293 {} |
|
294 |
|
295 void add(const ScriptSourceInfo &other) { |
|
296 FOR_EACH_SIZE(ADD_OTHER_SIZE) |
|
297 numScripts++; |
|
298 } |
|
299 |
|
300 void subtract(const ScriptSourceInfo &other) { |
|
301 FOR_EACH_SIZE(SUB_OTHER_SIZE) |
|
302 numScripts--; |
|
303 } |
|
304 |
|
305 bool isNotable() const { |
|
306 static const size_t NotabilityThreshold = 16 * 1024; |
|
307 size_t n = 0; |
|
308 FOR_EACH_SIZE(ADD_SIZE_TO_N) |
|
309 return n >= NotabilityThreshold; |
|
310 } |
|
311 |
|
312 FOR_EACH_SIZE(DECL_SIZE) |
|
313 uint32_t numScripts; // How many ScriptSources come from this file? (It |
|
314 // can be more than one in XML files that have |
|
315 // multiple scripts in CDATA sections.) |
|
316 #undef FOR_EACH_SIZE |
|
317 }; |
|
318 |
|
319 // Holds data about a notable script source file (one whose combined |
|
320 // script sources use more than a certain amount of memory) so we can report it |
|
321 // individually. |
|
322 // |
|
323 // The only difference between this class and ScriptSourceInfo is that this |
|
324 // class holds a copy of the filename. |
|
325 struct NotableScriptSourceInfo : public ScriptSourceInfo |
|
326 { |
|
327 NotableScriptSourceInfo(); |
|
328 NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info); |
|
329 NotableScriptSourceInfo(NotableScriptSourceInfo &&info); |
|
330 NotableScriptSourceInfo &operator=(NotableScriptSourceInfo &&info); |
|
331 |
|
332 ~NotableScriptSourceInfo() { |
|
333 js_free(filename_); |
|
334 } |
|
335 |
|
336 char *filename_; |
|
337 |
|
338 private: |
|
339 NotableScriptSourceInfo(const NotableScriptSourceInfo& info) MOZ_DELETE; |
|
340 }; |
|
341 |
|
342 // These measurements relate directly to the JSRuntime, and not to zones and |
|
343 // compartments within it. |
|
344 struct RuntimeSizes |
|
345 { |
|
346 #define FOR_EACH_SIZE(macro) \ |
|
347 macro(_, _, object) \ |
|
348 macro(_, _, atomsTable) \ |
|
349 macro(_, _, contexts) \ |
|
350 macro(_, _, dtoa) \ |
|
351 macro(_, _, temporary) \ |
|
352 macro(_, _, regexpData) \ |
|
353 macro(_, _, interpreterStack) \ |
|
354 macro(_, _, mathCache) \ |
|
355 macro(_, _, sourceDataCache) \ |
|
356 macro(_, _, scriptData) \ |
|
357 |
|
358 RuntimeSizes() |
|
359 : FOR_EACH_SIZE(ZERO_SIZE) |
|
360 scriptSourceInfo(), |
|
361 code(), |
|
362 gc(), |
|
363 notableScriptSources() |
|
364 { |
|
365 allScriptSources = js_new<ScriptSourcesHashMap>(); |
|
366 if (!allScriptSources || !allScriptSources->init()) |
|
367 MOZ_CRASH("oom"); |
|
368 } |
|
369 |
|
370 ~RuntimeSizes() { |
|
371 // |allScriptSources| is usually deleted and set to nullptr before this |
|
372 // destructor runs. But there are failure cases due to OOMs that may |
|
373 // prevent that, so it doesn't hurt to try again here. |
|
374 js_delete(allScriptSources); |
|
375 } |
|
376 |
|
377 // The script source measurements in |scriptSourceInfo| are initially for |
|
378 // all script sources. At the end, if the measurement granularity is |
|
379 // FineGrained, we subtract the measurements of the notable script sources |
|
380 // and move them into |notableScriptSources|. |
|
381 FOR_EACH_SIZE(DECL_SIZE) |
|
382 ScriptSourceInfo scriptSourceInfo; |
|
383 CodeSizes code; |
|
384 GCSizes gc; |
|
385 |
|
386 typedef js::HashMap<const char*, ScriptSourceInfo, |
|
387 js::CStringHashPolicy, |
|
388 js::SystemAllocPolicy> ScriptSourcesHashMap; |
|
389 |
|
390 // |allScriptSources| is only used transiently. During the reporting phase |
|
391 // it is filled with info about every script source in the runtime. It's |
|
392 // then used to fill in |notableScriptSources| (which actually gets |
|
393 // reported), and immediately discarded afterwards. |
|
394 ScriptSourcesHashMap *allScriptSources; |
|
395 js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources; |
|
396 |
|
397 #undef FOR_EACH_SIZE |
|
398 }; |
|
399 |
|
400 struct ZoneStats |
|
401 { |
|
402 #define FOR_EACH_SIZE(macro) \ |
|
403 macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \ |
|
404 macro(Other, NotLiveGCThing, unusedGCThings) \ |
|
405 macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \ |
|
406 macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \ |
|
407 macro(Other, IsLiveGCThing, jitCodesGCHeap) \ |
|
408 macro(Other, IsLiveGCThing, typeObjectsGCHeap) \ |
|
409 macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \ |
|
410 macro(Other, NotLiveGCThing, typePool) \ |
|
411 macro(Other, NotLiveGCThing, baselineStubsOptimized) \ |
|
412 |
|
413 ZoneStats() |
|
414 : FOR_EACH_SIZE(ZERO_SIZE) |
|
415 stringInfo(), |
|
416 extra(), |
|
417 allStrings(nullptr), |
|
418 notableStrings(), |
|
419 isTotals(true) |
|
420 {} |
|
421 |
|
422 ZoneStats(ZoneStats &&other) |
|
423 : FOR_EACH_SIZE(COPY_OTHER_SIZE) |
|
424 stringInfo(mozilla::Move(other.stringInfo)), |
|
425 extra(other.extra), |
|
426 allStrings(other.allStrings), |
|
427 notableStrings(mozilla::Move(other.notableStrings)), |
|
428 isTotals(other.isTotals) |
|
429 { |
|
430 other.allStrings = nullptr; |
|
431 MOZ_ASSERT(!other.isTotals); |
|
432 } |
|
433 |
|
434 ~ZoneStats() { |
|
435 // |allStrings| is usually deleted and set to nullptr before this |
|
436 // destructor runs. But there are failure cases due to OOMs that may |
|
437 // prevent that, so it doesn't hurt to try again here. |
|
438 js_delete(allStrings); |
|
439 } |
|
440 |
|
441 bool initStrings(JSRuntime *rt); |
|
442 |
|
443 void addSizes(const ZoneStats &other) { |
|
444 MOZ_ASSERT(isTotals); |
|
445 FOR_EACH_SIZE(ADD_OTHER_SIZE) |
|
446 stringInfo.add(other.stringInfo); |
|
447 } |
|
448 |
|
449 size_t sizeOfLiveGCThings() const { |
|
450 MOZ_ASSERT(isTotals); |
|
451 size_t n = 0; |
|
452 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) |
|
453 n += stringInfo.sizeOfLiveGCThings(); |
|
454 return n; |
|
455 } |
|
456 |
|
457 void addToTabSizes(JS::TabSizes *sizes) const { |
|
458 MOZ_ASSERT(isTotals); |
|
459 FOR_EACH_SIZE(ADD_TO_TAB_SIZES) |
|
460 stringInfo.addToTabSizes(sizes); |
|
461 } |
|
462 |
|
463 // These string measurements are initially for all strings. At the end, |
|
464 // if the measurement granularity is FineGrained, we subtract the |
|
465 // measurements of the notable script sources and move them into |
|
466 // |notableStrings|. |
|
467 FOR_EACH_SIZE(DECL_SIZE) |
|
468 StringInfo stringInfo; |
|
469 void *extra; // This field can be used by embedders. |
|
470 |
|
471 typedef js::HashMap<JSString*, StringInfo, |
|
472 js::InefficientNonFlatteningStringHashPolicy, |
|
473 js::SystemAllocPolicy> StringsHashMap; |
|
474 |
|
475 // |allStrings| is only used transiently. During the zone traversal it is |
|
476 // filled with info about every string in the zone. It's then used to fill |
|
477 // in |notableStrings| (which actually gets reported), and immediately |
|
478 // discarded afterwards. |
|
479 StringsHashMap *allStrings; |
|
480 js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings; |
|
481 bool isTotals; |
|
482 |
|
483 #undef FOR_EACH_SIZE |
|
484 }; |
|
485 |
|
486 struct CompartmentStats |
|
487 { |
|
488 #define FOR_EACH_SIZE(macro) \ |
|
489 macro(Objects, IsLiveGCThing, objectsGCHeapOrdinary) \ |
|
490 macro(Objects, IsLiveGCThing, objectsGCHeapFunction) \ |
|
491 macro(Objects, IsLiveGCThing, objectsGCHeapDenseArray) \ |
|
492 macro(Objects, IsLiveGCThing, objectsGCHeapSlowArray) \ |
|
493 macro(Objects, IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \ |
|
494 macro(Private, NotLiveGCThing, objectsPrivate) \ |
|
495 macro(Other, IsLiveGCThing, shapesGCHeapTreeGlobalParented) \ |
|
496 macro(Other, IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \ |
|
497 macro(Other, IsLiveGCThing, shapesGCHeapDict) \ |
|
498 macro(Other, IsLiveGCThing, shapesGCHeapBase) \ |
|
499 macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \ |
|
500 macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \ |
|
501 macro(Other, NotLiveGCThing, shapesMallocHeapTreeShapeKids) \ |
|
502 macro(Other, NotLiveGCThing, shapesMallocHeapCompartmentTables) \ |
|
503 macro(Other, IsLiveGCThing, scriptsGCHeap) \ |
|
504 macro(Other, NotLiveGCThing, scriptsMallocHeapData) \ |
|
505 macro(Other, NotLiveGCThing, baselineData) \ |
|
506 macro(Other, NotLiveGCThing, baselineStubsFallback) \ |
|
507 macro(Other, NotLiveGCThing, ionData) \ |
|
508 macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ |
|
509 macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ |
|
510 macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ |
|
511 macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ |
|
512 macro(Other, NotLiveGCThing, compartmentObject) \ |
|
513 macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \ |
|
514 macro(Other, NotLiveGCThing, regexpCompartment) \ |
|
515 macro(Other, NotLiveGCThing, debuggeesSet) \ |
|
516 macro(Other, NotLiveGCThing, savedStacksSet) |
|
517 |
|
518 CompartmentStats() |
|
519 : FOR_EACH_SIZE(ZERO_SIZE) |
|
520 objectsExtra(), |
|
521 extra() |
|
522 {} |
|
523 |
|
524 CompartmentStats(const CompartmentStats &other) |
|
525 : FOR_EACH_SIZE(COPY_OTHER_SIZE) |
|
526 objectsExtra(other.objectsExtra), |
|
527 extra(other.extra) |
|
528 {} |
|
529 |
|
530 void add(const CompartmentStats &other) { |
|
531 FOR_EACH_SIZE(ADD_OTHER_SIZE) |
|
532 objectsExtra.add(other.objectsExtra); |
|
533 // Do nothing with |extra|. |
|
534 } |
|
535 |
|
536 size_t sizeOfLiveGCThings() const { |
|
537 size_t n = 0; |
|
538 FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) |
|
539 n += objectsExtra.sizeOfLiveGCThings(); |
|
540 // Do nothing with |extra|. |
|
541 return n; |
|
542 } |
|
543 |
|
544 void addToTabSizes(TabSizes *sizes) const { |
|
545 FOR_EACH_SIZE(ADD_TO_TAB_SIZES); |
|
546 objectsExtra.addToTabSizes(sizes); |
|
547 // Do nothing with |extra|. |
|
548 } |
|
549 |
|
550 FOR_EACH_SIZE(DECL_SIZE) |
|
551 ObjectsExtraSizes objectsExtra; |
|
552 void *extra; // This field can be used by embedders. |
|
553 |
|
554 #undef FOR_EACH_SIZE |
|
555 }; |
|
556 |
|
557 typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector; |
|
558 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector; |
|
559 |
|
560 struct RuntimeStats |
|
561 { |
|
562 #define FOR_EACH_SIZE(macro) \ |
|
563 macro(_, _, gcHeapChunkTotal) \ |
|
564 macro(_, _, gcHeapDecommittedArenas) \ |
|
565 macro(_, _, gcHeapUnusedChunks) \ |
|
566 macro(_, _, gcHeapUnusedArenas) \ |
|
567 macro(_, _, gcHeapChunkAdmin) \ |
|
568 macro(_, _, gcHeapGCThings) \ |
|
569 |
|
570 RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) |
|
571 : FOR_EACH_SIZE(ZERO_SIZE) |
|
572 runtime(), |
|
573 cTotals(), |
|
574 zTotals(), |
|
575 compartmentStatsVector(), |
|
576 zoneStatsVector(), |
|
577 currZoneStats(nullptr), |
|
578 mallocSizeOf_(mallocSizeOf) |
|
579 {} |
|
580 |
|
581 // Here's a useful breakdown of the GC heap. |
|
582 // |
|
583 // - rtStats.gcHeapChunkTotal |
|
584 // - decommitted bytes |
|
585 // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks) |
|
586 // - unused bytes |
|
587 // - rtStats.gcHeapUnusedChunks (empty chunks) |
|
588 // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) |
|
589 // - rtStats.zTotals.unusedGCThings (empty GC thing slots within non-empty arenas) |
|
590 // - used bytes |
|
591 // - rtStats.gcHeapChunkAdmin |
|
592 // - rtStats.zTotals.gcHeapArenaAdmin |
|
593 // - rtStats.gcHeapGCThings (in-use GC things) |
|
594 // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings() |
|
595 // |
|
596 // It's possible that some arenas in empty chunks may be decommitted, but |
|
597 // we don't count those under rtStats.gcHeapDecommittedArenas because (a) |
|
598 // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a |
|
599 // multiple of the chunk size, which is good. |
|
600 |
|
601 FOR_EACH_SIZE(DECL_SIZE) |
|
602 |
|
603 RuntimeSizes runtime; |
|
604 |
|
605 CompartmentStats cTotals; // The sum of this runtime's compartments' measurements. |
|
606 ZoneStats zTotals; // The sum of this runtime's zones' measurements. |
|
607 |
|
608 CompartmentStatsVector compartmentStatsVector; |
|
609 ZoneStatsVector zoneStatsVector; |
|
610 |
|
611 ZoneStats *currZoneStats; |
|
612 |
|
613 mozilla::MallocSizeOf mallocSizeOf_; |
|
614 |
|
615 virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0; |
|
616 virtual void initExtraZoneStats(JS::Zone *zone, ZoneStats *zstats) = 0; |
|
617 |
|
618 #undef FOR_EACH_SIZE |
|
619 }; |
|
620 |
|
621 class ObjectPrivateVisitor |
|
622 { |
|
623 public: |
|
624 // Within CollectRuntimeStats, this method is called for each JS object |
|
625 // that has an nsISupports pointer. |
|
626 virtual size_t sizeOfIncludingThis(nsISupports *aSupports) = 0; |
|
627 |
|
628 // A callback that gets a JSObject's nsISupports pointer, if it has one. |
|
629 // Note: this function does *not* addref |iface|. |
|
630 typedef bool(*GetISupportsFun)(JSObject *obj, nsISupports **iface); |
|
631 GetISupportsFun getISupports_; |
|
632 |
|
633 ObjectPrivateVisitor(GetISupportsFun getISupports) |
|
634 : getISupports_(getISupports) |
|
635 {} |
|
636 }; |
|
637 |
|
638 extern JS_PUBLIC_API(bool) |
|
639 CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv); |
|
640 |
|
641 extern JS_PUBLIC_API(size_t) |
|
642 SystemCompartmentCount(JSRuntime *rt); |
|
643 |
|
644 extern JS_PUBLIC_API(size_t) |
|
645 UserCompartmentCount(JSRuntime *rt); |
|
646 |
|
647 extern JS_PUBLIC_API(size_t) |
|
648 PeakSizeOfTemporary(const JSRuntime *rt); |
|
649 |
|
650 extern JS_PUBLIC_API(bool) |
|
651 AddSizeOfTab(JSRuntime *rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, |
|
652 ObjectPrivateVisitor *opv, TabSizes *sizes); |
|
653 |
|
654 } // namespace JS |
|
655 |
|
656 #undef DECL_SIZE |
|
657 #undef ZERO_SIZE |
|
658 #undef COPY_OTHER_SIZE |
|
659 #undef ADD_OTHER_SIZE |
|
660 #undef SUB_OTHER_SIZE |
|
661 #undef ADD_SIZE_TO_N |
|
662 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING |
|
663 #undef ADD_TO_TAB_SIZES |
|
664 |
|
665 #endif /* js_MemoryMetrics_h */ |