зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1229277 - Introduce a map to match JSScript to script filenames instead of relying on ScriptSourceObject. r=nbp,jonco
--HG-- extra : rebase_source : 1c0c5cc201785e78dcae55313e9e25a1c862d7e1
This commit is contained in:
Родитель
5eb2117b57
Коммит
7c833da65d
|
@ -85,6 +85,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
|||
randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
|
||||
watchpointMap(nullptr),
|
||||
scriptCountsMap(nullptr),
|
||||
scriptNameMap(nullptr),
|
||||
debugScriptMap(nullptr),
|
||||
debugEnvs(nullptr),
|
||||
enumerators(nullptr),
|
||||
|
@ -115,6 +116,7 @@ JSCompartment::~JSCompartment()
|
|||
js_delete(jitCompartment_);
|
||||
js_delete(watchpointMap);
|
||||
js_delete(scriptCountsMap);
|
||||
js_delete(scriptNameMap);
|
||||
js_delete(debugScriptMap);
|
||||
js_delete(debugEnvs);
|
||||
js_delete(objectMetadataTable);
|
||||
|
@ -810,6 +812,7 @@ JSCompartment::finishRoots()
|
|||
objectMetadataTable->clear();
|
||||
|
||||
clearScriptCounts();
|
||||
clearScriptNames();
|
||||
|
||||
if (nonSyntacticLexicalEnvironments_)
|
||||
nonSyntacticLexicalEnvironments_->clear();
|
||||
|
@ -1008,6 +1011,14 @@ JSCompartment::fixupScriptMapsAfterMovingGC()
|
|||
}
|
||||
}
|
||||
|
||||
if (scriptNameMap) {
|
||||
for (ScriptNameMap::Enum e(*scriptNameMap); !e.empty(); e.popFront()) {
|
||||
JSScript* script = e.front().key();
|
||||
if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
|
||||
e.rekeyFront(script);
|
||||
}
|
||||
}
|
||||
|
||||
if (debugScriptMap) {
|
||||
for (DebugScriptMap::Enum e(*debugScriptMap); !e.empty(); e.popFront()) {
|
||||
JSScript* script = e.front().key();
|
||||
|
@ -1030,6 +1041,15 @@ JSCompartment::checkScriptMapsAfterMovingGC()
|
|||
}
|
||||
}
|
||||
|
||||
if (scriptNameMap) {
|
||||
for (auto r = scriptNameMap->all(); !r.empty(); r.popFront()) {
|
||||
JSScript* script = r.front().key();
|
||||
CheckGCThingAfterMovingGC(script);
|
||||
auto ptr = scriptNameMap->lookup(script);
|
||||
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
|
||||
}
|
||||
}
|
||||
|
||||
if (debugScriptMap) {
|
||||
for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
|
||||
JSScript* script = r.front().key();
|
||||
|
@ -1274,6 +1294,7 @@ JSCompartment::updateDebuggerObservesCoverage()
|
|||
return;
|
||||
|
||||
clearScriptCounts();
|
||||
clearScriptNames();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1315,6 +1336,19 @@ JSCompartment::clearScriptCounts()
|
|||
scriptCountsMap = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::clearScriptNames()
|
||||
{
|
||||
if (!scriptNameMap)
|
||||
return;
|
||||
|
||||
for (ScriptNameMap::Range r = scriptNameMap->all(); !r.empty(); r.popFront())
|
||||
js_delete(r.front().value());
|
||||
|
||||
js_delete(scriptNameMap);
|
||||
scriptNameMap = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
|
||||
{
|
||||
|
|
|
@ -1090,6 +1090,7 @@ struct JSCompartment
|
|||
bool collectCoverageForDebug() const;
|
||||
bool collectCoverageForPGO() const;
|
||||
void clearScriptCounts();
|
||||
void clearScriptNames();
|
||||
|
||||
bool needsDelazificationForDebugger() const {
|
||||
return debugModeBits & DebuggerNeedsDelazification;
|
||||
|
@ -1116,6 +1117,7 @@ struct JSCompartment
|
|||
js::WatchpointMap* watchpointMap;
|
||||
|
||||
js::ScriptCountsMap* scriptCountsMap;
|
||||
js::ScriptNameMap* scriptNameMap;
|
||||
|
||||
js::DebugScriptMap* debugScriptMap;
|
||||
|
||||
|
|
|
@ -7315,6 +7315,28 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
|||
|
||||
// Atoms which are marked in source's zone are now marked in target's zone.
|
||||
cx->atomMarking().adoptMarkedAtoms(target->zone(), source->zone());
|
||||
|
||||
// Merge script name maps in the target compartment's map.
|
||||
if (cx->runtime()->lcovOutput().isEnabled()) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
||||
if (!target->scriptNameMap) {
|
||||
target->scriptNameMap = cx->new_<ScriptNameMap>();
|
||||
|
||||
if (!target->scriptNameMap)
|
||||
oomUnsafe.crash("Failed to create a script name map.");
|
||||
|
||||
if (!target->scriptNameMap->init())
|
||||
oomUnsafe.crash("Failed to initialize a script name map.");
|
||||
}
|
||||
|
||||
for (ScriptNameMap::Range r = source->scriptNameMap->all(); !r.empty(); r.popFront()) {
|
||||
JSScript* key = r.front().key();
|
||||
const char* value = r.front().value();
|
||||
if (!target->scriptNameMap->putNew(key, value))
|
||||
oomUnsafe.crash("Failed to add an entry in the script name map.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -2966,7 +2966,6 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
|
|||
coverage::LCovCompartment compCover;
|
||||
for (JSScript* topLevel: topScripts) {
|
||||
RootedScript topScript(cx, topLevel);
|
||||
compCover.collectSourceFile(comp, &topScript->scriptSourceUnwrap());
|
||||
|
||||
// We found the top-level script, visit all the functions reachable
|
||||
// from the top-level function, and delazify them.
|
||||
|
@ -2978,7 +2977,10 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
|
|||
RootedFunction fun(cx);
|
||||
do {
|
||||
script = queue.popCopy();
|
||||
compCover.collectCodeCoverageInfo(comp, script->sourceObject(), script);
|
||||
if (!script->initScriptName(cx))
|
||||
return false;
|
||||
if (script->hasScriptName())
|
||||
compCover.collectCodeCoverageInfo(comp, script);
|
||||
|
||||
// Iterate from the last to the first object in order to have
|
||||
// the functions them visited in the opposite order when popping
|
||||
|
|
|
@ -1159,6 +1159,15 @@ static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
|
|||
return p;
|
||||
}
|
||||
|
||||
static inline ScriptNameMap::Ptr
|
||||
GetScriptNameMapEntry(JSScript* script)
|
||||
{
|
||||
ScriptNameMap* map = script->compartment()->scriptNameMap;
|
||||
auto p = map->lookup(script);
|
||||
MOZ_ASSERT(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
ScriptCounts&
|
||||
JSScript::getScriptCounts()
|
||||
{
|
||||
|
@ -1166,6 +1175,13 @@ JSScript::getScriptCounts()
|
|||
return *p->value();
|
||||
}
|
||||
|
||||
const char*
|
||||
JSScript::getScriptName()
|
||||
{
|
||||
auto p = GetScriptNameMapEntry(this);
|
||||
return p->value();
|
||||
}
|
||||
|
||||
js::PCCounts*
|
||||
ScriptCounts::maybeGetPCCounts(size_t offset) {
|
||||
PCCounts searched = PCCounts(offset);
|
||||
|
@ -1347,6 +1363,23 @@ JSScript::destroyScriptCounts(FreeOp* fop)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::destroyScriptName()
|
||||
{
|
||||
auto p = GetScriptNameMapEntry(this);
|
||||
compartment()->scriptNameMap->remove(p);
|
||||
}
|
||||
|
||||
bool
|
||||
JSScript::hasScriptName()
|
||||
{
|
||||
if (!compartment()->scriptNameMap)
|
||||
return false;
|
||||
|
||||
auto p = compartment()->scriptNameMap->lookup(this);
|
||||
return p.found();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptSourceObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
|
@ -1367,12 +1400,6 @@ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onActiveCooperatingThread());
|
||||
ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
|
||||
|
||||
// If code coverage is enabled, record the filename associated with this
|
||||
// source object.
|
||||
if (fop->runtime()->lcovOutput().isEnabled())
|
||||
sso->compartment()->lcovOutput.collectSourceFile(sso->compartment(), sso);
|
||||
|
||||
sso->source()->decref();
|
||||
sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
@ -2666,6 +2693,8 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
MOZ_ASSERT(script->getVersion() == options.version); // assert that no overflow occurred
|
||||
|
||||
script->setSourceObject(sourceObject);
|
||||
if (cx->runtime()->lcovOutput().isEnabled() && !script->initScriptName(cx))
|
||||
return nullptr;
|
||||
script->sourceStart_ = bufStart;
|
||||
script->sourceEnd_ = bufEnd;
|
||||
script->toStringStart_ = toStringStart;
|
||||
|
@ -2678,6 +2707,48 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
return script;
|
||||
}
|
||||
|
||||
bool
|
||||
JSScript::initScriptName(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(!hasScriptName());
|
||||
|
||||
if (!filename())
|
||||
return true;
|
||||
|
||||
// Create compartment's scriptNameMap if necessary.
|
||||
ScriptNameMap* map = compartment()->scriptNameMap;
|
||||
if (!map) {
|
||||
map = cx->new_<ScriptNameMap>();
|
||||
if (!map) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!map->init()) {
|
||||
js_delete(map);
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
compartment()->scriptNameMap = map;
|
||||
}
|
||||
|
||||
char* name = js_strdup(filename());
|
||||
if (!name) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register the script name in the compartment's map.
|
||||
if (!map->putNew(this, name)) {
|
||||
js_delete(name);
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline uint8_t*
|
||||
AllocScriptData(JS::Zone* zone, size_t size)
|
||||
{
|
||||
|
@ -3090,8 +3161,10 @@ JSScript::finalize(FreeOp* fop)
|
|||
|
||||
// Collect code coverage information for this script and all its inner
|
||||
// scripts, and store the aggregated information on the compartment.
|
||||
if (fop->runtime()->lcovOutput().isEnabled())
|
||||
compartment()->lcovOutput.collectCodeCoverageInfo(compartment(), sourceObject(), this);
|
||||
if (fop->runtime()->lcovOutput().isEnabled() && hasScriptName()) {
|
||||
compartment()->lcovOutput.collectCodeCoverageInfo(compartment(), this);
|
||||
destroyScriptName();
|
||||
}
|
||||
|
||||
fop->runtime()->geckoProfiler().onScriptFinalized(this);
|
||||
|
||||
|
|
|
@ -240,6 +240,10 @@ typedef HashMap<JSScript*,
|
|||
ScriptCounts*,
|
||||
DefaultHasher<JSScript*>,
|
||||
SystemAllocPolicy> ScriptCountsMap;
|
||||
typedef HashMap<JSScript*,
|
||||
const char*,
|
||||
DefaultHasher<JSScript*>,
|
||||
SystemAllocPolicy> ScriptNameMap;
|
||||
|
||||
class DebugScript
|
||||
{
|
||||
|
@ -1419,6 +1423,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
void setIsDefaultClassConstructor() { isDefaultClassConstructor_ = true; }
|
||||
|
||||
bool hasScriptCounts() const { return hasScriptCounts_; }
|
||||
bool hasScriptName();
|
||||
|
||||
bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
|
||||
void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
|
||||
|
@ -1783,7 +1788,9 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
public:
|
||||
bool initScriptCounts(JSContext* cx);
|
||||
bool initScriptName(JSContext* cx);
|
||||
js::ScriptCounts& getScriptCounts();
|
||||
const char* getScriptName();
|
||||
js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
|
||||
const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
|
||||
js::PCCounts* getThrowCounts(jsbytecode* pc);
|
||||
|
@ -1793,6 +1800,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
js::jit::IonScriptCounts* getIonCounts();
|
||||
void releaseScriptCounts(js::ScriptCounts* counts);
|
||||
void destroyScriptCounts(js::FreeOp* fop);
|
||||
void destroyScriptName();
|
||||
// The entry should be removed after using this function.
|
||||
void takeOverScriptCountsMapEntry(js::ScriptCounts* entryValue);
|
||||
|
||||
|
|
|
@ -63,9 +63,8 @@
|
|||
namespace js {
|
||||
namespace coverage {
|
||||
|
||||
LCovSource::LCovSource(LifoAlloc* alloc, JSObject* sso)
|
||||
: source_(sso),
|
||||
outSF_(alloc),
|
||||
LCovSource::LCovSource(LifoAlloc* alloc, const char* name)
|
||||
: name_(name),
|
||||
outFN_(alloc),
|
||||
outFNDA_(alloc),
|
||||
numFunctionsFound_(0),
|
||||
|
@ -76,7 +75,6 @@ LCovSource::LCovSource(LifoAlloc* alloc, JSObject* sso)
|
|||
outDA_(alloc),
|
||||
numLinesInstrumented_(0),
|
||||
numLinesHit_(0),
|
||||
hasFilename_(false),
|
||||
hasTopLevelScript_(false)
|
||||
{
|
||||
}
|
||||
|
@ -85,10 +83,10 @@ void
|
|||
LCovSource::exportInto(GenericPrinter& out) const
|
||||
{
|
||||
// Only write if everything got recorded.
|
||||
if (!hasFilename_ || !hasTopLevelScript_)
|
||||
if (!hasTopLevelScript_)
|
||||
return;
|
||||
|
||||
outSF_.exportInto(out);
|
||||
out.printf("SF:%s\n", name_);
|
||||
|
||||
outFN_.exportInto(out);
|
||||
outFNDA_.exportInto(out);
|
||||
|
@ -106,17 +104,6 @@ LCovSource::exportInto(GenericPrinter& out) const
|
|||
out.put("end_of_record\n");
|
||||
}
|
||||
|
||||
bool
|
||||
LCovSource::writeSourceFilename(ScriptSourceObject* sso)
|
||||
{
|
||||
outSF_.printf("SF:%s\n", sso->source()->filename());
|
||||
if (outSF_.hadOutOfMemory())
|
||||
return false;
|
||||
|
||||
hasFilename_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LCovSource::writeScriptName(LSprinter& out, JSScript* script)
|
||||
{
|
||||
|
@ -404,8 +391,7 @@ LCovCompartment::LCovCompartment()
|
|||
}
|
||||
|
||||
void
|
||||
LCovCompartment::collectCodeCoverageInfo(JSCompartment* comp, JSObject* sso,
|
||||
JSScript* script)
|
||||
LCovCompartment::collectCodeCoverageInfo(JSCompartment* comp, JSScript* script)
|
||||
{
|
||||
// Skip any operation if we already some out-of memory issues.
|
||||
if (outTN_.hadOutOfMemory())
|
||||
|
@ -415,7 +401,7 @@ LCovCompartment::collectCodeCoverageInfo(JSCompartment* comp, JSObject* sso,
|
|||
return;
|
||||
|
||||
// Get the existing source LCov summary, or create a new one.
|
||||
LCovSource* source = lookupOrAdd(comp, sso);
|
||||
LCovSource* source = lookupOrAdd(comp, script->getScriptName());
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
|
@ -426,31 +412,8 @@ LCovCompartment::collectCodeCoverageInfo(JSCompartment* comp, JSObject* sso,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LCovCompartment::collectSourceFile(JSCompartment* comp, ScriptSourceObject* sso)
|
||||
{
|
||||
// Do not add sources if there is no file name associated to it.
|
||||
if (!sso->source()->filename())
|
||||
return;
|
||||
|
||||
// Skip any operation if we already some out-of memory issues.
|
||||
if (outTN_.hadOutOfMemory())
|
||||
return;
|
||||
|
||||
// Get the existing source LCov summary, or create a new one.
|
||||
LCovSource* source = lookupOrAdd(comp, sso);
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
// Write source filename into the LCovSource.
|
||||
if (!source->writeSourceFilename(sso)) {
|
||||
outTN_.reportOutOfMemory();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LCovSource*
|
||||
LCovCompartment::lookupOrAdd(JSCompartment* comp, JSObject* sso)
|
||||
LCovCompartment::lookupOrAdd(JSCompartment* comp, const char* name)
|
||||
{
|
||||
// On the first call, write the compartment name, and allocate a LCovSource
|
||||
// vector in the LifoAlloc.
|
||||
|
@ -468,13 +431,13 @@ LCovCompartment::lookupOrAdd(JSCompartment* comp, JSObject* sso)
|
|||
} else {
|
||||
// Find the first matching source.
|
||||
for (LCovSource& source : *sources_) {
|
||||
if (source.match(sso))
|
||||
if (source.match(name))
|
||||
return &source;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new LCovSource for the current top-level.
|
||||
if (!sources_->append(Move(LCovSource(&alloc_, sso)))) {
|
||||
if (!sources_->append(Move(LCovSource(&alloc_, name)))) {
|
||||
outTN_.reportOutOfMemory();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -28,16 +28,16 @@ class LCovCompartment;
|
|||
class LCovSource
|
||||
{
|
||||
public:
|
||||
explicit LCovSource(LifoAlloc* alloc, JSObject* sso);
|
||||
explicit LCovSource(LifoAlloc* alloc, const char* name);
|
||||
|
||||
// Whether the given script source object matches this LCovSource.
|
||||
bool match(JSObject* sso) const {
|
||||
return sso == source_;
|
||||
// Whether the given script name matches this LCovSource.
|
||||
bool match(const char* name) const {
|
||||
return strcmp(name_, name) == 0;
|
||||
}
|
||||
|
||||
// Whether the current source is complete and if it can be flushed.
|
||||
bool isComplete() const {
|
||||
return hasFilename_ && hasTopLevelScript_;
|
||||
return hasTopLevelScript_;
|
||||
}
|
||||
|
||||
// Iterate over the bytecode and collect the lcov output based on the
|
||||
|
@ -48,19 +48,13 @@ class LCovSource
|
|||
// the runtime code coverage trace file.
|
||||
void exportInto(GenericPrinter& out) const;
|
||||
|
||||
// Write the script name in out.
|
||||
bool writeSourceFilename(ScriptSourceObject* sso);
|
||||
|
||||
private:
|
||||
// Write the script name in out.
|
||||
bool writeScriptName(LSprinter& out, JSScript* script);
|
||||
|
||||
private:
|
||||
// Weak pointer of the Script Source Object used by the current source.
|
||||
JSObject *source_;
|
||||
|
||||
// LifoAlloc string which hold the filename of the source.
|
||||
LSprinter outSF_;
|
||||
// Name of the source file.
|
||||
const char* name_;
|
||||
|
||||
// LifoAlloc strings which hold the filename of each function as
|
||||
// well as the number of hits for each function.
|
||||
|
@ -80,7 +74,6 @@ class LCovSource
|
|||
size_t numLinesHit_;
|
||||
|
||||
// Status flags.
|
||||
bool hasFilename_ : 1;
|
||||
bool hasTopLevelScript_ : 1;
|
||||
};
|
||||
|
||||
|
@ -90,10 +83,7 @@ class LCovCompartment
|
|||
LCovCompartment();
|
||||
|
||||
// Collect code coverage information for the given source.
|
||||
void collectCodeCoverageInfo(JSCompartment* comp, JSObject* sso, JSScript* topLevel);
|
||||
|
||||
// Create an ebtry for the current ScriptSourceObject.
|
||||
void collectSourceFile(JSCompartment* comp, ScriptSourceObject* sso);
|
||||
void collectCodeCoverageInfo(JSCompartment* comp, JSScript* topLevel);
|
||||
|
||||
// Write the Lcov output in a buffer, such as the one associated with
|
||||
// the runtime code coverage trace file.
|
||||
|
@ -104,7 +94,7 @@ class LCovCompartment
|
|||
bool writeCompartmentName(JSCompartment* comp);
|
||||
|
||||
// Return the LCovSource entry which matches the given ScriptSourceObject.
|
||||
LCovSource* lookupOrAdd(JSCompartment* comp, JSObject* sso);
|
||||
LCovSource* lookupOrAdd(JSCompartment* comp, const char* name);
|
||||
|
||||
private:
|
||||
typedef mozilla::Vector<LCovSource, 16, LifoAllocPolicy<Fallible>> LCovSourceVector;
|
||||
|
|
Загрузка…
Ссылка в новой задаче