LCOV - code coverage report
Current view: top level - js/src/vm - MemoryMetrics.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 465 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 54 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "gc/Heap.h"
      18             : #include "jit/BaselineJIT.h"
      19             : #include "jit/Ion.h"
      20             : #include "vm/ArrayObject.h"
      21             : #include "vm/Runtime.h"
      22             : #include "vm/Shape.h"
      23             : #include "vm/String.h"
      24             : #include "vm/Symbol.h"
      25             : #include "vm/WrapperObject.h"
      26             : #include "wasm/WasmInstance.h"
      27             : #include "wasm/WasmJS.h"
      28             : #include "wasm/WasmModule.h"
      29             : 
      30             : using mozilla::DebugOnly;
      31             : using mozilla::MallocSizeOf;
      32             : using mozilla::Move;
      33             : using mozilla::PodCopy;
      34             : using mozilla::PodEqual;
      35             : 
      36             : using namespace js;
      37             : 
      38             : using JS::RuntimeStats;
      39             : using JS::ObjectPrivateVisitor;
      40             : using JS::ZoneStats;
      41             : using JS::CompartmentStats;
      42             : 
      43             : namespace js {
      44             : 
      45             : JS_FRIEND_API(size_t)
      46           0 : MemoryReportingSundriesThreshold()
      47             : {
      48           0 :     return 8 * 1024;
      49             : }
      50             : 
      51             : template <typename CharT>
      52             : static uint32_t
      53           0 : HashStringChars(JSString* s)
      54             : {
      55           0 :     ScopedJSFreePtr<CharT> ownedChars;
      56             :     const CharT* chars;
      57           0 :     JS::AutoCheckCannotGC nogc;
      58           0 :     if (s->isLinear()) {
      59           0 :         chars = s->asLinear().chars<CharT>(nogc);
      60             :     } else {
      61             :         // Slowest hash function evar!
      62           0 :         if (!s->asRope().copyChars<CharT>(/* tcx */ nullptr, ownedChars))
      63           0 :             MOZ_CRASH("oom");
      64           0 :         chars = ownedChars;
      65             :     }
      66             : 
      67           0 :     return mozilla::HashString(chars, s->length());
      68             : }
      69             : 
      70             : /* static */ HashNumber
      71           0 : InefficientNonFlatteningStringHashPolicy::hash(const Lookup& l)
      72             : {
      73           0 :     return l->hasLatin1Chars()
      74           0 :            ? HashStringChars<Latin1Char>(l)
      75           0 :            : HashStringChars<char16_t>(l);
      76             : }
      77             : 
      78             : template <typename Char1, typename Char2>
      79             : static bool
      80           0 : EqualStringsPure(JSString* s1, JSString* s2)
      81             : {
      82           0 :     if (s1->length() != s2->length())
      83           0 :         return false;
      84             : 
      85             :     const Char1* c1;
      86           0 :     ScopedJSFreePtr<Char1> ownedChars1;
      87           0 :     JS::AutoCheckCannotGC nogc;
      88           0 :     if (s1->isLinear()) {
      89           0 :         c1 = s1->asLinear().chars<Char1>(nogc);
      90             :     } else {
      91           0 :         if (!s1->asRope().copyChars<Char1>(/* tcx */ nullptr, ownedChars1))
      92           0 :             MOZ_CRASH("oom");
      93           0 :         c1 = ownedChars1;
      94             :     }
      95             : 
      96             :     const Char2* c2;
      97           0 :     ScopedJSFreePtr<Char2> ownedChars2;
      98           0 :     if (s2->isLinear()) {
      99           0 :         c2 = s2->asLinear().chars<Char2>(nogc);
     100             :     } else {
     101           0 :         if (!s2->asRope().copyChars<Char2>(/* tcx */ nullptr, ownedChars2))
     102           0 :             MOZ_CRASH("oom");
     103           0 :         c2 = ownedChars2;
     104             :     }
     105             : 
     106           0 :     return EqualChars(c1, c2, s1->length());
     107             : }
     108             : 
     109             : /* static */ bool
     110           0 : InefficientNonFlatteningStringHashPolicy::match(const JSString* const& k, const Lookup& l)
     111             : {
     112             :     // We can't use js::EqualStrings, because that flattens our strings.
     113           0 :     JSString* s1 = const_cast<JSString*>(k);
     114           0 :     if (k->hasLatin1Chars()) {
     115           0 :         return l->hasLatin1Chars()
     116           0 :                ? EqualStringsPure<Latin1Char, Latin1Char>(s1, l)
     117           0 :                : EqualStringsPure<Latin1Char, char16_t>(s1, l);
     118             :     }
     119             : 
     120           0 :     return l->hasLatin1Chars()
     121           0 :            ? EqualStringsPure<char16_t, Latin1Char>(s1, l)
     122           0 :            : EqualStringsPure<char16_t, char16_t>(s1, l);
     123             : }
     124             : 
     125             : /* static */ HashNumber
     126           0 : CStringHashPolicy::hash(const Lookup& l)
     127             : {
     128           0 :     return mozilla::HashString(l);
     129             : }
     130             : 
     131             : /* static */ bool
     132           0 : CStringHashPolicy::match(const char* const& k, const Lookup& l)
     133             : {
     134           0 :     return strcmp(k, l) == 0;
     135             : }
     136             : 
     137             : } // namespace js
     138             : 
     139             : namespace JS {
     140             : 
     141           0 : NotableStringInfo::NotableStringInfo()
     142             :   : StringInfo(),
     143             :     buffer(0),
     144           0 :     length(0)
     145             : {
     146           0 : }
     147             : 
     148             : template <typename CharT>
     149             : static void
     150           0 : StoreStringChars(char* buffer, size_t bufferSize, JSString* str)
     151             : {
     152             :     const CharT* chars;
     153           0 :     ScopedJSFreePtr<CharT> ownedChars;
     154           0 :     JS::AutoCheckCannotGC nogc;
     155           0 :     if (str->isLinear()) {
     156           0 :         chars = str->asLinear().chars<CharT>(nogc);
     157             :     } else {
     158           0 :         if (!str->asRope().copyChars<CharT>(/* tcx */ nullptr, ownedChars))
     159           0 :             MOZ_CRASH("oom");
     160           0 :         chars = ownedChars;
     161             :     }
     162             : 
     163             :     // We might truncate |str| even if it's much shorter than 1024 chars, if
     164             :     // |str| contains unicode chars.  Since this is just for a memory reporter,
     165             :     // we don't care.
     166           0 :     PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
     167           0 : }
     168             : 
     169           0 : NotableStringInfo::NotableStringInfo(JSString* str, const StringInfo& info)
     170             :   : StringInfo(info),
     171           0 :     length(str->length())
     172             : {
     173           0 :     size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS));
     174           0 :     buffer = js_pod_malloc<char>(bufferSize);
     175           0 :     if (!buffer) {
     176           0 :         MOZ_CRASH("oom");
     177             :     }
     178             : 
     179           0 :     if (str->hasLatin1Chars())
     180           0 :         StoreStringChars<Latin1Char>(buffer, bufferSize, str);
     181             :     else
     182           0 :         StoreStringChars<char16_t>(buffer, bufferSize, str);
     183           0 : }
     184             : 
     185           0 : NotableStringInfo::NotableStringInfo(NotableStringInfo&& info)
     186           0 :   : StringInfo(Move(info)),
     187           0 :     length(info.length)
     188             : {
     189           0 :     buffer = info.buffer;
     190           0 :     info.buffer = nullptr;
     191           0 : }
     192             : 
     193           0 : NotableStringInfo& NotableStringInfo::operator=(NotableStringInfo&& info)
     194             : {
     195           0 :     MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
     196           0 :     this->~NotableStringInfo();
     197           0 :     new (this) NotableStringInfo(Move(info));
     198           0 :     return *this;
     199             : }
     200             : 
     201           0 : NotableClassInfo::NotableClassInfo()
     202             :   : ClassInfo(),
     203           0 :     className_(nullptr)
     204             : {
     205           0 : }
     206             : 
     207           0 : NotableClassInfo::NotableClassInfo(const char* className, const ClassInfo& info)
     208           0 :   : ClassInfo(info)
     209             : {
     210           0 :     size_t bytes = strlen(className) + 1;
     211           0 :     className_ = js_pod_malloc<char>(bytes);
     212           0 :     if (!className_)
     213           0 :         MOZ_CRASH("oom");
     214           0 :     PodCopy(className_, className, bytes);
     215           0 : }
     216             : 
     217           0 : NotableClassInfo::NotableClassInfo(NotableClassInfo&& info)
     218           0 :   : ClassInfo(Move(info))
     219             : {
     220           0 :     className_ = info.className_;
     221           0 :     info.className_ = nullptr;
     222           0 : }
     223             : 
     224           0 : NotableClassInfo& NotableClassInfo::operator=(NotableClassInfo&& info)
     225             : {
     226           0 :     MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
     227           0 :     this->~NotableClassInfo();
     228           0 :     new (this) NotableClassInfo(Move(info));
     229           0 :     return *this;
     230             : }
     231             : 
     232           0 : NotableScriptSourceInfo::NotableScriptSourceInfo()
     233             :   : ScriptSourceInfo(),
     234           0 :     filename_(nullptr)
     235             : {
     236           0 : }
     237             : 
     238           0 : NotableScriptSourceInfo::NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info)
     239           0 :   : ScriptSourceInfo(info)
     240             : {
     241           0 :     size_t bytes = strlen(filename) + 1;
     242           0 :     filename_ = js_pod_malloc<char>(bytes);
     243           0 :     if (!filename_)
     244           0 :         MOZ_CRASH("oom");
     245           0 :     PodCopy(filename_, filename, bytes);
     246           0 : }
     247             : 
     248           0 : NotableScriptSourceInfo::NotableScriptSourceInfo(NotableScriptSourceInfo&& info)
     249           0 :   : ScriptSourceInfo(Move(info))
     250             : {
     251           0 :     filename_ = info.filename_;
     252           0 :     info.filename_ = nullptr;
     253           0 : }
     254             : 
     255           0 : NotableScriptSourceInfo& NotableScriptSourceInfo::operator=(NotableScriptSourceInfo&& info)
     256             : {
     257           0 :     MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
     258           0 :     this->~NotableScriptSourceInfo();
     259           0 :     new (this) NotableScriptSourceInfo(Move(info));
     260           0 :     return *this;
     261             : }
     262             : 
     263             : 
     264             : } // namespace JS
     265             : 
     266             : typedef HashSet<ScriptSource*, DefaultHasher<ScriptSource*>, SystemAllocPolicy> SourceSet;
     267             : 
     268           0 : struct StatsClosure
     269             : {
     270             :     RuntimeStats* rtStats;
     271             :     ObjectPrivateVisitor* opv;
     272             :     SourceSet seenSources;
     273             :     wasm::Metadata::SeenSet wasmSeenMetadata;
     274             :     wasm::ShareableBytes::SeenSet wasmSeenBytes;
     275             :     wasm::Code::SeenSet wasmSeenCode;
     276             :     wasm::Table::SeenSet wasmSeenTables;
     277             :     bool anonymize;
     278             : 
     279           0 :     StatsClosure(RuntimeStats* rt, ObjectPrivateVisitor* v, bool anon)
     280           0 :       : rtStats(rt),
     281             :         opv(v),
     282           0 :         anonymize(anon)
     283           0 :     {}
     284             : 
     285           0 :     bool init() {
     286           0 :         return seenSources.init() &&
     287           0 :                wasmSeenMetadata.init() &&
     288           0 :                wasmSeenBytes.init() &&
     289           0 :                wasmSeenCode.init() &&
     290           0 :                wasmSeenTables.init();
     291             :     }
     292             : };
     293             : 
     294             : static void
     295           0 : DecommittedArenasChunkCallback(JSRuntime* rt, void* data, gc::Chunk* chunk)
     296             : {
     297             :     // This case is common and fast to check.  Do it first.
     298           0 :     if (chunk->decommittedArenas.isAllClear())
     299           0 :         return;
     300             : 
     301           0 :     size_t n = 0;
     302           0 :     for (size_t i = 0; i < gc::ArenasPerChunk; i++) {
     303           0 :         if (chunk->decommittedArenas.get(i))
     304           0 :             n += gc::ArenaSize;
     305             :     }
     306           0 :     MOZ_ASSERT(n > 0);
     307           0 :     *static_cast<size_t*>(data) += n;
     308             : }
     309             : 
     310             : static void
     311           0 : StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone)
     312             : {
     313             :     // Append a new CompartmentStats to the vector.
     314           0 :     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
     315             : 
     316             :     // CollectRuntimeStats reserves enough space.
     317           0 :     MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
     318           0 :     ZoneStats& zStats = rtStats->zoneStatsVector.back();
     319           0 :     if (!zStats.initStrings(rt))
     320           0 :         MOZ_CRASH("oom");
     321           0 :     rtStats->initExtraZoneStats(zone, &zStats);
     322           0 :     rtStats->currZoneStats = &zStats;
     323             : 
     324           0 :     zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
     325             :                                  &zStats.typePool,
     326             :                                  &zStats.regexpZone,
     327             :                                  &zStats.jitZone,
     328             :                                  &zStats.baselineStubsOptimized,
     329             :                                  &zStats.cachedCFG,
     330             :                                  &zStats.uniqueIdMap,
     331             :                                  &zStats.shapeTables,
     332           0 :                                  &rtStats->runtime.atomsMarkBitmaps);
     333           0 : }
     334             : 
     335             : static void
     336           0 : StatsCompartmentCallback(JSContext* cx, void* data, JSCompartment* compartment)
     337             : {
     338             :     // Append a new CompartmentStats to the vector.
     339           0 :     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
     340             : 
     341             :     // CollectRuntimeStats reserves enough space.
     342           0 :     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     343           0 :     CompartmentStats& cStats = rtStats->compartmentStatsVector.back();
     344           0 :     if (!cStats.initClasses(cx->runtime()))
     345           0 :         MOZ_CRASH("oom");
     346           0 :     rtStats->initExtraCompartmentStats(compartment, &cStats);
     347             : 
     348           0 :     compartment->setCompartmentStats(&cStats);
     349             : 
     350             :     // Measure the compartment object itself, and things hanging off it.
     351           0 :     compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
     352             :                                         &cStats.typeInferenceAllocationSiteTables,
     353             :                                         &cStats.typeInferenceArrayTypeTables,
     354             :                                         &cStats.typeInferenceObjectTypeTables,
     355             :                                         &cStats.compartmentObject,
     356             :                                         &cStats.compartmentTables,
     357             :                                         &cStats.innerViewsTable,
     358             :                                         &cStats.lazyArrayBuffersTable,
     359             :                                         &cStats.objectMetadataTable,
     360             :                                         &cStats.crossCompartmentWrappersTable,
     361             :                                         &cStats.savedStacksSet,
     362             :                                         &cStats.varNamesSet,
     363             :                                         &cStats.nonSyntacticLexicalScopesTable,
     364             :                                         &cStats.templateLiteralMap,
     365             :                                         &cStats.jitCompartment,
     366           0 :                                         &cStats.privateData);
     367           0 : }
     368             : 
     369             : static void
     370           0 : StatsArenaCallback(JSRuntime* rt, void* data, gc::Arena* arena,
     371             :                    JS::TraceKind traceKind, size_t thingSize)
     372             : {
     373           0 :     RuntimeStats* rtStats = static_cast<StatsClosure*>(data)->rtStats;
     374             : 
     375             :     // The admin space includes (a) the header fields and (b) the padding
     376             :     // between the end of the header fields and the first GC thing.
     377           0 :     size_t allocationSpace = gc::Arena::thingsSpan(arena->getAllocKind());
     378           0 :     rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace;
     379             : 
     380             :     // We don't call the callback on unused things.  So we compute the
     381             :     // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
     382             :     // We do this by setting arenaUnused to maxArenaUnused here, and then
     383             :     // subtracting thingSize for every used cell, in StatsCellCallback().
     384           0 :     rtStats->currZoneStats->unusedGCThings.addToKind(traceKind, allocationSpace);
     385           0 : }
     386             : 
     387             : // FineGrained is used for normal memory reporting.  CoarseGrained is used by
     388             : // AddSizeOfTab(), which aggregates all the measurements into a handful of
     389             : // high-level numbers, which means that fine-grained reporting would be a waste
     390             : // of effort.
     391             : enum Granularity {
     392             :     FineGrained,
     393             :     CoarseGrained
     394             : };
     395             : 
     396             : static void
     397           0 : AddClassInfo(Granularity granularity, CompartmentStats& cStats, const char* className,
     398             :              JS::ClassInfo& info)
     399             : {
     400           0 :     if (granularity == FineGrained) {
     401           0 :         if (!className)
     402           0 :             className = "<no class name>";
     403           0 :         CompartmentStats::ClassesHashMap::AddPtr p = cStats.allClasses->lookupForAdd(className);
     404           0 :         if (!p) {
     405           0 :             bool ok = cStats.allClasses->add(p, className, info);
     406             :             // Ignore failure -- we just won't record the
     407             :             // object/shape/base-shape as notable.
     408             :             (void)ok;
     409             :         } else {
     410           0 :             p->value().add(info);
     411             :         }
     412             :     }
     413           0 : }
     414             : 
     415             : template <Granularity granularity>
     416             : static void
     417           0 : CollectScriptSourceStats(StatsClosure* closure, ScriptSource* ss)
     418             : {
     419           0 :     RuntimeStats* rtStats = closure->rtStats;
     420             : 
     421           0 :     SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
     422           0 :     if (entry)
     423           0 :         return;
     424             : 
     425           0 :     bool ok = closure->seenSources.add(entry, ss);
     426             :     (void)ok; // Not much to be done on failure.
     427             : 
     428           0 :     JS::ScriptSourceInfo info;  // This zeroes all the sizes.
     429           0 :     ss->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &info);
     430             : 
     431           0 :     rtStats->runtime.scriptSourceInfo.add(info);
     432             : 
     433             :     if (granularity == FineGrained) {
     434           0 :         const char* filename = ss->filename();
     435           0 :         if (!filename)
     436           0 :             filename = "<no filename>";
     437             : 
     438             :         JS::RuntimeSizes::ScriptSourcesHashMap::AddPtr p =
     439           0 :             rtStats->runtime.allScriptSources->lookupForAdd(filename);
     440           0 :         if (!p) {
     441           0 :             bool ok = rtStats->runtime.allScriptSources->add(p, filename, info);
     442             :             // Ignore failure -- we just won't record the script source as notable.
     443             :             (void)ok;
     444             :         } else {
     445           0 :             p->value().add(info);
     446             :         }
     447             :     }
     448             : }
     449             : 
     450             : 
     451             : // The various kinds of hashing are expensive, and the results are unused when
     452             : // doing coarse-grained measurements. Skipping them more than doubles the
     453             : // profile speed for complex pages such as gmail.com.
     454             : template <Granularity granularity>
     455             : static void
     456           0 : StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKind,
     457             :                   size_t thingSize)
     458             : {
     459           0 :     StatsClosure* closure = static_cast<StatsClosure*>(data);
     460           0 :     RuntimeStats* rtStats = closure->rtStats;
     461           0 :     ZoneStats* zStats = rtStats->currZoneStats;
     462           0 :     switch (traceKind) {
     463             :       case JS::TraceKind::Object: {
     464           0 :         JSObject* obj = static_cast<JSObject*>(thing);
     465           0 :         CompartmentStats& cStats = obj->compartment()->compartmentStats();
     466           0 :         JS::ClassInfo info;        // This zeroes all the sizes.
     467           0 :         info.objectsGCHeap += thingSize;
     468             : 
     469           0 :         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
     470             : 
     471             :         // These classes require special handling due to shared resources which
     472             :         // we must be careful not to report twice.
     473           0 :         if (obj->is<WasmModuleObject>()) {
     474           0 :             wasm::Module& module = obj->as<WasmModuleObject>().module();
     475           0 :             if (ScriptSource* ss = module.metadata().maybeScriptSource())
     476           0 :                 CollectScriptSourceStats<granularity>(closure, ss);
     477           0 :             module.addSizeOfMisc(rtStats->mallocSizeOf_,
     478             :                                  &closure->wasmSeenMetadata,
     479             :                                  &closure->wasmSeenBytes,
     480             :                                  &closure->wasmSeenCode,
     481             :                                  &info.objectsNonHeapCodeWasm,
     482             :                                  &info.objectsMallocHeapMisc);
     483           0 :         } else if (obj->is<WasmInstanceObject>()) {
     484           0 :             wasm::Instance& instance = obj->as<WasmInstanceObject>().instance();
     485           0 :             if (ScriptSource* ss = instance.metadata().maybeScriptSource())
     486           0 :                 CollectScriptSourceStats<granularity>(closure, ss);
     487           0 :             instance.addSizeOfMisc(rtStats->mallocSizeOf_,
     488             :                                    &closure->wasmSeenMetadata,
     489             :                                    &closure->wasmSeenBytes,
     490             :                                    &closure->wasmSeenCode,
     491             :                                    &closure->wasmSeenTables,
     492             :                                    &info.objectsNonHeapCodeWasm,
     493             :                                    &info.objectsMallocHeapMisc);
     494             :         }
     495             : 
     496           0 :         cStats.classInfo.add(info);
     497             : 
     498           0 :         const Class* clasp = obj->getClass();
     499           0 :         const char* className = clasp->name;
     500           0 :         AddClassInfo(granularity, cStats, className, info);
     501             : 
     502           0 :         if (ObjectPrivateVisitor* opv = closure->opv) {
     503             :             nsISupports* iface;
     504           0 :             if (opv->getISupports_(obj, &iface) && iface)
     505           0 :                 cStats.objectsPrivate += opv->sizeOfIncludingThis(iface);
     506             :         }
     507           0 :         break;
     508             :       }
     509             : 
     510             :       case JS::TraceKind::Script: {
     511           0 :         JSScript* script = static_cast<JSScript*>(thing);
     512           0 :         CompartmentStats& cStats = script->compartment()->compartmentStats();
     513           0 :         cStats.scriptsGCHeap += thingSize;
     514           0 :         cStats.scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
     515           0 :         cStats.typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
     516           0 :         jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats.baselineData,
     517             :                                    &cStats.baselineStubsFallback);
     518           0 :         cStats.ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
     519           0 :         CollectScriptSourceStats<granularity>(closure, script->scriptSource());
     520           0 :         break;
     521             :       }
     522             : 
     523             :       case JS::TraceKind::String: {
     524           0 :         JSString* str = static_cast<JSString*>(thing);
     525             : 
     526           0 :         JS::StringInfo info;
     527           0 :         if (str->hasLatin1Chars()) {
     528           0 :             info.gcHeapLatin1 = thingSize;
     529           0 :             info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     530             :         } else {
     531           0 :             info.gcHeapTwoByte = thingSize;
     532           0 :             info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     533             :         }
     534           0 :         info.numCopies = 1;
     535             : 
     536           0 :         zStats->stringInfo.add(info);
     537             : 
     538             :         // The primary use case for anonymization is automated crash submission
     539             :         // (to help detect OOM crashes). In that case, we don't want to pay the
     540             :         // memory cost required to do notable string detection.
     541           0 :         if (granularity == FineGrained && !closure->anonymize) {
     542           0 :             ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
     543           0 :             if (!p) {
     544           0 :                 bool ok = zStats->allStrings->add(p, str, info);
     545             :                 // Ignore failure -- we just won't record the string as notable.
     546             :                 (void)ok;
     547             :             } else {
     548           0 :                 p->value().add(info);
     549             :             }
     550             :         }
     551           0 :         break;
     552             :       }
     553             : 
     554             :       case JS::TraceKind::Symbol:
     555           0 :         zStats->symbolsGCHeap += thingSize;
     556           0 :         break;
     557             : 
     558             :       case JS::TraceKind::BaseShape: {
     559           0 :         JS::ShapeInfo info;        // This zeroes all the sizes.
     560           0 :         info.shapesGCHeapBase += thingSize;
     561             :         // No malloc-heap measurements.
     562             : 
     563           0 :         zStats->shapeInfo.add(info);
     564           0 :         break;
     565             :       }
     566             : 
     567             :       case JS::TraceKind::JitCode: {
     568           0 :         zStats->jitCodesGCHeap += thingSize;
     569             :         // The code for a script is counted in ExecutableAllocator::sizeOfCode().
     570           0 :         break;
     571             :       }
     572             : 
     573             :       case JS::TraceKind::LazyScript: {
     574           0 :         LazyScript* lazy = static_cast<LazyScript*>(thing);
     575           0 :         zStats->lazyScriptsGCHeap += thingSize;
     576           0 :         zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     577           0 :         break;
     578             :       }
     579             : 
     580             :       case JS::TraceKind::Shape: {
     581           0 :         Shape* shape = static_cast<Shape*>(thing);
     582             : 
     583           0 :         JS::ShapeInfo info;        // This zeroes all the sizes.
     584           0 :         if (shape->inDictionary())
     585           0 :             info.shapesGCHeapDict += thingSize;
     586             :         else
     587           0 :             info.shapesGCHeapTree += thingSize;
     588           0 :         shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
     589           0 :         zStats->shapeInfo.add(info);
     590           0 :         break;
     591             :       }
     592             : 
     593             :       case JS::TraceKind::ObjectGroup: {
     594           0 :         ObjectGroup* group = static_cast<ObjectGroup*>(thing);
     595           0 :         zStats->objectGroupsGCHeap += thingSize;
     596           0 :         zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     597           0 :         break;
     598             :       }
     599             : 
     600             :       case JS::TraceKind::Scope: {
     601           0 :         Scope* scope = static_cast<Scope*>(thing);
     602           0 :         zStats->scopesGCHeap += thingSize;
     603           0 :         zStats->scopesMallocHeap += scope->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     604           0 :         break;
     605             :       }
     606             : 
     607             :       case JS::TraceKind::RegExpShared: {
     608           0 :         auto regexp = static_cast<RegExpShared*>(thing);
     609           0 :         zStats->regExpSharedsGCHeap += thingSize;
     610           0 :         zStats->regExpSharedsMallocHeap += regexp->sizeOfExcludingThis(rtStats->mallocSizeOf_);
     611           0 :         break;
     612             :       }
     613             : 
     614             :       default:
     615           0 :         MOZ_CRASH("invalid traceKind in StatsCellCallback");
     616             :     }
     617             : 
     618             :     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     619           0 :     zStats->unusedGCThings.addToKind(traceKind, -thingSize);
     620           0 : }
     621             : 
     622             : bool
     623           0 : ZoneStats::initStrings(JSRuntime* rt)
     624             : {
     625           0 :     isTotals = false;
     626           0 :     allStrings = rt->new_<StringsHashMap>();
     627           0 :     if (!allStrings || !allStrings->init()) {
     628           0 :         js_delete(allStrings);
     629           0 :         allStrings = nullptr;
     630           0 :         return false;
     631             :     }
     632           0 :     return true;
     633             : }
     634             : 
     635             : bool
     636           0 : CompartmentStats::initClasses(JSRuntime* rt)
     637             : {
     638           0 :     isTotals = false;
     639           0 :     allClasses = rt->new_<ClassesHashMap>();
     640           0 :     if (!allClasses || !allClasses->init()) {
     641           0 :         js_delete(allClasses);
     642           0 :         allClasses = nullptr;
     643           0 :         return false;
     644             :     }
     645           0 :     return true;
     646             : }
     647             : 
     648             : static bool
     649           0 : FindNotableStrings(ZoneStats& zStats)
     650             : {
     651             :     using namespace JS;
     652             : 
     653             :     // We should only run FindNotableStrings once per ZoneStats object.
     654           0 :     MOZ_ASSERT(zStats.notableStrings.empty());
     655             : 
     656           0 :     for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) {
     657             : 
     658           0 :         JSString* str = r.front().key();
     659           0 :         StringInfo& info = r.front().value();
     660             : 
     661           0 :         if (!info.isNotable())
     662           0 :             continue;
     663             : 
     664           0 :         if (!zStats.notableStrings.growBy(1))
     665           0 :             return false;
     666             : 
     667           0 :         zStats.notableStrings.back() = NotableStringInfo(str, info);
     668             : 
     669             :         // We're moving this string from a non-notable to a notable bucket, so
     670             :         // subtract it out of the non-notable tallies.
     671           0 :         zStats.stringInfo.subtract(info);
     672             :     }
     673             :     // Delete |allStrings| now, rather than waiting for zStats's destruction,
     674             :     // to reduce peak memory consumption during reporting.
     675           0 :     js_delete(zStats.allStrings);
     676           0 :     zStats.allStrings = nullptr;
     677           0 :     return true;
     678             : }
     679             : 
     680             : static bool
     681           0 : FindNotableClasses(CompartmentStats& cStats)
     682             : {
     683             :     using namespace JS;
     684             : 
     685             :     // We should only run FindNotableClasses once per ZoneStats object.
     686           0 :     MOZ_ASSERT(cStats.notableClasses.empty());
     687             : 
     688           0 :     for (CompartmentStats::ClassesHashMap::Range r = cStats.allClasses->all();
     689           0 :          !r.empty();
     690           0 :          r.popFront())
     691             :     {
     692           0 :         const char* className = r.front().key();
     693           0 :         ClassInfo& info = r.front().value();
     694             : 
     695             :         // If this class isn't notable, or if we can't grow the notableStrings
     696             :         // vector, skip this string.
     697           0 :         if (!info.isNotable())
     698           0 :             continue;
     699             : 
     700           0 :         if (!cStats.notableClasses.growBy(1))
     701           0 :             return false;
     702             : 
     703           0 :         cStats.notableClasses.back() = NotableClassInfo(className, info);
     704             : 
     705             :         // We're moving this class from a non-notable to a notable bucket, so
     706             :         // subtract it out of the non-notable tallies.
     707           0 :         cStats.classInfo.subtract(info);
     708             :     }
     709             :     // Delete |allClasses| now, rather than waiting for zStats's destruction,
     710             :     // to reduce peak memory consumption during reporting.
     711           0 :     js_delete(cStats.allClasses);
     712           0 :     cStats.allClasses = nullptr;
     713           0 :     return true;
     714             : }
     715             : 
     716             : static bool
     717           0 : FindNotableScriptSources(JS::RuntimeSizes& runtime)
     718             : {
     719             :     using namespace JS;
     720             : 
     721             :     // We should only run FindNotableScriptSources once per RuntimeSizes.
     722           0 :     MOZ_ASSERT(runtime.notableScriptSources.empty());
     723             : 
     724           0 :     for (RuntimeSizes::ScriptSourcesHashMap::Range r = runtime.allScriptSources->all();
     725           0 :          !r.empty();
     726           0 :          r.popFront())
     727             :     {
     728           0 :         const char* filename = r.front().key();
     729           0 :         ScriptSourceInfo& info = r.front().value();
     730             : 
     731           0 :         if (!info.isNotable())
     732           0 :             continue;
     733             : 
     734           0 :         if (!runtime.notableScriptSources.growBy(1))
     735           0 :             return false;
     736             : 
     737           0 :         runtime.notableScriptSources.back() = NotableScriptSourceInfo(filename, info);
     738             : 
     739             :         // We're moving this script source from a non-notable to a notable
     740             :         // bucket, so subtract its sizes from the non-notable tallies.
     741           0 :         runtime.scriptSourceInfo.subtract(info);
     742             :     }
     743             :     // Delete |allScriptSources| now, rather than waiting for zStats's
     744             :     // destruction, to reduce peak memory consumption during reporting.
     745           0 :     js_delete(runtime.allScriptSources);
     746           0 :     runtime.allScriptSources = nullptr;
     747           0 :     return true;
     748             : }
     749             : 
     750             : static bool
     751           0 : CollectRuntimeStatsHelper(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv,
     752             :                           bool anonymize, IterateCellCallback statsCellCallback)
     753             : {
     754           0 :     JSRuntime* rt = cx->runtime();
     755           0 :     if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
     756           0 :         return false;
     757             : 
     758           0 :     size_t totalZones = 1; // For the atoms zone.
     759           0 :     for (ZoneGroupsIter group(rt); !group.done(); group.next())
     760           0 :         totalZones += group->zones().length();
     761           0 :     if (!rtStats->zoneStatsVector.reserve(totalZones))
     762           0 :         return false;
     763             : 
     764           0 :     rtStats->gcHeapChunkTotal =
     765           0 :         size_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
     766             : 
     767           0 :     rtStats->gcHeapUnusedChunks =
     768           0 :         size_t(JS_GetGCParameter(cx, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
     769             : 
     770           0 :     IterateChunks(cx, &rtStats->gcHeapDecommittedArenas,
     771           0 :                   DecommittedArenasChunkCallback);
     772             : 
     773             :     // Take the per-compartment measurements.
     774           0 :     StatsClosure closure(rtStats, opv, anonymize);
     775           0 :     if (!closure.init())
     776           0 :         return false;
     777             :     IterateHeapUnbarriered(cx, &closure,
     778             :                                                    StatsZoneCallback,
     779             :                                                    StatsCompartmentCallback,
     780             :                                                    StatsArenaCallback,
     781           0 :                                                    statsCellCallback);
     782             : 
     783             :     // Take the "explicit/js/runtime/" measurements.
     784           0 :     rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
     785             : 
     786           0 :     if (!FindNotableScriptSources(rtStats->runtime))
     787           0 :         return false;
     788             : 
     789           0 :     JS::ZoneStatsVector& zs = rtStats->zoneStatsVector;
     790           0 :     ZoneStats& zTotals = rtStats->zTotals;
     791             : 
     792             :     // We don't look for notable strings for zTotals. So we first sum all the
     793             :     // zones' measurements to get the totals. Then we find the notable strings
     794             :     // within each zone.
     795           0 :     for (size_t i = 0; i < zs.length(); i++)
     796           0 :         zTotals.addSizes(zs[i]);
     797             : 
     798           0 :     for (size_t i = 0; i < zs.length(); i++)
     799           0 :         if (!FindNotableStrings(zs[i]))
     800           0 :             return false;
     801             : 
     802           0 :     MOZ_ASSERT(!zTotals.allStrings);
     803             : 
     804           0 :     JS::CompartmentStatsVector& cs = rtStats->compartmentStatsVector;
     805           0 :     CompartmentStats& cTotals = rtStats->cTotals;
     806             : 
     807             :     // As with the zones, we sum all compartments first, and then get the
     808             :     // notable classes within each zone.
     809           0 :     for (size_t i = 0; i < cs.length(); i++)
     810           0 :         cTotals.addSizes(cs[i]);
     811             : 
     812           0 :     for (size_t i = 0; i < cs.length(); i++) {
     813           0 :         if (!FindNotableClasses(cs[i]))
     814           0 :             return false;
     815             :     }
     816             : 
     817           0 :     MOZ_ASSERT(!cTotals.allClasses);
     818             : 
     819           0 :     rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
     820           0 :                               rtStats->cTotals.sizeOfLiveGCThings();
     821             : 
     822             : #ifdef DEBUG
     823             :     // Check that the in-arena measurements look ok.
     824           0 :     size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
     825           0 :                             rtStats->zTotals.unusedGCThings.totalSize() +
     826           0 :                             rtStats->gcHeapGCThings;
     827           0 :     MOZ_ASSERT(totalArenaSize % gc::ArenaSize == 0);
     828             : #endif
     829             : 
     830           0 :     for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next())
     831           0 :         comp->nullCompartmentStats();
     832             : 
     833             :     size_t numDirtyChunks =
     834           0 :         (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
     835             :     size_t perChunkAdmin =
     836           0 :         sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
     837           0 :     rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
     838             : 
     839             :     // |gcHeapUnusedArenas| is the only thing left.  Compute it in terms of
     840             :     // all the others.  See the comment in RuntimeStats for explanation.
     841           0 :     rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
     842           0 :                                   rtStats->gcHeapDecommittedArenas -
     843           0 :                                   rtStats->gcHeapUnusedChunks -
     844           0 :                                   rtStats->zTotals.unusedGCThings.totalSize() -
     845           0 :                                   rtStats->gcHeapChunkAdmin -
     846           0 :                                   rtStats->zTotals.gcHeapArenaAdmin -
     847           0 :                                   rtStats->gcHeapGCThings;
     848           0 :     return true;
     849             : }
     850             : 
     851             : JS_PUBLIC_API(bool)
     852           0 : JS::CollectRuntimeStats(JSContext* cx, RuntimeStats *rtStats, ObjectPrivateVisitor *opv,
     853             :                         bool anonymize)
     854             : {
     855           0 :     return CollectRuntimeStatsHelper(cx, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
     856             : }
     857             : 
     858             : JS_PUBLIC_API(size_t)
     859           0 : JS::SystemCompartmentCount(JSContext* cx)
     860             : {
     861           0 :     size_t n = 0;
     862           0 :     for (CompartmentsIter comp(cx->runtime(), WithAtoms); !comp.done(); comp.next()) {
     863           0 :         if (comp->isSystem())
     864           0 :             ++n;
     865             :     }
     866           0 :     return n;
     867             : }
     868             : 
     869             : JS_PUBLIC_API(size_t)
     870           0 : JS::UserCompartmentCount(JSContext* cx)
     871             : {
     872           0 :     size_t n = 0;
     873           0 :     for (CompartmentsIter comp(cx->runtime(), WithAtoms); !comp.done(); comp.next()) {
     874           0 :         if (!comp->isSystem())
     875           0 :             ++n;
     876             :     }
     877           0 :     return n;
     878             : }
     879             : 
     880             : JS_PUBLIC_API(size_t)
     881           0 : JS::PeakSizeOfTemporary(const JSContext* cx)
     882             : {
     883           0 :     return cx->tempLifoAlloc().peakSizeOfExcludingThis();
     884             : }
     885             : 
     886             : JS_PUBLIC_API(void)
     887           0 : JS::CollectTraceLoggerStateStats(RuntimeStats* rtStats)
     888             : {
     889             : #ifdef JS_TRACE_LOGGING
     890           0 :     rtStats->runtime.tracelogger += SizeOfTraceLogState(rtStats->mallocSizeOf_);
     891           0 :     rtStats->runtime.tracelogger += SizeOfTraceLogGraphState(rtStats->mallocSizeOf_);
     892             : #endif
     893           0 : }
     894             : 
     895             : namespace JS {
     896             : 
     897           0 : class SimpleJSRuntimeStats : public JS::RuntimeStats
     898             : {
     899             :   public:
     900           0 :     explicit SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
     901           0 :       : JS::RuntimeStats(mallocSizeOf)
     902           0 :     {}
     903             : 
     904           0 :     virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats)
     905             :         override
     906           0 :     {}
     907             : 
     908           0 :     virtual void initExtraCompartmentStats(
     909             :         JSCompartment* c, JS::CompartmentStats* cStats) override
     910           0 :     {}
     911             : };
     912             : 
     913             : JS_PUBLIC_API(bool)
     914           0 : AddSizeOfTab(JSContext* cx, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv,
     915             :              TabSizes* sizes)
     916             : {
     917           0 :     SimpleJSRuntimeStats rtStats(mallocSizeOf);
     918             : 
     919           0 :     JS::Zone* zone = GetObjectZone(obj);
     920             : 
     921           0 :     if (!rtStats.compartmentStatsVector.reserve(zone->compartments().length()))
     922           0 :         return false;
     923             : 
     924           0 :     if (!rtStats.zoneStatsVector.reserve(1))
     925           0 :         return false;
     926             : 
     927             :     // Take the per-compartment measurements. No need to anonymize because
     928             :     // these measurements will be aggregated.
     929           0 :     StatsClosure closure(&rtStats, opv, /* anonymize = */ false);
     930           0 :     if (!closure.init())
     931           0 :         return false;
     932             :     IterateHeapUnbarrieredForZone(cx, zone, &closure,
     933             :                                                   StatsZoneCallback,
     934             :                                                   StatsCompartmentCallback,
     935             :                                                   StatsArenaCallback,
     936           0 :                                                   StatsCellCallback<CoarseGrained>);
     937             : 
     938           0 :     MOZ_ASSERT(rtStats.zoneStatsVector.length() == 1);
     939           0 :     rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
     940             : 
     941           0 :     for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++)
     942           0 :         rtStats.cTotals.addSizes(rtStats.compartmentStatsVector[i]);
     943             : 
     944           0 :     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
     945           0 :         comp->nullCompartmentStats();
     946             : 
     947           0 :     rtStats.zTotals.addToTabSizes(sizes);
     948           0 :     rtStats.cTotals.addToTabSizes(sizes);
     949             : 
     950           0 :     return true;
     951             : }
     952             : 
     953             : JS_PUBLIC_API(bool)
     954           0 : AddServoSizeOf(JSContext* cx, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv,
     955             :                ServoSizes* sizes)
     956             : {
     957           0 :     SimpleJSRuntimeStats rtStats(mallocSizeOf);
     958             : 
     959             :     // No need to anonymize because the results will be aggregated.
     960           0 :     if (!CollectRuntimeStatsHelper(cx, &rtStats, opv, /* anonymize = */ false,
     961             :                                    StatsCellCallback<CoarseGrained>))
     962           0 :         return false;
     963             : 
     964             : #ifdef DEBUG
     965           0 :     size_t gcHeapTotalOriginal = sizes->gcHeapUsed +
     966           0 :                                  sizes->gcHeapUnused +
     967           0 :                                  sizes->gcHeapAdmin +
     968           0 :                                  sizes->gcHeapDecommitted;
     969             : #endif
     970             : 
     971           0 :     rtStats.addToServoSizes(sizes);
     972           0 :     rtStats.zTotals.addToServoSizes(sizes);
     973           0 :     rtStats.cTotals.addToServoSizes(sizes);
     974             : 
     975             : #ifdef DEBUG
     976           0 :     size_t gcHeapTotal = sizes->gcHeapUsed +
     977           0 :                          sizes->gcHeapUnused +
     978           0 :                          sizes->gcHeapAdmin +
     979           0 :                          sizes->gcHeapDecommitted;
     980           0 :     MOZ_ASSERT(rtStats.gcHeapChunkTotal == gcHeapTotal - gcHeapTotalOriginal);
     981             : #endif
     982             : 
     983           0 :     return true;
     984             : }
     985             : 
     986             : } // namespace JS

Generated by: LCOV version 1.13