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.
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/. */
7 /* Per JSRuntime object */
9 #include "mozilla/MemoryReporting.h"
11 #include "xpcprivate.h"
12 #include "xpcpublic.h"
13 #include "XPCWrapper.h"
14 #include "XPCJSMemoryReporter.h"
15 #include "WrapperFactory.h"
16 #include "dom_quickstubs.h"
17 #include "mozJSComponentLoader.h"
19 #include "nsIMemoryInfoDumper.h"
20 #include "nsIMemoryReporter.h"
21 #include "nsIObserverService.h"
22 #include "nsIDebug2.h"
23 #include "amIAddonManager.h"
24 #include "nsPIDOMWindow.h"
25 #include "nsPrintfCString.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/Telemetry.h"
28 #include "mozilla/Services.h"
30 #include "nsContentUtils.h"
31 #include "nsCxPusher.h"
32 #include "nsCCUncollectableMarker.h"
33 #include "nsCycleCollectionNoteRootCallback.h"
34 #include "nsScriptLoader.h"
35 #include "jsfriendapi.h"
36 #include "jsprf.h"
37 #include "js/MemoryMetrics.h"
38 #include "js/OldDebugAPI.h"
39 #include "mozilla/dom/GeneratedAtomList.h"
40 #include "mozilla/dom/BindingUtils.h"
41 #include "mozilla/dom/Element.h"
42 #include "mozilla/dom/WindowBinding.h"
43 #include "mozilla/Atomics.h"
44 #include "mozilla/Attributes.h"
45 #include "AccessCheck.h"
46 #include "nsGlobalWindow.h"
47 #include "nsAboutProtocolUtils.h"
49 #include "GeckoProfiler.h"
50 #include "nsIXULRuntime.h"
51 #include "nsJSPrincipals.h"
53 #ifdef MOZ_CRASHREPORTER
54 #include "nsExceptionHandler.h"
55 #endif
57 using namespace mozilla;
58 using namespace xpc;
59 using namespace JS;
60 using mozilla::dom::PerThreadAtomCache;
62 /***************************************************************************/
64 const char* const XPCJSRuntime::mStrings[] = {
65 "constructor", // IDX_CONSTRUCTOR
66 "toString", // IDX_TO_STRING
67 "toSource", // IDX_TO_SOURCE
68 "lastResult", // IDX_LAST_RESULT
69 "returnCode", // IDX_RETURN_CODE
70 "value", // IDX_VALUE
71 "QueryInterface", // IDX_QUERY_INTERFACE
72 "Components", // IDX_COMPONENTS
73 "wrappedJSObject", // IDX_WRAPPED_JSOBJECT
74 "Object", // IDX_OBJECT
75 "Function", // IDX_FUNCTION
76 "prototype", // IDX_PROTOTYPE
77 "createInstance", // IDX_CREATE_INSTANCE
78 "item", // IDX_ITEM
79 "__proto__", // IDX_PROTO
80 "__iterator__", // IDX_ITERATOR
81 "__exposedProps__", // IDX_EXPOSEDPROPS
82 "eval", // IDX_EVAL
83 "controllers", // IDX_CONTROLLERS
84 };
86 /***************************************************************************/
88 static mozilla::Atomic<bool> sDiscardSystemSource(false);
90 bool
91 xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
93 static void * const UNMARK_ONLY = nullptr;
94 static void * const UNMARK_AND_SWEEP = (void *)1;
96 static PLDHashOperator
97 NativeInterfaceSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
98 uint32_t number, void *arg)
99 {
100 XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
101 if (iface->IsMarked()) {
102 iface->Unmark();
103 return PL_DHASH_NEXT;
104 }
106 if (arg == UNMARK_ONLY)
107 return PL_DHASH_NEXT;
109 XPCNativeInterface::DestroyInstance(iface);
110 return PL_DHASH_REMOVE;
111 }
113 // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
114 // *All* NativeSets are referenced from mNativeSetMap.
115 // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
116 // In mNativeSetMap we clear the references to the unmarked *and* delete them.
118 static PLDHashOperator
119 NativeUnMarkedSetRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
120 uint32_t number, void *arg)
121 {
122 XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value;
123 if (set->IsMarked())
124 return PL_DHASH_NEXT;
125 return PL_DHASH_REMOVE;
126 }
128 static PLDHashOperator
129 NativeSetSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
130 uint32_t number, void *arg)
131 {
132 XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value;
133 if (set->IsMarked()) {
134 set->Unmark();
135 return PL_DHASH_NEXT;
136 }
138 if (arg == UNMARK_ONLY)
139 return PL_DHASH_NEXT;
141 XPCNativeSet::DestroyInstance(set);
142 return PL_DHASH_REMOVE;
143 }
145 static PLDHashOperator
146 JSClassSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
147 uint32_t number, void *arg)
148 {
149 XPCNativeScriptableShared* shared =
150 ((XPCNativeScriptableSharedMap::Entry*) hdr)->key;
151 if (shared->IsMarked()) {
152 shared->Unmark();
153 return PL_DHASH_NEXT;
154 }
156 if (arg == UNMARK_ONLY)
157 return PL_DHASH_NEXT;
159 delete shared;
160 return PL_DHASH_REMOVE;
161 }
163 static PLDHashOperator
164 DyingProtoKiller(PLDHashTable *table, PLDHashEntryHdr *hdr,
165 uint32_t number, void *arg)
166 {
167 XPCWrappedNativeProto* proto =
168 (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
169 delete proto;
170 return PL_DHASH_REMOVE;
171 }
173 static PLDHashOperator
174 DetachedWrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
175 uint32_t number, void *arg)
176 {
177 XPCWrappedNativeProto* proto =
178 (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
180 proto->Mark();
181 return PL_DHASH_NEXT;
182 }
184 bool
185 XPCJSRuntime::CustomContextCallback(JSContext *cx, unsigned operation)
186 {
187 if (operation == JSCONTEXT_NEW) {
188 if (!OnJSContextNew(cx)) {
189 return false;
190 }
191 } else if (operation == JSCONTEXT_DESTROY) {
192 delete XPCContext::GetXPCContext(cx);
193 }
195 nsTArray<xpcContextCallback> callbacks(extraContextCallbacks);
196 for (uint32_t i = 0; i < callbacks.Length(); ++i) {
197 if (!callbacks[i](cx, operation)) {
198 return false;
199 }
200 }
202 return true;
203 }
205 class AsyncFreeSnowWhite : public nsRunnable
206 {
207 public:
208 NS_IMETHOD Run()
209 {
210 TimeStamp start = TimeStamp::Now();
211 bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
212 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
213 uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
214 if (hadSnowWhiteObjects && !mContinuation) {
215 mContinuation = true;
216 if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
217 mActive = false;
218 }
219 } else {
220 mActive = false;
221 }
222 return NS_OK;
223 }
225 void Dispatch(bool aContinuation = false)
226 {
227 if (mContinuation) {
228 mContinuation = aContinuation;
229 }
230 if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) {
231 mActive = true;
232 }
233 }
235 AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
237 public:
238 bool mContinuation;
239 bool mActive;
240 };
242 namespace xpc {
244 static uint32_t kLivingAdopters = 0;
246 void
247 RecordAdoptedNode(JSCompartment *c)
248 {
249 CompartmentPrivate *priv = EnsureCompartmentPrivate(c);
250 if (!priv->adoptedNode) {
251 priv->adoptedNode = true;
252 ++kLivingAdopters;
253 }
254 }
256 void
257 RecordDonatedNode(JSCompartment *c)
258 {
259 EnsureCompartmentPrivate(c)->donatedNode = true;
260 }
262 CompartmentPrivate::~CompartmentPrivate()
263 {
264 MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
266 Telemetry::Accumulate(Telemetry::COMPARTMENT_ADOPTED_NODE, adoptedNode);
267 Telemetry::Accumulate(Telemetry::COMPARTMENT_DONATED_NODE, donatedNode);
268 if (adoptedNode)
269 --kLivingAdopters;
270 }
272 static bool
273 TryParseLocationURICandidate(const nsACString& uristr,
274 CompartmentPrivate::LocationHint aLocationHint,
275 nsIURI** aURI)
276 {
277 static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
278 static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
279 static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
281 if (aLocationHint == CompartmentPrivate::LocationHintAddon) {
282 // Blacklist some known locations which are clearly not add-on related.
283 if (StringBeginsWith(uristr, kGRE) ||
284 StringBeginsWith(uristr, kToolkit) ||
285 StringBeginsWith(uristr, kBrowser))
286 return false;
287 }
289 nsCOMPtr<nsIURI> uri;
290 if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
291 return false;
293 nsAutoCString scheme;
294 if (NS_FAILED(uri->GetScheme(scheme)))
295 return false;
297 // Cannot really map data: and blob:.
298 // Also, data: URIs are pretty memory hungry, which is kinda bad
299 // for memory reporter use.
300 if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
301 return false;
303 uri.forget(aURI);
304 return true;
305 }
307 bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint,
308 nsIURI **aURI)
309 {
310 if (!aURI)
311 return false;
313 // Need to parse the URI.
314 if (location.IsEmpty())
315 return false;
317 // Handle Sandbox location strings.
318 // A sandbox string looks like this:
319 // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
320 // where <sandboxName> is user-provided via Cu.Sandbox()
321 // and <js-stack-frame-filename> and <lineno> is the stack frame location
322 // from where Cu.Sandbox was called.
323 // <js-stack-frame-filename> furthermore is "free form", often using a
324 // "uri -> uri -> ..." chain. The following code will and must handle this
325 // common case.
326 // It should be noted that other parts of the code may already rely on the
327 // "format" of these strings, such as the add-on SDK.
329 static const nsDependentCString from("(from: ");
330 static const nsDependentCString arrow(" -> ");
331 static const size_t fromLength = from.Length();
332 static const size_t arrowLength = arrow.Length();
334 // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
335 int32_t idx = location.Find(from);
336 if (idx < 0)
337 return TryParseLocationURICandidate(location, aLocationHint, aURI);
340 // When parsing we're looking for the right-most URI. This URI may be in
341 // <sandboxName>, so we try this first.
342 if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
343 aURI))
344 return true;
346 // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
347 // the chain that is potentially contained within and grab the rightmost
348 // item that is actually a URI.
350 // First, hack off the :<lineno>) part as well
351 int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
352 nsAutoCString chain(Substring(location, idx + fromLength,
353 ridx - idx - fromLength));
355 // Loop over the "->" chain. This loop also works for non-chains, or more
356 // correctly chains with only one item.
357 for (;;) {
358 idx = chain.RFind(arrow);
359 if (idx < 0) {
360 // This is the last chain item. Try to parse what is left.
361 return TryParseLocationURICandidate(chain, aLocationHint, aURI);
362 }
364 // Try to parse current chain item
365 if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
366 aLocationHint, aURI))
367 return true;
369 // Current chain item couldn't be parsed.
370 // Strip current item and continue.
371 chain = Substring(chain, 0, idx);
372 }
373 MOZ_ASSUME_UNREACHABLE("Chain parser loop does not terminate");
374 }
376 CompartmentPrivate*
377 EnsureCompartmentPrivate(JSObject *obj)
378 {
379 return EnsureCompartmentPrivate(js::GetObjectCompartment(obj));
380 }
382 CompartmentPrivate*
383 EnsureCompartmentPrivate(JSCompartment *c)
384 {
385 CompartmentPrivate *priv = GetCompartmentPrivate(c);
386 if (priv)
387 return priv;
388 priv = new CompartmentPrivate(c);
389 JS_SetCompartmentPrivate(c, priv);
390 return priv;
391 }
393 XPCWrappedNativeScope*
394 MaybeGetObjectScope(JSObject *obj)
395 {
396 MOZ_ASSERT(obj);
397 JSCompartment *compartment = js::GetObjectCompartment(obj);
399 MOZ_ASSERT(compartment);
400 CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
401 if (!priv)
402 return nullptr;
404 return priv->scope;
405 }
407 static bool
408 PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
409 {
410 // System principal gets a free pass.
411 if (XPCWrapper::GetSecurityManager()->IsSystemPrincipal(aPrincipal))
412 return true;
414 // nsExpandedPrincipal gets a free pass.
415 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
416 if (ep)
417 return true;
419 // Check whether our URI is an "about:" URI that allows scripts. If it is,
420 // we need to allow JS to run.
421 nsCOMPtr<nsIURI> principalURI;
422 aPrincipal->GetURI(getter_AddRefs(principalURI));
423 MOZ_ASSERT(principalURI);
424 bool isAbout;
425 nsresult rv = principalURI->SchemeIs("about", &isAbout);
426 if (NS_SUCCEEDED(rv) && isAbout) {
427 nsCOMPtr<nsIAboutModule> module;
428 rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
429 if (NS_SUCCEEDED(rv)) {
430 uint32_t flags;
431 rv = module->GetURIFlags(principalURI, &flags);
432 if (NS_SUCCEEDED(rv) &&
433 (flags & nsIAboutModule::ALLOW_SCRIPT)) {
434 return true;
435 }
436 }
437 }
439 return false;
440 }
442 Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0)
443 , mDocShellAllowsScript(true)
444 , mScriptBlockedByPolicy(false)
445 {
446 nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
447 mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
449 // If we're not immune, we should have a real principal with a codebase URI.
450 // Check the URI against the new-style domain policy.
451 if (!mImmuneToScriptPolicy) {
452 nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
453 nsCOMPtr<nsIURI> codebase;
454 nsresult rv = prin->GetURI(getter_AddRefs(codebase));
455 bool policyAllows;
456 if (NS_SUCCEEDED(rv) && codebase &&
457 NS_SUCCEEDED(ssm->PolicyAllowsScript(codebase, &policyAllows)))
458 {
459 mScriptBlockedByPolicy = !policyAllows;
460 } else {
461 // Something went wrong - be safe and block script.
462 mScriptBlockedByPolicy = true;
463 }
464 }
465 }
467 bool
468 Scriptability::Allowed()
469 {
470 return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
471 mScriptBlocks == 0;
472 }
474 bool
475 Scriptability::IsImmuneToScriptPolicy()
476 {
477 return mImmuneToScriptPolicy;
478 }
480 void
481 Scriptability::Block()
482 {
483 ++mScriptBlocks;
484 }
486 void
487 Scriptability::Unblock()
488 {
489 MOZ_ASSERT(mScriptBlocks > 0);
490 --mScriptBlocks;
491 }
493 void
494 Scriptability::SetDocShellAllowsScript(bool aAllowed)
495 {
496 mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
497 }
499 /* static */
500 Scriptability&
501 Scriptability::Get(JSObject *aScope)
502 {
503 return EnsureCompartmentPrivate(aScope)->scriptability;
504 }
506 bool
507 IsXBLScope(JSCompartment *compartment)
508 {
509 // We always eagerly create compartment privates for XBL scopes.
510 CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
511 if (!priv || !priv->scope)
512 return false;
513 return priv->scope->IsXBLScope();
514 }
516 bool
517 IsInXBLScope(JSObject *obj)
518 {
519 return IsXBLScope(js::GetObjectCompartment(obj));
520 }
522 bool
523 IsUniversalXPConnectEnabled(JSCompartment *compartment)
524 {
525 CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
526 if (!priv)
527 return false;
528 return priv->universalXPConnectEnabled;
529 }
531 bool
532 IsUniversalXPConnectEnabled(JSContext *cx)
533 {
534 JSCompartment *compartment = js::GetContextCompartment(cx);
535 if (!compartment)
536 return false;
537 return IsUniversalXPConnectEnabled(compartment);
538 }
540 bool
541 EnableUniversalXPConnect(JSContext *cx)
542 {
543 JSCompartment *compartment = js::GetContextCompartment(cx);
544 if (!compartment)
545 return true;
546 // Never set universalXPConnectEnabled on a chrome compartment - it confuses
547 // the security wrapping code.
548 if (AccessCheck::isChrome(compartment))
549 return true;
550 CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
551 if (!priv)
552 return true;
553 priv->universalXPConnectEnabled = true;
555 // Recompute all the cross-compartment wrappers leaving the newly-privileged
556 // compartment.
557 bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
558 js::AllCompartments());
559 NS_ENSURE_TRUE(ok, false);
561 // The Components object normally isn't defined for unprivileged web content,
562 // but we define it when UniversalXPConnect is enabled to support legacy
563 // tests.
564 XPCWrappedNativeScope *scope = priv->scope;
565 if (!scope)
566 return true;
567 scope->ForcePrivilegedComponents();
568 return scope->AttachComponentsObject(cx);
569 }
571 JSObject *
572 GetJunkScope()
573 {
574 XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
575 NS_ENSURE_TRUE(self, nullptr);
576 return self->GetJunkScope();
577 }
579 nsIGlobalObject *
580 GetJunkScopeGlobal()
581 {
582 JSObject *junkScope = GetJunkScope();
583 // GetJunkScope would ideally never fail, currently it is not yet the case
584 // unfortunately...(see Bug 874158)
585 if (!junkScope)
586 return nullptr;
587 return GetNativeForGlobal(junkScope);
588 }
590 JSObject *
591 GetCompilationScope()
592 {
593 XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
594 NS_ENSURE_TRUE(self, nullptr);
595 return self->GetCompilationScope();
596 }
598 JSObject *
599 GetSafeJSContextGlobal()
600 {
601 return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal();
602 }
604 nsGlobalWindow*
605 WindowOrNull(JSObject *aObj)
606 {
607 MOZ_ASSERT(aObj);
608 MOZ_ASSERT(!js::IsWrapper(aObj));
610 // This will always return null until we have Window on WebIDL bindings,
611 // at which point it will do the right thing.
612 if (!IS_WN_CLASS(js::GetObjectClass(aObj))) {
613 nsGlobalWindow* win = nullptr;
614 UNWRAP_OBJECT(Window, aObj, win);
615 return win;
616 }
618 nsISupports* supports = XPCWrappedNative::Get(aObj)->GetIdentityObject();
619 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(supports);
620 if (!piWin)
621 return nullptr;
622 return static_cast<nsGlobalWindow*>(piWin.get());
623 }
625 nsGlobalWindow*
626 WindowGlobalOrNull(JSObject *aObj)
627 {
628 MOZ_ASSERT(aObj);
629 JSObject *glob = js::GetGlobalForObjectCrossCompartment(aObj);
631 return WindowOrNull(glob);
632 }
634 }
636 static void
637 CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
638 {
639 // NB - This callback may be called in JS_DestroyRuntime, which happens
640 // after the XPCJSRuntime has been torn down.
642 // Get the current compartment private into an AutoPtr (which will do the
643 // cleanup for us), and null out the private (which may already be null).
644 nsAutoPtr<CompartmentPrivate> priv(GetCompartmentPrivate(compartment));
645 JS_SetCompartmentPrivate(compartment, nullptr);
646 }
648 void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
649 {
650 // Skip this part if XPConnect is shutting down. We get into
651 // bad locking problems with the thread iteration otherwise.
652 if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
653 // Trace those AutoMarkingPtr lists!
654 if (AutoMarkingPtr *roots = Get()->mAutoRoots)
655 roots->TraceJSAll(trc);
656 }
658 // XPCJSObjectHolders don't participate in cycle collection, so always
659 // trace them here.
660 XPCRootSetElem *e;
661 for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
662 static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
664 dom::TraceBlackJS(trc, JS_GetGCParameter(Runtime(), JSGC_NUMBER),
665 nsXPConnect::XPConnect()->IsShuttingDown());
666 }
668 void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer *trc)
669 {
670 XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
672 for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
673 static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
675 for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
676 static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
677 }
679 // static
680 void
681 XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper,
682 nsCycleCollectionNoteRootCallback &cb)
683 {
684 if (!wrapper->IsValid() || wrapper->IsWrapperExpired())
685 return;
687 MOZ_ASSERT(NS_IsMainThread(),
688 "Suspecting wrapped natives from non-main thread");
690 // Only record objects that might be part of a cycle as roots, unless
691 // the callback wants all traces (a debug feature).
692 JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
693 if (xpc_IsGrayGCThing(obj) || cb.WantAllTraces())
694 cb.NoteJSRoot(obj);
695 }
697 void
698 XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb)
699 {
700 XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
702 for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) {
703 XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
704 if (nsCCUncollectableMarker::InGeneration(cb,
705 v->CCGeneration())) {
706 jsval val = v->GetJSValPreserveColor();
707 if (val.isObject() && !xpc_IsGrayGCThing(&val.toObject()))
708 continue;
709 }
710 cb.NoteXPCOMRoot(v);
711 }
713 for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
714 cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
715 }
716 }
718 void
719 XPCJSRuntime::UnmarkSkippableJSHolders()
720 {
721 CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
722 }
724 void
725 XPCJSRuntime::PrepareForForgetSkippable()
726 {
727 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
728 if (obs) {
729 obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
730 }
731 }
733 void
734 XPCJSRuntime::BeginCycleCollectionCallback()
735 {
736 nsJSContext::BeginCycleCollectionCallback();
738 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
739 if (obs) {
740 obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
741 }
742 }
744 void
745 XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults &aResults)
746 {
747 nsJSContext::EndCycleCollectionCallback(aResults);
749 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
750 if (obs) {
751 obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
752 }
753 }
755 void
756 XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation)
757 {
758 mAsyncSnowWhiteFreer->Dispatch(aContinuation);
759 }
761 void
762 xpc_UnmarkSkippableJSHolders()
763 {
764 if (nsXPConnect::XPConnect()->GetRuntime()) {
765 nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders();
766 }
767 }
769 template<class T> static void
770 DoDeferredRelease(nsTArray<T> &array)
771 {
772 while (1) {
773 uint32_t count = array.Length();
774 if (!count) {
775 array.Compact();
776 break;
777 }
778 T wrapper = array[count-1];
779 array.RemoveElementAt(count-1);
780 NS_RELEASE(wrapper);
781 }
782 }
784 /* static */ void
785 XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
786 JS::GCProgress progress,
787 const JS::GCDescription &desc)
788 {
789 XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
790 if (!self)
791 return;
793 #ifdef MOZ_CRASHREPORTER
794 CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN ||
795 progress == JS::GC_SLICE_BEGIN);
796 #endif
798 if (self->mPrevGCSliceCallback)
799 (*self->mPrevGCSliceCallback)(rt, progress, desc);
800 }
802 void
803 XPCJSRuntime::CustomGCCallback(JSGCStatus status)
804 {
805 // Record kLivingAdopters once per GC to approximate time sampling during
806 // periods of activity.
807 if (status == JSGC_BEGIN) {
808 Telemetry::Accumulate(Telemetry::COMPARTMENT_LIVING_ADOPTERS,
809 kLivingAdopters);
810 }
812 nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
813 for (uint32_t i = 0; i < callbacks.Length(); ++i)
814 callbacks[i](status);
815 }
817 /* static */ void
818 XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC)
819 {
820 XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
821 if (!self)
822 return;
824 switch (status) {
825 case JSFINALIZE_GROUP_START:
826 {
827 MOZ_ASSERT(!self->mDoingFinalization, "bad state");
829 MOZ_ASSERT(!self->mGCIsRunning, "bad state");
830 self->mGCIsRunning = true;
832 nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray =
833 &self->mWrappedJSToReleaseArray;
835 // Add any wrappers whose JSObjects are to be finalized to
836 // this array. Note that we do not want to be changing the
837 // refcount of these wrappers.
838 // We add them to the array now and Release the array members
839 // later to avoid the posibility of doing any JS GCThing
840 // allocations during the gc cycle.
841 self->mWrappedJSMap->FindDyingJSObjects(dyingWrappedJSArray);
843 // Find dying scopes.
844 XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
846 self->mDoingFinalization = true;
847 break;
848 }
849 case JSFINALIZE_GROUP_END:
850 {
851 MOZ_ASSERT(self->mDoingFinalization, "bad state");
852 self->mDoingFinalization = false;
854 // Release all the members whose JSObjects are now known
855 // to be dead.
856 DoDeferredRelease(self->mWrappedJSToReleaseArray);
858 // Sweep scopes needing cleanup
859 XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
861 MOZ_ASSERT(self->mGCIsRunning, "bad state");
862 self->mGCIsRunning = false;
864 break;
865 }
866 case JSFINALIZE_COLLECTION_END:
867 {
868 MOZ_ASSERT(!self->mGCIsRunning, "bad state");
869 self->mGCIsRunning = true;
871 // We use this occasion to mark and sweep NativeInterfaces,
872 // NativeSets, and the WrappedNativeJSClasses...
874 // Do the marking...
875 XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos();
877 self->mDetachedWrappedNativeProtoMap->
878 Enumerate(DetachedWrappedNativeProtoMarker, nullptr);
880 DOM_MarkInterfaces();
882 // Mark the sets used in the call contexts. There is a small
883 // chance that a wrapper's set will change *while* a call is
884 // happening which uses that wrapper's old interfface set. So,
885 // we need to do this marking to avoid collecting those sets
886 // that might no longer be otherwise reachable from the wrappers
887 // or the wrapperprotos.
889 // Skip this part if XPConnect is shutting down. We get into
890 // bad locking problems with the thread iteration otherwise.
891 if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
893 // Mark those AutoMarkingPtr lists!
894 if (AutoMarkingPtr *roots = Get()->mAutoRoots)
895 roots->MarkAfterJSFinalizeAll();
897 XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
898 while (ccxp) {
899 // Deal with the strictness of callcontext that
900 // complains if you ask for a set when
901 // it is in a state where the set could not
902 // possibly be valid.
903 if (ccxp->CanGetSet()) {
904 XPCNativeSet* set = ccxp->GetSet();
905 if (set)
906 set->Mark();
907 }
908 if (ccxp->CanGetInterface()) {
909 XPCNativeInterface* iface = ccxp->GetInterface();
910 if (iface)
911 iface->Mark();
912 }
913 ccxp = ccxp->GetPrevCallContext();
914 }
915 }
917 // Do the sweeping. During a compartment GC, only
918 // WrappedNativeProtos in collected compartments will be
919 // marked. Therefore, some reachable NativeInterfaces will not be
920 // marked, so it is not safe to sweep them. We still need to unmark
921 // them, since the ones pointed to by WrappedNativeProtos in a
922 // compartment being collected will be marked.
923 //
924 // Ideally, if NativeInterfaces from different compartments were
925 // kept separate, we could sweep only the ones belonging to
926 // compartments being collected. Currently, though, NativeInterfaces
927 // are shared between compartments. This ought to be fixed.
928 void *sweepArg = isCompartmentGC ? UNMARK_ONLY : UNMARK_AND_SWEEP;
930 // We don't want to sweep the JSClasses at shutdown time.
931 // At this point there may be JSObjects using them that have
932 // been removed from the other maps.
933 if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
934 self->mNativeScriptableSharedMap->
935 Enumerate(JSClassSweeper, sweepArg);
936 }
938 if (!isCompartmentGC) {
939 self->mClassInfo2NativeSetMap->
940 Enumerate(NativeUnMarkedSetRemover, nullptr);
941 }
943 self->mNativeSetMap->
944 Enumerate(NativeSetSweeper, sweepArg);
946 self->mIID2NativeInterfaceMap->
947 Enumerate(NativeInterfaceSweeper, sweepArg);
949 #ifdef DEBUG
950 XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked();
951 #endif
953 // Now we are going to recycle any unused WrappedNativeTearoffs.
954 // We do this by iterating all the live callcontexts
955 // and marking the tearoffs in use. And then we
956 // iterate over all the WrappedNative wrappers and sweep their
957 // tearoffs.
958 //
959 // This allows us to perhaps minimize the growth of the
960 // tearoffs. And also makes us not hold references to interfaces
961 // on our wrapped natives that we are not actually using.
962 //
963 // XXX We may decide to not do this on *every* gc cycle.
965 // Skip this part if XPConnect is shutting down. We get into
966 // bad locking problems with the thread iteration otherwise.
967 if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
968 // Do the marking...
970 XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
971 while (ccxp) {
972 // Deal with the strictness of callcontext that
973 // complains if you ask for a tearoff when
974 // it is in a state where the tearoff could not
975 // possibly be valid.
976 if (ccxp->CanGetTearOff()) {
977 XPCWrappedNativeTearOff* to =
978 ccxp->GetTearOff();
979 if (to)
980 to->Mark();
981 }
982 ccxp = ccxp->GetPrevCallContext();
983 }
985 // Do the sweeping...
986 XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
987 }
989 // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
990 // We transfered these native objects to this table when their
991 // JSObject's were finalized. We did not destroy them immediately
992 // at that point because the ordering of JS finalization is not
993 // deterministic and we did not yet know if any wrappers that
994 // might still be referencing the protos where still yet to be
995 // finalized and destroyed. We *do* know that the protos'
996 // JSObjects would not have been finalized if there were any
997 // wrappers that referenced the proto but where not themselves
998 // slated for finalization in this gc cycle. So... at this point
999 // we know that any and all wrappers that might have been
1000 // referencing the protos in the dying list are themselves dead.
1001 // So, we can safely delete all the protos in the list.
1003 self->mDyingWrappedNativeProtoMap->
1004 Enumerate(DyingProtoKiller, nullptr);
1006 MOZ_ASSERT(self->mGCIsRunning, "bad state");
1007 self->mGCIsRunning = false;
1009 break;
1010 }
1011 }
1012 }
1014 static void WatchdogMain(void *arg);
1015 class Watchdog;
1016 class WatchdogManager;
1017 class AutoLockWatchdog {
1018 Watchdog* const mWatchdog;
1019 public:
1020 AutoLockWatchdog(Watchdog* aWatchdog);
1021 ~AutoLockWatchdog();
1022 };
1024 class Watchdog
1025 {
1026 public:
1027 Watchdog(WatchdogManager *aManager)
1028 : mManager(aManager)
1029 , mLock(nullptr)
1030 , mWakeup(nullptr)
1031 , mThread(nullptr)
1032 , mHibernating(false)
1033 , mInitialized(false)
1034 , mShuttingDown(false)
1035 {}
1036 ~Watchdog() { MOZ_ASSERT(!Initialized()); }
1038 WatchdogManager* Manager() { return mManager; }
1039 bool Initialized() { return mInitialized; }
1040 bool ShuttingDown() { return mShuttingDown; }
1041 PRLock *GetLock() { return mLock; }
1042 bool Hibernating() { return mHibernating; }
1043 void WakeUp()
1044 {
1045 MOZ_ASSERT(Initialized());
1046 MOZ_ASSERT(Hibernating());
1047 mHibernating = false;
1048 PR_NotifyCondVar(mWakeup);
1049 }
1051 //
1052 // Invoked by the main thread only.
1053 //
1055 void Init()
1056 {
1057 MOZ_ASSERT(NS_IsMainThread());
1058 mLock = PR_NewLock();
1059 if (!mLock)
1060 NS_RUNTIMEABORT("PR_NewLock failed.");
1061 mWakeup = PR_NewCondVar(mLock);
1062 if (!mWakeup)
1063 NS_RUNTIMEABORT("PR_NewCondVar failed.");
1065 {
1066 AutoLockWatchdog lock(this);
1068 mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
1069 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
1070 PR_UNJOINABLE_THREAD, 0);
1071 if (!mThread)
1072 NS_RUNTIMEABORT("PR_CreateThread failed!");
1074 // WatchdogMain acquires the lock and then asserts mInitialized. So
1075 // make sure to set mInitialized before releasing the lock here so
1076 // that it's atomic with the creation of the thread.
1077 mInitialized = true;
1078 }
1079 }
1081 void Shutdown()
1082 {
1083 MOZ_ASSERT(NS_IsMainThread());
1084 MOZ_ASSERT(Initialized());
1085 { // Scoped lock.
1086 AutoLockWatchdog lock(this);
1088 // Signal to the watchdog thread that it's time to shut down.
1089 mShuttingDown = true;
1091 // Wake up the watchdog, and wait for it to call us back.
1092 PR_NotifyCondVar(mWakeup);
1093 PR_WaitCondVar(mWakeup, PR_INTERVAL_NO_TIMEOUT);
1094 MOZ_ASSERT(!mShuttingDown);
1095 }
1097 // Destroy state.
1098 mThread = nullptr;
1099 PR_DestroyCondVar(mWakeup);
1100 mWakeup = nullptr;
1101 PR_DestroyLock(mLock);
1102 mLock = nullptr;
1104 // All done.
1105 mInitialized = false;
1106 }
1108 //
1109 // Invoked by the watchdog thread only.
1110 //
1112 void Hibernate()
1113 {
1114 MOZ_ASSERT(!NS_IsMainThread());
1115 mHibernating = true;
1116 Sleep(PR_INTERVAL_NO_TIMEOUT);
1117 }
1118 void Sleep(PRIntervalTime timeout)
1119 {
1120 MOZ_ASSERT(!NS_IsMainThread());
1121 MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
1122 }
1123 void Finished()
1124 {
1125 MOZ_ASSERT(!NS_IsMainThread());
1126 mShuttingDown = false;
1127 PR_NotifyCondVar(mWakeup);
1128 }
1130 private:
1131 WatchdogManager *mManager;
1133 PRLock *mLock;
1134 PRCondVar *mWakeup;
1135 PRThread *mThread;
1136 bool mHibernating;
1137 bool mInitialized;
1138 bool mShuttingDown;
1139 };
1141 #ifdef MOZ_NUWA_PROCESS
1142 #include "ipc/Nuwa.h"
1143 #endif
1145 class WatchdogManager : public nsIObserver
1146 {
1147 public:
1149 NS_DECL_ISUPPORTS
1150 WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime)
1151 , mRuntimeState(RUNTIME_INACTIVE)
1152 {
1153 // All the timestamps start at zero except for runtime state change.
1154 PodArrayZero(mTimestamps);
1155 mTimestamps[TimestampRuntimeStateChange] = PR_Now();
1157 // Enable the watchdog, if appropriate.
1158 RefreshWatchdog();
1160 // Register ourselves as an observer to get updates on the pref.
1161 mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
1162 }
1163 virtual ~WatchdogManager()
1164 {
1165 // Shutting down the watchdog requires context-switching to the watchdog
1166 // thread, which isn't great to do in a destructor. So we require
1167 // consumers to shut it down manually before releasing it.
1168 MOZ_ASSERT(!mWatchdog);
1169 mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
1170 }
1172 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
1173 const char16_t* aData)
1174 {
1175 RefreshWatchdog();
1176 return NS_OK;
1177 }
1179 // Runtime statistics. These live on the watchdog manager, are written
1180 // from the main thread, and are read from the watchdog thread (holding
1181 // the lock in each case).
1182 void
1183 RecordRuntimeActivity(bool active)
1184 {
1185 // The watchdog reads this state, so acquire the lock before writing it.
1186 MOZ_ASSERT(NS_IsMainThread());
1187 Maybe<AutoLockWatchdog> lock;
1188 if (mWatchdog)
1189 lock.construct(mWatchdog);
1191 // Write state.
1192 mTimestamps[TimestampRuntimeStateChange] = PR_Now();
1193 mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
1195 // The watchdog may be hibernating, waiting for the runtime to go
1196 // active. Wake it up if necessary.
1197 if (active && mWatchdog && mWatchdog->Hibernating())
1198 mWatchdog->WakeUp();
1199 }
1200 bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
1201 PRTime TimeSinceLastRuntimeStateChange()
1202 {
1203 return PR_Now() - GetTimestamp(TimestampRuntimeStateChange);
1204 }
1206 // Note - Because of the runtime activity timestamp, these are read and
1207 // written from both threads.
1208 void RecordTimestamp(WatchdogTimestampCategory aCategory)
1209 {
1210 // The watchdog thread always holds the lock when it runs.
1211 Maybe<AutoLockWatchdog> maybeLock;
1212 if (NS_IsMainThread() && mWatchdog)
1213 maybeLock.construct(mWatchdog);
1214 mTimestamps[aCategory] = PR_Now();
1215 }
1216 PRTime GetTimestamp(WatchdogTimestampCategory aCategory)
1217 {
1218 // The watchdog thread always holds the lock when it runs.
1219 Maybe<AutoLockWatchdog> maybeLock;
1220 if (NS_IsMainThread() && mWatchdog)
1221 maybeLock.construct(mWatchdog);
1222 return mTimestamps[aCategory];
1223 }
1225 XPCJSRuntime* Runtime() { return mRuntime; }
1226 Watchdog* GetWatchdog() { return mWatchdog; }
1228 void RefreshWatchdog()
1229 {
1230 bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
1231 if (wantWatchdog == !!mWatchdog)
1232 return;
1233 if (wantWatchdog)
1234 StartWatchdog();
1235 else
1236 StopWatchdog();
1237 }
1239 void StartWatchdog()
1240 {
1241 MOZ_ASSERT(!mWatchdog);
1242 mWatchdog = new Watchdog(this);
1243 mWatchdog->Init();
1244 }
1246 void StopWatchdog()
1247 {
1248 MOZ_ASSERT(mWatchdog);
1249 mWatchdog->Shutdown();
1250 mWatchdog = nullptr;
1251 }
1253 private:
1254 XPCJSRuntime *mRuntime;
1255 nsAutoPtr<Watchdog> mWatchdog;
1257 enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
1258 PRTime mTimestamps[TimestampCount];
1259 };
1261 NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
1263 AutoLockWatchdog::AutoLockWatchdog(Watchdog *aWatchdog) : mWatchdog(aWatchdog)
1264 {
1265 PR_Lock(mWatchdog->GetLock());
1266 }
1268 AutoLockWatchdog::~AutoLockWatchdog()
1269 {
1270 PR_Unlock(mWatchdog->GetLock());
1271 }
1273 static void
1274 WatchdogMain(void *arg)
1275 {
1276 PR_SetCurrentThreadName("JS Watchdog");
1278 #ifdef MOZ_NUWA_PROCESS
1279 if (IsNuwaProcess()) {
1280 NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
1281 "NuwaMarkCurrentThread is undefined!");
1282 NuwaMarkCurrentThread(nullptr, nullptr);
1283 NuwaFreezeCurrentThread();
1284 }
1285 #endif
1287 Watchdog* self = static_cast<Watchdog*>(arg);
1288 WatchdogManager* manager = self->Manager();
1290 // Lock lasts until we return
1291 AutoLockWatchdog lock(self);
1293 MOZ_ASSERT(self->Initialized());
1294 MOZ_ASSERT(!self->ShuttingDown());
1295 while (!self->ShuttingDown()) {
1296 // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
1297 if (manager->IsRuntimeActive() ||
1298 manager->TimeSinceLastRuntimeStateChange() <= PRTime(2*PR_USEC_PER_SEC))
1299 {
1300 self->Sleep(PR_TicksPerSecond());
1301 } else {
1302 manager->RecordTimestamp(TimestampWatchdogHibernateStart);
1303 self->Hibernate();
1304 manager->RecordTimestamp(TimestampWatchdogHibernateStop);
1305 }
1307 // Rise and shine.
1308 manager->RecordTimestamp(TimestampWatchdogWakeup);
1310 // Don't request an interrupt callback if activity started less than one second ago.
1311 // The callback is only used for detecting long running scripts, and triggering the
1312 // callback from off the main thread can be expensive.
1313 if (manager->IsRuntimeActive() &&
1314 manager->TimeSinceLastRuntimeStateChange() >= PRTime(PR_USEC_PER_SEC))
1315 {
1316 bool debuggerAttached = false;
1317 nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
1318 if (dbg)
1319 dbg->GetIsDebuggerAttached(&debuggerAttached);
1320 if (!debuggerAttached)
1321 JS_RequestInterruptCallback(manager->Runtime()->Runtime());
1322 }
1323 }
1325 // Tell the manager that we've shut down.
1326 self->Finished();
1327 }
1329 PRTime
1330 XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
1331 {
1332 return mWatchdogManager->GetTimestamp(aCategory);
1333 }
1335 void
1336 xpc::SimulateActivityCallback(bool aActive)
1337 {
1338 XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive);
1339 }
1341 // static
1342 JSContext*
1343 XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt)
1344 {
1345 MOZ_ASSERT(rt == Get()->Runtime());
1346 return Get()->GetJSContextStack()->GetSafeJSContext();
1347 }
1349 // static
1350 void
1351 XPCJSRuntime::ActivityCallback(void *arg, bool active)
1352 {
1353 XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
1354 self->mWatchdogManager->RecordRuntimeActivity(active);
1355 }
1357 // static
1358 //
1359 // JS-CTypes creates and caches a JSContext that it uses when executing JS
1360 // callbacks. When we're notified that ctypes is about to call into some JS,
1361 // push the cx to maintain the integrity of the context stack.
1362 void
1363 XPCJSRuntime::CTypesActivityCallback(JSContext *cx, js::CTypesActivityType type)
1364 {
1365 if (type == js::CTYPES_CALLBACK_BEGIN) {
1366 if (!xpc::PushJSContextNoScriptContext(cx))
1367 MOZ_CRASH();
1368 } else if (type == js::CTYPES_CALLBACK_END) {
1369 xpc::PopJSContextNoScriptContext();
1370 }
1371 }
1373 // static
1374 bool
1375 XPCJSRuntime::InterruptCallback(JSContext *cx)
1376 {
1377 XPCJSRuntime *self = XPCJSRuntime::Get();
1379 // If this is the first time the interrupt callback has fired since we last
1380 // returned to the event loop, mark the checkpoint.
1381 if (self->mSlowScriptCheckpoint.IsNull()) {
1382 self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
1383 return true;
1384 }
1386 // This is at least the second interrupt callback we've received since
1387 // returning to the event loop. See how long it's been, and what the limit
1388 // is.
1389 TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
1390 bool chrome =
1391 nsContentUtils::IsSystemPrincipal(nsContentUtils::GetSubjectPrincipal());
1392 const char *prefName = chrome ? "dom.max_chrome_script_run_time"
1393 : "dom.max_script_run_time";
1394 int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
1396 // If there's no limit, or we're within the limit, let it go.
1397 if (limit == 0 || duration.ToSeconds() < limit)
1398 return true;
1400 //
1401 // This has gone on long enough! Time to take action. ;-)
1402 //
1404 // Get the DOM window associated with the running script. If the script is
1405 // running in a non-DOM scope, we have to just let it keep running.
1406 RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
1407 nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
1408 if (!win && IsSandbox(global)) {
1409 // If this is a sandbox associated with a DOMWindow via a
1410 // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
1411 // and JetPack content scripts.
1412 JS::Rooted<JSObject*> proto(cx);
1413 if (!JS_GetPrototype(cx, global, &proto))
1414 return false;
1415 if (proto && IsSandboxPrototypeProxy(proto) &&
1416 (proto = js::CheckedUnwrap(proto, /* stopAtOuter = */ false)))
1417 {
1418 win = WindowGlobalOrNull(proto);
1419 }
1420 }
1421 if (!win)
1422 return true;
1424 // Show the prompt to the user, and kill if requested.
1425 nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog();
1426 if (response == nsGlobalWindow::KillSlowScript)
1427 return false;
1429 // The user chose to continue the script. Reset the timer, and disable this
1430 // machinery with a pref of the user opted out of future slow-script dialogs.
1431 self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
1432 if (response == nsGlobalWindow::AlwaysContinueSlowScript)
1433 Preferences::SetInt(prefName, 0);
1435 return true;
1436 }
1438 /* static */ void
1439 XPCJSRuntime::OutOfMemoryCallback(JSContext *cx)
1440 {
1441 if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
1442 return;
1443 }
1445 nsCOMPtr<nsIMemoryInfoDumper> dumper =
1446 do_GetService("@mozilla.org/memory-info-dumper;1");
1447 if (!dumper) {
1448 return;
1449 }
1451 // If this fails, it fails silently.
1452 dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
1453 /* minimizeMemoryUsage = */ false);
1454 }
1456 size_t
1457 XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
1458 {
1459 size_t n = 0;
1460 n += mallocSizeOf(this);
1461 n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
1462 n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
1463 n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
1464 n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
1466 n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
1468 // There are other XPCJSRuntime members that could be measured; the above
1469 // ones have been seen by DMD to be worth measuring. More stuff may be
1470 // added later.
1472 return n;
1473 }
1475 nsString*
1476 XPCJSRuntime::NewShortLivedString()
1477 {
1478 for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
1479 if (mScratchStrings[i].empty()) {
1480 mScratchStrings[i].construct();
1481 return mScratchStrings[i].addr();
1482 }
1483 }
1485 // All our internal string wrappers are used, allocate a new string.
1486 return new nsString();
1487 }
1489 void
1490 XPCJSRuntime::DeleteShortLivedString(nsString *string)
1491 {
1492 if (string == &EmptyString() || string == &NullString())
1493 return;
1495 for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
1496 if (!mScratchStrings[i].empty() &&
1497 mScratchStrings[i].addr() == string) {
1498 // One of our internal strings is no longer in use, mark
1499 // it as such and free its data.
1500 mScratchStrings[i].destroy();
1501 return;
1502 }
1503 }
1505 // We're done with a string that's not one of our internal
1506 // strings, delete it.
1507 delete string;
1508 }
1510 /***************************************************************************/
1512 static PLDHashOperator
1513 DetachedWrappedNativeProtoShutdownMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
1514 uint32_t number, void *arg)
1515 {
1516 XPCWrappedNativeProto* proto =
1517 (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
1519 proto->SystemIsBeingShutDown();
1520 return PL_DHASH_NEXT;
1521 }
1523 void XPCJSRuntime::DestroyJSContextStack()
1524 {
1525 delete mJSContextStack;
1526 mJSContextStack = nullptr;
1527 }
1529 void XPCJSRuntime::SystemIsBeingShutDown()
1530 {
1531 DOM_ClearInterfaces();
1533 if (mDetachedWrappedNativeProtoMap)
1534 mDetachedWrappedNativeProtoMap->
1535 Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr);
1536 }
1538 #define JS_OPTIONS_DOT_STR "javascript.options."
1540 static void
1541 ReloadPrefsCallback(const char *pref, void *data)
1542 {
1543 XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data);
1544 JSRuntime *rt = runtime->Runtime();
1546 bool safeMode = false;
1547 nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
1548 if (xr) {
1549 xr->GetInSafeMode(&safeMode);
1550 }
1552 bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
1553 bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode;
1554 bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode;
1556 bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
1557 bool parallelIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
1558 "ion.parallel_compilation");
1559 bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
1560 "baselinejit.unsafe_eager_compilation");
1561 bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
1563 sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
1565 JS::RuntimeOptionsRef(rt).setBaseline(useBaseline)
1566 .setIon(useIon)
1567 . setAsmJS(useAsmJS);
1569 JS_SetParallelParsingEnabled(rt, parallelParsing);
1570 JS_SetParallelIonCompilationEnabled(rt, parallelIonCompilation);
1571 JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER,
1572 useBaselineEager ? 0 : -1);
1573 JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_ION_USECOUNT_TRIGGER,
1574 useIonEager ? 0 : -1);
1575 }
1577 XPCJSRuntime::~XPCJSRuntime()
1578 {
1579 // This destructor runs before ~CycleCollectedJSRuntime, which does the
1580 // actual JS_DestroyRuntime() call. But destroying the runtime triggers
1581 // one final GC, which can call back into the runtime with various
1582 // callback if we aren't careful. Null out the relevant callbacks.
1583 js::SetActivityCallback(Runtime(), nullptr, nullptr);
1584 JS_SetFinalizeCallback(Runtime(), nullptr);
1586 // Clear any pending exception. It might be an XPCWrappedJS, and if we try
1587 // to destroy it later we will crash.
1588 SetPendingException(nullptr);
1590 JS::SetGCSliceCallback(Runtime(), mPrevGCSliceCallback);
1592 xpc_DelocalizeRuntime(Runtime());
1594 if (mWatchdogManager->GetWatchdog())
1595 mWatchdogManager->StopWatchdog();
1597 if (mCallContext)
1598 mCallContext->SystemIsBeingShutDown();
1600 auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime()));
1601 delete rtPrivate;
1602 JS_SetRuntimePrivate(Runtime(), nullptr);
1604 // clean up and destroy maps...
1605 if (mWrappedJSMap) {
1606 mWrappedJSMap->ShutdownMarker();
1607 delete mWrappedJSMap;
1608 mWrappedJSMap = nullptr;
1609 }
1611 if (mWrappedJSClassMap) {
1612 delete mWrappedJSClassMap;
1613 mWrappedJSClassMap = nullptr;
1614 }
1616 if (mIID2NativeInterfaceMap) {
1617 delete mIID2NativeInterfaceMap;
1618 mIID2NativeInterfaceMap = nullptr;
1619 }
1621 if (mClassInfo2NativeSetMap) {
1622 delete mClassInfo2NativeSetMap;
1623 mClassInfo2NativeSetMap = nullptr;
1624 }
1626 if (mNativeSetMap) {
1627 delete mNativeSetMap;
1628 mNativeSetMap = nullptr;
1629 }
1631 if (mThisTranslatorMap) {
1632 delete mThisTranslatorMap;
1633 mThisTranslatorMap = nullptr;
1634 }
1636 if (mNativeScriptableSharedMap) {
1637 delete mNativeScriptableSharedMap;
1638 mNativeScriptableSharedMap = nullptr;
1639 }
1641 if (mDyingWrappedNativeProtoMap) {
1642 delete mDyingWrappedNativeProtoMap;
1643 mDyingWrappedNativeProtoMap = nullptr;
1644 }
1646 if (mDetachedWrappedNativeProtoMap) {
1647 delete mDetachedWrappedNativeProtoMap;
1648 mDetachedWrappedNativeProtoMap = nullptr;
1649 }
1651 #ifdef MOZ_ENABLE_PROFILER_SPS
1652 // Tell the profiler that the runtime is gone
1653 if (PseudoStack *stack = mozilla_get_pseudo_stack())
1654 stack->sampleRuntime(nullptr);
1655 #endif
1657 #ifdef DEBUG
1658 for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
1659 MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
1660 }
1661 #endif
1663 Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
1664 }
1666 static void
1667 GetCompartmentName(JSCompartment *c, nsCString &name, bool replaceSlashes)
1668 {
1669 if (js::IsAtomsCompartment(c)) {
1670 name.AssignLiteral("atoms");
1671 } else if (JSPrincipals *principals = JS_GetCompartmentPrincipals(c)) {
1672 nsJSPrincipals::get(principals)->GetScriptLocation(name);
1674 // If the compartment's location (name) differs from the principal's
1675 // script location, append the compartment's location to allow
1676 // differentiation of multiple compartments owned by the same principal
1677 // (e.g. components owned by the system or null principal).
1678 CompartmentPrivate *compartmentPrivate = GetCompartmentPrivate(c);
1679 if (compartmentPrivate) {
1680 const nsACString& location = compartmentPrivate->GetLocation();
1681 if (!location.IsEmpty() && !location.Equals(name)) {
1682 name.AppendLiteral(", ");
1683 name.Append(location);
1684 }
1685 }
1687 // A hack: replace forward slashes with '\\' so they aren't
1688 // treated as path separators. Users of the reporters
1689 // (such as about:memory) have to undo this change.
1690 if (replaceSlashes)
1691 name.ReplaceChar('/', '\\');
1692 } else {
1693 name.AssignLiteral("null-principal");
1694 }
1695 }
1697 static int64_t
1698 JSMainRuntimeGCHeapDistinguishedAmount()
1699 {
1700 JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
1701 return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
1702 js::gc::ChunkSize;
1703 }
1705 static int64_t
1706 JSMainRuntimeTemporaryPeakDistinguishedAmount()
1707 {
1708 JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
1709 return JS::PeakSizeOfTemporary(rt);
1710 }
1712 static int64_t
1713 JSMainRuntimeCompartmentsSystemDistinguishedAmount()
1714 {
1715 JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
1716 return JS::SystemCompartmentCount(rt);
1717 }
1719 static int64_t
1720 JSMainRuntimeCompartmentsUserDistinguishedAmount()
1721 {
1722 JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
1723 return JS::UserCompartmentCount(rt);
1724 }
1726 class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter
1727 {
1728 public:
1729 NS_DECL_ISUPPORTS
1731 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
1732 nsISupports* aData)
1733 {
1734 return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak",
1735 KIND_OTHER, UNITS_BYTES,
1736 JSMainRuntimeTemporaryPeakDistinguishedAmount(),
1737 "Peak transient data size in the main JSRuntime (the current size "
1738 "of which is reported as "
1739 "'explicit/js-non-window/runtime/temporary').");
1740 }
1741 };
1743 NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
1745 // The REPORT* macros do an unconditional report. The ZCREPORT* macros are for
1746 // compartments and zones; they aggregate any entries smaller than
1747 // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
1748 // entries for the compartment.
1750 #define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
1752 #define REPORT(_path, _kind, _units, _amount, _desc) \
1753 do { \
1754 nsresult rv; \
1755 rv = cb->Callback(EmptyCString(), _path, \
1756 nsIMemoryReporter::_kind, \
1757 nsIMemoryReporter::_units, \
1758 _amount, \
1759 NS_LITERAL_CSTRING(_desc), \
1760 closure); \
1761 NS_ENSURE_SUCCESS(rv, rv); \
1762 } while (0)
1764 #define REPORT_BYTES(_path, _kind, _amount, _desc) \
1765 REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
1767 #define REPORT_GC_BYTES(_path, _amount, _desc) \
1768 do { \
1769 size_t amount = _amount; /* evaluate _amount only once */ \
1770 nsresult rv; \
1771 rv = cb->Callback(EmptyCString(), _path, \
1772 nsIMemoryReporter::KIND_NONHEAP, \
1773 nsIMemoryReporter::UNITS_BYTES, amount, \
1774 NS_LITERAL_CSTRING(_desc), closure); \
1775 NS_ENSURE_SUCCESS(rv, rv); \
1776 gcTotal += amount; \
1777 } while (0)
1779 // Report compartment/zone non-GC (KIND_HEAP) bytes.
1780 #define ZCREPORT_BYTES(_path, _amount, _desc) \
1781 do { \
1782 /* Assign _descLiteral plus "" into a char* to prove that it's */ \
1783 /* actually a literal. */ \
1784 size_t amount = _amount; /* evaluate _amount only once */ \
1785 if (amount >= SUNDRIES_THRESHOLD) { \
1786 nsresult rv; \
1787 rv = cb->Callback(EmptyCString(), _path, \
1788 nsIMemoryReporter::KIND_HEAP, \
1789 nsIMemoryReporter::UNITS_BYTES, amount, \
1790 NS_LITERAL_CSTRING(_desc), closure); \
1791 NS_ENSURE_SUCCESS(rv, rv); \
1792 } else { \
1793 sundriesMallocHeap += amount; \
1794 } \
1795 } while (0)
1797 // Report compartment/zone GC bytes.
1798 #define ZCREPORT_GC_BYTES(_path, _amount, _desc) \
1799 do { \
1800 size_t amount = _amount; /* evaluate _amount only once */ \
1801 if (amount >= SUNDRIES_THRESHOLD) { \
1802 nsresult rv; \
1803 rv = cb->Callback(EmptyCString(), _path, \
1804 nsIMemoryReporter::KIND_NONHEAP, \
1805 nsIMemoryReporter::UNITS_BYTES, amount, \
1806 NS_LITERAL_CSTRING(_desc), closure); \
1807 NS_ENSURE_SUCCESS(rv, rv); \
1808 gcTotal += amount; \
1809 } else { \
1810 sundriesGCHeap += amount; \
1811 } \
1812 } while (0)
1814 // Report runtime bytes.
1815 #define RREPORT_BYTES(_path, _kind, _amount, _desc) \
1816 do { \
1817 size_t amount = _amount; /* evaluate _amount only once */ \
1818 nsresult rv; \
1819 rv = cb->Callback(EmptyCString(), _path, \
1820 nsIMemoryReporter::_kind, \
1821 nsIMemoryReporter::UNITS_BYTES, amount, \
1822 NS_LITERAL_CSTRING(_desc), closure); \
1823 NS_ENSURE_SUCCESS(rv, rv); \
1824 rtTotal += amount; \
1825 } while (0)
1827 MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
1829 namespace xpc {
1831 static nsresult
1832 ReportZoneStats(const JS::ZoneStats &zStats,
1833 const xpc::ZoneStatsExtras &extras,
1834 nsIMemoryReporterCallback *cb,
1835 nsISupports *closure, size_t *gcTotalOut = nullptr)
1836 {
1837 const nsAutoCString& pathPrefix = extras.pathPrefix;
1838 size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
1840 MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
1842 ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
1843 zStats.gcHeapArenaAdmin,
1844 "Bookkeeping information and alignment padding within GC arenas.");
1846 ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
1847 zStats.unusedGCThings,
1848 "Empty GC thing cells within non-empty arenas.");
1850 ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
1851 zStats.lazyScriptsGCHeap,
1852 "Scripts that haven't executed yet.");
1854 ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
1855 zStats.lazyScriptsMallocHeap,
1856 "Lazy script tables containing free variables or inner functions.");
1858 ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
1859 zStats.jitCodesGCHeap,
1860 "References to executable code pools used by the JITs.");
1862 ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"),
1863 zStats.typeObjectsGCHeap,
1864 "Type inference information about objects.");
1866 ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"),
1867 zStats.typeObjectsMallocHeap,
1868 "Type object addenda.");
1870 ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
1871 zStats.typePool,
1872 "Type sets and related data.");
1874 ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
1875 zStats.baselineStubsOptimized,
1876 "The Baseline JIT's optimized IC stubs (excluding code).");
1878 size_t stringsNotableAboutMemoryGCHeap = 0;
1879 size_t stringsNotableAboutMemoryMallocHeap = 0;
1881 #define MAYBE_INLINE \
1882 "The characters may be inline or on the malloc heap."
1883 #define MAYBE_OVERALLOCATED \
1884 "Sometimes over-allocated to simplify string concatenation."
1886 for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
1887 const JS::NotableStringInfo& info = zStats.notableStrings[i];
1889 MOZ_ASSERT(!zStats.isTotals);
1891 nsDependentCString notableString(info.buffer);
1893 // Viewing about:memory generates many notable strings which contain
1894 // "string(length=". If we report these as notable, then we'll create
1895 // even more notable strings the next time we open about:memory (unless
1896 // there's a GC in the meantime), and so on ad infinitum.
1897 //
1898 // To avoid cluttering up about:memory like this, we stick notable
1899 // strings which contain "string(length=" into their own bucket.
1900 # define STRING_LENGTH "string(length="
1901 if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
1902 stringsNotableAboutMemoryGCHeap += info.gcHeap;
1903 stringsNotableAboutMemoryMallocHeap += info.mallocHeap;
1904 continue;
1905 }
1907 // Escape / to \ before we put notableString into the memory reporter
1908 // path, because we don't want any forward slashes in the string to
1909 // count as path separators.
1910 nsCString escapedString(notableString);
1911 escapedString.ReplaceSubstring("/", "\\");
1913 bool truncated = notableString.Length() < info.length;
1915 nsCString path = pathPrefix +
1916 nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
1917 info.length, info.numCopies, escapedString.get(),
1918 truncated ? " (truncated)" : "");
1920 if (info.gcHeap > 0) {
1921 REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
1922 info.gcHeap,
1923 "Strings. " MAYBE_INLINE);
1924 }
1926 if (info.mallocHeap > 0) {
1927 REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
1928 KIND_HEAP, info.mallocHeap,
1929 "Non-inline string characters. " MAYBE_OVERALLOCATED);
1930 }
1931 }
1933 nsCString nonNotablePath = pathPrefix;
1934 nonNotablePath += zStats.isTotals
1935 ? NS_LITERAL_CSTRING("strings/")
1936 : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
1938 if (zStats.stringInfo.gcHeap > 0) {
1939 REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
1940 zStats.stringInfo.gcHeap,
1941 "Strings. " MAYBE_INLINE);
1942 }
1944 if (zStats.stringInfo.mallocHeap > 0) {
1945 REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
1946 KIND_HEAP, zStats.stringInfo.mallocHeap,
1947 "Non-inline string characters. " MAYBE_OVERALLOCATED);
1948 }
1950 if (stringsNotableAboutMemoryGCHeap > 0) {
1951 MOZ_ASSERT(!zStats.isTotals);
1952 REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
1953 stringsNotableAboutMemoryGCHeap,
1954 "Strings that contain the characters '" STRING_LENGTH "', which "
1955 "are probably from about:memory itself." MAYBE_INLINE
1956 " We filter them out rather than display them, because displaying "
1957 "them would create even more such strings every time about:memory "
1958 "is refreshed.");
1959 }
1961 if (stringsNotableAboutMemoryMallocHeap > 0) {
1962 MOZ_ASSERT(!zStats.isTotals);
1963 REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
1964 KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
1965 "Non-inline string characters of strings that contain the "
1966 "characters '" STRING_LENGTH "', which are probably from "
1967 "about:memory itself. " MAYBE_OVERALLOCATED
1968 " We filter them out rather than display them, because displaying "
1969 "them would create even more such strings every time about:memory "
1970 "is refreshed.");
1971 }
1973 if (sundriesGCHeap > 0) {
1974 // We deliberately don't use ZCREPORT_GC_BYTES here.
1975 REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
1976 sundriesGCHeap,
1977 "The sum of all 'gc-heap' measurements that are too small to be "
1978 "worth showing individually.");
1979 }
1981 if (sundriesMallocHeap > 0) {
1982 // We deliberately don't use ZCREPORT_BYTES here.
1983 REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
1984 KIND_HEAP, sundriesMallocHeap,
1985 "The sum of all 'malloc-heap' measurements that are too small to "
1986 "be worth showing individually.");
1987 }
1989 if (gcTotalOut)
1990 *gcTotalOut += gcTotal;
1992 return NS_OK;
1994 # undef STRING_LENGTH
1995 }
1997 static nsresult
1998 ReportCompartmentStats(const JS::CompartmentStats &cStats,
1999 const xpc::CompartmentStatsExtras &extras,
2000 amIAddonManager *addonManager,
2001 nsIMemoryReporterCallback *cb,
2002 nsISupports *closure, size_t *gcTotalOut = nullptr)
2003 {
2004 static const nsDependentCString addonPrefix("explicit/add-ons/");
2006 size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
2007 nsAutoCString cJSPathPrefix = extras.jsPathPrefix;
2008 nsAutoCString cDOMPathPrefix = extras.domPathPrefix;
2010 // Only attempt to prefix if we got a location and the path wasn't already
2011 // prefixed.
2012 if (extras.location && addonManager &&
2013 cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) {
2014 nsAutoCString addonId;
2015 bool ok;
2016 if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location,
2017 addonId, &ok))
2018 && ok) {
2019 // Insert the add-on id as "add-ons/@id@/" after "explicit/" to
2020 // aggregate add-on compartments.
2021 static const size_t explicitLength = strlen("explicit/");
2022 addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
2023 addonId += "/";
2024 cJSPathPrefix.Insert(addonId, explicitLength);
2025 cDOMPathPrefix.Insert(addonId, explicitLength);
2026 }
2027 }
2029 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/ordinary"),
2030 cStats.objectsGCHeapOrdinary,
2031 "Ordinary objects, i.e. not otherwise distinguished by memory "
2032 "reporters.");
2034 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/function"),
2035 cStats.objectsGCHeapFunction,
2036 "Function objects.");
2038 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/dense-array"),
2039 cStats.objectsGCHeapDenseArray,
2040 "Dense array objects.");
2042 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/slow-array"),
2043 cStats.objectsGCHeapSlowArray,
2044 "Slow array objects.");
2046 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/cross-compartment-wrapper"),
2047 cStats.objectsGCHeapCrossCompartmentWrapper,
2048 "Cross-compartment wrapper objects.");
2050 // Note that we use cDOMPathPrefix here. This is because we measure orphan
2051 // DOM nodes in the JS reporter, but we want to report them in a "dom"
2052 // sub-tree rather than a "js" sub-tree.
2053 ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
2054 cStats.objectsPrivate,
2055 "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
2056 "objects.");
2058 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/global-parented"),
2059 cStats.shapesGCHeapTreeGlobalParented,
2060 "Shapes that (a) are in a property tree, and (b) represent an object "
2061 "whose parent is the global object.");
2063 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/non-global-parented"),
2064 cStats.shapesGCHeapTreeNonGlobalParented,
2065 "Shapes that (a) are in a property tree, and (b) represent an object "
2066 "whose parent is not the global object.");
2068 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
2069 cStats.shapesGCHeapDict,
2070 "Shapes that are in dictionary mode.");
2072 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
2073 cStats.shapesGCHeapBase,
2074 "Base shapes, which collate data common to many shapes.");
2076 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
2077 cStats.shapesMallocHeapTreeTables,
2078 "Property tables belonging to shapes that are in a property tree.");
2080 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
2081 cStats.shapesMallocHeapDictTables,
2082 "Property tables that belong to shapes that are in dictionary mode.");
2084 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-shape-kids"),
2085 cStats.shapesMallocHeapTreeShapeKids,
2086 "Kid hashes that belong to shapes that are in a property tree.");
2088 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/compartment-tables"),
2089 cStats.shapesMallocHeapCompartmentTables,
2090 "Compartment-wide tables storing shape information used during object "
2091 "construction.");
2093 ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
2094 cStats.scriptsGCHeap,
2095 "JSScript instances. There is one per user-defined function in a "
2096 "script, and one for the top-level code in a script.");
2098 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
2099 cStats.scriptsMallocHeapData,
2100 "Various variable-length tables in JSScripts.");
2102 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
2103 cStats.baselineData,
2104 "The Baseline JIT's compilation data (BaselineScripts).");
2106 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
2107 cStats.baselineStubsFallback,
2108 "The Baseline JIT's fallback IC stubs (excluding code).");
2110 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
2111 cStats.ionData,
2112 "The IonMonkey JIT's compilation data (IonScripts).");
2114 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
2115 cStats.typeInferenceTypeScripts,
2116 "Type sets associated with scripts.");
2118 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
2119 cStats.typeInferenceAllocationSiteTables,
2120 "Tables of type objects associated with allocation sites.");
2122 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
2123 cStats.typeInferenceArrayTypeTables,
2124 "Tables of type objects associated with array literals.");
2126 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
2127 cStats.typeInferenceObjectTypeTables,
2128 "Tables of type objects associated with object literals.");
2130 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"),
2131 cStats.compartmentObject,
2132 "The JSCompartment object itself.");
2134 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
2135 cStats.crossCompartmentWrappersTable,
2136 "The cross-compartment wrapper table.");
2138 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"),
2139 cStats.regexpCompartment,
2140 "The regexp compartment.");
2142 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("debuggees-set"),
2143 cStats.debuggeesSet,
2144 "The debuggees set.");
2146 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
2147 cStats.objectsExtra.mallocHeapSlots,
2148 "Non-fixed object slot arrays, which represent object properties.");
2150 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
2151 cStats.objectsExtra.mallocHeapElementsNonAsmJS,
2152 "Non-asm.js indexed elements.");
2154 // asm.js arrays are heap-allocated on some platforms and
2155 // non-heap-allocated on others. We never put them under sundries,
2156 // because (a) in practice they're almost always larger than the sundries
2157 // threshold, and (b) we'd need a third category of sundries ("non-heap"),
2158 // which would be a pain.
2159 size_t mallocHeapElementsAsmJS = cStats.objectsExtra.mallocHeapElementsAsmJS;
2160 size_t nonHeapElementsAsmJS = cStats.objectsExtra.nonHeapElementsAsmJS;
2161 MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0);
2162 if (mallocHeapElementsAsmJS > 0) {
2163 REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
2164 KIND_HEAP, mallocHeapElementsAsmJS,
2165 "asm.js array buffer elements on the malloc heap.");
2166 }
2167 if (nonHeapElementsAsmJS > 0) {
2168 REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
2169 KIND_NONHEAP, nonHeapElementsAsmJS,
2170 "asm.js array buffer elements outside both the malloc heap and "
2171 "the GC heap.");
2172 }
2174 if (cStats.objectsExtra.nonHeapElementsMapped > 0) {
2175 REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
2176 KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped,
2177 "Memory-mapped array buffer elements.");
2178 }
2180 REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
2181 KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS,
2182 "AOT-compiled asm.js code.");
2184 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"),
2185 cStats.objectsExtra.mallocHeapAsmJSModuleData,
2186 "asm.js module data.");
2188 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/arguments-data"),
2189 cStats.objectsExtra.mallocHeapArgumentsData,
2190 "Data belonging to Arguments objects.");
2192 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/regexp-statics"),
2193 cStats.objectsExtra.mallocHeapRegExpStatics,
2194 "Data belonging to the RegExpStatics object.");
2196 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/property-iterator-data"),
2197 cStats.objectsExtra.mallocHeapPropertyIteratorData,
2198 "Data belonging to property iterator objects.");
2200 ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"),
2201 cStats.objectsExtra.mallocHeapCtypesData,
2202 "Data belonging to ctypes objects.");
2204 if (sundriesGCHeap > 0) {
2205 // We deliberately don't use ZCREPORT_GC_BYTES here.
2206 REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
2207 sundriesGCHeap,
2208 "The sum of all 'gc-heap' measurements that are too small to be "
2209 "worth showing individually.");
2210 }
2212 if (sundriesMallocHeap > 0) {
2213 // We deliberately don't use ZCREPORT_BYTES here.
2214 REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
2215 KIND_HEAP, sundriesMallocHeap,
2216 "The sum of all 'malloc-heap' measurements that are too small to "
2217 "be worth showing individually.");
2218 }
2220 if (gcTotalOut)
2221 *gcTotalOut += gcTotal;
2223 return NS_OK;
2224 }
2226 static nsresult
2227 ReportScriptSourceStats(const ScriptSourceInfo &scriptSourceInfo,
2228 const nsACString &path,
2229 nsIHandleReportCallback *cb, nsISupports *closure,
2230 size_t &rtTotal)
2231 {
2232 if (scriptSourceInfo.compressed > 0) {
2233 RREPORT_BYTES(path + NS_LITERAL_CSTRING("compressed"),
2234 KIND_HEAP, scriptSourceInfo.compressed,
2235 "Compressed JavaScript source code.");
2236 }
2238 if (scriptSourceInfo.uncompressed > 0) {
2239 RREPORT_BYTES(path + NS_LITERAL_CSTRING("uncompressed"),
2240 KIND_HEAP, scriptSourceInfo.uncompressed,
2241 "Uncompressed JavaScript source code.");
2242 }
2244 if (scriptSourceInfo.misc > 0) {
2245 RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
2246 KIND_HEAP, scriptSourceInfo.misc,
2247 "Miscellaneous data relating to JavaScript source code.");
2248 }
2250 return NS_OK;
2251 }
2253 static nsresult
2254 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
2255 const nsACString &rtPath,
2256 amIAddonManager* addonManager,
2257 nsIMemoryReporterCallback *cb,
2258 nsISupports *closure, size_t *rtTotalOut)
2259 {
2260 nsresult rv;
2262 size_t gcTotal = 0;
2264 for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
2265 const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i];
2266 const xpc::ZoneStatsExtras *extras =
2267 static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
2268 rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal);
2269 NS_ENSURE_SUCCESS(rv, rv);
2270 }
2272 for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
2273 JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
2274 const xpc::CompartmentStatsExtras *extras =
2275 static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
2276 rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure,
2277 &gcTotal);
2278 NS_ENSURE_SUCCESS(rv, rv);
2279 }
2281 // Report the rtStats.runtime numbers under "runtime/", and compute their
2282 // total for later.
2284 size_t rtTotal = 0;
2286 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
2287 KIND_HEAP, rtStats.runtime.object,
2288 "The JSRuntime object.");
2290 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
2291 KIND_HEAP, rtStats.runtime.atomsTable,
2292 "The atoms table.");
2294 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
2295 KIND_HEAP, rtStats.runtime.contexts,
2296 "JSContext objects and structures that belong to them.");
2298 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"),
2299 KIND_HEAP, rtStats.runtime.dtoa,
2300 "The DtoaState object, which is used for converting strings to "
2301 "numbers and vice versa.");
2303 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
2304 KIND_HEAP, rtStats.runtime.temporary,
2305 "Transient data (mostly parse nodes) held by the JSRuntime during "
2306 "compilation.");
2308 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-data"),
2309 KIND_NONHEAP, rtStats.runtime.regexpData,
2310 "Regexp JIT data.");
2312 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
2313 KIND_HEAP, rtStats.runtime.interpreterStack,
2314 "JS interpreter frames.");
2316 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
2317 KIND_HEAP, rtStats.runtime.mathCache,
2318 "The math cache.");
2320 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/source-data-cache"),
2321 KIND_HEAP, rtStats.runtime.sourceDataCache,
2322 "The source data cache, which holds decompressed script source code.");
2324 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
2325 KIND_HEAP, rtStats.runtime.scriptData,
2326 "The table holding script data shared in the runtime.");
2328 nsCString nonNotablePath =
2329 rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
2330 rtStats.runtime.scriptSourceInfo.numScripts);
2332 rv = ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
2333 nonNotablePath, cb, closure, rtTotal);
2334 NS_ENSURE_SUCCESS(rv, rv);
2336 for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
2337 const JS::NotableScriptSourceInfo& scriptSourceInfo =
2338 rtStats.runtime.notableScriptSources[i];
2340 // Escape / to \ before we put the filename into the memory reporter
2341 // path, because we don't want any forward slashes in the string to
2342 // count as path separators. Consumers of memory reporters (e.g.
2343 // about:memory) will convert them back to / after doing path
2344 // splitting.
2345 nsDependentCString filename(scriptSourceInfo.filename_);
2346 nsCString escapedFilename(filename);
2347 escapedFilename.ReplaceSubstring("/", "\\");
2349 nsCString notablePath = rtPath +
2350 nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
2351 scriptSourceInfo.numScripts, escapedFilename.get());
2353 rv = ReportScriptSourceStats(scriptSourceInfo, notablePath,
2354 cb, closure, rtTotal);
2355 NS_ENSURE_SUCCESS(rv, rv);
2356 }
2358 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
2359 KIND_NONHEAP, rtStats.runtime.code.ion,
2360 "Code generated by the IonMonkey JIT.");
2362 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
2363 KIND_NONHEAP, rtStats.runtime.code.baseline,
2364 "Code generated by the Baseline JIT.");
2366 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
2367 KIND_NONHEAP, rtStats.runtime.code.regexp,
2368 "Code generated by the regexp JIT.");
2370 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
2371 KIND_NONHEAP, rtStats.runtime.code.other,
2372 "Code generated by the JITs for wrappers and trampolines.");
2374 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
2375 KIND_NONHEAP, rtStats.runtime.code.unused,
2376 "Memory allocated by one of the JITs to hold code, but which is "
2377 "currently unused.");
2379 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
2380 KIND_HEAP, rtStats.runtime.gc.marker,
2381 "The GC mark stack and gray roots.");
2383 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
2384 KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
2385 "Memory being used by the GC's nursery.");
2387 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"),
2388 KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots,
2389 "Out-of-line slots and elements belonging to objects in the "
2390 "nursery.");
2392 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
2393 KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
2394 "Values in the store buffer.");
2396 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
2397 KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
2398 "Cells in the store buffer.");
2400 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
2401 KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
2402 "Slots in the store buffer.");
2404 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
2405 KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
2406 "Whole cells in the store buffer.");
2408 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-vals"),
2409 KIND_HEAP, rtStats.runtime.gc.storeBufferRelocVals,
2410 "Relocatable values in the store buffer.");
2412 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-cells"),
2413 KIND_HEAP, rtStats.runtime.gc.storeBufferRelocCells,
2414 "Relocatable cells in the store buffer.");
2416 RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
2417 KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
2418 "Generic things in the store buffer.");
2420 if (rtTotalOut)
2421 *rtTotalOut = rtTotal;
2423 // Report GC numbers that don't belong to a compartment.
2425 // We don't want to report decommitted memory in "explicit", so we just
2426 // change the leading "explicit/" to "decommitted/".
2427 nsCString rtPath2(rtPath);
2428 rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted"));
2429 REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
2430 rtStats.gcHeapDecommittedArenas,
2431 "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
2432 "address space but no physical memory or swap space.");
2434 REPORT_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"),
2435 KIND_NONHEAP, rtStats.runtime.gc.nurseryDecommitted,
2436 "Memory allocated to the GC's nursery this is decommitted, i.e. it takes up "
2437 "address space but no physical memory or swap space.");
2439 REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
2440 rtStats.gcHeapUnusedChunks,
2441 "Empty GC chunks which will soon be released unless claimed for new "
2442 "allocations.");
2444 REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
2445 rtStats.gcHeapUnusedArenas,
2446 "Empty GC arenas within non-empty chunks.");
2448 REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
2449 rtStats.gcHeapChunkAdmin,
2450 "Bookkeeping information within GC chunks.");
2452 // gcTotal is the sum of everything we've reported for the GC heap. It
2453 // should equal rtStats.gcHeapChunkTotal.
2454 MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
2456 return NS_OK;
2457 }
2459 nsresult
2460 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
2461 const nsACString &rtPath,
2462 nsIMemoryReporterCallback *cb,
2463 nsISupports *closure, size_t *rtTotalOut)
2464 {
2465 nsCOMPtr<amIAddonManager> am;
2466 if (XRE_GetProcessType() == GeckoProcessType_Default) {
2467 // Only try to access the service from the main process.
2468 am = do_GetService("@mozilla.org/addons/integration;1");
2469 }
2470 return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb,
2471 closure, rtTotalOut);
2472 }
2475 } // namespace xpc
2477 class JSMainRuntimeCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter
2478 {
2479 public:
2480 NS_DECL_ISUPPORTS
2482 typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths;
2484 static void CompartmentCallback(JSRuntime *rt, void* data, JSCompartment *c) {
2485 // silently ignore OOM errors
2486 Paths *paths = static_cast<Paths *>(data);
2487 nsCString path;
2488 GetCompartmentName(c, path, true);
2489 path.Insert(js::IsSystemCompartment(c)
2490 ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
2491 : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
2492 0);
2493 paths->append(path);
2494 }
2496 NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb,
2497 nsISupports *closure)
2498 {
2499 // First we collect the compartment paths. Then we report them. Doing
2500 // the two steps interleaved is a bad idea, because calling |cb|
2501 // from within CompartmentCallback() leads to all manner of assertions.
2503 // Collect.
2505 Paths paths;
2506 JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Runtime(),
2507 &paths, CompartmentCallback);
2509 // Report.
2510 for (size_t i = 0; i < paths.length(); i++)
2511 // These ones don't need a description, hence the "".
2512 REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1,
2513 "A live compartment in the main JSRuntime.");
2515 return NS_OK;
2516 }
2517 };
2519 NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
2521 MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
2523 namespace xpc {
2525 static size_t
2526 SizeOfTreeIncludingThis(nsINode *tree)
2527 {
2528 size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
2529 for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
2530 n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
2532 return n;
2533 }
2535 class OrphanReporter : public JS::ObjectPrivateVisitor
2536 {
2537 public:
2538 OrphanReporter(GetISupportsFun aGetISupports)
2539 : JS::ObjectPrivateVisitor(aGetISupports)
2540 {
2541 }
2543 virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE {
2544 size_t n = 0;
2545 nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
2546 // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
2547 // that we have to skip XBL elements because they violate certain
2548 // assumptions. Yuk.
2549 if (node && !node->IsInDoc() &&
2550 !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
2551 {
2552 // This is an orphan node. If we haven't already handled the
2553 // sub-tree that this node belongs to, measure the sub-tree's size
2554 // and then record its root so we don't measure it again.
2555 nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
2556 if (!mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
2557 n += SizeOfTreeIncludingThis(orphanTree);
2558 mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree);
2559 }
2560 }
2561 return n;
2562 }
2564 private:
2565 nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
2566 };
2568 #ifdef DEBUG
2569 static bool
2570 StartsWithExplicit(nsACString& s)
2571 {
2572 const char* e = "explicit/";
2573 return Substring(s, 0, strlen(e)).Equals(e);
2574 }
2575 #endif
2577 class XPCJSRuntimeStats : public JS::RuntimeStats
2578 {
2579 WindowPaths *mWindowPaths;
2580 WindowPaths *mTopWindowPaths;
2581 bool mGetLocations;
2583 public:
2584 XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths,
2585 bool getLocations)
2586 : JS::RuntimeStats(JSMallocSizeOf),
2587 mWindowPaths(windowPaths),
2588 mTopWindowPaths(topWindowPaths),
2589 mGetLocations(getLocations)
2590 {}
2592 ~XPCJSRuntimeStats() {
2593 for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
2594 delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
2597 for (size_t i = 0; i != zoneStatsVector.length(); ++i)
2598 delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
2599 }
2601 virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) MOZ_OVERRIDE {
2602 // Get the compartment's global.
2603 nsXPConnect *xpc = nsXPConnect::XPConnect();
2604 AutoSafeJSContext cx;
2605 JSCompartment *comp = js::GetAnyCompartmentInZone(zone);
2606 xpc::ZoneStatsExtras *extras = new xpc::ZoneStatsExtras;
2607 extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2608 RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp));
2609 if (global) {
2610 // Need to enter the compartment, otherwise GetNativeOfWrapper()
2611 // might crash.
2612 JSAutoCompartment ac(cx, global);
2613 nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
2614 if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
2615 // The global is a |window| object. Use the path prefix that
2616 // we should have already created for it.
2617 if (mTopWindowPaths->Get(piwindow->WindowID(),
2618 &extras->pathPrefix))
2619 extras->pathPrefix.AppendLiteral("/js-");
2620 }
2621 }
2623 extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)zone);
2625 MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
2627 zStats->extra = extras;
2628 }
2630 virtual void initExtraCompartmentStats(JSCompartment *c,
2631 JS::CompartmentStats *cstats) MOZ_OVERRIDE
2632 {
2633 xpc::CompartmentStatsExtras *extras = new xpc::CompartmentStatsExtras;
2634 nsCString cName;
2635 GetCompartmentName(c, cName, true);
2636 if (mGetLocations) {
2637 CompartmentPrivate *cp = GetCompartmentPrivate(c);
2638 if (cp)
2639 cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
2640 getter_AddRefs(extras->location));
2641 // Note: cannot use amIAddonManager implementation at this point,
2642 // as it is a JS service and the JS heap is currently not idle.
2643 // Otherwise, we could have computed the add-on id at this point.
2644 }
2646 // Get the compartment's global.
2647 nsXPConnect *xpc = nsXPConnect::XPConnect();
2648 AutoSafeJSContext cx;
2649 bool needZone = true;
2650 RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c));
2651 if (global) {
2652 // Need to enter the compartment, otherwise GetNativeOfWrapper()
2653 // might crash.
2654 JSAutoCompartment ac(cx, global);
2655 nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
2656 if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
2657 // The global is a |window| object. Use the path prefix that
2658 // we should have already created for it.
2659 if (mWindowPaths->Get(piwindow->WindowID(),
2660 &extras->jsPathPrefix)) {
2661 extras->domPathPrefix.Assign(extras->jsPathPrefix);
2662 extras->domPathPrefix.AppendLiteral("/dom/");
2663 extras->jsPathPrefix.AppendLiteral("/js-");
2664 needZone = false;
2665 } else {
2666 extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2667 extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
2668 }
2669 } else {
2670 extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2671 extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
2672 }
2673 } else {
2674 extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2675 extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
2676 }
2678 if (needZone)
2679 extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void *)js::GetCompartmentZone(c));
2681 extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
2683 // extras->jsPathPrefix is used for almost all the compartment-specific
2684 // reports. At this point it has the form
2685 // "<something>compartment(<cname>)/".
2686 //
2687 // extras->domPathPrefix is used for DOM orphan nodes, which are
2688 // counted by the JS reporter but reported as part of the DOM
2689 // measurements. At this point it has the form "<something>/dom/" if
2690 // this compartment belongs to an nsGlobalWindow, and
2691 // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
2692 // be used, because non-nsGlobalWindow compartments shouldn't have
2693 // orphan DOM nodes).
2695 MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
2696 MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
2698 cstats->extra = extras;
2699 }
2700 };
2702 nsresult
2703 JSReporter::CollectReports(WindowPaths *windowPaths,
2704 WindowPaths *topWindowPaths,
2705 nsIMemoryReporterCallback *cb,
2706 nsISupports *closure)
2707 {
2708 XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
2710 // In the first step we get all the stats and stash them in a local
2711 // data structure. In the second step we pass all the stashed stats to
2712 // the callback. Separating these steps is important because the
2713 // callback may be a JS function, and executing JS while getting these
2714 // stats seems like a bad idea.
2716 nsCOMPtr<amIAddonManager> addonManager;
2717 if (XRE_GetProcessType() == GeckoProcessType_Default) {
2718 // Only try to access the service from the main process.
2719 addonManager = do_GetService("@mozilla.org/addons/integration;1");
2720 }
2721 bool getLocations = !!addonManager;
2722 XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations);
2723 OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
2724 if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter))
2725 return NS_ERROR_FAILURE;
2727 size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
2729 XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
2730 XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
2732 mozJSComponentLoader* loader = mozJSComponentLoader::Get();
2733 size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
2735 // This is the second step (see above). First we report stuff in the
2736 // "explicit" tree, then we report other stuff.
2738 nsresult rv;
2739 size_t rtTotal = 0;
2740 rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
2741 NS_LITERAL_CSTRING("explicit/js-non-window/"),
2742 addonManager, cb, closure,
2743 &rtTotal);
2744 NS_ENSURE_SUCCESS(rv, rv);
2746 // Report the sums of the compartment numbers.
2747 xpc::CompartmentStatsExtras cExtrasTotal;
2748 cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
2749 cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
2750 rv = ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
2751 cb, closure);
2752 NS_ENSURE_SUCCESS(rv, rv);
2754 xpc::ZoneStatsExtras zExtrasTotal;
2755 zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
2756 rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure);
2757 NS_ENSURE_SUCCESS(rv, rv);
2759 // Report the sum of the runtime/ numbers.
2760 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
2761 KIND_OTHER, rtTotal,
2762 "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
2764 // Report the numbers for memory outside of compartments.
2766 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
2767 KIND_OTHER, rtStats.gcHeapUnusedChunks,
2768 "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
2770 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
2771 KIND_OTHER, rtStats.gcHeapUnusedArenas,
2772 "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
2774 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
2775 KIND_OTHER, rtStats.gcHeapChunkAdmin,
2776 "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
2778 // Report a breakdown of the committed GC space.
2780 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
2781 KIND_OTHER, rtStats.gcHeapUnusedChunks,
2782 "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
2784 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
2785 KIND_OTHER, rtStats.gcHeapUnusedArenas,
2786 "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
2788 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
2789 KIND_OTHER, rtStats.zTotals.unusedGCThings,
2790 "The same as 'js-main-runtime/zones/unused-gc-things'.");
2792 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
2793 KIND_OTHER, rtStats.gcHeapChunkAdmin,
2794 "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
2796 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
2797 KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
2798 "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
2800 REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
2801 KIND_OTHER, rtStats.gcHeapGCThings,
2802 "GC things: objects, strings, scripts, etc.");
2804 // Report xpconnect.
2806 REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
2807 KIND_HEAP, xpconnect,
2808 "The XPConnect runtime.");
2810 REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
2811 KIND_HEAP, sizeInfo.mScopeAndMapSize,
2812 "XPConnect scopes.");
2814 REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
2815 KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
2816 "Prototype and interface binding caches.");
2818 REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
2819 KIND_HEAP, jsComponentLoaderSize,
2820 "XPConnect's JS component loader.");
2822 return NS_OK;
2823 }
2825 static nsresult
2826 JSSizeOfTab(JSObject *objArg, size_t *jsObjectsSize, size_t *jsStringsSize,
2827 size_t *jsPrivateSize, size_t *jsOtherSize)
2828 {
2829 JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
2830 JS::RootedObject obj(rt, objArg);
2832 TabSizes sizes;
2833 OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
2834 NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of,
2835 &orphanReporter, &sizes),
2836 NS_ERROR_OUT_OF_MEMORY);
2838 *jsObjectsSize = sizes.objects;
2839 *jsStringsSize = sizes.strings;
2840 *jsPrivateSize = sizes.private_;
2841 *jsOtherSize = sizes.other;
2842 return NS_OK;
2843 }
2845 } // namespace xpc
2847 #ifdef MOZ_CRASHREPORTER
2848 static bool
2849 DiagnosticMemoryCallback(void *ptr, size_t size)
2850 {
2851 return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
2852 }
2853 #endif
2855 static void
2856 AccumulateTelemetryCallback(int id, uint32_t sample)
2857 {
2858 switch (id) {
2859 case JS_TELEMETRY_GC_REASON:
2860 Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
2861 break;
2862 case JS_TELEMETRY_GC_IS_COMPARTMENTAL:
2863 Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
2864 break;
2865 case JS_TELEMETRY_GC_MS:
2866 Telemetry::Accumulate(Telemetry::GC_MS, sample);
2867 break;
2868 case JS_TELEMETRY_GC_MAX_PAUSE_MS:
2869 Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample);
2870 break;
2871 case JS_TELEMETRY_GC_MARK_MS:
2872 Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
2873 break;
2874 case JS_TELEMETRY_GC_SWEEP_MS:
2875 Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
2876 break;
2877 case JS_TELEMETRY_GC_MARK_ROOTS_MS:
2878 Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
2879 break;
2880 case JS_TELEMETRY_GC_MARK_GRAY_MS:
2881 Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
2882 break;
2883 case JS_TELEMETRY_GC_SLICE_MS:
2884 Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
2885 break;
2886 case JS_TELEMETRY_GC_MMU_50:
2887 Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
2888 break;
2889 case JS_TELEMETRY_GC_RESET:
2890 Telemetry::Accumulate(Telemetry::GC_RESET, sample);
2891 break;
2892 case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
2893 Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
2894 break;
2895 case JS_TELEMETRY_GC_NON_INCREMENTAL:
2896 Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
2897 break;
2898 case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
2899 Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
2900 break;
2901 case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
2902 Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
2903 break;
2904 }
2905 }
2907 static void
2908 CompartmentNameCallback(JSRuntime *rt, JSCompartment *comp,
2909 char *buf, size_t bufsize)
2910 {
2911 nsCString name;
2912 GetCompartmentName(comp, name, false);
2913 if (name.Length() >= bufsize)
2914 name.Truncate(bufsize - 1);
2915 memcpy(buf, name.get(), name.Length() + 1);
2916 }
2918 static bool
2919 PreserveWrapper(JSContext *cx, JSObject *obj)
2920 {
2921 MOZ_ASSERT(cx);
2922 MOZ_ASSERT(obj);
2923 MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
2925 return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
2926 }
2928 static nsresult
2929 ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len)
2930 {
2931 nsresult rv;
2933 // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
2934 // the filename of its caller. Axe that if present.
2935 const char *arrow;
2936 while ((arrow = strstr(filename, " -> ")))
2937 filename = arrow + strlen(" -> ");
2939 // Get the URI.
2940 nsCOMPtr<nsIURI> uri;
2941 rv = NS_NewURI(getter_AddRefs(uri), filename);
2942 NS_ENSURE_SUCCESS(rv, rv);
2944 nsCOMPtr<nsIChannel> scriptChannel;
2945 rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri);
2946 NS_ENSURE_SUCCESS(rv, rv);
2948 // Only allow local reading.
2949 nsCOMPtr<nsIURI> actualUri;
2950 rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
2951 NS_ENSURE_SUCCESS(rv, rv);
2952 nsCString scheme;
2953 rv = actualUri->GetScheme(scheme);
2954 NS_ENSURE_SUCCESS(rv, rv);
2955 if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
2956 return NS_OK;
2958 nsCOMPtr<nsIInputStream> scriptStream;
2959 rv = scriptChannel->Open(getter_AddRefs(scriptStream));
2960 NS_ENSURE_SUCCESS(rv, rv);
2962 uint64_t rawLen;
2963 rv = scriptStream->Available(&rawLen);
2964 NS_ENSURE_SUCCESS(rv, rv);
2965 if (!rawLen)
2966 return NS_ERROR_FAILURE;
2968 // Technically, this should be SIZE_MAX, but we don't run on machines
2969 // where that would be less than UINT32_MAX, and the latter is already
2970 // well beyond a reasonable limit.
2971 if (rawLen > UINT32_MAX)
2972 return NS_ERROR_FILE_TOO_BIG;
2974 // Allocate an internal buf the size of the file.
2975 nsAutoArrayPtr<unsigned char> buf(new unsigned char[rawLen]);
2976 if (!buf)
2977 return NS_ERROR_OUT_OF_MEMORY;
2979 unsigned char *ptr = buf, *end = ptr + rawLen;
2980 while (ptr < end) {
2981 uint32_t bytesRead;
2982 rv = scriptStream->Read(reinterpret_cast<char *>(ptr), end - ptr, &bytesRead);
2983 if (NS_FAILED(rv))
2984 return rv;
2985 MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
2986 ptr += bytesRead;
2987 }
2989 rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(),
2990 nullptr, *src, *len);
2991 NS_ENSURE_SUCCESS(rv, rv);
2993 if (!*src)
2994 return NS_ERROR_FAILURE;
2996 // Historically this method used JS_malloc() which updates the GC memory
2997 // accounting. Since ConvertToUTF16() now uses js_malloc() instead we
2998 // update the accounting manually after the fact.
2999 JS_updateMallocCounter(cx, *len);
3001 return NS_OK;
3002 }
3004 // The JS engine calls this object's 'load' member function when it needs
3005 // the source for a chrome JS function. See the comment in the XPCJSRuntime
3006 // constructor.
3007 class XPCJSSourceHook: public js::SourceHook {
3008 bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) {
3009 *src = nullptr;
3010 *length = 0;
3012 if (!nsContentUtils::IsCallerChrome())
3013 return true;
3015 if (!filename)
3016 return true;
3018 nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
3019 if (NS_FAILED(rv)) {
3020 xpc::Throw(cx, rv);
3021 return false;
3022 }
3024 return true;
3025 }
3026 };
3028 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
3029 xpc::WrapperFactory::Rewrap,
3030 xpc::WrapperFactory::PrepareForWrapping
3031 };
3033 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
3034 : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
3035 mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())),
3036 mCallContext(nullptr),
3037 mAutoRoots(nullptr),
3038 mResolveName(JSID_VOID),
3039 mResolvingWrapper(nullptr),
3040 mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
3041 mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
3042 mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
3043 mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
3044 mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
3045 mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
3046 mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
3047 mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
3048 mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
3049 mGCIsRunning(false),
3050 mWrappedJSToReleaseArray(),
3051 mNativesToReleaseArray(),
3052 mDoingFinalization(false),
3053 mVariantRoots(nullptr),
3054 mWrappedJSRoots(nullptr),
3055 mObjectHolderRoots(nullptr),
3056 mWatchdogManager(new WatchdogManager(MOZ_THIS_IN_INITIALIZER_LIST())),
3057 mJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
3058 mCompilationScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
3059 mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
3060 {
3061 DOM_InitInterfaces();
3063 // these jsids filled in later when we have a JSContext to work with.
3064 mStrIDs[0] = JSID_VOID;
3066 MOZ_ASSERT(Runtime());
3067 JSRuntime* runtime = Runtime();
3069 auto rtPrivate = new PerThreadAtomCache();
3070 memset(rtPrivate, 0, sizeof(PerThreadAtomCache));
3071 JS_SetRuntimePrivate(runtime, rtPrivate);
3073 // Unconstrain the runtime's threshold on nominal heap size, to avoid
3074 // triggering GC too often if operating continuously near an arbitrary
3075 // finite threshold (0xffffffff is infinity for uint32_t parameters).
3076 // This leaves the maximum-JS_malloc-bytes threshold still in effect
3077 // to cause period, and we hope hygienic, last-ditch GCs from within
3078 // the GC's allocator.
3079 JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff);
3081 // The JS engine permits us to set different stack limits for system code,
3082 // trusted script, and untrusted script. We have tests that ensure that
3083 // we can always execute 10 "heavy" (eval+with) stack frames deeper in
3084 // privileged code. Our stack sizes vary greatly in different configurations,
3085 // so satisfying those tests requires some care. Manual measurements of the
3086 // number of heavy stack frames achievable gives us the following rough data,
3087 // ordered by the effective categories in which they are grouped in the
3088 // JS_SetNativeStackQuota call (which predates this analysis).
3089 //
3090 // (NB: These numbers may have drifted recently - see bug 938429)
3091 // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
3092 // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
3093 //
3094 // Linux 32-bit Debug: 2MB stack, 447 stack frames => ~4.6k per stack frame
3095 // Linux 64-bit Debug: 4MB stack, 501 stack frames => ~8.2k per stack frame
3096 //
3097 // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
3098 //
3099 // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
3100 // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
3101 //
3102 // We tune the trusted/untrusted quotas for each configuration to achieve our
3103 // invariants while attempting to minimize overhead. In contrast, our buffer
3104 // between system code and trusted script is a very unscientific 10k.
3105 const size_t kSystemCodeBuffer = 10 * 1024;
3107 // Our "default" stack is what we use in configurations where we don't have
3108 // a compelling reason to do things differently. This is effectively 1MB on
3109 // 32-bit platforms and 2MB on 64-bit platforms.
3110 const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
3112 // Set stack sizes for different configurations. It's probably not great for
3113 // the web to base this decision primarily on the default stack size that the
3114 // underlying platform makes available, but that seems to be what we do. :-(
3116 #if defined(XP_MACOSX) || defined(DARWIN)
3117 // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
3118 // and give trusted script 140k extra. The stack is huge on mac anyway.
3119 const size_t kStackQuota = 7 * 1024 * 1024;
3120 const size_t kTrustedScriptBuffer = 140 * 1024;
3121 #elif defined(MOZ_ASAN)
3122 // ASan requires more stack space due to red-zones, so give it double the
3123 // default (2MB on 32-bit, 4MB on 64-bit). ASAN stack frame measurements
3124 // were not taken at the time of this writing, so we hazard a guess that
3125 // ASAN builds have roughly thrice the stack overhead as normal builds.
3126 // On normal builds, the largest stack frame size we might encounter is
3127 // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k.
3128 const size_t kStackQuota = 2 * kDefaultStackQuota;
3129 const size_t kTrustedScriptBuffer = 246 * 1024;
3130 #elif defined(XP_WIN)
3131 // 1MB is the default stack size on Windows, so the default 1MB stack quota
3132 // we'd get on win32 is slightly too large. Use 900k instead. And since
3133 // windows stack frames are 3.4k each, let's use a buffer of 50k.
3134 const size_t kStackQuota = 900 * 1024;
3135 const size_t kTrustedScriptBuffer = 50 * 1024;
3136 // The following two configurations are linux-only. Given the numbers above,
3137 // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
3138 #elif defined(DEBUG)
3139 // Bug 803182: account for the 4x difference in the size of js::Interpret
3140 // between optimized and debug builds.
3141 // XXXbholley - Then why do we only account for 2x of difference?
3142 const size_t kStackQuota = 2 * kDefaultStackQuota;
3143 const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
3144 #else
3145 const size_t kStackQuota = kDefaultStackQuota;
3146 const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
3147 #endif
3149 // Avoid an unused variable warning on platforms where we don't use the
3150 // default.
3151 (void) kDefaultStackQuota;
3153 JS_SetNativeStackQuota(runtime,
3154 kStackQuota,
3155 kStackQuota - kSystemCodeBuffer,
3156 kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
3158 JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback);
3159 JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
3160 mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
3161 JS_SetFinalizeCallback(runtime, FinalizeCallback);
3162 JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
3163 js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
3164 #ifdef MOZ_CRASHREPORTER
3165 JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
3166 #endif
3167 #ifdef MOZ_ENABLE_PROFILER_SPS
3168 if (PseudoStack *stack = mozilla_get_pseudo_stack())
3169 stack->sampleRuntime(runtime);
3170 #endif
3171 JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback);
3172 js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback);
3173 js::SetActivityCallback(runtime, ActivityCallback, this);
3174 js::SetCTypesActivityCallback(runtime, CTypesActivityCallback);
3175 JS_SetInterruptCallback(runtime, InterruptCallback);
3176 JS::SetOutOfMemoryCallback(runtime, OutOfMemoryCallback);
3178 // The JS engine needs to keep the source code around in order to implement
3179 // Function.prototype.toSource(). It'd be nice to not have to do this for
3180 // chrome code and simply stub out requests for source on it. Life is not so
3181 // easy, unfortunately. Nobody relies on chrome toSource() working in core
3182 // browser code, but chrome tests use it. The worst offenders are addons,
3183 // which like to monkeypatch chrome functions by calling toSource() on them
3184 // and using regular expressions to modify them. We avoid keeping most browser
3185 // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
3186 // compiling some chrome code. This causes the JS engine not save the source
3187 // code in memory. When the JS engine is asked to provide the source for a
3188 // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
3189 ///
3190 // Note we do have to retain the source code in memory for scripts compiled in
3191 // compileAndGo mode and compiled function bodies (from
3192 // JS_CompileFunction*). In practice, this means content scripts and event
3193 // handlers.
3194 js::SetSourceHook(runtime, new XPCJSSourceHook);
3196 // Set up locale information and callbacks for the newly-created runtime so
3197 // that the various toLocaleString() methods, localeCompare(), and other
3198 // internationalization APIs work as desired.
3199 if (!xpc_LocalizeRuntime(runtime))
3200 NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
3202 // Register memory reporters and distinguished amount functions.
3203 RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
3204 RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
3205 RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
3206 RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
3207 RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
3208 RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
3209 mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
3211 // Install a JavaScript 'debugger' keyword handler in debug builds only
3212 #ifdef DEBUG
3213 if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
3214 xpc_InstallJSDebuggerKeywordHandler(runtime);
3215 #endif
3217 // Watch for the JS boolean options.
3218 ReloadPrefsCallback(nullptr, this);
3219 Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
3220 }
3222 // static
3223 XPCJSRuntime*
3224 XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
3225 {
3226 NS_PRECONDITION(aXPConnect,"bad param");
3228 XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
3230 if (self &&
3231 self->Runtime() &&
3232 self->GetWrappedJSMap() &&
3233 self->GetWrappedJSClassMap() &&
3234 self->GetIID2NativeInterfaceMap() &&
3235 self->GetClassInfo2NativeSetMap() &&
3236 self->GetNativeSetMap() &&
3237 self->GetThisTranslatorMap() &&
3238 self->GetNativeScriptableSharedMap() &&
3239 self->GetDyingWrappedNativeProtoMap() &&
3240 self->mWatchdogManager) {
3241 return self;
3242 }
3244 NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
3246 delete self;
3247 return nullptr;
3248 }
3250 bool
3251 XPCJSRuntime::OnJSContextNew(JSContext *cx)
3252 {
3253 // If we were the first cx ever created (like the SafeJSContext), the caller
3254 // would have had no way to enter a request. Enter one now before doing the
3255 // rest of the cx setup.
3256 JSAutoRequest ar(cx);
3258 // if it is our first context then we need to generate our string ids
3259 if (JSID_IS_VOID(mStrIDs[0])) {
3260 RootedString str(cx);
3261 for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
3262 str = JS_InternString(cx, mStrings[i]);
3263 if (!str) {
3264 mStrIDs[0] = JSID_VOID;
3265 return false;
3266 }
3267 mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
3268 mStrJSVals[i] = STRING_TO_JSVAL(str);
3269 }
3271 if (!mozilla::dom::DefineStaticJSVals(cx)) {
3272 return false;
3273 }
3274 }
3276 XPCContext* xpc = new XPCContext(this, cx);
3277 if (!xpc)
3278 return false;
3280 return true;
3281 }
3283 bool
3284 XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
3285 char (&name)[72]) const
3286 {
3287 XPCNativeScriptableInfo *si = nullptr;
3289 if (!IS_PROTO_CLASS(clasp)) {
3290 return false;
3291 }
3293 XPCWrappedNativeProto *p =
3294 static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
3295 si = p->GetScriptableInfo();
3297 if (!si) {
3298 return false;
3299 }
3301 JS_snprintf(name, sizeof(name), "JS Object (%s - %s)",
3302 clasp->name, si->GetJSClass()->name);
3303 return true;
3304 }
3306 bool
3307 XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
3308 nsCycleCollectionTraversalCallback& cb) const
3309 {
3310 if (clasp != &XPC_WN_Tearoff_JSClass) {
3311 return false;
3312 }
3314 // A tearoff holds a strong reference to its native object
3315 // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
3316 // will be held alive through the parent of the JSObject of the tearoff.
3317 XPCWrappedNativeTearOff *to =
3318 static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
3319 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
3320 cb.NoteXPCOMChild(to->GetNative());
3321 return true;
3322 }
3324 /***************************************************************************/
3326 #ifdef DEBUG
3327 static PLDHashOperator
3328 WrappedJSClassMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
3329 uint32_t number, void *arg)
3330 {
3331 ((IID2WrappedJSClassMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
3332 return PL_DHASH_NEXT;
3333 }
3334 static PLDHashOperator
3335 NativeSetDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
3336 uint32_t number, void *arg)
3337 {
3338 ((NativeSetMap::Entry*)hdr)->key_value->DebugDump(*(int16_t*)arg);
3339 return PL_DHASH_NEXT;
3340 }
3341 #endif
3343 void
3344 XPCJSRuntime::DebugDump(int16_t depth)
3345 {
3346 #ifdef DEBUG
3347 depth--;
3348 XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
3349 XPC_LOG_INDENT();
3350 XPC_LOG_ALWAYS(("mJSRuntime @ %x", Runtime()));
3352 XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \
3353 &mWrappedJSToReleaseArray,
3354 mWrappedJSToReleaseArray.Length()));
3356 int cxCount = 0;
3357 JSContext* iter = nullptr;
3358 while (JS_ContextIterator(Runtime(), &iter))
3359 ++cxCount;
3360 XPC_LOG_ALWAYS(("%d JS context(s)", cxCount));
3362 iter = nullptr;
3363 while (JS_ContextIterator(Runtime(), &iter)) {
3364 XPCContext *xpc = XPCContext::GetXPCContext(iter);
3365 XPC_LOG_INDENT();
3366 xpc->DebugDump(depth);
3367 XPC_LOG_OUTDENT();
3368 }
3370 XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)", \
3371 mWrappedJSClassMap, mWrappedJSClassMap ? \
3372 mWrappedJSClassMap->Count() : 0));
3373 // iterate wrappersclasses...
3374 if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) {
3375 XPC_LOG_INDENT();
3376 mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth);
3377 XPC_LOG_OUTDENT();
3378 }
3379 XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)", \
3380 mWrappedJSMap, mWrappedJSMap ? \
3381 mWrappedJSMap->Count() : 0));
3382 // iterate wrappers...
3383 if (depth && mWrappedJSMap && mWrappedJSMap->Count()) {
3384 XPC_LOG_INDENT();
3385 mWrappedJSMap->Dump(depth);
3386 XPC_LOG_OUTDENT();
3387 }
3389 XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)", \
3390 mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ? \
3391 mIID2NativeInterfaceMap->Count() : 0));
3393 XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)", \
3394 mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ? \
3395 mClassInfo2NativeSetMap->Count() : 0));
3397 XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)", \
3398 mThisTranslatorMap, mThisTranslatorMap ? \
3399 mThisTranslatorMap->Count() : 0));
3401 XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)", \
3402 mNativeSetMap, mNativeSetMap ? \
3403 mNativeSetMap->Count() : 0));
3405 // iterate sets...
3406 if (depth && mNativeSetMap && mNativeSetMap->Count()) {
3407 XPC_LOG_INDENT();
3408 mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth);
3409 XPC_LOG_OUTDENT();
3410 }
3412 XPC_LOG_OUTDENT();
3413 #endif
3414 }
3416 /***************************************************************************/
3418 void
3419 XPCRootSetElem::AddToRootSet(XPCRootSetElem **listHead)
3420 {
3421 MOZ_ASSERT(!mSelfp, "Must be not linked");
3423 mSelfp = listHead;
3424 mNext = *listHead;
3425 if (mNext) {
3426 MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
3427 mNext->mSelfp = &mNext;
3428 }
3429 *listHead = this;
3430 }
3432 void
3433 XPCRootSetElem::RemoveFromRootSet()
3434 {
3435 nsXPConnect *xpc = nsXPConnect::XPConnect();
3436 JS::PokeGC(xpc->GetRuntime()->Runtime());
3438 MOZ_ASSERT(mSelfp, "Must be linked");
3440 MOZ_ASSERT(*mSelfp == this, "Link invariant");
3441 *mSelfp = mNext;
3442 if (mNext)
3443 mNext->mSelfp = mSelfp;
3444 #ifdef DEBUG
3445 mSelfp = nullptr;
3446 mNext = nullptr;
3447 #endif
3448 }
3450 void
3451 XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
3452 {
3453 MOZ_ASSERT(cb, "null callback");
3454 extraGCCallbacks.AppendElement(cb);
3455 }
3457 void
3458 XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
3459 {
3460 MOZ_ASSERT(cb, "null callback");
3461 bool found = extraGCCallbacks.RemoveElement(cb);
3462 if (!found) {
3463 NS_ERROR("Removing a callback which was never added.");
3464 }
3465 }
3467 void
3468 XPCJSRuntime::AddContextCallback(xpcContextCallback cb)
3469 {
3470 MOZ_ASSERT(cb, "null callback");
3471 extraContextCallbacks.AppendElement(cb);
3472 }
3474 void
3475 XPCJSRuntime::RemoveContextCallback(xpcContextCallback cb)
3476 {
3477 MOZ_ASSERT(cb, "null callback");
3478 bool found = extraContextCallbacks.RemoveElement(cb);
3479 if (!found) {
3480 NS_ERROR("Removing a callback which was never added.");
3481 }
3482 }
3484 JSObject *
3485 XPCJSRuntime::GetJunkScope()
3486 {
3487 if (!mJunkScope) {
3488 AutoSafeJSContext cx;
3489 SandboxOptions options;
3490 options.sandboxName.AssignLiteral("XPConnect Junk Compartment");
3491 RootedValue v(cx);
3492 nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options);
3493 NS_ENSURE_SUCCESS(rv, nullptr);
3495 mJunkScope = js::UncheckedUnwrap(&v.toObject());
3496 }
3497 return mJunkScope;
3498 }
3500 JSObject *
3501 XPCJSRuntime::GetCompilationScope()
3502 {
3503 if (!mCompilationScope) {
3504 AutoSafeJSContext cx;
3505 SandboxOptions options;
3506 options.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
3507 options.invisibleToDebugger = true;
3508 options.discardSource = ShouldDiscardSystemSource();
3509 RootedValue v(cx);
3510 nsresult rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, options);
3511 NS_ENSURE_SUCCESS(rv, nullptr);
3513 mCompilationScope = js::UncheckedUnwrap(&v.toObject());
3514 }
3515 return mCompilationScope;
3516 }
3519 void
3520 XPCJSRuntime::DeleteSingletonScopes()
3521 {
3522 mJunkScope = nullptr;
3523 mCompilationScope = nullptr;
3524 }