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:
Marco Castelluccio 2017-07-03 11:50:33 +02:00
Родитель 5eb2117b57
Коммит 7c833da65d
8 изменённых файлов: 169 добавлений и 75 удалений

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

@ -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;