js/src/vm/MemoryMetrics.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/MemoryMetrics.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,703 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "js/MemoryMetrics.h"
    1.11 +
    1.12 +#include "mozilla/DebugOnly.h"
    1.13 +
    1.14 +#include "jsapi.h"
    1.15 +#include "jscompartment.h"
    1.16 +#include "jsgc.h"
    1.17 +#include "jsobj.h"
    1.18 +#include "jsscript.h"
    1.19 +
    1.20 +#include "jit/BaselineJIT.h"
    1.21 +#include "jit/Ion.h"
    1.22 +#include "vm/ArrayObject.h"
    1.23 +#include "vm/Runtime.h"
    1.24 +#include "vm/Shape.h"
    1.25 +#include "vm/String.h"
    1.26 +#include "vm/WrapperObject.h"
    1.27 +
    1.28 +using mozilla::DebugOnly;
    1.29 +using mozilla::MallocSizeOf;
    1.30 +using mozilla::Move;
    1.31 +using mozilla::PodCopy;
    1.32 +using mozilla::PodEqual;
    1.33 +
    1.34 +using namespace js;
    1.35 +
    1.36 +using JS::RuntimeStats;
    1.37 +using JS::ObjectPrivateVisitor;
    1.38 +using JS::ZoneStats;
    1.39 +using JS::CompartmentStats;
    1.40 +
    1.41 +namespace js {
    1.42 +
    1.43 +JS_FRIEND_API(size_t)
    1.44 +MemoryReportingSundriesThreshold()
    1.45 +{
    1.46 +    return 8 * 1024;
    1.47 +}
    1.48 +
    1.49 +/* static */ HashNumber
    1.50 +InefficientNonFlatteningStringHashPolicy::hash(const Lookup &l)
    1.51 +{
    1.52 +    ScopedJSFreePtr<jschar> ownedChars;
    1.53 +    const jschar *chars;
    1.54 +    if (l->hasPureChars()) {
    1.55 +        chars = l->pureChars();
    1.56 +    } else {
    1.57 +        // Slowest hash function evar!
    1.58 +        if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars))
    1.59 +            MOZ_CRASH("oom");
    1.60 +        chars = ownedChars;
    1.61 +    }
    1.62 +
    1.63 +    return mozilla::HashString(chars, l->length());
    1.64 +}
    1.65 +
    1.66 +/* static */ bool
    1.67 +InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const Lookup &l)
    1.68 +{
    1.69 +    // We can't use js::EqualStrings, because that flattens our strings.
    1.70 +    if (k->length() != l->length())
    1.71 +        return false;
    1.72 +
    1.73 +    const jschar *c1;
    1.74 +    ScopedJSFreePtr<jschar> ownedChars1;
    1.75 +    if (k->hasPureChars()) {
    1.76 +        c1 = k->pureChars();
    1.77 +    } else {
    1.78 +        if (!k->copyNonPureChars(/* tcx */ nullptr, ownedChars1))
    1.79 +            MOZ_CRASH("oom");
    1.80 +        c1 = ownedChars1;
    1.81 +    }
    1.82 +
    1.83 +    const jschar *c2;
    1.84 +    ScopedJSFreePtr<jschar> ownedChars2;
    1.85 +    if (l->hasPureChars()) {
    1.86 +        c2 = l->pureChars();
    1.87 +    } else {
    1.88 +        if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars2))
    1.89 +            MOZ_CRASH("oom");
    1.90 +        c2 = ownedChars2;
    1.91 +    }
    1.92 +
    1.93 +    return PodEqual(c1, c2, k->length());
    1.94 +}
    1.95 +
    1.96 +/* static */ HashNumber
    1.97 +CStringHashPolicy::hash(const Lookup &l)
    1.98 +{
    1.99 +    return mozilla::HashString(l);
   1.100 +}
   1.101 +
   1.102 +/* static */ bool
   1.103 +CStringHashPolicy::match(const char *const &k, const Lookup &l)
   1.104 +{
   1.105 +    return strcmp(k, l) == 0;
   1.106 +}
   1.107 +
   1.108 +} // namespace js
   1.109 +
   1.110 +namespace JS {
   1.111 +
   1.112 +NotableStringInfo::NotableStringInfo()
   1.113 +  : StringInfo(),
   1.114 +    buffer(0),
   1.115 +    length(0)
   1.116 +{
   1.117 +}
   1.118 +
   1.119 +NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
   1.120 +  : StringInfo(info),
   1.121 +    length(str->length())
   1.122 +{
   1.123 +    size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS));
   1.124 +    buffer = js_pod_malloc<char>(bufferSize);
   1.125 +    if (!buffer) {
   1.126 +        MOZ_CRASH("oom");
   1.127 +    }
   1.128 +
   1.129 +    const jschar* chars;
   1.130 +    ScopedJSFreePtr<jschar> ownedChars;
   1.131 +    if (str->hasPureChars()) {
   1.132 +        chars = str->pureChars();
   1.133 +    } else {
   1.134 +        if (!str->copyNonPureChars(/* tcx */ nullptr, ownedChars))
   1.135 +            MOZ_CRASH("oom");
   1.136 +        chars = ownedChars;
   1.137 +    }
   1.138 +
   1.139 +    // We might truncate |str| even if it's much shorter than 1024 chars, if
   1.140 +    // |str| contains unicode chars.  Since this is just for a memory reporter,
   1.141 +    // we don't care.
   1.142 +    PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
   1.143 +}
   1.144 +
   1.145 +NotableStringInfo::NotableStringInfo(NotableStringInfo &&info)
   1.146 +  : StringInfo(Move(info)),
   1.147 +    length(info.length)
   1.148 +{
   1.149 +    buffer = info.buffer;
   1.150 +    info.buffer = nullptr;
   1.151 +}
   1.152 +
   1.153 +NotableStringInfo &NotableStringInfo::operator=(NotableStringInfo &&info)
   1.154 +{
   1.155 +    MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
   1.156 +    this->~NotableStringInfo();
   1.157 +    new (this) NotableStringInfo(Move(info));
   1.158 +    return *this;
   1.159 +}
   1.160 +
   1.161 +NotableScriptSourceInfo::NotableScriptSourceInfo()
   1.162 +  : ScriptSourceInfo(),
   1.163 +    filename_(nullptr)
   1.164 +{
   1.165 +}
   1.166 +
   1.167 +NotableScriptSourceInfo::NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info)
   1.168 +  : ScriptSourceInfo(info)
   1.169 +{
   1.170 +    size_t bytes = strlen(filename) + 1;
   1.171 +    filename_ = js_pod_malloc<char>(bytes);
   1.172 +    if (!filename_)
   1.173 +        MOZ_CRASH("oom");
   1.174 +    PodCopy(filename_, filename, bytes);
   1.175 +}
   1.176 +
   1.177 +NotableScriptSourceInfo::NotableScriptSourceInfo(NotableScriptSourceInfo &&info)
   1.178 +  : ScriptSourceInfo(Move(info))
   1.179 +{
   1.180 +    filename_ = info.filename_;
   1.181 +    info.filename_ = nullptr;
   1.182 +}
   1.183 +
   1.184 +NotableScriptSourceInfo &NotableScriptSourceInfo::operator=(NotableScriptSourceInfo &&info)
   1.185 +{
   1.186 +    MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
   1.187 +    this->~NotableScriptSourceInfo();
   1.188 +    new (this) NotableScriptSourceInfo(Move(info));
   1.189 +    return *this;
   1.190 +}
   1.191 +
   1.192 +
   1.193 +} // namespace JS
   1.194 +
   1.195 +typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
   1.196 +
   1.197 +struct StatsClosure
   1.198 +{
   1.199 +    RuntimeStats *rtStats;
   1.200 +    ObjectPrivateVisitor *opv;
   1.201 +    SourceSet seenSources;
   1.202 +    StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
   1.203 +    bool init() {
   1.204 +        return seenSources.init();
   1.205 +    }
   1.206 +};
   1.207 +
   1.208 +static void
   1.209 +DecommittedArenasChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
   1.210 +{
   1.211 +    // This case is common and fast to check.  Do it first.
   1.212 +    if (chunk->decommittedArenas.isAllClear())
   1.213 +        return;
   1.214 +
   1.215 +    size_t n = 0;
   1.216 +    for (size_t i = 0; i < gc::ArenasPerChunk; i++) {
   1.217 +        if (chunk->decommittedArenas.get(i))
   1.218 +            n += gc::ArenaSize;
   1.219 +    }
   1.220 +    JS_ASSERT(n > 0);
   1.221 +    *static_cast<size_t *>(data) += n;
   1.222 +}
   1.223 +
   1.224 +static void
   1.225 +StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone)
   1.226 +{
   1.227 +    // Append a new CompartmentStats to the vector.
   1.228 +    RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
   1.229 +
   1.230 +    // CollectRuntimeStats reserves enough space.
   1.231 +    MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
   1.232 +    ZoneStats &zStats = rtStats->zoneStatsVector.back();
   1.233 +    if (!zStats.initStrings(rt))
   1.234 +        MOZ_CRASH("oom");
   1.235 +    rtStats->initExtraZoneStats(zone, &zStats);
   1.236 +    rtStats->currZoneStats = &zStats;
   1.237 +
   1.238 +    zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
   1.239 +                                 &zStats.typePool,
   1.240 +                                 &zStats.baselineStubsOptimized);
   1.241 +}
   1.242 +
   1.243 +static void
   1.244 +StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
   1.245 +{
   1.246 +    // Append a new CompartmentStats to the vector.
   1.247 +    RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
   1.248 +
   1.249 +    // CollectRuntimeStats reserves enough space.
   1.250 +    MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
   1.251 +    CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
   1.252 +    rtStats->initExtraCompartmentStats(compartment, &cStats);
   1.253 +
   1.254 +    compartment->compartmentStats = &cStats;
   1.255 +
   1.256 +    // Measure the compartment object itself, and things hanging off it.
   1.257 +    compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
   1.258 +                                        &cStats.typeInferenceAllocationSiteTables,
   1.259 +                                        &cStats.typeInferenceArrayTypeTables,
   1.260 +                                        &cStats.typeInferenceObjectTypeTables,
   1.261 +                                        &cStats.compartmentObject,
   1.262 +                                        &cStats.shapesMallocHeapCompartmentTables,
   1.263 +                                        &cStats.crossCompartmentWrappersTable,
   1.264 +                                        &cStats.regexpCompartment,
   1.265 +                                        &cStats.debuggeesSet,
   1.266 +                                        &cStats.savedStacksSet);
   1.267 +}
   1.268 +
   1.269 +static void
   1.270 +StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
   1.271 +                   JSGCTraceKind traceKind, size_t thingSize)
   1.272 +{
   1.273 +    RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
   1.274 +
   1.275 +    // The admin space includes (a) the header and (b) the padding between the
   1.276 +    // end of the header and the start of the first GC thing.
   1.277 +    size_t allocationSpace = arena->thingsSpan(thingSize);
   1.278 +    rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace;
   1.279 +
   1.280 +    // We don't call the callback on unused things.  So we compute the
   1.281 +    // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
   1.282 +    // We do this by setting arenaUnused to maxArenaUnused here, and then
   1.283 +    // subtracting thingSize for every used cell, in StatsCellCallback().
   1.284 +    rtStats->currZoneStats->unusedGCThings += allocationSpace;
   1.285 +}
   1.286 +
   1.287 +static CompartmentStats *
   1.288 +GetCompartmentStats(JSCompartment *comp)
   1.289 +{
   1.290 +    return static_cast<CompartmentStats *>(comp->compartmentStats);
   1.291 +}
   1.292 +
   1.293 +enum Granularity {
   1.294 +    FineGrained,    // Corresponds to CollectRuntimeStats()
   1.295 +    CoarseGrained   // Corresponds to AddSizeOfTab()
   1.296 +};
   1.297 +
   1.298 +// The various kinds of hashing are expensive, and the results are unused when
   1.299 +// doing coarse-grained measurements. Skipping them more than doubles the
   1.300 +// profile speed for complex pages such as gmail.com.
   1.301 +template <Granularity granularity>
   1.302 +static void
   1.303 +StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
   1.304 +                  size_t thingSize)
   1.305 +{
   1.306 +    StatsClosure *closure = static_cast<StatsClosure *>(data);
   1.307 +    RuntimeStats *rtStats = closure->rtStats;
   1.308 +    ZoneStats *zStats = rtStats->currZoneStats;
   1.309 +    switch (traceKind) {
   1.310 +      case JSTRACE_OBJECT: {
   1.311 +        JSObject *obj = static_cast<JSObject *>(thing);
   1.312 +        CompartmentStats *cStats = GetCompartmentStats(obj->compartment());
   1.313 +        if (obj->is<JSFunction>())
   1.314 +            cStats->objectsGCHeapFunction += thingSize;
   1.315 +        else if (obj->is<ArrayObject>())
   1.316 +            cStats->objectsGCHeapDenseArray += thingSize;
   1.317 +        else if (obj->is<CrossCompartmentWrapperObject>())
   1.318 +            cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
   1.319 +        else
   1.320 +            cStats->objectsGCHeapOrdinary += thingSize;
   1.321 +
   1.322 +        obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
   1.323 +
   1.324 +        if (ObjectPrivateVisitor *opv = closure->opv) {
   1.325 +            nsISupports *iface;
   1.326 +            if (opv->getISupports_(obj, &iface) && iface)
   1.327 +                cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
   1.328 +        }
   1.329 +        break;
   1.330 +      }
   1.331 +
   1.332 +      case JSTRACE_STRING: {
   1.333 +        JSString *str = static_cast<JSString *>(thing);
   1.334 +
   1.335 +        JS::StringInfo info;
   1.336 +        info.gcHeap = thingSize;
   1.337 +        info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
   1.338 +        info.numCopies = 1;
   1.339 +
   1.340 +        zStats->stringInfo.add(info);
   1.341 +
   1.342 +        if (granularity == FineGrained) {
   1.343 +            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
   1.344 +            if (!p) {
   1.345 +                // Ignore failure -- we just won't record the string as notable.
   1.346 +                (void)zStats->allStrings->add(p, str, info);
   1.347 +            } else {
   1.348 +                p->value().add(info);
   1.349 +            }
   1.350 +        }
   1.351 +        break;
   1.352 +      }
   1.353 +
   1.354 +      case JSTRACE_SHAPE: {
   1.355 +        Shape *shape = static_cast<Shape *>(thing);
   1.356 +        CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
   1.357 +        if (shape->inDictionary()) {
   1.358 +            cStats->shapesGCHeapDict += thingSize;
   1.359 +
   1.360 +            // nullptr because kidsSize shouldn't be incremented in this case.
   1.361 +            shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
   1.362 +                                          &cStats->shapesMallocHeapDictTables, nullptr);
   1.363 +        } else {
   1.364 +            JSObject *parent = shape->base()->getObjectParent();
   1.365 +            if (parent && parent->is<GlobalObject>())
   1.366 +                cStats->shapesGCHeapTreeGlobalParented += thingSize;
   1.367 +            else
   1.368 +                cStats->shapesGCHeapTreeNonGlobalParented += thingSize;
   1.369 +
   1.370 +            shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
   1.371 +                                          &cStats->shapesMallocHeapTreeTables,
   1.372 +                                          &cStats->shapesMallocHeapTreeShapeKids);
   1.373 +        }
   1.374 +        break;
   1.375 +      }
   1.376 +
   1.377 +      case JSTRACE_BASE_SHAPE: {
   1.378 +        BaseShape *base = static_cast<BaseShape *>(thing);
   1.379 +        CompartmentStats *cStats = GetCompartmentStats(base->compartment());
   1.380 +        cStats->shapesGCHeapBase += thingSize;
   1.381 +        break;
   1.382 +      }
   1.383 +
   1.384 +      case JSTRACE_SCRIPT: {
   1.385 +        JSScript *script = static_cast<JSScript *>(thing);
   1.386 +        CompartmentStats *cStats = GetCompartmentStats(script->compartment());
   1.387 +        cStats->scriptsGCHeap += thingSize;
   1.388 +        cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
   1.389 +        cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
   1.390 +#ifdef JS_ION
   1.391 +        jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
   1.392 +                                   &cStats->baselineStubsFallback);
   1.393 +        cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
   1.394 +#endif
   1.395 +
   1.396 +        ScriptSource *ss = script->scriptSource();
   1.397 +        SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
   1.398 +        if (!entry) {
   1.399 +            (void)closure->seenSources.add(entry, ss); // Not much to be done on failure.
   1.400 +
   1.401 +            JS::ScriptSourceInfo info;  // This zeroes all the sizes.
   1.402 +            ss->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &info);
   1.403 +            MOZ_ASSERT(info.compressed == 0 || info.uncompressed == 0);
   1.404 +
   1.405 +            rtStats->runtime.scriptSourceInfo.add(info);
   1.406 +
   1.407 +            if (granularity == FineGrained) {
   1.408 +                const char* filename = ss->filename();
   1.409 +                if (!filename)
   1.410 +                    filename = "<no filename>";
   1.411 +
   1.412 +                JS::RuntimeSizes::ScriptSourcesHashMap::AddPtr p =
   1.413 +                    rtStats->runtime.allScriptSources->lookupForAdd(filename);
   1.414 +                if (!p) {
   1.415 +                    // Ignore failure -- we just won't record the script source as notable.
   1.416 +                    (void)rtStats->runtime.allScriptSources->add(p, filename, info);
   1.417 +                } else {
   1.418 +                    p->value().add(info);
   1.419 +                }
   1.420 +            }
   1.421 +        }
   1.422 +
   1.423 +        break;
   1.424 +      }
   1.425 +
   1.426 +      case JSTRACE_LAZY_SCRIPT: {
   1.427 +        LazyScript *lazy = static_cast<LazyScript *>(thing);
   1.428 +        zStats->lazyScriptsGCHeap += thingSize;
   1.429 +        zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
   1.430 +        break;
   1.431 +      }
   1.432 +
   1.433 +      case JSTRACE_JITCODE: {
   1.434 +#ifdef JS_ION
   1.435 +        zStats->jitCodesGCHeap += thingSize;
   1.436 +        // The code for a script is counted in ExecutableAllocator::sizeOfCode().
   1.437 +#endif
   1.438 +        break;
   1.439 +      }
   1.440 +
   1.441 +      case JSTRACE_TYPE_OBJECT: {
   1.442 +        types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
   1.443 +        zStats->typeObjectsGCHeap += thingSize;
   1.444 +        zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_);
   1.445 +        break;
   1.446 +      }
   1.447 +
   1.448 +      default:
   1.449 +        MOZ_ASSUME_UNREACHABLE("invalid traceKind");
   1.450 +    }
   1.451 +
   1.452 +    // Yes, this is a subtraction:  see StatsArenaCallback() for details.
   1.453 +    zStats->unusedGCThings -= thingSize;
   1.454 +}
   1.455 +
   1.456 +static bool
   1.457 +FindNotableStrings(ZoneStats &zStats)
   1.458 +{
   1.459 +    using namespace JS;
   1.460 +
   1.461 +    // We should only run FindNotableStrings once per ZoneStats object.
   1.462 +    MOZ_ASSERT(zStats.notableStrings.empty());
   1.463 +
   1.464 +    for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) {
   1.465 +
   1.466 +        JSString *str = r.front().key();
   1.467 +        StringInfo &info = r.front().value();
   1.468 +
   1.469 +        if (!info.isNotable())
   1.470 +            continue;
   1.471 +
   1.472 +        if (!zStats.notableStrings.growBy(1))
   1.473 +            return false;
   1.474 +
   1.475 +        zStats.notableStrings.back() = NotableStringInfo(str, info);
   1.476 +
   1.477 +        // We're moving this string from a non-notable to a notable bucket, so
   1.478 +        // subtract it out of the non-notable tallies.
   1.479 +        zStats.stringInfo.subtract(info);
   1.480 +    }
   1.481 +    // Delete |allStrings| now, rather than waiting for zStats's destruction,
   1.482 +    // to reduce peak memory consumption during reporting.
   1.483 +    js_delete(zStats.allStrings);
   1.484 +    zStats.allStrings = nullptr;
   1.485 +    return true;
   1.486 +}
   1.487 +
   1.488 +bool
   1.489 +ZoneStats::initStrings(JSRuntime *rt)
   1.490 +{
   1.491 +    isTotals = false;
   1.492 +    allStrings = rt->new_<StringsHashMap>();
   1.493 +    if (!allStrings)
   1.494 +        return false;
   1.495 +    if (!allStrings->init()) {
   1.496 +        js_delete(allStrings);
   1.497 +        allStrings = nullptr;
   1.498 +        return false;
   1.499 +    }
   1.500 +    return true;
   1.501 +}
   1.502 +
   1.503 +static bool
   1.504 +FindNotableScriptSources(JS::RuntimeSizes &runtime)
   1.505 +{
   1.506 +    using namespace JS;
   1.507 +
   1.508 +    // We should only run FindNotableScriptSources once per RuntimeSizes.
   1.509 +    MOZ_ASSERT(runtime.notableScriptSources.empty());
   1.510 +
   1.511 +    for (RuntimeSizes::ScriptSourcesHashMap::Range r = runtime.allScriptSources->all();
   1.512 +         !r.empty();
   1.513 +         r.popFront())
   1.514 +    {
   1.515 +        const char *filename = r.front().key();
   1.516 +        ScriptSourceInfo &info = r.front().value();
   1.517 +
   1.518 +        if (!info.isNotable())
   1.519 +            continue;
   1.520 +
   1.521 +        if (!runtime.notableScriptSources.growBy(1))
   1.522 +            return false;
   1.523 +
   1.524 +        runtime.notableScriptSources.back() = NotableScriptSourceInfo(filename, info);
   1.525 +
   1.526 +        // We're moving this script source from a non-notable to a notable
   1.527 +        // bucket, so subtract its sizes from the non-notable tallies.
   1.528 +        runtime.scriptSourceInfo.subtract(info);
   1.529 +    }
   1.530 +    // Delete |allScriptSources| now, rather than waiting for zStats's
   1.531 +    // destruction, to reduce peak memory consumption during reporting.
   1.532 +    js_delete(runtime.allScriptSources);
   1.533 +    runtime.allScriptSources = nullptr;
   1.534 +    return true;
   1.535 +}
   1.536 +
   1.537 +JS_PUBLIC_API(bool)
   1.538 +JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
   1.539 +{
   1.540 +    if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
   1.541 +        return false;
   1.542 +
   1.543 +    if (!rtStats->zoneStatsVector.reserve(rt->zones.length()))
   1.544 +        return false;
   1.545 +
   1.546 +    rtStats->gcHeapChunkTotal =
   1.547 +        size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
   1.548 +
   1.549 +    rtStats->gcHeapUnusedChunks =
   1.550 +        size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
   1.551 +
   1.552 +    IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
   1.553 +                  DecommittedArenasChunkCallback);
   1.554 +
   1.555 +    // Take the per-compartment measurements.
   1.556 +    StatsClosure closure(rtStats, opv);
   1.557 +    if (!closure.init())
   1.558 +        return false;
   1.559 +    IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
   1.560 +                                        StatsArenaCallback, StatsCellCallback<FineGrained>);
   1.561 +
   1.562 +    // Take the "explicit/js/runtime/" measurements.
   1.563 +    rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
   1.564 +
   1.565 +    if (!FindNotableScriptSources(rtStats->runtime))
   1.566 +        return false;
   1.567 +
   1.568 +    ZoneStatsVector &zs = rtStats->zoneStatsVector;
   1.569 +    ZoneStats &zTotals = rtStats->zTotals;
   1.570 +
   1.571 +    // We don't look for notable strings for zTotals. So we first sum all the
   1.572 +    // zones' measurements to get the totals. Then we find the notable strings
   1.573 +    // within each zone.
   1.574 +    for (size_t i = 0; i < zs.length(); i++)
   1.575 +        zTotals.addSizes(zs[i]);
   1.576 +
   1.577 +    for (size_t i = 0; i < zs.length(); i++)
   1.578 +        if (!FindNotableStrings(zs[i]))
   1.579 +            return false;
   1.580 +
   1.581 +    MOZ_ASSERT(!zTotals.allStrings);
   1.582 +
   1.583 +    for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
   1.584 +        CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
   1.585 +        rtStats->cTotals.add(cStats);
   1.586 +    }
   1.587 +
   1.588 +    rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
   1.589 +                              rtStats->cTotals.sizeOfLiveGCThings();
   1.590 +
   1.591 +#ifdef DEBUG
   1.592 +    // Check that the in-arena measurements look ok.
   1.593 +    size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
   1.594 +                            rtStats->zTotals.unusedGCThings +
   1.595 +                            rtStats->gcHeapGCThings;
   1.596 +    JS_ASSERT(totalArenaSize % gc::ArenaSize == 0);
   1.597 +#endif
   1.598 +
   1.599 +    for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next())
   1.600 +        comp->compartmentStats = nullptr;
   1.601 +
   1.602 +    size_t numDirtyChunks =
   1.603 +        (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
   1.604 +    size_t perChunkAdmin =
   1.605 +        sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
   1.606 +    rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
   1.607 +
   1.608 +    // |gcHeapUnusedArenas| is the only thing left.  Compute it in terms of
   1.609 +    // all the others.  See the comment in RuntimeStats for explanation.
   1.610 +    rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
   1.611 +                                  rtStats->gcHeapDecommittedArenas -
   1.612 +                                  rtStats->gcHeapUnusedChunks -
   1.613 +                                  rtStats->zTotals.unusedGCThings -
   1.614 +                                  rtStats->gcHeapChunkAdmin -
   1.615 +                                  rtStats->zTotals.gcHeapArenaAdmin -
   1.616 +                                  rtStats->gcHeapGCThings;
   1.617 +    return true;
   1.618 +}
   1.619 +
   1.620 +JS_PUBLIC_API(size_t)
   1.621 +JS::SystemCompartmentCount(JSRuntime *rt)
   1.622 +{
   1.623 +    size_t n = 0;
   1.624 +    for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
   1.625 +        if (comp->isSystem)
   1.626 +            ++n;
   1.627 +    }
   1.628 +    return n;
   1.629 +}
   1.630 +
   1.631 +JS_PUBLIC_API(size_t)
   1.632 +JS::UserCompartmentCount(JSRuntime *rt)
   1.633 +{
   1.634 +    size_t n = 0;
   1.635 +    for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
   1.636 +        if (!comp->isSystem)
   1.637 +            ++n;
   1.638 +    }
   1.639 +    return n;
   1.640 +}
   1.641 +
   1.642 +JS_PUBLIC_API(size_t)
   1.643 +JS::PeakSizeOfTemporary(const JSRuntime *rt)
   1.644 +{
   1.645 +    return rt->tempLifoAlloc.peakSizeOfExcludingThis();
   1.646 +}
   1.647 +
   1.648 +namespace JS {
   1.649 +
   1.650 +JS_PUBLIC_API(bool)
   1.651 +AddSizeOfTab(JSRuntime *rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
   1.652 +             TabSizes *sizes)
   1.653 +{
   1.654 +    class SimpleJSRuntimeStats : public JS::RuntimeStats
   1.655 +    {
   1.656 +      public:
   1.657 +        SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
   1.658 +          : JS::RuntimeStats(mallocSizeOf)
   1.659 +        {}
   1.660 +
   1.661 +        virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats)
   1.662 +            MOZ_OVERRIDE
   1.663 +        {}
   1.664 +
   1.665 +        virtual void initExtraCompartmentStats(
   1.666 +            JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE
   1.667 +        {}
   1.668 +    };
   1.669 +
   1.670 +    SimpleJSRuntimeStats rtStats(mallocSizeOf);
   1.671 +
   1.672 +    JS::Zone *zone = GetObjectZone(obj);
   1.673 +
   1.674 +    if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length()))
   1.675 +        return false;
   1.676 +
   1.677 +    if (!rtStats.zoneStatsVector.reserve(1))
   1.678 +        return false;
   1.679 +
   1.680 +    // Take the per-compartment measurements.
   1.681 +    StatsClosure closure(&rtStats, opv);
   1.682 +    if (!closure.init())
   1.683 +        return false;
   1.684 +    IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
   1.685 +                                       StatsCompartmentCallback, StatsArenaCallback,
   1.686 +                                       StatsCellCallback<CoarseGrained>);
   1.687 +
   1.688 +    JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
   1.689 +    rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
   1.690 +
   1.691 +    for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
   1.692 +        CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
   1.693 +        rtStats.cTotals.add(cStats);
   1.694 +    }
   1.695 +
   1.696 +    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
   1.697 +        comp->compartmentStats = nullptr;
   1.698 +
   1.699 +    rtStats.zTotals.addToTabSizes(sizes);
   1.700 +    rtStats.cTotals.addToTabSizes(sizes);
   1.701 +
   1.702 +    return true;
   1.703 +}
   1.704 +
   1.705 +} // namespace JS
   1.706 +

mercurial