js/src/vm/MemoryMetrics.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:8c309364f53d
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 #include "js/MemoryMetrics.h"
8
9 #include "mozilla/DebugOnly.h"
10
11 #include "jsapi.h"
12 #include "jscompartment.h"
13 #include "jsgc.h"
14 #include "jsobj.h"
15 #include "jsscript.h"
16
17 #include "jit/BaselineJIT.h"
18 #include "jit/Ion.h"
19 #include "vm/ArrayObject.h"
20 #include "vm/Runtime.h"
21 #include "vm/Shape.h"
22 #include "vm/String.h"
23 #include "vm/WrapperObject.h"
24
25 using mozilla::DebugOnly;
26 using mozilla::MallocSizeOf;
27 using mozilla::Move;
28 using mozilla::PodCopy;
29 using mozilla::PodEqual;
30
31 using namespace js;
32
33 using JS::RuntimeStats;
34 using JS::ObjectPrivateVisitor;
35 using JS::ZoneStats;
36 using JS::CompartmentStats;
37
38 namespace js {
39
40 JS_FRIEND_API(size_t)
41 MemoryReportingSundriesThreshold()
42 {
43 return 8 * 1024;
44 }
45
46 /* static */ HashNumber
47 InefficientNonFlatteningStringHashPolicy::hash(const Lookup &l)
48 {
49 ScopedJSFreePtr<jschar> ownedChars;
50 const jschar *chars;
51 if (l->hasPureChars()) {
52 chars = l->pureChars();
53 } else {
54 // Slowest hash function evar!
55 if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars))
56 MOZ_CRASH("oom");
57 chars = ownedChars;
58 }
59
60 return mozilla::HashString(chars, l->length());
61 }
62
63 /* static */ bool
64 InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const Lookup &l)
65 {
66 // We can't use js::EqualStrings, because that flattens our strings.
67 if (k->length() != l->length())
68 return false;
69
70 const jschar *c1;
71 ScopedJSFreePtr<jschar> ownedChars1;
72 if (k->hasPureChars()) {
73 c1 = k->pureChars();
74 } else {
75 if (!k->copyNonPureChars(/* tcx */ nullptr, ownedChars1))
76 MOZ_CRASH("oom");
77 c1 = ownedChars1;
78 }
79
80 const jschar *c2;
81 ScopedJSFreePtr<jschar> ownedChars2;
82 if (l->hasPureChars()) {
83 c2 = l->pureChars();
84 } else {
85 if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars2))
86 MOZ_CRASH("oom");
87 c2 = ownedChars2;
88 }
89
90 return PodEqual(c1, c2, k->length());
91 }
92
93 /* static */ HashNumber
94 CStringHashPolicy::hash(const Lookup &l)
95 {
96 return mozilla::HashString(l);
97 }
98
99 /* static */ bool
100 CStringHashPolicy::match(const char *const &k, const Lookup &l)
101 {
102 return strcmp(k, l) == 0;
103 }
104
105 } // namespace js
106
107 namespace JS {
108
109 NotableStringInfo::NotableStringInfo()
110 : StringInfo(),
111 buffer(0),
112 length(0)
113 {
114 }
115
116 NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
117 : StringInfo(info),
118 length(str->length())
119 {
120 size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS));
121 buffer = js_pod_malloc<char>(bufferSize);
122 if (!buffer) {
123 MOZ_CRASH("oom");
124 }
125
126 const jschar* chars;
127 ScopedJSFreePtr<jschar> ownedChars;
128 if (str->hasPureChars()) {
129 chars = str->pureChars();
130 } else {
131 if (!str->copyNonPureChars(/* tcx */ nullptr, ownedChars))
132 MOZ_CRASH("oom");
133 chars = ownedChars;
134 }
135
136 // We might truncate |str| even if it's much shorter than 1024 chars, if
137 // |str| contains unicode chars. Since this is just for a memory reporter,
138 // we don't care.
139 PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
140 }
141
142 NotableStringInfo::NotableStringInfo(NotableStringInfo &&info)
143 : StringInfo(Move(info)),
144 length(info.length)
145 {
146 buffer = info.buffer;
147 info.buffer = nullptr;
148 }
149
150 NotableStringInfo &NotableStringInfo::operator=(NotableStringInfo &&info)
151 {
152 MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
153 this->~NotableStringInfo();
154 new (this) NotableStringInfo(Move(info));
155 return *this;
156 }
157
158 NotableScriptSourceInfo::NotableScriptSourceInfo()
159 : ScriptSourceInfo(),
160 filename_(nullptr)
161 {
162 }
163
164 NotableScriptSourceInfo::NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info)
165 : ScriptSourceInfo(info)
166 {
167 size_t bytes = strlen(filename) + 1;
168 filename_ = js_pod_malloc<char>(bytes);
169 if (!filename_)
170 MOZ_CRASH("oom");
171 PodCopy(filename_, filename, bytes);
172 }
173
174 NotableScriptSourceInfo::NotableScriptSourceInfo(NotableScriptSourceInfo &&info)
175 : ScriptSourceInfo(Move(info))
176 {
177 filename_ = info.filename_;
178 info.filename_ = nullptr;
179 }
180
181 NotableScriptSourceInfo &NotableScriptSourceInfo::operator=(NotableScriptSourceInfo &&info)
182 {
183 MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
184 this->~NotableScriptSourceInfo();
185 new (this) NotableScriptSourceInfo(Move(info));
186 return *this;
187 }
188
189
190 } // namespace JS
191
192 typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
193
194 struct StatsClosure
195 {
196 RuntimeStats *rtStats;
197 ObjectPrivateVisitor *opv;
198 SourceSet seenSources;
199 StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
200 bool init() {
201 return seenSources.init();
202 }
203 };
204
205 static void
206 DecommittedArenasChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
207 {
208 // This case is common and fast to check. Do it first.
209 if (chunk->decommittedArenas.isAllClear())
210 return;
211
212 size_t n = 0;
213 for (size_t i = 0; i < gc::ArenasPerChunk; i++) {
214 if (chunk->decommittedArenas.get(i))
215 n += gc::ArenaSize;
216 }
217 JS_ASSERT(n > 0);
218 *static_cast<size_t *>(data) += n;
219 }
220
221 static void
222 StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone)
223 {
224 // Append a new CompartmentStats to the vector.
225 RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
226
227 // CollectRuntimeStats reserves enough space.
228 MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
229 ZoneStats &zStats = rtStats->zoneStatsVector.back();
230 if (!zStats.initStrings(rt))
231 MOZ_CRASH("oom");
232 rtStats->initExtraZoneStats(zone, &zStats);
233 rtStats->currZoneStats = &zStats;
234
235 zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
236 &zStats.typePool,
237 &zStats.baselineStubsOptimized);
238 }
239
240 static void
241 StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
242 {
243 // Append a new CompartmentStats to the vector.
244 RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
245
246 // CollectRuntimeStats reserves enough space.
247 MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
248 CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
249 rtStats->initExtraCompartmentStats(compartment, &cStats);
250
251 compartment->compartmentStats = &cStats;
252
253 // Measure the compartment object itself, and things hanging off it.
254 compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
255 &cStats.typeInferenceAllocationSiteTables,
256 &cStats.typeInferenceArrayTypeTables,
257 &cStats.typeInferenceObjectTypeTables,
258 &cStats.compartmentObject,
259 &cStats.shapesMallocHeapCompartmentTables,
260 &cStats.crossCompartmentWrappersTable,
261 &cStats.regexpCompartment,
262 &cStats.debuggeesSet,
263 &cStats.savedStacksSet);
264 }
265
266 static void
267 StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
268 JSGCTraceKind traceKind, size_t thingSize)
269 {
270 RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
271
272 // The admin space includes (a) the header and (b) the padding between the
273 // end of the header and the start of the first GC thing.
274 size_t allocationSpace = arena->thingsSpan(thingSize);
275 rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace;
276
277 // We don't call the callback on unused things. So we compute the
278 // unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
279 // We do this by setting arenaUnused to maxArenaUnused here, and then
280 // subtracting thingSize for every used cell, in StatsCellCallback().
281 rtStats->currZoneStats->unusedGCThings += allocationSpace;
282 }
283
284 static CompartmentStats *
285 GetCompartmentStats(JSCompartment *comp)
286 {
287 return static_cast<CompartmentStats *>(comp->compartmentStats);
288 }
289
290 enum Granularity {
291 FineGrained, // Corresponds to CollectRuntimeStats()
292 CoarseGrained // Corresponds to AddSizeOfTab()
293 };
294
295 // The various kinds of hashing are expensive, and the results are unused when
296 // doing coarse-grained measurements. Skipping them more than doubles the
297 // profile speed for complex pages such as gmail.com.
298 template <Granularity granularity>
299 static void
300 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
301 size_t thingSize)
302 {
303 StatsClosure *closure = static_cast<StatsClosure *>(data);
304 RuntimeStats *rtStats = closure->rtStats;
305 ZoneStats *zStats = rtStats->currZoneStats;
306 switch (traceKind) {
307 case JSTRACE_OBJECT: {
308 JSObject *obj = static_cast<JSObject *>(thing);
309 CompartmentStats *cStats = GetCompartmentStats(obj->compartment());
310 if (obj->is<JSFunction>())
311 cStats->objectsGCHeapFunction += thingSize;
312 else if (obj->is<ArrayObject>())
313 cStats->objectsGCHeapDenseArray += thingSize;
314 else if (obj->is<CrossCompartmentWrapperObject>())
315 cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
316 else
317 cStats->objectsGCHeapOrdinary += thingSize;
318
319 obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
320
321 if (ObjectPrivateVisitor *opv = closure->opv) {
322 nsISupports *iface;
323 if (opv->getISupports_(obj, &iface) && iface)
324 cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
325 }
326 break;
327 }
328
329 case JSTRACE_STRING: {
330 JSString *str = static_cast<JSString *>(thing);
331
332 JS::StringInfo info;
333 info.gcHeap = thingSize;
334 info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
335 info.numCopies = 1;
336
337 zStats->stringInfo.add(info);
338
339 if (granularity == FineGrained) {
340 ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
341 if (!p) {
342 // Ignore failure -- we just won't record the string as notable.
343 (void)zStats->allStrings->add(p, str, info);
344 } else {
345 p->value().add(info);
346 }
347 }
348 break;
349 }
350
351 case JSTRACE_SHAPE: {
352 Shape *shape = static_cast<Shape *>(thing);
353 CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
354 if (shape->inDictionary()) {
355 cStats->shapesGCHeapDict += thingSize;
356
357 // nullptr because kidsSize shouldn't be incremented in this case.
358 shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
359 &cStats->shapesMallocHeapDictTables, nullptr);
360 } else {
361 JSObject *parent = shape->base()->getObjectParent();
362 if (parent && parent->is<GlobalObject>())
363 cStats->shapesGCHeapTreeGlobalParented += thingSize;
364 else
365 cStats->shapesGCHeapTreeNonGlobalParented += thingSize;
366
367 shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
368 &cStats->shapesMallocHeapTreeTables,
369 &cStats->shapesMallocHeapTreeShapeKids);
370 }
371 break;
372 }
373
374 case JSTRACE_BASE_SHAPE: {
375 BaseShape *base = static_cast<BaseShape *>(thing);
376 CompartmentStats *cStats = GetCompartmentStats(base->compartment());
377 cStats->shapesGCHeapBase += thingSize;
378 break;
379 }
380
381 case JSTRACE_SCRIPT: {
382 JSScript *script = static_cast<JSScript *>(thing);
383 CompartmentStats *cStats = GetCompartmentStats(script->compartment());
384 cStats->scriptsGCHeap += thingSize;
385 cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
386 cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
387 #ifdef JS_ION
388 jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
389 &cStats->baselineStubsFallback);
390 cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
391 #endif
392
393 ScriptSource *ss = script->scriptSource();
394 SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
395 if (!entry) {
396 (void)closure->seenSources.add(entry, ss); // Not much to be done on failure.
397
398 JS::ScriptSourceInfo info; // This zeroes all the sizes.
399 ss->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &info);
400 MOZ_ASSERT(info.compressed == 0 || info.uncompressed == 0);
401
402 rtStats->runtime.scriptSourceInfo.add(info);
403
404 if (granularity == FineGrained) {
405 const char* filename = ss->filename();
406 if (!filename)
407 filename = "<no filename>";
408
409 JS::RuntimeSizes::ScriptSourcesHashMap::AddPtr p =
410 rtStats->runtime.allScriptSources->lookupForAdd(filename);
411 if (!p) {
412 // Ignore failure -- we just won't record the script source as notable.
413 (void)rtStats->runtime.allScriptSources->add(p, filename, info);
414 } else {
415 p->value().add(info);
416 }
417 }
418 }
419
420 break;
421 }
422
423 case JSTRACE_LAZY_SCRIPT: {
424 LazyScript *lazy = static_cast<LazyScript *>(thing);
425 zStats->lazyScriptsGCHeap += thingSize;
426 zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
427 break;
428 }
429
430 case JSTRACE_JITCODE: {
431 #ifdef JS_ION
432 zStats->jitCodesGCHeap += thingSize;
433 // The code for a script is counted in ExecutableAllocator::sizeOfCode().
434 #endif
435 break;
436 }
437
438 case JSTRACE_TYPE_OBJECT: {
439 types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
440 zStats->typeObjectsGCHeap += thingSize;
441 zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_);
442 break;
443 }
444
445 default:
446 MOZ_ASSUME_UNREACHABLE("invalid traceKind");
447 }
448
449 // Yes, this is a subtraction: see StatsArenaCallback() for details.
450 zStats->unusedGCThings -= thingSize;
451 }
452
453 static bool
454 FindNotableStrings(ZoneStats &zStats)
455 {
456 using namespace JS;
457
458 // We should only run FindNotableStrings once per ZoneStats object.
459 MOZ_ASSERT(zStats.notableStrings.empty());
460
461 for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) {
462
463 JSString *str = r.front().key();
464 StringInfo &info = r.front().value();
465
466 if (!info.isNotable())
467 continue;
468
469 if (!zStats.notableStrings.growBy(1))
470 return false;
471
472 zStats.notableStrings.back() = NotableStringInfo(str, info);
473
474 // We're moving this string from a non-notable to a notable bucket, so
475 // subtract it out of the non-notable tallies.
476 zStats.stringInfo.subtract(info);
477 }
478 // Delete |allStrings| now, rather than waiting for zStats's destruction,
479 // to reduce peak memory consumption during reporting.
480 js_delete(zStats.allStrings);
481 zStats.allStrings = nullptr;
482 return true;
483 }
484
485 bool
486 ZoneStats::initStrings(JSRuntime *rt)
487 {
488 isTotals = false;
489 allStrings = rt->new_<StringsHashMap>();
490 if (!allStrings)
491 return false;
492 if (!allStrings->init()) {
493 js_delete(allStrings);
494 allStrings = nullptr;
495 return false;
496 }
497 return true;
498 }
499
500 static bool
501 FindNotableScriptSources(JS::RuntimeSizes &runtime)
502 {
503 using namespace JS;
504
505 // We should only run FindNotableScriptSources once per RuntimeSizes.
506 MOZ_ASSERT(runtime.notableScriptSources.empty());
507
508 for (RuntimeSizes::ScriptSourcesHashMap::Range r = runtime.allScriptSources->all();
509 !r.empty();
510 r.popFront())
511 {
512 const char *filename = r.front().key();
513 ScriptSourceInfo &info = r.front().value();
514
515 if (!info.isNotable())
516 continue;
517
518 if (!runtime.notableScriptSources.growBy(1))
519 return false;
520
521 runtime.notableScriptSources.back() = NotableScriptSourceInfo(filename, info);
522
523 // We're moving this script source from a non-notable to a notable
524 // bucket, so subtract its sizes from the non-notable tallies.
525 runtime.scriptSourceInfo.subtract(info);
526 }
527 // Delete |allScriptSources| now, rather than waiting for zStats's
528 // destruction, to reduce peak memory consumption during reporting.
529 js_delete(runtime.allScriptSources);
530 runtime.allScriptSources = nullptr;
531 return true;
532 }
533
534 JS_PUBLIC_API(bool)
535 JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
536 {
537 if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
538 return false;
539
540 if (!rtStats->zoneStatsVector.reserve(rt->zones.length()))
541 return false;
542
543 rtStats->gcHeapChunkTotal =
544 size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
545
546 rtStats->gcHeapUnusedChunks =
547 size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
548
549 IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
550 DecommittedArenasChunkCallback);
551
552 // Take the per-compartment measurements.
553 StatsClosure closure(rtStats, opv);
554 if (!closure.init())
555 return false;
556 IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
557 StatsArenaCallback, StatsCellCallback<FineGrained>);
558
559 // Take the "explicit/js/runtime/" measurements.
560 rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
561
562 if (!FindNotableScriptSources(rtStats->runtime))
563 return false;
564
565 ZoneStatsVector &zs = rtStats->zoneStatsVector;
566 ZoneStats &zTotals = rtStats->zTotals;
567
568 // We don't look for notable strings for zTotals. So we first sum all the
569 // zones' measurements to get the totals. Then we find the notable strings
570 // within each zone.
571 for (size_t i = 0; i < zs.length(); i++)
572 zTotals.addSizes(zs[i]);
573
574 for (size_t i = 0; i < zs.length(); i++)
575 if (!FindNotableStrings(zs[i]))
576 return false;
577
578 MOZ_ASSERT(!zTotals.allStrings);
579
580 for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
581 CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
582 rtStats->cTotals.add(cStats);
583 }
584
585 rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
586 rtStats->cTotals.sizeOfLiveGCThings();
587
588 #ifdef DEBUG
589 // Check that the in-arena measurements look ok.
590 size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
591 rtStats->zTotals.unusedGCThings +
592 rtStats->gcHeapGCThings;
593 JS_ASSERT(totalArenaSize % gc::ArenaSize == 0);
594 #endif
595
596 for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next())
597 comp->compartmentStats = nullptr;
598
599 size_t numDirtyChunks =
600 (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
601 size_t perChunkAdmin =
602 sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
603 rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
604
605 // |gcHeapUnusedArenas| is the only thing left. Compute it in terms of
606 // all the others. See the comment in RuntimeStats for explanation.
607 rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
608 rtStats->gcHeapDecommittedArenas -
609 rtStats->gcHeapUnusedChunks -
610 rtStats->zTotals.unusedGCThings -
611 rtStats->gcHeapChunkAdmin -
612 rtStats->zTotals.gcHeapArenaAdmin -
613 rtStats->gcHeapGCThings;
614 return true;
615 }
616
617 JS_PUBLIC_API(size_t)
618 JS::SystemCompartmentCount(JSRuntime *rt)
619 {
620 size_t n = 0;
621 for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
622 if (comp->isSystem)
623 ++n;
624 }
625 return n;
626 }
627
628 JS_PUBLIC_API(size_t)
629 JS::UserCompartmentCount(JSRuntime *rt)
630 {
631 size_t n = 0;
632 for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
633 if (!comp->isSystem)
634 ++n;
635 }
636 return n;
637 }
638
639 JS_PUBLIC_API(size_t)
640 JS::PeakSizeOfTemporary(const JSRuntime *rt)
641 {
642 return rt->tempLifoAlloc.peakSizeOfExcludingThis();
643 }
644
645 namespace JS {
646
647 JS_PUBLIC_API(bool)
648 AddSizeOfTab(JSRuntime *rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
649 TabSizes *sizes)
650 {
651 class SimpleJSRuntimeStats : public JS::RuntimeStats
652 {
653 public:
654 SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
655 : JS::RuntimeStats(mallocSizeOf)
656 {}
657
658 virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats)
659 MOZ_OVERRIDE
660 {}
661
662 virtual void initExtraCompartmentStats(
663 JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE
664 {}
665 };
666
667 SimpleJSRuntimeStats rtStats(mallocSizeOf);
668
669 JS::Zone *zone = GetObjectZone(obj);
670
671 if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length()))
672 return false;
673
674 if (!rtStats.zoneStatsVector.reserve(1))
675 return false;
676
677 // Take the per-compartment measurements.
678 StatsClosure closure(&rtStats, opv);
679 if (!closure.init())
680 return false;
681 IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
682 StatsCompartmentCallback, StatsArenaCallback,
683 StatsCellCallback<CoarseGrained>);
684
685 JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
686 rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
687
688 for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
689 CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
690 rtStats.cTotals.add(cStats);
691 }
692
693 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
694 comp->compartmentStats = nullptr;
695
696 rtStats.zTotals.addToTabSizes(sizes);
697 rtStats.cTotals.addToTabSizes(sizes);
698
699 return true;
700 }
701
702 } // namespace JS
703

mercurial