Wed, 31 Dec 2014 06:09:35 +0100
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 |