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