Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* Per JSRuntime object */
8 :
9 : #include "mozilla/MemoryReporting.h"
10 : #include "mozilla/UniquePtr.h"
11 :
12 : #include "xpcprivate.h"
13 : #include "xpcpublic.h"
14 : #include "XPCWrapper.h"
15 : #include "XPCJSMemoryReporter.h"
16 : #include "XrayWrapper.h"
17 : #include "WrapperFactory.h"
18 : #include "mozJSComponentLoader.h"
19 : #include "nsAutoPtr.h"
20 : #include "nsNetUtil.h"
21 :
22 : #include "nsExceptionHandler.h"
23 : #include "nsIMemoryInfoDumper.h"
24 : #include "nsIMemoryReporter.h"
25 : #include "nsIObserverService.h"
26 : #include "nsIDebug2.h"
27 : #include "nsIDocShell.h"
28 : #include "nsIRunnable.h"
29 : #include "nsPIDOMWindow.h"
30 : #include "nsPrintfCString.h"
31 : #include "nsWindowSizes.h"
32 : #include "mozilla/Preferences.h"
33 : #include "mozilla/Telemetry.h"
34 : #include "mozilla/Services.h"
35 : #include "mozilla/dom/ScriptLoader.h"
36 : #include "mozilla/dom/ScriptSettings.h"
37 :
38 : #include "nsContentUtils.h"
39 : #include "nsCCUncollectableMarker.h"
40 : #include "nsCycleCollectionNoteRootCallback.h"
41 : #include "nsCycleCollector.h"
42 : #include "jsapi.h"
43 : #include "js/MemoryMetrics.h"
44 : #include "mozilla/dom/GeneratedAtomList.h"
45 : #include "mozilla/dom/BindingUtils.h"
46 : #include "mozilla/dom/Element.h"
47 : #include "mozilla/dom/WindowBinding.h"
48 : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
49 : #include "mozilla/Atomics.h"
50 : #include "mozilla/Attributes.h"
51 : #include "mozilla/ProcessHangMonitor.h"
52 : #include "mozilla/Sprintf.h"
53 : #include "mozilla/UniquePtrExtensions.h"
54 : #include "mozilla/Unused.h"
55 : #include "AccessCheck.h"
56 : #include "nsGlobalWindow.h"
57 : #include "nsAboutProtocolUtils.h"
58 :
59 : #include "GeckoProfiler.h"
60 : #include "nsIInputStream.h"
61 : #include "nsIXULRuntime.h"
62 : #include "nsJSPrincipals.h"
63 :
64 : #ifdef XP_WIN
65 : #include <windows.h>
66 : #endif
67 :
68 : using namespace mozilla;
69 : using namespace xpc;
70 : using namespace JS;
71 : using mozilla::dom::PerThreadAtomCache;
72 : using mozilla::dom::AutoEntryScript;
73 :
74 : /***************************************************************************/
75 :
76 : const char* const XPCJSRuntime::mStrings[] = {
77 : "constructor", // IDX_CONSTRUCTOR
78 : "toString", // IDX_TO_STRING
79 : "toSource", // IDX_TO_SOURCE
80 : "lastResult", // IDX_LAST_RESULT
81 : "returnCode", // IDX_RETURN_CODE
82 : "value", // IDX_VALUE
83 : "QueryInterface", // IDX_QUERY_INTERFACE
84 : "Components", // IDX_COMPONENTS
85 : "Cc", // IDX_CC
86 : "Ci", // IDX_CI
87 : "Cr", // IDX_CR
88 : "Cu", // IDX_CU
89 : "wrappedJSObject", // IDX_WRAPPED_JSOBJECT
90 : "Object", // IDX_OBJECT
91 : "Function", // IDX_FUNCTION
92 : "prototype", // IDX_PROTOTYPE
93 : "createInstance", // IDX_CREATE_INSTANCE
94 : "item", // IDX_ITEM
95 : "__proto__", // IDX_PROTO
96 : "eval", // IDX_EVAL
97 : "controllers", // IDX_CONTROLLERS
98 : "Controllers", // IDX_CONTROLLERS_CLASS
99 : "realFrameElement", // IDX_REALFRAMEELEMENT
100 : "length", // IDX_LENGTH
101 : "name", // IDX_NAME
102 : "undefined", // IDX_UNDEFINED
103 : "", // IDX_EMPTYSTRING
104 : "fileName", // IDX_FILENAME
105 : "lineNumber", // IDX_LINENUMBER
106 : "columnNumber", // IDX_COLUMNNUMBER
107 : "stack", // IDX_STACK
108 : "message", // IDX_MESSAGE
109 : "lastIndex", // IDX_LASTINDEX
110 : "then", // IDX_THEN
111 : "isInstance", // IDX_ISINSTANCE
112 : };
113 :
114 : /***************************************************************************/
115 :
116 : // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
117 : // *All* NativeSets are referenced from mNativeSetMap.
118 : // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
119 : // In mNativeSetMap we clear the references to the unmarked *and* delete them.
120 :
121 0 : class AsyncFreeSnowWhite : public Runnable
122 : {
123 : public:
124 0 : NS_IMETHOD Run() override
125 : {
126 0 : AUTO_PROFILER_LABEL("AsyncFreeSnowWhite::Run", GCCC);
127 :
128 0 : TimeStamp start = TimeStamp::Now();
129 0 : bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
130 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
131 0 : uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
132 0 : if (hadSnowWhiteObjects && !mContinuation) {
133 0 : mContinuation = true;
134 0 : if (NS_FAILED(Dispatch())) {
135 0 : mActive = false;
136 : }
137 : } else {
138 0 : mActive = false;
139 : }
140 0 : return NS_OK;
141 : }
142 :
143 0 : nsresult Dispatch()
144 : {
145 0 : nsCOMPtr<nsIRunnable> self(this);
146 0 : return NS_IdleDispatchToCurrentThread(self.forget(), 2500);
147 : }
148 :
149 0 : void Start(bool aContinuation = false, bool aPurge = false)
150 : {
151 0 : if (mContinuation) {
152 0 : mContinuation = aContinuation;
153 : }
154 0 : mPurge = aPurge;
155 0 : if (!mActive && NS_SUCCEEDED(Dispatch())) {
156 0 : mActive = true;
157 : }
158 0 : }
159 :
160 1 : AsyncFreeSnowWhite()
161 0 : : Runnable("AsyncFreeSnowWhite")
162 : , mContinuation(false)
163 : , mActive(false)
164 1 : , mPurge(false) {}
165 :
166 : public:
167 : bool mContinuation;
168 : bool mActive;
169 : bool mPurge;
170 : };
171 :
172 : namespace xpc {
173 :
174 38 : CompartmentPrivate::CompartmentPrivate(JS::Compartment* c)
175 : : wantXrays(false)
176 : , allowWaivers(true)
177 : , isWebExtensionContentScript(false)
178 : , allowCPOWs(false)
179 : , isContentXBLCompartment(false)
180 : , isAddonCompartment(false)
181 : , universalXPConnectEnabled(false)
182 : , forcePermissiveCOWs(false)
183 : , wasNuked(false)
184 0 : , mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
185 : {
186 38 : MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
187 0 : mozilla::PodArrayZero(wrapperDenialWarnings);
188 38 : }
189 :
190 0 : CompartmentPrivate::~CompartmentPrivate()
191 : {
192 0 : MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
193 0 : delete mWrappedJSMap;
194 0 : }
195 :
196 : void
197 0 : CompartmentPrivate::SystemIsBeingShutDown()
198 : {
199 0 : mWrappedJSMap->ShutdownMarker();
200 0 : }
201 :
202 38 : RealmPrivate::RealmPrivate(JS::Realm* realm)
203 : : scriptability(realm)
204 76 : , scope(nullptr)
205 : {
206 1 : }
207 :
208 : static bool
209 0 : TryParseLocationURICandidate(const nsACString& uristr,
210 : RealmPrivate::LocationHint aLocationHint,
211 : nsIURI** aURI)
212 : {
213 : static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
214 : static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
215 : static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
216 :
217 0 : if (aLocationHint == RealmPrivate::LocationHintAddon) {
218 : // Blacklist some known locations which are clearly not add-on related.
219 0 : if (StringBeginsWith(uristr, kGRE) ||
220 0 : StringBeginsWith(uristr, kToolkit) ||
221 0 : StringBeginsWith(uristr, kBrowser))
222 : return false;
223 :
224 : // -- GROSS HACK ALERT --
225 : // The Yandex Elements 8.10.2 extension implements its own "xb://" URL
226 : // scheme. If we call NS_NewURI() on an "xb://..." URL, we'll end up
227 : // calling into the extension's own JS-implemented nsIProtocolHandler
228 : // object, which we can't allow while we're iterating over the JS heap.
229 : // So just skip any such URL.
230 : // -- GROSS HACK ALERT --
231 0 : if (StringBeginsWith(uristr, NS_LITERAL_CSTRING("xb")))
232 : return false;
233 : }
234 :
235 0 : nsCOMPtr<nsIURI> uri;
236 0 : if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
237 : return false;
238 :
239 0 : nsAutoCString scheme;
240 0 : if (NS_FAILED(uri->GetScheme(scheme)))
241 : return false;
242 :
243 : // Cannot really map data: and blob:.
244 : // Also, data: URIs are pretty memory hungry, which is kinda bad
245 : // for memory reporter use.
246 0 : if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
247 : return false;
248 :
249 0 : uri.forget(aURI);
250 0 : return true;
251 : }
252 :
253 : bool
254 0 : RealmPrivate::TryParseLocationURI(RealmPrivate::LocationHint aLocationHint,
255 : nsIURI** aURI)
256 : {
257 0 : if (!aURI)
258 : return false;
259 :
260 : // Need to parse the URI.
261 0 : if (location.IsEmpty())
262 : return false;
263 :
264 : // Handle Sandbox location strings.
265 : // A sandbox string looks like this, for anonymous sandboxes, and builds
266 : // where Sandbox location tagging is enabled:
267 : //
268 : // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
269 : //
270 : // where <sandboxName> is user-provided via Cu.Sandbox()
271 : // and <js-stack-frame-filename> and <lineno> is the stack frame location
272 : // from where Cu.Sandbox was called.
273 : //
274 : // Otherwise, it is simply the caller-provided name, which is usually a URI.
275 : //
276 : // <js-stack-frame-filename> furthermore is "free form", often using a
277 : // "uri -> uri -> ..." chain. The following code will and must handle this
278 : // common case.
279 : //
280 : // It should be noted that other parts of the code may already rely on the
281 : // "format" of these strings.
282 :
283 0 : static const nsDependentCString from("(from: ");
284 0 : static const nsDependentCString arrow(" -> ");
285 0 : static const size_t fromLength = from.Length();
286 0 : static const size_t arrowLength = arrow.Length();
287 :
288 : // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
289 0 : int32_t idx = location.Find(from);
290 0 : if (idx < 0)
291 0 : return TryParseLocationURICandidate(location, aLocationHint, aURI);
292 :
293 :
294 : // When parsing we're looking for the right-most URI. This URI may be in
295 : // <sandboxName>, so we try this first.
296 0 : if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
297 : aURI))
298 : return true;
299 :
300 : // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
301 : // the chain that is potentially contained within and grab the rightmost
302 : // item that is actually a URI.
303 :
304 : // First, hack off the :<lineno>) part as well
305 0 : int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
306 0 : nsAutoCString chain(Substring(location, idx + fromLength,
307 0 : ridx - idx - fromLength));
308 :
309 : // Loop over the "->" chain. This loop also works for non-chains, or more
310 : // correctly chains with only one item.
311 : for (;;) {
312 0 : idx = chain.RFind(arrow);
313 0 : if (idx < 0) {
314 : // This is the last chain item. Try to parse what is left.
315 0 : return TryParseLocationURICandidate(chain, aLocationHint, aURI);
316 : }
317 :
318 : // Try to parse current chain item
319 0 : if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
320 : aLocationHint, aURI))
321 : return true;
322 :
323 : // Current chain item couldn't be parsed.
324 : // Strip current item and continue.
325 0 : chain = Substring(chain, 0, idx);
326 : }
327 :
328 : MOZ_CRASH("Chain parser loop does not terminate");
329 : }
330 :
331 : static bool
332 38 : PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
333 : {
334 : // System principal gets a free pass.
335 38 : if (nsXPConnect::SecurityManager()->IsSystemPrincipal(aPrincipal))
336 : return true;
337 :
338 10 : auto principal = BasePrincipal::Cast(aPrincipal);
339 :
340 : // ExpandedPrincipal gets a free pass.
341 10 : if (principal->Is<ExpandedPrincipal>()) {
342 : return true;
343 : }
344 :
345 : // WebExtension principals get a free pass.
346 10 : if (principal->AddonPolicy()) {
347 : return true;
348 : }
349 :
350 : // Check whether our URI is an "about:" URI that allows scripts. If it is,
351 : // we need to allow JS to run.
352 16 : nsCOMPtr<nsIURI> principalURI;
353 0 : aPrincipal->GetURI(getter_AddRefs(principalURI));
354 0 : MOZ_ASSERT(principalURI);
355 :
356 : bool isAbout;
357 0 : nsresult rv = principalURI->SchemeIs("about", &isAbout);
358 8 : if (NS_SUCCEEDED(rv) && isAbout) {
359 0 : nsCOMPtr<nsIAboutModule> module;
360 0 : rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
361 0 : if (NS_SUCCEEDED(rv)) {
362 : uint32_t flags;
363 0 : rv = module->GetURIFlags(principalURI, &flags);
364 0 : if (NS_SUCCEEDED(rv) &&
365 0 : (flags & nsIAboutModule::ALLOW_SCRIPT)) {
366 0 : return true;
367 : }
368 : }
369 : }
370 :
371 : return false;
372 : }
373 :
374 0 : Scriptability::Scriptability(JS::Realm* realm) : mScriptBlocks(0)
375 : , mDocShellAllowsScript(true)
376 38 : , mScriptBlockedByPolicy(false)
377 : {
378 38 : nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
379 0 : mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
380 :
381 : // If we're not immune, we should have a real principal with a codebase URI.
382 : // Check the URI against the new-style domain policy.
383 0 : if (!mImmuneToScriptPolicy) {
384 0 : nsCOMPtr<nsIURI> codebase;
385 16 : nsresult rv = prin->GetURI(getter_AddRefs(codebase));
386 : bool policyAllows;
387 24 : if (NS_SUCCEEDED(rv) && codebase &&
388 16 : NS_SUCCEEDED(nsXPConnect::SecurityManager()->PolicyAllowsScript(codebase, &policyAllows)))
389 : {
390 8 : mScriptBlockedByPolicy = !policyAllows;
391 : } else {
392 : // Something went wrong - be safe and block script.
393 0 : mScriptBlockedByPolicy = true;
394 : }
395 : }
396 38 : }
397 :
398 : bool
399 4480 : Scriptability::Allowed()
400 : {
401 8960 : return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
402 1 : mScriptBlocks == 0;
403 : }
404 :
405 : bool
406 0 : Scriptability::IsImmuneToScriptPolicy()
407 : {
408 0 : return mImmuneToScriptPolicy;
409 : }
410 :
411 : void
412 0 : Scriptability::Block()
413 : {
414 0 : ++mScriptBlocks;
415 0 : }
416 :
417 : void
418 0 : Scriptability::Unblock()
419 : {
420 0 : MOZ_ASSERT(mScriptBlocks > 0);
421 0 : --mScriptBlocks;
422 0 : }
423 :
424 : void
425 18 : Scriptability::SetDocShellAllowsScript(bool aAllowed)
426 : {
427 18 : mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
428 0 : }
429 :
430 : /* static */
431 : Scriptability&
432 4498 : Scriptability::Get(JSObject* aScope)
433 : {
434 0 : return RealmPrivate::Get(aScope)->scriptability;
435 : }
436 :
437 : bool
438 0 : IsContentXBLCompartment(JS::Compartment* compartment)
439 : {
440 : // We always eagerly create compartment privates for content XBL compartments.
441 4537 : CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
442 0 : return priv && priv->isContentXBLCompartment;
443 : }
444 :
445 : bool
446 0 : IsContentXBLScope(JS::Realm* realm)
447 : {
448 0 : return IsContentXBLCompartment(JS::GetCompartmentForRealm(realm));
449 : }
450 :
451 : bool
452 2435 : IsInContentXBLScope(JSObject* obj)
453 : {
454 0 : return IsContentXBLCompartment(js::GetObjectCompartment(obj));
455 : }
456 :
457 : bool
458 405 : IsUniversalXPConnectEnabled(JS::Compartment* compartment)
459 : {
460 405 : CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
461 405 : if (!priv)
462 : return false;
463 0 : return priv->universalXPConnectEnabled;
464 : }
465 :
466 : bool
467 64 : IsUniversalXPConnectEnabled(JSContext* cx)
468 : {
469 64 : JS::Compartment* compartment = js::GetContextCompartment(cx);
470 64 : if (!compartment)
471 : return false;
472 0 : return IsUniversalXPConnectEnabled(compartment);
473 : }
474 :
475 : bool
476 0 : EnableUniversalXPConnect(JSContext* cx)
477 : {
478 0 : JS::Compartment* compartment = js::GetContextCompartment(cx);
479 0 : if (!compartment)
480 : return true;
481 : // Never set universalXPConnectEnabled on a chrome compartment - it confuses
482 : // the security wrapping code.
483 0 : if (AccessCheck::isChrome(compartment))
484 : return true;
485 0 : CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
486 0 : if (!priv)
487 : return true;
488 0 : if (priv->universalXPConnectEnabled)
489 : return true;
490 0 : priv->universalXPConnectEnabled = true;
491 :
492 : // Recompute all the cross-compartment wrappers leaving the newly-privileged
493 : // compartment.
494 0 : bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
495 0 : js::AllCompartments());
496 0 : NS_ENSURE_TRUE(ok, false);
497 :
498 : // The Components object normally isn't defined for unprivileged web content,
499 : // but we define it when UniversalXPConnect is enabled to support legacy
500 : // tests.
501 0 : Realm* realm = GetCurrentRealmOrNull(cx);
502 0 : XPCWrappedNativeScope* scope = RealmPrivate::Get(realm)->scope;
503 0 : if (!scope)
504 : return true;
505 0 : scope->ForcePrivilegedComponents();
506 0 : return scope->AttachComponentsObject(cx);
507 : }
508 :
509 : JSObject*
510 4304 : UnprivilegedJunkScope()
511 : {
512 0 : return XPCJSRuntime::Get()->UnprivilegedJunkScope();
513 : }
514 :
515 : JSObject*
516 68 : PrivilegedJunkScope()
517 : {
518 0 : return XPCJSRuntime::Get()->PrivilegedJunkScope();
519 : }
520 :
521 : JSObject*
522 2524 : CompilationScope()
523 : {
524 0 : return XPCJSRuntime::Get()->CompilationScope();
525 : }
526 :
527 : nsGlobalWindowInner*
528 4869 : WindowOrNull(JSObject* aObj)
529 : {
530 0 : MOZ_ASSERT(aObj);
531 0 : MOZ_ASSERT(!js::IsWrapper(aObj));
532 :
533 4869 : nsGlobalWindowInner* win = nullptr;
534 4869 : UNWRAP_NON_WRAPPER_OBJECT(Window, aObj, win);
535 0 : return win;
536 : }
537 :
538 : nsGlobalWindowInner*
539 4458 : WindowGlobalOrNull(JSObject* aObj)
540 : {
541 4458 : MOZ_ASSERT(aObj);
542 4458 : JSObject* glob = js::GetGlobalForObjectCrossCompartment(aObj);
543 :
544 0 : return WindowOrNull(glob);
545 : }
546 :
547 : nsGlobalWindowInner*
548 1 : CurrentWindowOrNull(JSContext* cx)
549 : {
550 1 : JSObject* glob = JS::CurrentGlobalOrNull(cx);
551 1 : return glob ? WindowOrNull(glob) : nullptr;
552 : }
553 :
554 : // Nukes all wrappers into or out of the given compartment, and prevents new
555 : // wrappers from being created. Additionally marks the compartment as
556 : // unscriptable after wrappers have been nuked.
557 : //
558 : // Note: This should *only* be called for browser or extension compartments.
559 : // Wrappers between web compartments must never be cut in web-observable
560 : // ways.
561 : void
562 0 : NukeAllWrappersForCompartment(JSContext* cx, JS::Compartment* compartment,
563 : js::NukeReferencesToWindow nukeReferencesToWindow)
564 : {
565 : // First, nuke all wrappers into or out of the target compartment. Once
566 : // the compartment is marked as nuked, WrapperFactory will refuse to
567 : // create new live wrappers for it, in either direction. This means that
568 : // we need to be sure that we don't have any existing cross-compartment
569 : // wrappers which may be replaced with dead wrappers during unrelated
570 : // wrapper recomputation *before* we set that bit.
571 0 : js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(), compartment,
572 : nukeReferencesToWindow,
573 0 : js::NukeAllReferences);
574 :
575 : // At this point, we should cross-compartment wrappers for the nuked
576 : // compartment. Set the wasNuked bit so WrapperFactory will return a
577 : // DeadObjectProxy when asked to create a new wrapper for it, and mark as
578 : // unscriptable.
579 0 : xpc::CompartmentPrivate::Get(compartment)->wasNuked = true;
580 :
581 0 : auto blockScriptability = [](JSContext*, void*, Handle<Realm*> realm) {
582 0 : xpc::RealmPrivate::Get(realm)->scriptability.Block();
583 0 : };
584 0 : JS::IterateRealmsInCompartment(cx, compartment, nullptr, blockScriptability);
585 0 : }
586 :
587 : } // namespace xpc
588 :
589 : static void
590 5 : CompartmentDestroyedCallback(JSFreeOp* fop, JS::Compartment* compartment)
591 : {
592 : // NB - This callback may be called in JS_DestroyContext, which happens
593 : // after the XPCJSRuntime has been torn down.
594 :
595 : // Get the current compartment private into an AutoPtr (which will do the
596 : // cleanup for us), and null out the private (which may already be null).
597 0 : nsAutoPtr<CompartmentPrivate> priv(CompartmentPrivate::Get(compartment));
598 5 : JS_SetCompartmentPrivate(compartment, nullptr);
599 0 : }
600 :
601 : static size_t
602 0 : CompartmentSizeOfIncludingThisCallback(MallocSizeOf mallocSizeOf, JS::Compartment* compartment)
603 : {
604 0 : CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
605 0 : return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0;
606 : }
607 :
608 : /*
609 : * Return true if there exists a non-system inner window which is a current
610 : * inner window and whose reflector is gray. We don't merge system
611 : * compartments, so we don't use them to trigger merging CCs.
612 : */
613 0 : bool XPCJSRuntime::UsefulToMergeZones() const
614 : {
615 0 : MOZ_ASSERT(NS_IsMainThread());
616 :
617 : // Turns out, actually making this return true often enough makes Windows
618 : // mochitest-gl OOM a lot. Need to figure out what's going on there; see
619 : // bug 1277036.
620 :
621 0 : return false;
622 : }
623 :
624 0 : void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
625 : {
626 0 : for (CycleCollectedJSContext* ccx : Contexts()) {
627 0 : auto* cx = static_cast<const XPCJSContext*>(ccx);
628 0 : if (AutoMarkingPtr* roots = cx->mAutoRoots)
629 0 : roots->TraceJSAll(trc);
630 : }
631 :
632 0 : dom::TraceBlackJS(trc, nsXPConnect::XPConnect()->IsShuttingDown());
633 0 : }
634 :
635 0 : void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc)
636 : {
637 0 : XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc);
638 :
639 0 : for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot())
640 0 : static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
641 :
642 0 : for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot())
643 0 : static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
644 0 : }
645 :
646 : void
647 0 : XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb)
648 : {
649 0 : XPCWrappedNativeScope::SuspectAllWrappers(cb);
650 :
651 0 : for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot()) {
652 0 : XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
653 0 : if (nsCCUncollectableMarker::InGeneration(cb,
654 : v->CCGeneration())) {
655 0 : JS::Value val = v->GetJSValPreserveColor();
656 0 : if (val.isObject() && !JS::ObjectIsMarkedGray(&val.toObject()))
657 0 : continue;
658 : }
659 : cb.NoteXPCOMRoot(v,
660 0 : XPCTraceableVariant::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant());
661 : }
662 :
663 0 : for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
664 0 : cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)),
665 0 : nsXPCWrappedJS::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant());
666 : }
667 0 : }
668 :
669 : void
670 0 : XPCJSRuntime::UnmarkSkippableJSHolders()
671 : {
672 0 : CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
673 0 : }
674 :
675 : void
676 0 : XPCJSRuntime::PrepareForForgetSkippable()
677 : {
678 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
679 0 : if (obs) {
680 0 : obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
681 : }
682 0 : }
683 :
684 : void
685 0 : XPCJSRuntime::BeginCycleCollectionCallback()
686 : {
687 0 : nsJSContext::BeginCycleCollectionCallback();
688 :
689 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
690 0 : if (obs) {
691 0 : obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
692 : }
693 0 : }
694 :
695 : void
696 0 : XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults& aResults)
697 : {
698 0 : nsJSContext::EndCycleCollectionCallback(aResults);
699 :
700 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
701 0 : if (obs) {
702 0 : obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
703 : }
704 0 : }
705 :
706 : void
707 0 : XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
708 : {
709 0 : mAsyncSnowWhiteFreer->Start(aContinuation, aPurge);
710 0 : }
711 :
712 : void
713 0 : xpc_UnmarkSkippableJSHolders()
714 : {
715 0 : if (nsXPConnect::GetRuntimeInstance()) {
716 0 : nsXPConnect::GetRuntimeInstance()->UnmarkSkippableJSHolders();
717 : }
718 0 : }
719 :
720 : /* static */ void
721 0 : XPCJSRuntime::GCSliceCallback(JSContext* cx,
722 : JS::GCProgress progress,
723 : const JS::GCDescription& desc)
724 : {
725 0 : XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
726 0 : if (!self)
727 : return;
728 :
729 0 : CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN);
730 :
731 0 : if (self->mPrevGCSliceCallback)
732 0 : (*self->mPrevGCSliceCallback)(cx, progress, desc);
733 : }
734 :
735 : /* static */ void
736 0 : XPCJSRuntime::DoCycleCollectionCallback(JSContext* cx)
737 : {
738 : // The GC has detected that a CC at this point would collect a tremendous
739 : // amount of garbage that is being revivified unnecessarily.
740 0 : NS_DispatchToCurrentThread(
741 0 : NS_NewRunnableFunction("XPCJSRuntime::DoCycleCollectionCallback",
742 0 : []() { nsJSContext::CycleCollectNow(nullptr); }));
743 :
744 0 : XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
745 0 : if (!self)
746 : return;
747 :
748 0 : if (self->mPrevDoCycleCollectionCallback)
749 0 : (*self->mPrevDoCycleCollectionCallback)(cx);
750 : }
751 :
752 : void
753 0 : XPCJSRuntime::CustomGCCallback(JSGCStatus status)
754 : {
755 0 : nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
756 0 : for (uint32_t i = 0; i < callbacks.Length(); ++i)
757 0 : callbacks[i](status);
758 0 : }
759 :
760 : /* static */ void
761 0 : XPCJSRuntime::FinalizeCallback(JSFreeOp* fop,
762 : JSFinalizeStatus status,
763 : void* data)
764 : {
765 0 : XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
766 0 : if (!self)
767 : return;
768 :
769 0 : switch (status) {
770 : case JSFINALIZE_GROUP_PREPARE:
771 : {
772 0 : MOZ_ASSERT(!self->mDoingFinalization, "bad state");
773 :
774 0 : MOZ_ASSERT(!self->mGCIsRunning, "bad state");
775 0 : self->mGCIsRunning = true;
776 :
777 0 : self->mDoingFinalization = true;
778 :
779 0 : break;
780 : }
781 : case JSFINALIZE_GROUP_START:
782 : {
783 0 : MOZ_ASSERT(self->mDoingFinalization, "bad state");
784 :
785 0 : MOZ_ASSERT(self->mGCIsRunning, "bad state");
786 0 : self->mGCIsRunning = false;
787 :
788 0 : break;
789 : }
790 : case JSFINALIZE_GROUP_END:
791 : {
792 : // Sweep scopes needing cleanup
793 0 : XPCWrappedNativeScope::KillDyingScopes();
794 :
795 0 : MOZ_ASSERT(self->mDoingFinalization, "bad state");
796 0 : self->mDoingFinalization = false;
797 :
798 0 : break;
799 : }
800 : case JSFINALIZE_COLLECTION_END:
801 : {
802 0 : MOZ_ASSERT(!self->mGCIsRunning, "bad state");
803 0 : self->mGCIsRunning = true;
804 :
805 0 : for (CycleCollectedJSContext* ccx : self->Contexts()) {
806 0 : auto* cx = static_cast<const XPCJSContext*>(ccx);
807 0 : if (AutoMarkingPtr* roots = cx->mAutoRoots)
808 0 : roots->MarkAfterJSFinalizeAll();
809 :
810 : // Now we are going to recycle any unused WrappedNativeTearoffs.
811 : // We do this by iterating all the live callcontexts
812 : // and marking the tearoffs in use. And then we
813 : // iterate over all the WrappedNative wrappers and sweep their
814 : // tearoffs.
815 : //
816 : // This allows us to perhaps minimize the growth of the
817 : // tearoffs. And also makes us not hold references to interfaces
818 : // on our wrapped natives that we are not actually using.
819 : //
820 : // XXX We may decide to not do this on *every* gc cycle.
821 :
822 0 : XPCCallContext* ccxp = cx->GetCallContext();
823 0 : while (ccxp) {
824 : // Deal with the strictness of callcontext that
825 : // complains if you ask for a tearoff when
826 : // it is in a state where the tearoff could not
827 : // possibly be valid.
828 0 : if (ccxp->CanGetTearOff()) {
829 : XPCWrappedNativeTearOff* to =
830 0 : ccxp->GetTearOff();
831 0 : if (to)
832 0 : to->Mark();
833 : }
834 0 : ccxp = ccxp->GetPrevCallContext();
835 : }
836 : }
837 :
838 0 : XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
839 :
840 : // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
841 : // We transfered these native objects to this table when their
842 : // JSObject's were finalized. We did not destroy them immediately
843 : // at that point because the ordering of JS finalization is not
844 : // deterministic and we did not yet know if any wrappers that
845 : // might still be referencing the protos where still yet to be
846 : // finalized and destroyed. We *do* know that the protos'
847 : // JSObjects would not have been finalized if there were any
848 : // wrappers that referenced the proto but where not themselves
849 : // slated for finalization in this gc cycle. So... at this point
850 : // we know that any and all wrappers that might have been
851 : // referencing the protos in the dying list are themselves dead.
852 : // So, we can safely delete all the protos in the list.
853 :
854 0 : for (auto i = self->mDyingWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
855 0 : auto entry = static_cast<XPCWrappedNativeProtoMap::Entry*>(i.Get());
856 0 : delete static_cast<const XPCWrappedNativeProto*>(entry->key);
857 0 : i.Remove();
858 : }
859 :
860 0 : MOZ_ASSERT(self->mGCIsRunning, "bad state");
861 0 : self->mGCIsRunning = false;
862 :
863 0 : break;
864 : }
865 : }
866 : }
867 :
868 : /* static */ void
869 0 : XPCJSRuntime::WeakPointerZonesCallback(JSContext* cx, void* data)
870 : {
871 : // Called before each sweeping slice -- after processing any final marking
872 : // triggered by barriers -- to clear out any references to things that are
873 : // about to be finalized and update any pointers to moved GC things.
874 0 : XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
875 :
876 0 : self->mWrappedJSMap->UpdateWeakPointersAfterGC();
877 :
878 0 : XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
879 0 : }
880 :
881 : /* static */ void
882 0 : XPCJSRuntime::WeakPointerCompartmentCallback(JSContext* cx, JS::Compartment* comp, void* data)
883 : {
884 : // Called immediately after the ZoneGroup weak pointer callback, but only
885 : // once for each compartment that is being swept.
886 0 : CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
887 0 : if (xpcComp)
888 0 : xpcComp->UpdateWeakPointersAfterGC();
889 0 : }
890 :
891 : void
892 0 : CompartmentPrivate::UpdateWeakPointersAfterGC()
893 : {
894 0 : mWrappedJSMap->UpdateWeakPointersAfterGC();
895 0 : }
896 :
897 : void
898 0 : XPCJSRuntime::CustomOutOfMemoryCallback()
899 : {
900 0 : if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
901 0 : return;
902 : }
903 :
904 : nsCOMPtr<nsIMemoryInfoDumper> dumper =
905 0 : do_GetService("@mozilla.org/memory-info-dumper;1");
906 0 : if (!dumper) {
907 0 : return;
908 : }
909 :
910 : // If this fails, it fails silently.
911 0 : dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
912 : /* anonymize = */ false,
913 0 : /* minimizeMemoryUsage = */ false);
914 : }
915 :
916 : void
917 0 : XPCJSRuntime::OnLargeAllocationFailure()
918 : {
919 0 : CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reporting);
920 :
921 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
922 0 : if (os) {
923 0 : os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
924 : }
925 :
926 0 : CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reported);
927 0 : }
928 :
929 : class LargeAllocationFailureRunnable final : public Runnable
930 : {
931 : Mutex mMutex;
932 : CondVar mCondVar;
933 : bool mWaiting;
934 :
935 0 : virtual ~LargeAllocationFailureRunnable()
936 0 : {
937 0 : MOZ_ASSERT(!mWaiting);
938 0 : }
939 :
940 : protected:
941 0 : NS_IMETHOD Run() override
942 : {
943 0 : MOZ_ASSERT(NS_IsMainThread());
944 :
945 0 : XPCJSRuntime::Get()->OnLargeAllocationFailure();
946 :
947 0 : MutexAutoLock lock(mMutex);
948 0 : MOZ_ASSERT(mWaiting);
949 :
950 0 : mWaiting = false;
951 0 : mCondVar.Notify();
952 0 : return NS_OK;
953 : }
954 :
955 : public:
956 0 : LargeAllocationFailureRunnable()
957 0 : : mozilla::Runnable("LargeAllocationFailureRunnable")
958 : , mMutex("LargeAllocationFailureRunnable::mMutex")
959 : , mCondVar(mMutex, "LargeAllocationFailureRunnable::mCondVar")
960 0 : , mWaiting(true)
961 : {
962 0 : MOZ_ASSERT(!NS_IsMainThread());
963 0 : }
964 :
965 0 : void BlockUntilDone()
966 : {
967 0 : MOZ_ASSERT(!NS_IsMainThread());
968 :
969 0 : MutexAutoLock lock(mMutex);
970 0 : while (mWaiting) {
971 0 : mCondVar.Wait();
972 : }
973 0 : }
974 : };
975 :
976 : static void
977 0 : OnLargeAllocationFailureCallback()
978 : {
979 : // This callback can be called from any thread, including internal JS helper
980 : // and DOM worker threads. We need to send the low-memory event via the
981 : // observer service which can only be called on the main thread, so proxy to
982 : // the main thread if we're not there already. The purpose of this callback
983 : // is to synchronously free some memory so the caller can retry a failed
984 : // allocation, so block on the completion.
985 :
986 0 : if (NS_IsMainThread()) {
987 0 : XPCJSRuntime::Get()->OnLargeAllocationFailure();
988 0 : return;
989 : }
990 :
991 0 : RefPtr<LargeAllocationFailureRunnable> r = new LargeAllocationFailureRunnable;
992 0 : if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
993 0 : return;
994 : }
995 :
996 0 : r->BlockUntilDone();
997 : }
998 :
999 : size_t
1000 0 : XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
1001 : {
1002 0 : size_t n = 0;
1003 0 : n += mallocSizeOf(this);
1004 0 : n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
1005 0 : n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
1006 0 : n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
1007 0 : n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
1008 :
1009 0 : n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
1010 :
1011 : // There are other XPCJSRuntime members that could be measured; the above
1012 : // ones have been seen by DMD to be worth measuring. More stuff may be
1013 : // added later.
1014 :
1015 0 : return n;
1016 : }
1017 :
1018 : size_t
1019 0 : CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
1020 : {
1021 0 : size_t n = mallocSizeOf(this);
1022 0 : n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
1023 0 : n += mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
1024 0 : return n;
1025 : }
1026 :
1027 : /***************************************************************************/
1028 :
1029 : void
1030 0 : XPCJSRuntime::SystemIsBeingShutDown()
1031 : {
1032 : // We don't want to track wrapped JS roots after this point since we're
1033 : // making them !IsValid anyway through SystemIsBeingShutDown.
1034 0 : mWrappedJSRoots = nullptr;
1035 0 : }
1036 :
1037 : void
1038 0 : XPCJSRuntime::Shutdown(JSContext* cx)
1039 : {
1040 : // This destructor runs before ~CycleCollectedJSContext, which does the
1041 : // actual JS_DestroyContext() call. But destroying the context triggers
1042 : // one final GC, which can call back into the context with various
1043 : // callbacks if we aren't careful. Null out the relevant callbacks.
1044 0 : JS_RemoveFinalizeCallback(cx, FinalizeCallback);
1045 0 : JS_RemoveWeakPointerZonesCallback(cx, WeakPointerZonesCallback);
1046 0 : JS_RemoveWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback);
1047 0 : xpc_DelocalizeRuntime(JS_GetRuntime(cx));
1048 :
1049 0 : JS::SetGCSliceCallback(cx, mPrevGCSliceCallback);
1050 :
1051 : // clean up and destroy maps...
1052 0 : mWrappedJSMap->ShutdownMarker();
1053 0 : delete mWrappedJSMap;
1054 0 : mWrappedJSMap = nullptr;
1055 :
1056 0 : delete mWrappedJSClassMap;
1057 0 : mWrappedJSClassMap = nullptr;
1058 :
1059 0 : delete mIID2NativeInterfaceMap;
1060 0 : mIID2NativeInterfaceMap = nullptr;
1061 :
1062 0 : delete mClassInfo2NativeSetMap;
1063 0 : mClassInfo2NativeSetMap = nullptr;
1064 :
1065 0 : delete mNativeSetMap;
1066 0 : mNativeSetMap = nullptr;
1067 :
1068 0 : delete mDyingWrappedNativeProtoMap;
1069 0 : mDyingWrappedNativeProtoMap = nullptr;
1070 :
1071 0 : CycleCollectedJSRuntime::Shutdown(cx);
1072 0 : }
1073 :
1074 0 : XPCJSRuntime::~XPCJSRuntime()
1075 : {
1076 0 : MOZ_COUNT_DTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
1077 0 : }
1078 :
1079 : // If |*anonymizeID| is non-zero and this is a user realm, the name will
1080 : // be anonymized.
1081 : static void
1082 0 : GetRealmName(JS::Realm* realm, nsCString& name, int* anonymizeID,
1083 : bool replaceSlashes)
1084 : {
1085 0 : if (*anonymizeID && !js::IsSystemRealm(realm)) {
1086 0 : name.AppendPrintf("<anonymized-%d>", *anonymizeID);
1087 0 : *anonymizeID += 1;
1088 0 : } else if (JSPrincipals* principals = JS::GetRealmPrincipals(realm)) {
1089 0 : nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
1090 0 : if (NS_FAILED(rv)) {
1091 0 : name.AssignLiteral("(unknown)");
1092 : }
1093 :
1094 : // If the realm's location (name) differs from the principal's script
1095 : // location, append the realm's location to allow differentiation of
1096 : // multiple realms owned by the same principal (e.g. components owned
1097 : // by the system or null principal).
1098 0 : RealmPrivate* realmPrivate = RealmPrivate::Get(realm);
1099 0 : if (realmPrivate) {
1100 0 : const nsACString& location = realmPrivate->GetLocation();
1101 0 : if (!location.IsEmpty() && !location.Equals(name)) {
1102 0 : name.AppendLiteral(", ");
1103 0 : name.Append(location);
1104 : }
1105 : }
1106 :
1107 0 : if (*anonymizeID) {
1108 : // We might have a file:// URL that includes a path from the local
1109 : // filesystem, which should be omitted if we're anonymizing.
1110 : static const char* filePrefix = "file://";
1111 0 : int filePos = name.Find(filePrefix);
1112 0 : if (filePos >= 0) {
1113 0 : int pathPos = filePos + strlen(filePrefix);
1114 0 : int lastSlashPos = -1;
1115 0 : for (int i = pathPos; i < int(name.Length()); i++) {
1116 0 : if (name[i] == '/' || name[i] == '\\') {
1117 0 : lastSlashPos = i;
1118 : }
1119 : }
1120 0 : if (lastSlashPos != -1) {
1121 0 : name.ReplaceASCII(pathPos, lastSlashPos - pathPos,
1122 0 : "<anonymized>");
1123 : } else {
1124 : // Something went wrong. Anonymize the entire path to be
1125 : // safe.
1126 0 : name.Truncate(pathPos);
1127 0 : name += "<anonymized?!>";
1128 : }
1129 : }
1130 :
1131 : // We might have a location like this:
1132 : // inProcessTabChildGlobal?ownedBy=http://www.example.com/
1133 : // The owner should be omitted if it's not a chrome: URI and we're
1134 : // anonymizing.
1135 : static const char* ownedByPrefix =
1136 : "inProcessTabChildGlobal?ownedBy=";
1137 0 : int ownedByPos = name.Find(ownedByPrefix);
1138 0 : if (ownedByPos >= 0) {
1139 0 : const char* chrome = "chrome:";
1140 0 : int ownerPos = ownedByPos + strlen(ownedByPrefix);
1141 : const nsDependentCSubstring& ownerFirstPart =
1142 0 : Substring(name, ownerPos, strlen(chrome));
1143 0 : if (!ownerFirstPart.EqualsASCII(chrome)) {
1144 0 : name.Truncate(ownerPos);
1145 0 : name += "<anonymized>";
1146 : }
1147 : }
1148 : }
1149 :
1150 : // A hack: replace forward slashes with '\\' so they aren't
1151 : // treated as path separators. Users of the reporters
1152 : // (such as about:memory) have to undo this change.
1153 0 : if (replaceSlashes)
1154 0 : name.ReplaceChar('/', '\\');
1155 : } else {
1156 0 : name.AssignLiteral("null-principal");
1157 : }
1158 0 : }
1159 :
1160 : extern void
1161 0 : xpc::GetCurrentRealmName(JSContext* cx, nsCString& name)
1162 : {
1163 0 : RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
1164 0 : if (!global) {
1165 0 : name.AssignLiteral("no global");
1166 0 : return;
1167 : }
1168 :
1169 0 : JS::Realm* realm = GetNonCCWObjectRealm(global);
1170 0 : int anonymizeID = 0;
1171 0 : GetRealmName(realm, name, &anonymizeID, false);
1172 : }
1173 :
1174 : void
1175 0 : xpc::AddGCCallback(xpcGCCallback cb)
1176 : {
1177 0 : XPCJSRuntime::Get()->AddGCCallback(cb);
1178 0 : }
1179 :
1180 : void
1181 0 : xpc::RemoveGCCallback(xpcGCCallback cb)
1182 : {
1183 0 : XPCJSRuntime::Get()->RemoveGCCallback(cb);
1184 0 : }
1185 :
1186 : static int64_t
1187 0 : JSMainRuntimeGCHeapDistinguishedAmount()
1188 : {
1189 0 : JSContext* cx = danger::GetJSContext();
1190 0 : return int64_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) *
1191 0 : js::gc::ChunkSize;
1192 : }
1193 :
1194 : static int64_t
1195 0 : JSMainRuntimeTemporaryPeakDistinguishedAmount()
1196 : {
1197 0 : JSContext* cx = danger::GetJSContext();
1198 0 : return JS::PeakSizeOfTemporary(cx);
1199 : }
1200 :
1201 : static int64_t
1202 0 : JSMainRuntimeRealmsSystemDistinguishedAmount()
1203 : {
1204 0 : JSContext* cx = danger::GetJSContext();
1205 0 : return JS::SystemRealmCount(cx);
1206 : }
1207 :
1208 : static int64_t
1209 0 : JSMainRuntimeRealmsUserDistinguishedAmount()
1210 : {
1211 0 : JSContext* cx = XPCJSContext::Get()->Context();
1212 0 : return JS::UserRealmCount(cx);
1213 : }
1214 :
1215 3 : class JSMainRuntimeTemporaryPeakReporter final : public nsIMemoryReporter
1216 : {
1217 0 : ~JSMainRuntimeTemporaryPeakReporter() {}
1218 :
1219 : public:
1220 : NS_DECL_ISUPPORTS
1221 :
1222 0 : NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
1223 : nsISupports* aData, bool aAnonymize) override
1224 : {
1225 0 : MOZ_COLLECT_REPORT(
1226 : "js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES,
1227 : JSMainRuntimeTemporaryPeakDistinguishedAmount(),
1228 : "Peak transient data size in the main JSRuntime (the current size "
1229 : "of which is reported as "
1230 0 : "'explicit/js-non-window/runtime/temporary').");
1231 :
1232 0 : return NS_OK;
1233 : }
1234 : };
1235 :
1236 46 : NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
1237 :
1238 : // The REPORT* macros do an unconditional report. The ZRREPORT* macros are for
1239 : // realms and zones; they aggregate any entries smaller than
1240 : // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
1241 : // entries for the realm.
1242 :
1243 : #define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
1244 :
1245 : #define REPORT(_path, _kind, _units, _amount, _desc) \
1246 : handleReport->Callback(EmptyCString(), _path, \
1247 : nsIMemoryReporter::_kind, \
1248 : nsIMemoryReporter::_units, _amount, \
1249 : NS_LITERAL_CSTRING(_desc), data); \
1250 :
1251 : #define REPORT_BYTES(_path, _kind, _amount, _desc) \
1252 : REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
1253 :
1254 : #define REPORT_GC_BYTES(_path, _amount, _desc) \
1255 : do { \
1256 : size_t amount = _amount; /* evaluate _amount only once */ \
1257 : handleReport->Callback(EmptyCString(), _path, \
1258 : nsIMemoryReporter::KIND_NONHEAP, \
1259 : nsIMemoryReporter::UNITS_BYTES, amount, \
1260 : NS_LITERAL_CSTRING(_desc), data); \
1261 : gcTotal += amount; \
1262 : } while (0)
1263 :
1264 : // Report realm/zone non-GC (KIND_HEAP) bytes.
1265 : #define ZRREPORT_BYTES(_path, _amount, _desc) \
1266 : do { \
1267 : /* Assign _descLiteral plus "" into a char* to prove that it's */ \
1268 : /* actually a literal. */ \
1269 : size_t amount = _amount; /* evaluate _amount only once */ \
1270 : if (amount >= SUNDRIES_THRESHOLD) { \
1271 : handleReport->Callback(EmptyCString(), _path, \
1272 : nsIMemoryReporter::KIND_HEAP, \
1273 : nsIMemoryReporter::UNITS_BYTES, amount, \
1274 : NS_LITERAL_CSTRING(_desc), data); \
1275 : } else { \
1276 : sundriesMallocHeap += amount; \
1277 : } \
1278 : } while (0)
1279 :
1280 : // Report realm/zone GC bytes.
1281 : #define ZRREPORT_GC_BYTES(_path, _amount, _desc) \
1282 : do { \
1283 : size_t amount = _amount; /* evaluate _amount only once */ \
1284 : if (amount >= SUNDRIES_THRESHOLD) { \
1285 : handleReport->Callback(EmptyCString(), _path, \
1286 : nsIMemoryReporter::KIND_NONHEAP, \
1287 : nsIMemoryReporter::UNITS_BYTES, amount, \
1288 : NS_LITERAL_CSTRING(_desc), data); \
1289 : gcTotal += amount; \
1290 : } else { \
1291 : sundriesGCHeap += amount; \
1292 : } \
1293 : } while (0)
1294 :
1295 : // Report runtime bytes.
1296 : #define RREPORT_BYTES(_path, _kind, _amount, _desc) \
1297 : do { \
1298 : size_t amount = _amount; /* evaluate _amount only once */ \
1299 : handleReport->Callback(EmptyCString(), _path, \
1300 : nsIMemoryReporter::_kind, \
1301 : nsIMemoryReporter::UNITS_BYTES, amount, \
1302 : NS_LITERAL_CSTRING(_desc), data); \
1303 : rtTotal += amount; \
1304 : } while (0)
1305 :
1306 : // Report GC thing bytes.
1307 : #define MREPORT_BYTES(_path, _kind, _amount, _desc) \
1308 : do { \
1309 : size_t amount = _amount; /* evaluate _amount only once */ \
1310 : handleReport->Callback(EmptyCString(), _path, \
1311 : nsIMemoryReporter::_kind, \
1312 : nsIMemoryReporter::UNITS_BYTES, amount, \
1313 : NS_LITERAL_CSTRING(_desc), data); \
1314 : gcThingTotal += amount; \
1315 : } while (0)
1316 :
1317 0 : MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
1318 :
1319 : namespace xpc {
1320 :
1321 : static void
1322 0 : ReportZoneStats(const JS::ZoneStats& zStats,
1323 : const xpc::ZoneStatsExtras& extras,
1324 : nsIHandleReportCallback* handleReport,
1325 : nsISupports* data,
1326 : bool anonymize,
1327 : size_t* gcTotalOut = nullptr)
1328 : {
1329 0 : const nsCString& pathPrefix = extras.pathPrefix;
1330 0 : size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
1331 :
1332 0 : MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
1333 :
1334 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("symbols/gc-heap"),
1335 : zStats.symbolsGCHeap,
1336 : "Symbols.");
1337 :
1338 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
1339 : zStats.gcHeapArenaAdmin,
1340 : "Bookkeeping information and alignment padding within GC arenas.");
1341 :
1342 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
1343 : zStats.unusedGCThings.totalSize(),
1344 : "Unused GC thing cells within non-empty arenas.");
1345 :
1346 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("unique-id-map"),
1347 : zStats.uniqueIdMap,
1348 : "Address-independent cell identities.");
1349 :
1350 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shape-tables"),
1351 : zStats.shapeTables,
1352 : "Tables storing shape information.");
1353 :
1354 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/compartment-objects"),
1355 : zStats.compartmentObjects,
1356 : "The JS::Compartment objects in this zone.");
1357 :
1358 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/cross-compartment-wrapper-tables"),
1359 : zStats.crossCompartmentWrappersTables,
1360 : "The cross-compartment wrapper tables.");
1361 :
1362 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/private-data"),
1363 : zStats.compartmentsPrivateData,
1364 : "Extra data attached to each compartment by XPConnect, including "
1365 : "its wrapped-js.");
1366 :
1367 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
1368 : zStats.lazyScriptsGCHeap,
1369 : "Scripts that haven't executed yet.");
1370 :
1371 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
1372 : zStats.lazyScriptsMallocHeap,
1373 : "Lazy script tables containing closed-over bindings or inner functions.");
1374 :
1375 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
1376 : zStats.jitCodesGCHeap,
1377 : "References to executable code pools used by the JITs.");
1378 :
1379 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/gc-heap"),
1380 : zStats.objectGroupsGCHeap,
1381 : "Classification and type inference information about objects.");
1382 :
1383 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/malloc-heap"),
1384 : zStats.objectGroupsMallocHeap,
1385 : "Object group addenda.");
1386 :
1387 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/gc-heap"),
1388 : zStats.scopesGCHeap,
1389 : "Scope information for scripts.");
1390 :
1391 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/malloc-heap"),
1392 : zStats.scopesMallocHeap,
1393 : "Arrays of binding names and other binding-related data.");
1394 :
1395 0 : ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/gc-heap"),
1396 : zStats.regExpSharedsGCHeap,
1397 : "Shared compiled regexp data.");
1398 :
1399 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/malloc-heap"),
1400 : zStats.regExpSharedsMallocHeap,
1401 : "Shared compiled regexp data.");
1402 :
1403 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
1404 : zStats.typePool,
1405 : "Type sets and related data.");
1406 :
1407 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-zone"),
1408 : zStats.regexpZone,
1409 : "The regexp zone and regexp data.");
1410 :
1411 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-zone"),
1412 : zStats.jitZone,
1413 : "The JIT zone.");
1414 :
1415 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
1416 : zStats.baselineStubsOptimized,
1417 : "The Baseline JIT's optimized IC stubs (excluding code).");
1418 :
1419 0 : ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-cached-cfg"),
1420 : zStats.cachedCFG,
1421 : "The cached CFG to construct Ion code out of it.");
1422 :
1423 0 : size_t stringsNotableAboutMemoryGCHeap = 0;
1424 0 : size_t stringsNotableAboutMemoryMallocHeap = 0;
1425 :
1426 : #define MAYBE_INLINE \
1427 : "The characters may be inline or on the malloc heap."
1428 : #define MAYBE_OVERALLOCATED \
1429 : "Sometimes over-allocated to simplify string concatenation."
1430 :
1431 0 : for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
1432 0 : const JS::NotableStringInfo& info = zStats.notableStrings[i];
1433 :
1434 0 : MOZ_ASSERT(!zStats.isTotals);
1435 :
1436 : // We don't do notable string detection when anonymizing, because
1437 : // there's a good chance its for crash submission, and the memory
1438 : // required for notable string detection is high.
1439 0 : MOZ_ASSERT(!anonymize);
1440 :
1441 0 : nsDependentCString notableString(info.buffer);
1442 :
1443 : // Viewing about:memory generates many notable strings which contain
1444 : // "string(length=". If we report these as notable, then we'll create
1445 : // even more notable strings the next time we open about:memory (unless
1446 : // there's a GC in the meantime), and so on ad infinitum.
1447 : //
1448 : // To avoid cluttering up about:memory like this, we stick notable
1449 : // strings which contain "string(length=" into their own bucket.
1450 : # define STRING_LENGTH "string(length="
1451 0 : if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
1452 0 : stringsNotableAboutMemoryGCHeap += info.gcHeapLatin1;
1453 0 : stringsNotableAboutMemoryGCHeap += info.gcHeapTwoByte;
1454 0 : stringsNotableAboutMemoryMallocHeap += info.mallocHeapLatin1;
1455 0 : stringsNotableAboutMemoryMallocHeap += info.mallocHeapTwoByte;
1456 0 : continue;
1457 : }
1458 :
1459 : // Escape / to \ before we put notableString into the memory reporter
1460 : // path, because we don't want any forward slashes in the string to
1461 : // count as path separators.
1462 0 : nsCString escapedString(notableString);
1463 0 : escapedString.ReplaceSubstring("/", "\\");
1464 :
1465 0 : bool truncated = notableString.Length() < info.length;
1466 :
1467 0 : nsCString path = pathPrefix +
1468 0 : nsPrintfCString("strings/" STRING_LENGTH "%zu, copies=%d, \"%s\"%s)/",
1469 0 : info.length, info.numCopies, escapedString.get(),
1470 0 : truncated ? " (truncated)" : "");
1471 :
1472 0 : if (info.gcHeapLatin1 > 0) {
1473 0 : REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/latin1"),
1474 : info.gcHeapLatin1,
1475 : "Latin1 strings. " MAYBE_INLINE);
1476 : }
1477 :
1478 0 : if (info.gcHeapTwoByte > 0) {
1479 0 : REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/two-byte"),
1480 : info.gcHeapTwoByte,
1481 : "TwoByte strings. " MAYBE_INLINE);
1482 : }
1483 :
1484 0 : if (info.mallocHeapLatin1 > 0) {
1485 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/latin1"),
1486 : KIND_HEAP, info.mallocHeapLatin1,
1487 : "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
1488 : }
1489 :
1490 0 : if (info.mallocHeapTwoByte > 0) {
1491 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
1492 : KIND_HEAP, info.mallocHeapTwoByte,
1493 : "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
1494 : }
1495 : }
1496 :
1497 0 : nsCString nonNotablePath = pathPrefix;
1498 0 : nonNotablePath += (zStats.isTotals || anonymize)
1499 0 : ? NS_LITERAL_CSTRING("strings/")
1500 0 : : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
1501 :
1502 0 : if (zStats.stringInfo.gcHeapLatin1 > 0) {
1503 0 : REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/latin1"),
1504 : zStats.stringInfo.gcHeapLatin1,
1505 : "Latin1 strings. " MAYBE_INLINE);
1506 : }
1507 :
1508 0 : if (zStats.stringInfo.gcHeapTwoByte > 0) {
1509 0 : REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/two-byte"),
1510 : zStats.stringInfo.gcHeapTwoByte,
1511 : "TwoByte strings. " MAYBE_INLINE);
1512 : }
1513 :
1514 0 : if (zStats.stringInfo.mallocHeapLatin1 > 0) {
1515 0 : REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/latin1"),
1516 : KIND_HEAP, zStats.stringInfo.mallocHeapLatin1,
1517 : "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
1518 : }
1519 :
1520 0 : if (zStats.stringInfo.mallocHeapTwoByte > 0) {
1521 0 : REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
1522 : KIND_HEAP, zStats.stringInfo.mallocHeapTwoByte,
1523 : "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
1524 : }
1525 :
1526 0 : if (stringsNotableAboutMemoryGCHeap > 0) {
1527 0 : MOZ_ASSERT(!zStats.isTotals);
1528 0 : REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
1529 : stringsNotableAboutMemoryGCHeap,
1530 : "Strings that contain the characters '" STRING_LENGTH "', which "
1531 : "are probably from about:memory itself." MAYBE_INLINE
1532 : " We filter them out rather than display them, because displaying "
1533 : "them would create even more such strings every time about:memory "
1534 : "is refreshed.");
1535 : }
1536 :
1537 0 : if (stringsNotableAboutMemoryMallocHeap > 0) {
1538 0 : MOZ_ASSERT(!zStats.isTotals);
1539 0 : REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
1540 : KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
1541 : "Non-inline string characters of strings that contain the "
1542 : "characters '" STRING_LENGTH "', which are probably from "
1543 : "about:memory itself. " MAYBE_OVERALLOCATED
1544 : " We filter them out rather than display them, because displaying "
1545 : "them would create even more such strings every time about:memory "
1546 : "is refreshed.");
1547 : }
1548 :
1549 0 : const JS::ShapeInfo& shapeInfo = zStats.shapeInfo;
1550 0 : if (shapeInfo.shapesGCHeapTree > 0) {
1551 0 : REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
1552 : shapeInfo.shapesGCHeapTree,
1553 : "Shapes in a property tree.");
1554 : }
1555 :
1556 0 : if (shapeInfo.shapesGCHeapDict > 0) {
1557 0 : REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
1558 : shapeInfo.shapesGCHeapDict,
1559 : "Shapes in dictionary mode.");
1560 : }
1561 :
1562 0 : if (shapeInfo.shapesGCHeapBase > 0) {
1563 0 : REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
1564 : shapeInfo.shapesGCHeapBase,
1565 : "Base shapes, which collate data common to many shapes.");
1566 : }
1567 :
1568 0 : if (shapeInfo.shapesMallocHeapTreeTables > 0) {
1569 0 : REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
1570 : KIND_HEAP, shapeInfo.shapesMallocHeapTreeTables,
1571 : "Property tables of shapes in a property tree.");
1572 : }
1573 :
1574 0 : if (shapeInfo.shapesMallocHeapDictTables > 0) {
1575 0 : REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
1576 : KIND_HEAP, shapeInfo.shapesMallocHeapDictTables,
1577 : "Property tables of shapes in dictionary mode.");
1578 : }
1579 :
1580 0 : if (shapeInfo.shapesMallocHeapTreeKids > 0) {
1581 0 : REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
1582 : KIND_HEAP, shapeInfo.shapesMallocHeapTreeKids,
1583 : "Kid hashes of shapes in a property tree.");
1584 : }
1585 :
1586 0 : if (sundriesGCHeap > 0) {
1587 : // We deliberately don't use ZRREPORT_GC_BYTES here.
1588 0 : REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
1589 : sundriesGCHeap,
1590 : "The sum of all 'gc-heap' measurements that are too small to be "
1591 : "worth showing individually.");
1592 : }
1593 :
1594 0 : if (sundriesMallocHeap > 0) {
1595 : // We deliberately don't use ZRREPORT_BYTES here.
1596 0 : REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
1597 : KIND_HEAP, sundriesMallocHeap,
1598 : "The sum of all 'malloc-heap' measurements that are too small to "
1599 : "be worth showing individually.");
1600 : }
1601 :
1602 0 : if (gcTotalOut)
1603 0 : *gcTotalOut += gcTotal;
1604 :
1605 : # undef STRING_LENGTH
1606 0 : }
1607 :
1608 : static void
1609 0 : ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
1610 : nsIHandleReportCallback* handleReport,
1611 : nsISupports* data, size_t& gcTotal)
1612 : {
1613 : // We deliberately don't use ZRREPORT_BYTES, so that these per-class values
1614 : // don't go into sundries.
1615 :
1616 0 : if (classInfo.objectsGCHeap > 0) {
1617 0 : REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("objects/gc-heap"),
1618 : classInfo.objectsGCHeap,
1619 : "Objects, including fixed slots.");
1620 : }
1621 :
1622 0 : if (classInfo.objectsMallocHeapSlots > 0) {
1623 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
1624 : KIND_HEAP, classInfo.objectsMallocHeapSlots,
1625 : "Non-fixed object slots.");
1626 : }
1627 :
1628 0 : if (classInfo.objectsMallocHeapElementsNormal > 0) {
1629 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/normal"),
1630 : KIND_HEAP, classInfo.objectsMallocHeapElementsNormal,
1631 : "Normal (non-wasm) indexed elements.");
1632 : }
1633 :
1634 0 : if (classInfo.objectsMallocHeapElementsAsmJS > 0) {
1635 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
1636 : KIND_HEAP, classInfo.objectsMallocHeapElementsAsmJS,
1637 : "asm.js array buffer elements allocated in the malloc heap.");
1638 : }
1639 :
1640 0 : if (classInfo.objectsMallocHeapMisc > 0) {
1641 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/misc"),
1642 : KIND_HEAP, classInfo.objectsMallocHeapMisc,
1643 : "Miscellaneous object data.");
1644 : }
1645 :
1646 0 : if (classInfo.objectsNonHeapElementsNormal > 0) {
1647 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/normal"),
1648 : KIND_NONHEAP, classInfo.objectsNonHeapElementsNormal,
1649 : "Memory-mapped non-shared array buffer elements.");
1650 : }
1651 :
1652 0 : if (classInfo.objectsNonHeapElementsShared > 0) {
1653 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/shared"),
1654 : KIND_NONHEAP, classInfo.objectsNonHeapElementsShared,
1655 : "Memory-mapped shared array buffer elements. These elements are "
1656 : "shared between one or more runtimes; the reported size is divided "
1657 : "by the buffer's refcount.");
1658 : }
1659 :
1660 : // WebAssembly memories are always non-heap-allocated (mmap). We never put
1661 : // these under sundries, because (a) in practice they're almost always
1662 : // larger than the sundries threshold, and (b) we'd need a third category of
1663 : // sundries ("non-heap"), which would be a pain.
1664 0 : if (classInfo.objectsNonHeapElementsWasm > 0) {
1665 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/wasm"),
1666 : KIND_NONHEAP, classInfo.objectsNonHeapElementsWasm,
1667 : "wasm/asm.js array buffer elements allocated outside both the "
1668 : "malloc heap and the GC heap.");
1669 : }
1670 :
1671 0 : if (classInfo.objectsNonHeapCodeWasm > 0) {
1672 0 : REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/code/wasm"),
1673 : KIND_NONHEAP, classInfo.objectsNonHeapCodeWasm,
1674 : "AOT-compiled wasm/asm.js code.");
1675 : }
1676 :
1677 : // Although wasm guard pages aren't committed in memory they can be very
1678 : // large and contribute greatly to vsize and so are worth reporting.
1679 0 : if (classInfo.wasmGuardPages > 0) {
1680 0 : REPORT_BYTES(NS_LITERAL_CSTRING("wasm-guard-pages"),
1681 : KIND_OTHER, classInfo.wasmGuardPages,
1682 : "Guard pages mapped after the end of wasm memories, reserved for "
1683 : "optimization tricks, but not committed and thus never contributing"
1684 : " to RSS, only vsize.");
1685 : }
1686 0 : }
1687 :
1688 : static void
1689 0 : ReportRealmStats(const JS::RealmStats& realmStats,
1690 : const xpc::RealmStatsExtras& extras,
1691 : nsIHandleReportCallback* handleReport,
1692 : nsISupports* data, size_t* gcTotalOut = nullptr)
1693 : {
1694 0 : static const nsDependentCString addonPrefix("explicit/add-ons/");
1695 :
1696 0 : size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
1697 0 : nsAutoCString realmJSPathPrefix(extras.jsPathPrefix);
1698 0 : nsAutoCString realmDOMPathPrefix(extras.domPathPrefix);
1699 :
1700 0 : MOZ_ASSERT(!gcTotalOut == realmStats.isTotals);
1701 :
1702 0 : nsCString nonNotablePath = realmJSPathPrefix;
1703 0 : nonNotablePath += realmStats.isTotals
1704 0 : ? NS_LITERAL_CSTRING("classes/")
1705 0 : : NS_LITERAL_CSTRING("classes/class(<non-notable classes>)/");
1706 :
1707 0 : ReportClassStats(realmStats.classInfo, nonNotablePath, handleReport, data,
1708 0 : gcTotal);
1709 :
1710 0 : for (size_t i = 0; i < realmStats.notableClasses.length(); i++) {
1711 0 : MOZ_ASSERT(!realmStats.isTotals);
1712 0 : const JS::NotableClassInfo& classInfo = realmStats.notableClasses[i];
1713 :
1714 0 : nsCString classPath = realmJSPathPrefix +
1715 0 : nsPrintfCString("classes/class(%s)/", classInfo.className_);
1716 :
1717 0 : ReportClassStats(classInfo, classPath, handleReport, data, gcTotal);
1718 : }
1719 :
1720 : // Note that we use realmDOMPathPrefix here. This is because we measure orphan
1721 : // DOM nodes in the JS reporter, but we want to report them in a "dom"
1722 : // sub-tree rather than a "js" sub-tree.
1723 0 : ZRREPORT_BYTES(realmDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
1724 : realmStats.objectsPrivate,
1725 : "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
1726 : "objects.");
1727 :
1728 0 : ZRREPORT_GC_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
1729 : realmStats.scriptsGCHeap,
1730 : "JSScript instances. There is one per user-defined function in a "
1731 : "script, and one for the top-level code in a script.");
1732 :
1733 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
1734 : realmStats.scriptsMallocHeapData,
1735 : "Various variable-length tables in JSScripts.");
1736 :
1737 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
1738 : realmStats.baselineData,
1739 : "The Baseline JIT's compilation data (BaselineScripts).");
1740 :
1741 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
1742 : realmStats.baselineStubsFallback,
1743 : "The Baseline JIT's fallback IC stubs (excluding code).");
1744 :
1745 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
1746 : realmStats.ionData,
1747 : "The IonMonkey JIT's compilation data (IonScripts).");
1748 :
1749 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
1750 : realmStats.typeInferenceTypeScripts,
1751 : "Type sets associated with scripts.");
1752 :
1753 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
1754 : realmStats.typeInferenceAllocationSiteTables,
1755 : "Tables of type objects associated with allocation sites.");
1756 :
1757 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
1758 : realmStats.typeInferenceArrayTypeTables,
1759 : "Tables of type objects associated with array literals.");
1760 :
1761 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
1762 : realmStats.typeInferenceObjectTypeTables,
1763 : "Tables of type objects associated with object literals.");
1764 :
1765 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-object"),
1766 : realmStats.realmObject,
1767 : "The JS::Realm object itself.");
1768 :
1769 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-tables"),
1770 : realmStats.realmTables,
1771 : "Realm-wide tables storing object group information and wasm instances.");
1772 :
1773 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("inner-views"),
1774 : realmStats.innerViewsTable,
1775 : "The table for array buffer inner views.");
1776 :
1777 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"),
1778 : realmStats.lazyArrayBuffersTable,
1779 : "The table for typed object lazy array buffers.");
1780 :
1781 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("object-metadata"),
1782 : realmStats.objectMetadataTable,
1783 : "The table used by debugging tools for tracking object metadata");
1784 :
1785 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("saved-stacks-set"),
1786 : realmStats.savedStacksSet,
1787 : "The saved stacks set.");
1788 :
1789 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("non-syntactic-lexical-scopes-table"),
1790 : realmStats.nonSyntacticLexicalScopesTable,
1791 : "The non-syntactic lexical scopes table.");
1792 :
1793 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("jit-realm"),
1794 : realmStats.jitRealm,
1795 : "The JIT realm.");
1796 :
1797 0 : ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("script-counts-map"),
1798 : realmStats.scriptCountsMap,
1799 : "Profiling-related information for scripts.");
1800 :
1801 0 : if (sundriesGCHeap > 0) {
1802 : // We deliberately don't use ZRREPORT_GC_BYTES here.
1803 0 : REPORT_GC_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
1804 : sundriesGCHeap,
1805 : "The sum of all 'gc-heap' measurements that are too small to be "
1806 : "worth showing individually.");
1807 : }
1808 :
1809 0 : if (sundriesMallocHeap > 0) {
1810 : // We deliberately don't use ZRREPORT_BYTES here.
1811 0 : REPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
1812 : KIND_HEAP, sundriesMallocHeap,
1813 : "The sum of all 'malloc-heap' measurements that are too small to "
1814 : "be worth showing individually.");
1815 : }
1816 :
1817 0 : if (gcTotalOut)
1818 0 : *gcTotalOut += gcTotal;
1819 0 : }
1820 :
1821 : static void
1822 0 : ReportScriptSourceStats(const ScriptSourceInfo& scriptSourceInfo,
1823 : const nsACString& path,
1824 : nsIHandleReportCallback* handleReport,
1825 : nsISupports* data, size_t& rtTotal)
1826 : {
1827 0 : if (scriptSourceInfo.misc > 0) {
1828 0 : RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
1829 : KIND_HEAP, scriptSourceInfo.misc,
1830 : "Miscellaneous data relating to JavaScript source code.");
1831 : }
1832 0 : }
1833 :
1834 : void
1835 0 : ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
1836 : const nsACString& rtPath,
1837 : nsIHandleReportCallback* handleReport,
1838 : nsISupports* data,
1839 : bool anonymize,
1840 : size_t* rtTotalOut)
1841 : {
1842 0 : size_t gcTotal = 0;
1843 :
1844 0 : for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
1845 0 : const JS::ZoneStats& zStats = rtStats.zoneStatsVector[i];
1846 : const xpc::ZoneStatsExtras* extras =
1847 0 : static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
1848 0 : ReportZoneStats(zStats, *extras, handleReport, data, anonymize,
1849 0 : &gcTotal);
1850 : }
1851 :
1852 0 : for (size_t i = 0; i < rtStats.realmStatsVector.length(); i++) {
1853 0 : const JS::RealmStats& realmStats = rtStats.realmStatsVector[i];
1854 : const xpc::RealmStatsExtras* extras =
1855 0 : static_cast<const xpc::RealmStatsExtras*>(realmStats.extra);
1856 :
1857 0 : ReportRealmStats(realmStats, *extras, handleReport, data, &gcTotal);
1858 : }
1859 :
1860 : // Report the rtStats.runtime numbers under "runtime/", and compute their
1861 : // total for later.
1862 :
1863 0 : size_t rtTotal = 0;
1864 :
1865 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
1866 : KIND_HEAP, rtStats.runtime.object,
1867 : "The JSRuntime object.");
1868 :
1869 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
1870 : KIND_HEAP, rtStats.runtime.atomsTable,
1871 : "The atoms table.");
1872 :
1873 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-mark-bitmaps"),
1874 : KIND_HEAP, rtStats.runtime.atomsMarkBitmaps,
1875 : "Mark bitmaps for atoms held by each zone.");
1876 :
1877 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
1878 : KIND_HEAP, rtStats.runtime.contexts,
1879 : "JSContext objects and structures that belong to them.");
1880 :
1881 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
1882 : KIND_HEAP, rtStats.runtime.temporary,
1883 : "Transient data (mostly parse nodes) held by the JSRuntime during "
1884 : "compilation.");
1885 :
1886 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
1887 : KIND_HEAP, rtStats.runtime.interpreterStack,
1888 : "JS interpreter frames.");
1889 :
1890 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
1891 : KIND_HEAP, rtStats.runtime.mathCache,
1892 : "The math cache.");
1893 :
1894 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-immutable-strings-cache"),
1895 : KIND_HEAP, rtStats.runtime.sharedImmutableStringsCache,
1896 : "Immutable strings (such as JS scripts' source text) shared across all JSRuntimes.");
1897 :
1898 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-intl-data"),
1899 : KIND_HEAP, rtStats.runtime.sharedIntlData,
1900 : "Shared internationalization data.");
1901 :
1902 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/uncompressed-source-cache"),
1903 : KIND_HEAP, rtStats.runtime.uncompressedSourceCache,
1904 : "The uncompressed source code cache.");
1905 :
1906 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
1907 : KIND_HEAP, rtStats.runtime.scriptData,
1908 : "The table holding script data shared in the runtime.");
1909 :
1910 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/tracelogger"),
1911 : KIND_HEAP, rtStats.runtime.tracelogger,
1912 : "The memory used for the tracelogger (per-runtime).");
1913 :
1914 : nsCString nonNotablePath =
1915 0 : rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
1916 0 : rtStats.runtime.scriptSourceInfo.numScripts);
1917 :
1918 0 : ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
1919 0 : nonNotablePath, handleReport, data, rtTotal);
1920 :
1921 0 : for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
1922 : const JS::NotableScriptSourceInfo& scriptSourceInfo =
1923 0 : rtStats.runtime.notableScriptSources[i];
1924 :
1925 : // Escape / to \ before we put the filename into the memory reporter
1926 : // path, because we don't want any forward slashes in the string to
1927 : // count as path separators. Consumers of memory reporters (e.g.
1928 : // about:memory) will convert them back to / after doing path
1929 : // splitting.
1930 0 : nsCString escapedFilename;
1931 0 : if (anonymize) {
1932 0 : escapedFilename.AppendPrintf("<anonymized-source-%d>", int(i));
1933 : } else {
1934 0 : nsDependentCString filename(scriptSourceInfo.filename_);
1935 0 : escapedFilename.Append(filename);
1936 0 : escapedFilename.ReplaceSubstring("/", "\\");
1937 : }
1938 :
1939 0 : nsCString notablePath = rtPath +
1940 0 : nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
1941 0 : scriptSourceInfo.numScripts, escapedFilename.get());
1942 :
1943 0 : ReportScriptSourceStats(scriptSourceInfo, notablePath,
1944 0 : handleReport, data, rtTotal);
1945 : }
1946 :
1947 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
1948 : KIND_NONHEAP, rtStats.runtime.code.ion,
1949 : "Code generated by the IonMonkey JIT.");
1950 :
1951 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
1952 : KIND_NONHEAP, rtStats.runtime.code.baseline,
1953 : "Code generated by the Baseline JIT.");
1954 :
1955 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
1956 : KIND_NONHEAP, rtStats.runtime.code.regexp,
1957 : "Code generated by the regexp JIT.");
1958 :
1959 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
1960 : KIND_NONHEAP, rtStats.runtime.code.other,
1961 : "Code generated by the JITs for wrappers and trampolines.");
1962 :
1963 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
1964 : KIND_NONHEAP, rtStats.runtime.code.unused,
1965 : "Memory allocated by one of the JITs to hold code, but which is "
1966 : "currently unused.");
1967 :
1968 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
1969 : KIND_HEAP, rtStats.runtime.gc.marker,
1970 : "The GC mark stack and gray roots.");
1971 :
1972 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
1973 : KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
1974 : "Memory being used by the GC's nursery.");
1975 :
1976 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-malloced-buffers"),
1977 : KIND_HEAP, rtStats.runtime.gc.nurseryMallocedBuffers,
1978 : "Out-of-line slots and elements belonging to objects in the nursery.");
1979 :
1980 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
1981 : KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
1982 : "Values in the store buffer.");
1983 :
1984 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
1985 : KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
1986 : "Cells in the store buffer.");
1987 :
1988 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
1989 : KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
1990 : "Slots in the store buffer.");
1991 :
1992 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
1993 : KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
1994 : "Whole cells in the store buffer.");
1995 :
1996 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
1997 : KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
1998 : "Generic things in the store buffer.");
1999 :
2000 0 : RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/jit-lazylink"),
2001 : KIND_HEAP, rtStats.runtime.jitLazyLink,
2002 : "IonMonkey compilations waiting for lazy linking.");
2003 :
2004 0 : if (rtTotalOut)
2005 0 : *rtTotalOut = rtTotal;
2006 :
2007 : // Report GC numbers that don't belong to a realm.
2008 :
2009 : // We don't want to report decommitted memory in "explicit", so we just
2010 : // change the leading "explicit/" to "decommitted/".
2011 0 : nsCString rtPath2(rtPath);
2012 0 : rtPath2.ReplaceLiteral(0, strlen("explicit"), "decommitted");
2013 :
2014 0 : REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
2015 : rtStats.gcHeapDecommittedArenas,
2016 : "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
2017 : "address space but no physical memory or swap space.");
2018 :
2019 0 : REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
2020 : rtStats.gcHeapUnusedChunks,
2021 : "Empty GC chunks which will soon be released unless claimed for new "
2022 : "allocations.");
2023 :
2024 0 : REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
2025 : rtStats.gcHeapUnusedArenas,
2026 : "Empty GC arenas within non-empty chunks.");
2027 :
2028 0 : REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
2029 : rtStats.gcHeapChunkAdmin,
2030 : "Bookkeeping information within GC chunks.");
2031 :
2032 : // gcTotal is the sum of everything we've reported for the GC heap. It
2033 : // should equal rtStats.gcHeapChunkTotal.
2034 0 : MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
2035 0 : }
2036 :
2037 :
2038 : } // namespace xpc
2039 :
2040 0 : class JSMainRuntimeRealmsReporter final : public nsIMemoryReporter
2041 : {
2042 :
2043 0 : ~JSMainRuntimeRealmsReporter() {}
2044 :
2045 : public:
2046 : NS_DECL_ISUPPORTS
2047 :
2048 0 : struct Data {
2049 : int anonymizeID;
2050 : js::Vector<nsCString, 0, js::SystemAllocPolicy> paths;
2051 : };
2052 :
2053 0 : static void RealmCallback(JSContext* cx, void* vdata, Handle<Realm*> realm) {
2054 : // silently ignore OOM errors
2055 0 : Data* data = static_cast<Data*>(vdata);
2056 0 : nsCString path;
2057 0 : GetRealmName(realm, path, &data->anonymizeID, /* replaceSlashes = */ true);
2058 0 : path.Insert(js::IsSystemRealm(realm)
2059 0 : ? NS_LITERAL_CSTRING("js-main-runtime-realms/system/")
2060 0 : : NS_LITERAL_CSTRING("js-main-runtime-realms/user/"),
2061 0 : 0);
2062 0 : mozilla::Unused << data->paths.append(path);
2063 0 : }
2064 :
2065 0 : NS_IMETHOD CollectReports(nsIHandleReportCallback* handleReport,
2066 : nsISupports* data, bool anonymize) override
2067 : {
2068 : // First we collect the realm paths. Then we report them. Doing
2069 : // the two steps interleaved is a bad idea, because calling
2070 : // |handleReport| from within RealmCallback() leads to all manner
2071 : // of assertions.
2072 :
2073 0 : Data d;
2074 0 : d.anonymizeID = anonymize ? 1 : 0;
2075 0 : JS::IterateRealms(XPCJSContext::Get()->Context(), &d, RealmCallback);
2076 :
2077 0 : for (size_t i = 0; i < d.paths.length(); i++)
2078 0 : REPORT(nsCString(d.paths[i]), KIND_OTHER, UNITS_COUNT, 1,
2079 : "A live realm in the main JSRuntime.");
2080 :
2081 0 : return NS_OK;
2082 : }
2083 : };
2084 :
2085 46 : NS_IMPL_ISUPPORTS(JSMainRuntimeRealmsReporter, nsIMemoryReporter)
2086 :
2087 0 : MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
2088 :
2089 : namespace xpc {
2090 :
2091 0 : class OrphanReporter : public JS::ObjectPrivateVisitor
2092 : {
2093 : public:
2094 0 : explicit OrphanReporter(GetISupportsFun aGetISupports)
2095 0 : : JS::ObjectPrivateVisitor(aGetISupports)
2096 0 : , mState(OrphanMallocSizeOf)
2097 0 : {}
2098 :
2099 0 : virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override
2100 : {
2101 0 : size_t n = 0;
2102 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
2103 : // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
2104 : // that we have to skip XBL elements because they violate certain
2105 : // assumptions. Yuk.
2106 0 : if (node && !node->IsInUncomposedDoc() &&
2107 0 : !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
2108 : {
2109 : // This is an orphan node. If we haven't already handled the
2110 : // sub-tree that this node belongs to, measure the sub-tree's size
2111 : // and then record its root so we don't measure it again.
2112 0 : nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
2113 0 : if (orphanTree && !mState.HaveSeenPtr(orphanTree.get())) {
2114 0 : n += SizeOfTreeIncludingThis(orphanTree);
2115 : }
2116 : }
2117 0 : return n;
2118 : }
2119 :
2120 0 : size_t SizeOfTreeIncludingThis(nsINode* tree)
2121 : {
2122 0 : size_t nodeSize = 0;
2123 0 : nsWindowSizes sizes(mState);
2124 0 : tree->AddSizeOfIncludingThis(sizes, &nodeSize);
2125 0 : for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
2126 0 : child->AddSizeOfIncludingThis(sizes, &nodeSize);
2127 :
2128 : // We combine the node size with nsStyleSizes here. It's not ideal, but
2129 : // it's hard to get the style structs measurements out to
2130 : // nsWindowMemoryReporter. Also, we drop mServoData in
2131 : // UnbindFromTree(), so in theory any non-in-tree element won't have
2132 : // any style data to measure.
2133 0 : return nodeSize + sizes.getTotalSize();
2134 : }
2135 :
2136 : private:
2137 : SizeOfState mState;
2138 : };
2139 :
2140 : #ifdef DEBUG
2141 : static bool
2142 0 : StartsWithExplicit(nsACString& s)
2143 : {
2144 0 : return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
2145 : }
2146 : #endif
2147 :
2148 : class XPCJSRuntimeStats : public JS::RuntimeStats
2149 : {
2150 : WindowPaths* mWindowPaths;
2151 : WindowPaths* mTopWindowPaths;
2152 : int mAnonymizeID;
2153 :
2154 : public:
2155 0 : XPCJSRuntimeStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
2156 : bool anonymize)
2157 0 : : JS::RuntimeStats(JSMallocSizeOf),
2158 : mWindowPaths(windowPaths),
2159 : mTopWindowPaths(topWindowPaths),
2160 0 : mAnonymizeID(anonymize ? 1 : 0)
2161 0 : {}
2162 :
2163 0 : ~XPCJSRuntimeStats() {
2164 0 : for (size_t i = 0; i != realmStatsVector.length(); ++i)
2165 0 : delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
2166 :
2167 0 : for (size_t i = 0; i != zoneStatsVector.length(); ++i)
2168 0 : delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
2169 0 : }
2170 :
2171 0 : virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override {
2172 0 : AutoSafeJSContext cx;
2173 0 : xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
2174 0 : extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2175 :
2176 : // Get some global in this zone.
2177 0 : Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone));
2178 0 : if (realm) {
2179 0 : RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
2180 0 : if (global) {
2181 0 : RefPtr<nsGlobalWindowInner> window;
2182 0 : if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
2183 : // The global is a |window| object. Use the path prefix that
2184 : // we should have already created for it.
2185 0 : if (mTopWindowPaths->Get(window->WindowID(),
2186 : &extras->pathPrefix))
2187 0 : extras->pathPrefix.AppendLiteral("/js-");
2188 : }
2189 : }
2190 : }
2191 :
2192 0 : extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
2193 :
2194 0 : MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
2195 :
2196 0 : zStats->extra = extras;
2197 0 : }
2198 :
2199 0 : virtual void initExtraRealmStats(Handle<Realm*> realm,
2200 : JS::RealmStats* realmStats) override
2201 : {
2202 0 : xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
2203 0 : nsCString rName;
2204 0 : GetRealmName(realm, rName, &mAnonymizeID, /* replaceSlashes = */ true);
2205 :
2206 : // Get the realm's global.
2207 0 : AutoSafeJSContext cx;
2208 0 : bool needZone = true;
2209 0 : RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
2210 0 : if (global) {
2211 0 : RefPtr<nsGlobalWindowInner> window;
2212 0 : if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
2213 : // The global is a |window| object. Use the path prefix that
2214 : // we should have already created for it.
2215 0 : if (mWindowPaths->Get(window->WindowID(),
2216 : &extras->jsPathPrefix)) {
2217 0 : extras->domPathPrefix.Assign(extras->jsPathPrefix);
2218 0 : extras->domPathPrefix.AppendLiteral("/dom/");
2219 0 : extras->jsPathPrefix.AppendLiteral("/js-");
2220 0 : needZone = false;
2221 : } else {
2222 0 : extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2223 0 : extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
2224 : }
2225 : } else {
2226 0 : extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2227 0 : extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
2228 : }
2229 : } else {
2230 0 : extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
2231 0 : extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
2232 : }
2233 :
2234 0 : if (needZone)
2235 0 : extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(realm));
2236 :
2237 0 : extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(") + rName + NS_LITERAL_CSTRING(")/");
2238 :
2239 : // extras->jsPathPrefix is used for almost all the realm-specific
2240 : // reports. At this point it has the form
2241 : // "<something>realm(<rname>)/".
2242 : //
2243 : // extras->domPathPrefix is used for DOM orphan nodes, which are
2244 : // counted by the JS reporter but reported as part of the DOM
2245 : // measurements. At this point it has the form "<something>/dom/" if
2246 : // this realm belongs to an nsGlobalWindow, and
2247 : // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
2248 : // be used, because non-nsGlobalWindow realms shouldn't have
2249 : // orphan DOM nodes).
2250 :
2251 0 : MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
2252 0 : MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
2253 :
2254 0 : realmStats->extra = extras;
2255 0 : }
2256 : };
2257 :
2258 : void
2259 0 : JSReporter::CollectReports(WindowPaths* windowPaths,
2260 : WindowPaths* topWindowPaths,
2261 : nsIHandleReportCallback* handleReport,
2262 : nsISupports* data,
2263 : bool anonymize)
2264 : {
2265 0 : XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
2266 :
2267 : // In the first step we get all the stats and stash them in a local
2268 : // data structure. In the second step we pass all the stashed stats to
2269 : // the callback. Separating these steps is important because the
2270 : // callback may be a JS function, and executing JS while getting these
2271 : // stats seems like a bad idea.
2272 :
2273 0 : XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, anonymize);
2274 0 : OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
2275 0 : JSContext* cx = XPCJSContext::Get()->Context();
2276 0 : if (!JS::CollectRuntimeStats(cx, &rtStats, &orphanReporter,
2277 : anonymize))
2278 : {
2279 0 : return;
2280 : }
2281 :
2282 : // Collect JS stats not associated with a Runtime such as helper threads or
2283 : // global tracelogger data. We do this here in JSReporter::CollectReports
2284 : // as this is used for the main Runtime in process.
2285 0 : JS::GlobalStats gStats(JSMallocSizeOf);
2286 0 : if (!JS::CollectGlobalStats(&gStats))
2287 : return;
2288 :
2289 0 : size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
2290 :
2291 0 : size_t wrappedJSSize = xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
2292 :
2293 0 : XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
2294 0 : XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
2295 :
2296 0 : mozJSComponentLoader* loader = mozJSComponentLoader::Get();
2297 0 : size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
2298 :
2299 : // This is the second step (see above). First we report stuff in the
2300 : // "explicit" tree, then we report other stuff.
2301 :
2302 0 : size_t rtTotal = 0;
2303 0 : xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
2304 0 : NS_LITERAL_CSTRING("explicit/js-non-window/"),
2305 : handleReport, data,
2306 0 : anonymize, &rtTotal);
2307 :
2308 : // Report the sums of the realm numbers.
2309 0 : xpc::RealmStatsExtras realmExtrasTotal;
2310 0 : realmExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/realms/");
2311 0 : realmExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
2312 0 : ReportRealmStats(rtStats.realmTotals, realmExtrasTotal, handleReport, data);
2313 :
2314 0 : xpc::ZoneStatsExtras zExtrasTotal;
2315 0 : zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
2316 : ReportZoneStats(rtStats.zTotals, zExtrasTotal, handleReport, data,
2317 0 : anonymize);
2318 :
2319 : // Report the sum of the runtime/ numbers.
2320 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
2321 : KIND_OTHER, rtTotal,
2322 : "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
2323 :
2324 : // Report the number of HelperThread
2325 :
2326 0 : REPORT(NS_LITERAL_CSTRING("js-helper-threads/idle"),
2327 : KIND_OTHER, UNITS_COUNT, gStats.helperThread.idleThreadCount,
2328 : "The current number of idle JS HelperThreads.");
2329 :
2330 0 : REPORT(NS_LITERAL_CSTRING("js-helper-threads/active"),
2331 : KIND_OTHER, UNITS_COUNT, gStats.helperThread.activeThreadCount,
2332 : "The current number of active JS HelperThreads. Memory held by these is"
2333 : " not reported.");
2334 :
2335 : // Report the numbers for memory used by wasm Runtime state.
2336 0 : REPORT_BYTES(NS_LITERAL_CSTRING("wasm-runtime"),
2337 : KIND_OTHER, rtStats.runtime.wasmRuntime,
2338 : "The memory used for wasm runtime bookkeeping.");
2339 :
2340 : // Report the numbers for memory outside of realms.
2341 :
2342 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
2343 : KIND_OTHER, rtStats.gcHeapUnusedChunks,
2344 : "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
2345 :
2346 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
2347 : KIND_OTHER, rtStats.gcHeapUnusedArenas,
2348 : "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
2349 :
2350 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
2351 : KIND_OTHER, rtStats.gcHeapChunkAdmin,
2352 : "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
2353 :
2354 : // Report a breakdown of the committed GC space.
2355 :
2356 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
2357 : KIND_OTHER, rtStats.gcHeapUnusedChunks,
2358 : "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
2359 :
2360 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
2361 : KIND_OTHER, rtStats.gcHeapUnusedArenas,
2362 : "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
2363 :
2364 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/objects"),
2365 : KIND_OTHER, rtStats.zTotals.unusedGCThings.object,
2366 : "Unused object cells within non-empty arenas.");
2367 :
2368 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/strings"),
2369 : KIND_OTHER, rtStats.zTotals.unusedGCThings.string,
2370 : "Unused string cells within non-empty arenas.");
2371 :
2372 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/symbols"),
2373 : KIND_OTHER, rtStats.zTotals.unusedGCThings.symbol,
2374 : "Unused symbol cells within non-empty arenas.");
2375 :
2376 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/shapes"),
2377 : KIND_OTHER, rtStats.zTotals.unusedGCThings.shape,
2378 : "Unused shape cells within non-empty arenas.");
2379 :
2380 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/base-shapes"),
2381 : KIND_OTHER, rtStats.zTotals.unusedGCThings.baseShape,
2382 : "Unused base shape cells within non-empty arenas.");
2383 :
2384 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/object-groups"),
2385 : KIND_OTHER, rtStats.zTotals.unusedGCThings.objectGroup,
2386 : "Unused object group cells within non-empty arenas.");
2387 :
2388 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scopes"),
2389 : KIND_OTHER, rtStats.zTotals.unusedGCThings.scope,
2390 : "Unused scope cells within non-empty arenas.");
2391 :
2392 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scripts"),
2393 : KIND_OTHER, rtStats.zTotals.unusedGCThings.script,
2394 : "Unused script cells within non-empty arenas.");
2395 :
2396 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
2397 : KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
2398 : "Unused lazy script cells within non-empty arenas.");
2399 :
2400 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
2401 : KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
2402 : "Unused jitcode cells within non-empty arenas.");
2403 :
2404 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"),
2405 : KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared,
2406 : "Unused regexpshared cells within non-empty arenas.");
2407 :
2408 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
2409 : KIND_OTHER, rtStats.gcHeapChunkAdmin,
2410 : "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
2411 :
2412 0 : REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
2413 : KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
2414 : "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
2415 :
2416 0 : size_t gcThingTotal = 0;
2417 :
2418 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/objects"),
2419 : KIND_OTHER, rtStats.realmTotals.classInfo.objectsGCHeap,
2420 : "Used object cells.");
2421 :
2422 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/strings"),
2423 : KIND_OTHER, rtStats.zTotals.stringInfo.sizeOfLiveGCThings(),
2424 : "Used string cells.");
2425 :
2426 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/symbols"),
2427 : KIND_OTHER, rtStats.zTotals.symbolsGCHeap,
2428 : "Used symbol cells.");
2429 :
2430 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/shapes"),
2431 : KIND_OTHER,
2432 : rtStats.zTotals.shapeInfo.shapesGCHeapTree + rtStats.zTotals.shapeInfo.shapesGCHeapDict,
2433 : "Used shape cells.");
2434 :
2435 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/base-shapes"),
2436 : KIND_OTHER, rtStats.zTotals.shapeInfo.shapesGCHeapBase,
2437 : "Used base shape cells.");
2438 :
2439 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/object-groups"),
2440 : KIND_OTHER, rtStats.zTotals.objectGroupsGCHeap,
2441 : "Used object group cells.");
2442 :
2443 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scopes"),
2444 : KIND_OTHER, rtStats.zTotals.scopesGCHeap,
2445 : "Used scope cells.");
2446 :
2447 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scripts"),
2448 : KIND_OTHER, rtStats.realmTotals.scriptsGCHeap,
2449 : "Used script cells.");
2450 :
2451 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
2452 : KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
2453 : "Used lazy script cells.");
2454 :
2455 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
2456 : KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
2457 : "Used jitcode cells.");
2458 :
2459 0 : MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"),
2460 : KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap,
2461 : "Used regexpshared cells.");
2462 :
2463 0 : MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
2464 :
2465 : // Report xpconnect.
2466 :
2467 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
2468 : KIND_HEAP, xpcJSRuntimeSize,
2469 : "The XPConnect runtime.");
2470 :
2471 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/wrappedjs"),
2472 : KIND_HEAP, wrappedJSSize,
2473 : "Wrappers used to implement XPIDL interfaces with JS.");
2474 :
2475 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
2476 : KIND_HEAP, sizeInfo.mScopeAndMapSize,
2477 : "XPConnect scopes.");
2478 :
2479 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
2480 : KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
2481 : "Prototype and interface binding caches.");
2482 :
2483 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
2484 : KIND_HEAP, jsComponentLoaderSize,
2485 : "XPConnect's JS component loader.");
2486 :
2487 : // Report tracelogger (global).
2488 :
2489 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/tracelogger"),
2490 : KIND_HEAP, gStats.tracelogger,
2491 : "The memory used for the tracelogger, including the graph and events.");
2492 :
2493 : // Report HelperThreadState.
2494 :
2495 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/helper-thread/heap-other"),
2496 : KIND_HEAP, gStats.helperThread.stateData,
2497 : "Memory used by HelperThreadState.");
2498 :
2499 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/helper-thread/parse-task"),
2500 : KIND_HEAP, gStats.helperThread.parseTask,
2501 : "The memory used by ParseTasks waiting in HelperThreadState.");
2502 :
2503 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/helper-thread/ion-builder"),
2504 : KIND_HEAP, gStats.helperThread.ionBuilder,
2505 : "The memory used by IonBuilders waiting in HelperThreadState.");
2506 :
2507 0 : REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/helper-thread/wasm-compile"),
2508 : KIND_HEAP, gStats.helperThread.parseTask,
2509 : "The memory used by Wasm compilations waiting in HelperThreadState.");
2510 : }
2511 :
2512 : static nsresult
2513 0 : JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize,
2514 : size_t* jsPrivateSize, size_t* jsOtherSize)
2515 : {
2516 0 : JSContext* cx = XPCJSContext::Get()->Context();
2517 0 : JS::RootedObject obj(cx, objArg);
2518 :
2519 0 : TabSizes sizes;
2520 0 : OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
2521 0 : NS_ENSURE_TRUE(JS::AddSizeOfTab(cx, obj, moz_malloc_size_of,
2522 : &orphanReporter, &sizes),
2523 : NS_ERROR_OUT_OF_MEMORY);
2524 :
2525 0 : *jsObjectsSize = sizes.objects;
2526 0 : *jsStringsSize = sizes.strings;
2527 0 : *jsPrivateSize = sizes.private_;
2528 0 : *jsOtherSize = sizes.other;
2529 0 : return NS_OK;
2530 : }
2531 :
2532 : } // namespace xpc
2533 :
2534 : static void
2535 24 : AccumulateTelemetryCallback(int id, uint32_t sample, const char* key)
2536 : {
2537 0 : switch (id) {
2538 : case JS_TELEMETRY_GC_REASON:
2539 0 : Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
2540 0 : break;
2541 : case JS_TELEMETRY_GC_IS_ZONE_GC:
2542 0 : Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
2543 0 : break;
2544 : case JS_TELEMETRY_GC_MS:
2545 0 : Telemetry::Accumulate(Telemetry::GC_MS, sample);
2546 0 : break;
2547 : case JS_TELEMETRY_GC_BUDGET_MS:
2548 0 : Telemetry::Accumulate(Telemetry::GC_BUDGET_MS, sample);
2549 0 : break;
2550 : case JS_TELEMETRY_GC_BUDGET_OVERRUN:
2551 0 : Telemetry::Accumulate(Telemetry::GC_BUDGET_OVERRUN, sample);
2552 0 : break;
2553 : case JS_TELEMETRY_GC_ANIMATION_MS:
2554 0 : Telemetry::Accumulate(Telemetry::GC_ANIMATION_MS, sample);
2555 0 : break;
2556 : case JS_TELEMETRY_GC_MAX_PAUSE_MS_2:
2557 0 : Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS_2, sample);
2558 0 : break;
2559 : case JS_TELEMETRY_GC_MARK_MS:
2560 0 : Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
2561 0 : break;
2562 : case JS_TELEMETRY_GC_SWEEP_MS:
2563 0 : Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
2564 0 : break;
2565 : case JS_TELEMETRY_GC_COMPACT_MS:
2566 0 : Telemetry::Accumulate(Telemetry::GC_COMPACT_MS, sample);
2567 0 : break;
2568 : case JS_TELEMETRY_GC_MARK_ROOTS_MS:
2569 0 : Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
2570 0 : break;
2571 : case JS_TELEMETRY_GC_MARK_GRAY_MS:
2572 0 : Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
2573 0 : break;
2574 : case JS_TELEMETRY_GC_SLICE_MS:
2575 0 : Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
2576 0 : break;
2577 : case JS_TELEMETRY_GC_SLOW_PHASE:
2578 0 : Telemetry::Accumulate(Telemetry::GC_SLOW_PHASE, sample);
2579 0 : break;
2580 : case JS_TELEMETRY_GC_SLOW_TASK:
2581 0 : Telemetry::Accumulate(Telemetry::GC_SLOW_TASK, sample);
2582 0 : break;
2583 : case JS_TELEMETRY_GC_MMU_50:
2584 0 : Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
2585 0 : break;
2586 : case JS_TELEMETRY_GC_RESET:
2587 0 : Telemetry::Accumulate(Telemetry::GC_RESET, sample);
2588 0 : break;
2589 : case JS_TELEMETRY_GC_RESET_REASON:
2590 0 : Telemetry::Accumulate(Telemetry::GC_RESET_REASON, sample);
2591 0 : break;
2592 : case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
2593 0 : Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
2594 0 : break;
2595 : case JS_TELEMETRY_GC_NON_INCREMENTAL:
2596 0 : Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
2597 0 : break;
2598 : case JS_TELEMETRY_GC_NON_INCREMENTAL_REASON:
2599 0 : Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL_REASON, sample);
2600 0 : break;
2601 : case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
2602 0 : Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
2603 0 : break;
2604 : case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
2605 0 : Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
2606 0 : break;
2607 : case JS_TELEMETRY_GC_MINOR_REASON:
2608 0 : Telemetry::Accumulate(Telemetry::GC_MINOR_REASON, sample);
2609 0 : break;
2610 : case JS_TELEMETRY_GC_MINOR_REASON_LONG:
2611 0 : Telemetry::Accumulate(Telemetry::GC_MINOR_REASON_LONG, sample);
2612 0 : break;
2613 : case JS_TELEMETRY_GC_MINOR_US:
2614 0 : Telemetry::Accumulate(Telemetry::GC_MINOR_US, sample);
2615 0 : break;
2616 : case JS_TELEMETRY_GC_NURSERY_BYTES:
2617 1 : Telemetry::Accumulate(Telemetry::GC_NURSERY_BYTES, sample);
2618 1 : break;
2619 : case JS_TELEMETRY_GC_PRETENURE_COUNT:
2620 1 : Telemetry::Accumulate(Telemetry::GC_PRETENURE_COUNT, sample);
2621 1 : break;
2622 : case JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS:
2623 0 : Telemetry::Accumulate(Telemetry::JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS, sample);
2624 0 : break;
2625 : case JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS:
2626 0 : Telemetry::Accumulate(Telemetry::JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS, sample);
2627 0 : break;
2628 : default:
2629 0 : MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
2630 : }
2631 24 : }
2632 :
2633 : static void
2634 0 : SetUseCounterCallback(JSObject* obj, JSUseCounter counter)
2635 : {
2636 0 : switch (counter) {
2637 : case JSUseCounter::ASMJS:
2638 0 : SetDocumentAndPageUseCounter(obj, eUseCounter_custom_JS_asmjs);
2639 0 : break;
2640 : case JSUseCounter::WASM:
2641 0 : SetDocumentAndPageUseCounter(obj, eUseCounter_custom_JS_wasm);
2642 0 : break;
2643 : default:
2644 0 : MOZ_ASSERT_UNREACHABLE("Unexpected JSUseCounter id");
2645 : }
2646 0 : }
2647 :
2648 : static void
2649 0 : GetRealmNameCallback(JSContext* cx, Handle<Realm*> realm,
2650 : char* buf, size_t bufsize)
2651 : {
2652 0 : nsCString name;
2653 : // This is called via the JSAPI and isn't involved in memory reporting, so
2654 : // we don't need to anonymize realm names.
2655 0 : int anonymizeID = 0;
2656 0 : GetRealmName(realm, name, &anonymizeID, /* replaceSlashes = */ false);
2657 0 : if (name.Length() >= bufsize)
2658 0 : name.Truncate(bufsize - 1);
2659 0 : memcpy(buf, name.get(), name.Length() + 1);
2660 0 : }
2661 :
2662 : static void
2663 0 : DestroyRealm(JSFreeOp* fop, JS::Realm* realm)
2664 : {
2665 : // Get the current compartment private into an AutoPtr (which will do the
2666 : // cleanup for us), and null out the private field.
2667 15 : mozilla::UniquePtr<RealmPrivate> priv(RealmPrivate::Get(realm));
2668 0 : JS::SetRealmPrivate(realm, nullptr);
2669 0 : }
2670 :
2671 : static bool
2672 5 : PreserveWrapper(JSContext* cx, JSObject* obj)
2673 : {
2674 5 : MOZ_ASSERT(cx);
2675 0 : MOZ_ASSERT(obj);
2676 0 : MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
2677 :
2678 5 : return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
2679 : }
2680 :
2681 : static nsresult
2682 2 : ReadSourceFromFilename(JSContext* cx, const char* filename, char16_t** src, size_t* len)
2683 : {
2684 : nsresult rv;
2685 :
2686 : // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
2687 : // the filename of its caller. Axe that if present.
2688 : const char* arrow;
2689 4 : while ((arrow = strstr(filename, " -> ")))
2690 0 : filename = arrow + strlen(" -> ");
2691 :
2692 : // Get the URI.
2693 4 : nsCOMPtr<nsIURI> uri;
2694 0 : rv = NS_NewURI(getter_AddRefs(uri), filename);
2695 0 : NS_ENSURE_SUCCESS(rv, rv);
2696 :
2697 4 : nsCOMPtr<nsIChannel> scriptChannel;
2698 0 : rv = NS_NewChannel(getter_AddRefs(scriptChannel),
2699 : uri,
2700 : nsContentUtils::GetSystemPrincipal(),
2701 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
2702 2 : nsIContentPolicy::TYPE_OTHER);
2703 0 : NS_ENSURE_SUCCESS(rv, rv);
2704 :
2705 : // Only allow local reading.
2706 4 : nsCOMPtr<nsIURI> actualUri;
2707 0 : rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
2708 0 : NS_ENSURE_SUCCESS(rv, rv);
2709 0 : nsCString scheme;
2710 0 : rv = actualUri->GetScheme(scheme);
2711 0 : NS_ENSURE_SUCCESS(rv, rv);
2712 0 : if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
2713 : return NS_OK;
2714 :
2715 : // Explicitly set the content type so that we don't load the
2716 : // exthandler to guess it.
2717 6 : scriptChannel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
2718 :
2719 4 : nsCOMPtr<nsIInputStream> scriptStream;
2720 0 : rv = scriptChannel->Open2(getter_AddRefs(scriptStream));
2721 0 : NS_ENSURE_SUCCESS(rv, rv);
2722 :
2723 : uint64_t rawLen;
2724 2 : rv = scriptStream->Available(&rawLen);
2725 0 : NS_ENSURE_SUCCESS(rv, rv);
2726 0 : if (!rawLen)
2727 : return NS_ERROR_FAILURE;
2728 :
2729 : // Technically, this should be SIZE_MAX, but we don't run on machines
2730 : // where that would be less than UINT32_MAX, and the latter is already
2731 : // well beyond a reasonable limit.
2732 2 : if (rawLen > UINT32_MAX)
2733 : return NS_ERROR_FILE_TOO_BIG;
2734 :
2735 : // Allocate an internal buf the size of the file.
2736 2 : auto buf = MakeUniqueFallible<unsigned char[]>(rawLen);
2737 0 : if (!buf)
2738 : return NS_ERROR_OUT_OF_MEMORY;
2739 :
2740 2 : unsigned char* ptr = buf.get();
2741 0 : unsigned char* end = ptr + rawLen;
2742 0 : while (ptr < end) {
2743 : uint32_t bytesRead;
2744 2 : rv = scriptStream->Read(reinterpret_cast<char*>(ptr), end - ptr, &bytesRead);
2745 0 : if (NS_FAILED(rv))
2746 0 : return rv;
2747 1 : MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
2748 0 : ptr += bytesRead;
2749 : }
2750 :
2751 6 : rv = ScriptLoader::ConvertToUTF16(scriptChannel, buf.get(), rawLen,
2752 0 : EmptyString(), nullptr, *src, *len);
2753 0 : NS_ENSURE_SUCCESS(rv, rv);
2754 :
2755 2 : if (!*src)
2756 : return NS_ERROR_FAILURE;
2757 :
2758 : // Historically this method used JS_malloc() which updates the GC memory
2759 : // accounting. Since ConvertToUTF16() now uses js_malloc() instead we
2760 : // update the accounting manually after the fact.
2761 2 : JS_updateMallocCounter(cx, *len);
2762 :
2763 2 : return NS_OK;
2764 : }
2765 :
2766 : // The JS engine calls this object's 'load' member function when it needs
2767 : // the source for a chrome JS function. See the comment in the XPCJSRuntime
2768 : // constructor.
2769 1 : class XPCJSSourceHook: public js::SourceHook {
2770 0 : bool load(JSContext* cx, const char* filename, char16_t** src, size_t* length) override {
2771 0 : *src = nullptr;
2772 0 : *length = 0;
2773 :
2774 2 : if (!nsContentUtils::IsSystemCaller(cx))
2775 : return true;
2776 :
2777 2 : if (!filename)
2778 : return true;
2779 :
2780 2 : nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
2781 0 : if (NS_FAILED(rv)) {
2782 0 : xpc::Throw(cx, rv);
2783 0 : return false;
2784 : }
2785 :
2786 : return true;
2787 : }
2788 : };
2789 :
2790 : static const JSWrapObjectCallbacks WrapObjectCallbacks = {
2791 : xpc::WrapperFactory::Rewrap,
2792 : xpc::WrapperFactory::PrepareForWrapping
2793 : };
2794 :
2795 1 : XPCJSRuntime::XPCJSRuntime(JSContext* aCx)
2796 : : CycleCollectedJSRuntime(aCx),
2797 1 : mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH)),
2798 0 : mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_LENGTH)),
2799 0 : mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_LENGTH)),
2800 0 : mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
2801 0 : mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
2802 0 : mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_LENGTH)),
2803 : mGCIsRunning(false),
2804 : mNativesToReleaseArray(),
2805 : mDoingFinalization(false),
2806 : mVariantRoots(nullptr),
2807 : mWrappedJSRoots(nullptr),
2808 13 : mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
2809 : {
2810 1 : MOZ_COUNT_CTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
2811 0 : }
2812 :
2813 : /* static */
2814 : XPCJSRuntime*
2815 11753 : XPCJSRuntime::Get()
2816 : {
2817 18649 : return nsXPConnect::GetRuntimeInstance();
2818 : }
2819 :
2820 : void
2821 1 : XPCJSRuntime::Initialize(JSContext* cx)
2822 : {
2823 1 : mUnprivilegedJunkScope.init(cx, nullptr);
2824 0 : mPrivilegedJunkScope.init(cx, nullptr);
2825 0 : mCompilationScope.init(cx, nullptr);
2826 :
2827 : // these jsids filled in later when we have a JSContext to work with.
2828 1 : mStrIDs[0] = JSID_VOID;
2829 :
2830 : // Unconstrain the runtime's threshold on nominal heap size, to avoid
2831 : // triggering GC too often if operating continuously near an arbitrary
2832 : // finite threshold (0xffffffff is infinity for uint32_t parameters).
2833 : // This leaves the maximum-JS_malloc-bytes threshold still in effect
2834 : // to cause period, and we hope hygienic, last-ditch GCs from within
2835 : // the GC's allocator.
2836 1 : JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
2837 :
2838 1 : JS_SetDestroyCompartmentCallback(cx, CompartmentDestroyedCallback);
2839 0 : JS_SetSizeOfIncludingThisCompartmentCallback(cx, CompartmentSizeOfIncludingThisCallback);
2840 0 : JS::SetDestroyRealmCallback(cx, DestroyRealm);
2841 0 : JS::SetRealmNameCallback(cx, GetRealmNameCallback);
2842 0 : mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
2843 0 : mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
2844 : DoCycleCollectionCallback);
2845 0 : JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
2846 1 : JS_AddWeakPointerZonesCallback(cx, WeakPointerZonesCallback, this);
2847 0 : JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
2848 0 : JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
2849 0 : js::SetPreserveWrapperCallback(cx, PreserveWrapper);
2850 0 : JS_InitReadPrincipalsCallback(cx, nsJSPrincipals::ReadPrincipals);
2851 0 : JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
2852 0 : JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
2853 0 : js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
2854 0 : js::SetXrayJitInfo(&gXrayJitInfo);
2855 0 : JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
2856 :
2857 : // The JS engine needs to keep the source code around in order to implement
2858 : // Function.prototype.toSource(). It'd be nice to not have to do this for
2859 : // chrome code and simply stub out requests for source on it. Life is not so
2860 : // easy, unfortunately. Nobody relies on chrome toSource() working in core
2861 : // browser code, but chrome tests use it. The worst offenders are addons,
2862 : // which like to monkeypatch chrome functions by calling toSource() on them
2863 : // and using regular expressions to modify them. We avoid keeping most browser
2864 : // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
2865 : // compiling some chrome code. This causes the JS engine not save the source
2866 : // code in memory. When the JS engine is asked to provide the source for a
2867 : // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
2868 : ///
2869 : // Note we do have to retain the source code in memory for scripts compiled in
2870 : // isRunOnce mode and compiled function bodies (from
2871 : // JS::CompileFunction). In practice, this means content scripts and event
2872 : // handlers.
2873 4 : mozilla::UniquePtr<XPCJSSourceHook> hook(new XPCJSSourceHook);
2874 2 : js::SetSourceHook(cx, std::move(hook));
2875 :
2876 : // Register memory reporters and distinguished amount functions.
2877 1 : RegisterStrongMemoryReporter(new JSMainRuntimeRealmsReporter());
2878 1 : RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
2879 0 : RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
2880 0 : RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
2881 0 : RegisterJSMainRuntimeRealmsSystemDistinguishedAmount(JSMainRuntimeRealmsSystemDistinguishedAmount);
2882 0 : RegisterJSMainRuntimeRealmsUserDistinguishedAmount(JSMainRuntimeRealmsUserDistinguishedAmount);
2883 0 : mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
2884 :
2885 0 : xpc_LocalizeRuntime(JS_GetRuntime(cx));
2886 1 : }
2887 :
2888 : bool
2889 1 : XPCJSRuntime::InitializeStrings(JSContext* cx)
2890 : {
2891 0 : JSAutoRequest ar(cx);
2892 :
2893 : // if it is our first context then we need to generate our string ids
2894 1 : if (JSID_IS_VOID(mStrIDs[0])) {
2895 2 : RootedString str(cx);
2896 0 : for (unsigned i = 0; i < XPCJSContext::IDX_TOTAL_COUNT; i++) {
2897 0 : str = JS_AtomizeAndPinString(cx, mStrings[i]);
2898 0 : if (!str) {
2899 0 : mStrIDs[0] = JSID_VOID;
2900 0 : return false;
2901 : }
2902 1 : mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
2903 35 : mStrJSVals[i].setString(str);
2904 : }
2905 :
2906 1 : if (!mozilla::dom::DefineStaticJSVals(cx)) {
2907 : return false;
2908 : }
2909 : }
2910 :
2911 : return true;
2912 : }
2913 :
2914 : bool
2915 0 : XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
2916 : char (&name)[72]) const
2917 : {
2918 :
2919 0 : if (clasp != &XPC_WN_Proto_JSClass) {
2920 : return false;
2921 : }
2922 :
2923 : XPCWrappedNativeProto* p =
2924 0 : static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
2925 0 : nsCOMPtr<nsIXPCScriptable> scr = p->GetScriptable();
2926 0 : if (!scr) {
2927 : return false;
2928 : }
2929 :
2930 : SprintfLiteral(name, "JS Object (%s - %s)",
2931 0 : clasp->name, scr->GetJSClass()->name);
2932 0 : return true;
2933 : }
2934 :
2935 : bool
2936 0 : XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
2937 : nsCycleCollectionTraversalCallback& cb) const
2938 : {
2939 0 : if (clasp != &XPC_WN_Tearoff_JSClass) {
2940 : return false;
2941 : }
2942 :
2943 : // A tearoff holds a strong reference to its native object
2944 : // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
2945 : // will be held alive through the parent of the JSObject of the tearoff.
2946 : XPCWrappedNativeTearOff* to =
2947 0 : static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
2948 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
2949 0 : cb.NoteXPCOMChild(to->GetNative());
2950 0 : return true;
2951 : }
2952 :
2953 : /***************************************************************************/
2954 :
2955 : void
2956 0 : XPCJSRuntime::DebugDump(int16_t depth)
2957 : {
2958 : #ifdef DEBUG
2959 0 : depth--;
2960 0 : XPC_LOG_ALWAYS(("XPCJSRuntime @ %p", this));
2961 0 : XPC_LOG_INDENT();
2962 :
2963 0 : XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %p with %d wrapperclasses(s)",
2964 : mWrappedJSClassMap, mWrappedJSClassMap->Count()));
2965 : // iterate wrappersclasses...
2966 0 : if (depth && mWrappedJSClassMap->Count()) {
2967 0 : XPC_LOG_INDENT();
2968 0 : for (auto i = mWrappedJSClassMap->Iter(); !i.Done(); i.Next()) {
2969 0 : auto entry = static_cast<IID2WrappedJSClassMap::Entry*>(i.Get());
2970 0 : entry->value->DebugDump(depth);
2971 : }
2972 0 : XPC_LOG_OUTDENT();
2973 : }
2974 :
2975 : // iterate wrappers...
2976 0 : XPC_LOG_ALWAYS(("mWrappedJSMap @ %p with %d wrappers(s)",
2977 : mWrappedJSMap, mWrappedJSMap->Count()));
2978 0 : if (depth && mWrappedJSMap->Count()) {
2979 0 : XPC_LOG_INDENT();
2980 0 : mWrappedJSMap->Dump(depth);
2981 0 : XPC_LOG_OUTDENT();
2982 : }
2983 :
2984 0 : XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %p with %d interface(s)",
2985 : mIID2NativeInterfaceMap,
2986 : mIID2NativeInterfaceMap->Count()));
2987 :
2988 0 : XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %p with %d sets(s)",
2989 : mClassInfo2NativeSetMap,
2990 : mClassInfo2NativeSetMap->Count()));
2991 :
2992 0 : XPC_LOG_ALWAYS(("mNativeSetMap @ %p with %d sets(s)",
2993 : mNativeSetMap, mNativeSetMap->Count()));
2994 :
2995 : // iterate sets...
2996 0 : if (depth && mNativeSetMap->Count()) {
2997 0 : XPC_LOG_INDENT();
2998 0 : for (auto i = mNativeSetMap->Iter(); !i.Done(); i.Next()) {
2999 0 : auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
3000 0 : entry->key_value->DebugDump(depth);
3001 : }
3002 0 : XPC_LOG_OUTDENT();
3003 : }
3004 :
3005 0 : XPC_LOG_OUTDENT();
3006 : #endif
3007 0 : }
3008 :
3009 : /***************************************************************************/
3010 :
3011 : void
3012 719 : XPCRootSetElem::AddToRootSet(XPCRootSetElem** listHead)
3013 : {
3014 0 : MOZ_ASSERT(!mSelfp, "Must be not linked");
3015 :
3016 0 : mSelfp = listHead;
3017 719 : mNext = *listHead;
3018 0 : if (mNext) {
3019 0 : MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
3020 0 : mNext->mSelfp = &mNext;
3021 : }
3022 0 : *listHead = this;
3023 719 : }
3024 :
3025 : void
3026 244 : XPCRootSetElem::RemoveFromRootSet()
3027 : {
3028 0 : JS::NotifyGCRootsRemoved(XPCJSContext::Get()->Context());
3029 :
3030 0 : MOZ_ASSERT(mSelfp, "Must be linked");
3031 :
3032 0 : MOZ_ASSERT(*mSelfp == this, "Link invariant");
3033 244 : *mSelfp = mNext;
3034 0 : if (mNext)
3035 0 : mNext->mSelfp = mSelfp;
3036 : #ifdef DEBUG
3037 0 : mSelfp = nullptr;
3038 244 : mNext = nullptr;
3039 : #endif
3040 0 : }
3041 :
3042 : void
3043 0 : XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
3044 : {
3045 0 : MOZ_ASSERT(cb, "null callback");
3046 0 : extraGCCallbacks.AppendElement(cb);
3047 0 : }
3048 :
3049 : void
3050 0 : XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
3051 : {
3052 0 : MOZ_ASSERT(cb, "null callback");
3053 0 : bool found = extraGCCallbacks.RemoveElement(cb);
3054 0 : if (!found) {
3055 0 : NS_ERROR("Removing a callback which was never added.");
3056 : }
3057 0 : }
3058 :
3059 : void
3060 1 : XPCJSRuntime::InitSingletonScopes()
3061 : {
3062 : // This all happens very early, so we don't bother with cx pushing.
3063 1 : JSContext* cx = XPCJSContext::Get()->Context();
3064 3 : JSAutoRequest ar(cx);
3065 0 : RootedValue v(cx);
3066 : nsresult rv;
3067 :
3068 : // Create the Unprivileged Junk Scope.
3069 2 : SandboxOptions unprivilegedJunkScopeOptions;
3070 1 : unprivilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Junk Compartment");
3071 0 : unprivilegedJunkScopeOptions.invisibleToDebugger = true;
3072 0 : rv = CreateSandboxObject(cx, &v, nullptr, unprivilegedJunkScopeOptions);
3073 0 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
3074 0 : mUnprivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
3075 :
3076 : // Create the Privileged Junk Scope.
3077 2 : SandboxOptions privilegedJunkScopeOptions;
3078 1 : privilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Privileged Junk Compartment");
3079 0 : privilegedJunkScopeOptions.invisibleToDebugger = true;
3080 0 : privilegedJunkScopeOptions.wantComponents = false;
3081 0 : rv = CreateSandboxObject(cx, &v, nsXPConnect::SystemPrincipal(), privilegedJunkScopeOptions);
3082 0 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
3083 0 : mPrivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
3084 :
3085 : // Create the Compilation Scope.
3086 2 : SandboxOptions compilationScopeOptions;
3087 1 : compilationScopeOptions.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
3088 0 : compilationScopeOptions.invisibleToDebugger = true;
3089 0 : compilationScopeOptions.discardSource = ShouldDiscardSystemSource();
3090 0 : rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
3091 0 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
3092 0 : mCompilationScope = js::UncheckedUnwrap(&v.toObject());
3093 0 : }
3094 :
3095 : void
3096 0 : XPCJSRuntime::DeleteSingletonScopes()
3097 : {
3098 : // We're pretty late in shutdown, so we call ReleaseWrapper on the scopes. This way
3099 : // the GC can collect them immediately, and we don't rely on the CC to clean up.
3100 0 : RefPtr<SandboxPrivate> sandbox = SandboxPrivate::GetPrivate(mUnprivilegedJunkScope);
3101 0 : sandbox->ReleaseWrapper(sandbox);
3102 0 : mUnprivilegedJunkScope = nullptr;
3103 0 : sandbox = SandboxPrivate::GetPrivate(mPrivilegedJunkScope);
3104 0 : sandbox->ReleaseWrapper(sandbox);
3105 0 : mPrivilegedJunkScope = nullptr;
3106 0 : sandbox = SandboxPrivate::GetPrivate(mCompilationScope);
3107 0 : sandbox->ReleaseWrapper(sandbox);
3108 0 : mCompilationScope = nullptr;
3109 0 : }
|