js/src/vm/MemoryMetrics.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial