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