|
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 |