Bug 1398198 - browser_startup.js should show the stack when a JS file was loaded earlier than expected, r=felipe,mccr8.

This commit is contained in:
Florian Quèze 2017-09-13 21:19:53 +02:00
Родитель 775235cd02
Коммит c3c302fe33
6 изменённых файлов: 109 добавлений и 5 удалений

Просмотреть файл

@ -1,4 +1,6 @@
[DEFAULT]
prefs =
browser.startup.record=true
support-files =
head.js
[browser_appmenu_reflows.js]

Просмотреть файл

@ -16,6 +16,9 @@
"use strict";
/* Set this to true only for debugging purpose; it makes the output noisy. */
const kDumpAllStacks = false;
const startupPhases = {
// For app-startup, we have a whitelist of acceptable JS files.
// Anything loaded during app-startup must have a compelling reason
@ -152,13 +155,25 @@ add_task(async function() {
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
await startupRecorder.done;
let loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
let componentStacks = new Map();
let data = startupRecorder.data.code;
// Keep only the file name for components, as the path is an absolute file
// URL rather than a resource:// URL like for modules.
for (let phase in data) {
data[phase].components =
data[phase].components.map(f => f.replace(/.*\//, ""))
.filter(c => c != "startupRecorder.js");
data[phase].components.map(uri => {
let fileName = uri.replace(/.*\//, "");
componentStacks.set(fileName, loader.getComponentLoadStack(uri));
return fileName;
}).filter(c => c != "startupRecorder.js");
}
function printStack(scriptType, name) {
if (scriptType == "modules")
info(loader.getModuleImportStack(name));
else if (scriptType == "components")
info(componentStacks.get(name));
}
// This block only adds debug output to help find the next bugs to file,
@ -171,8 +186,11 @@ add_task(async function() {
// phases are ordered, so if a script wasn't loaded yet at the immediate
// previous phase, it wasn't loaded during any of the previous phases
// either, and is new in the current phase.
if (!previous || !data[previous][scriptType].includes(f))
if (!previous || !data[previous][scriptType].includes(f)) {
info(`${scriptType} loaded ${phase}: ${f}`);
if (kDumpAllStacks)
printStack(scriptType, f);
}
}
}
previous = phase;
@ -193,6 +211,7 @@ add_task(async function() {
`should have no unexpected ${scriptType} loaded ${phase}`);
for (let script of loadedList[scriptType]) {
ok(false, `unexpected ${scriptType}: ${script}`);
printStack(scriptType, script);
}
is(whitelist[scriptType].size, 0,
`all ${scriptType} whitelist entries should have been used`);
@ -205,7 +224,10 @@ add_task(async function() {
if (blacklist) {
for (let scriptType in blacklist) {
for (let file of blacklist[scriptType]) {
ok(!loadedList[scriptType].includes(file), `${file} is not allowed ${phase}`);
let loaded = loadedList[scriptType].includes(file);
ok(!loaded, `${file} is not allowed ${phase}`);
if (loaded)
printStack(scriptType, file);
}
}
}

Просмотреть файл

@ -41,6 +41,9 @@ startupRecorder.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
record(name) {
if (!Services.prefs.getBoolPref("browser.startup.record", false))
return;
this.data.code[name] = {
components: this.loader.loadedComponents(),
modules: this.loader.loadedModules(),
@ -81,6 +84,12 @@ startupRecorder.prototype = {
Services.obs.removeObserver(this, topic);
if (topic == "sessionstore-windows-restored") {
if (!Services.prefs.getBoolPref("browser.startup.record", false)) {
this._resolve();
this._resolve = null;
return;
}
// We use idleDispatchToMainThread here to record the set of
// loaded scripts after we are fully done with startup and ready
// to react to user events.

Просмотреть файл

@ -8,11 +8,17 @@
[scriptable, builtinclass, uuid(4f94b21f-2920-4bd9-8251-5fb60fb054b2)]
interface xpcIJSModuleLoader : nsISupports
{
// These 2 functions are for startup testing purposes. They are not expected
// These functions are for startup testing purposes. They are not expected
// to be used for production code.
void loadedModules([optional] out unsigned long length,
[retval, array, size_is(length)] out string aModules);
void loadedComponents([optional] out unsigned long length,
[retval, array, size_is(length)] out string aComponents);
// These 2 functions will only return useful values if the
// "browser.startup.record" preference was true at the time the JS file
// was loaded.
ACString getModuleImportStack(in AUTF8String aLocation);
ACString getComponentLoadStack(in AUTF8String aLocation);
};

Просмотреть файл

@ -433,6 +433,12 @@ mozJSComponentLoader::LoadModule(FileLocation& aFile)
return nullptr;
}
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
if (Preferences::GetBool("browser.startup.record", false)) {
entry->importStack = xpc_PrintJSStack(cx, false, false, false).get();
}
#endif
// Cache this module for later
mModules.Put(spec, entry);
@ -1000,6 +1006,52 @@ NS_IMETHODIMP mozJSComponentLoader::LoadedComponents(uint32_t* length,
return NS_OK;
}
NS_IMETHODIMP
mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation,
nsACString& retval)
{
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
MOZ_ASSERT(mInitialized);
ComponentLoaderInfo info(aLocation);
nsresult rv = info.EnsureKey();
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
if (!mImports.Get(info.Key(), &mod))
return NS_ERROR_FAILURE;
retval = mod->importStack;
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
mozJSComponentLoader::GetComponentLoadStack(const nsACString& aLocation,
nsACString& retval)
{
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
MOZ_ASSERT(mInitialized);
ComponentLoaderInfo info(aLocation);
nsresult rv = info.EnsureURI();
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
if (!mModules.Get(info.Key(), &mod))
return NS_ERROR_FAILURE;
retval = mod->importStack;
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
static JSObject*
ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id)
{
@ -1100,6 +1152,13 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
return NS_ERROR_FILE_NOT_FOUND;
}
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
if (Preferences::GetBool("browser.startup.record", false)) {
newEntry->importStack =
xpc_PrintJSStack(callercx, false, false, false).get();
}
#endif
mod = newEntry;
}

Просмотреть файл

@ -163,6 +163,9 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
obj = nullptr;
thisObjectKey = nullptr;
location = nullptr;
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
importStack.Truncate();
#endif
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
@ -175,6 +178,9 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
JS::PersistentRootedScript thisObjectKey;
char* location;
nsCString resolvedURL;
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
nsCString importStack;
#endif
};
static size_t DataEntrySizeOfExcludingThis(const nsACString& aKey, ModuleEntry* const& aData,