blob: 8ab6272d13db04b8b3c4cae39ffcb0cbbffc507a [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_CodeCoverage_h
#define vm_CodeCoverage_h
#include "mozilla/Vector.h"
#include "ds/LifoAlloc.h"
#include "vm/Printer.h"
struct JSCompartment;
class JSScript;
class JSObject;
namespace js {
class ScriptSourceObject;
namespace coverage {
class LCovCompartment;
class LCovSource
{
public:
explicit LCovSource(LifoAlloc* alloc, JSObject* sso);
// Whether the given script source object matches this LCovSource.
bool match(JSObject* sso) const {
return sso == source_;
}
// Whether the current source is complete and if it can be flushed.
bool isComplete() const {
return hasFilename_ && hasTopLevelScript_;
}
// Iterate over the bytecode and collect the lcov output based on the
// ScriptCounts counters.
bool writeScript(JSScript* script);
// Write the Lcov output in a buffer, such as the one associated with
// 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_;
// LifoAlloc strings which hold the filename of each function as
// well as the number of hits for each function.
LSprinter outFN_;
LSprinter outFNDA_;
size_t numFunctionsFound_;
size_t numFunctionsHit_;
// LifoAlloc string which hold branches statistics.
LSprinter outBRDA_;
size_t numBranchesFound_;
size_t numBranchesHit_;
// LifoAlloc string which hold lines statistics.
LSprinter outDA_;
size_t numLinesInstrumented_;
size_t numLinesHit_;
// Status flags.
bool hasFilename_ : 1;
bool hasTopLevelScript_ : 1;
};
class LCovCompartment
{
public:
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);
// Write the Lcov output in a buffer, such as the one associated with
// the runtime code coverage trace file.
void exportInto(GenericPrinter& out, bool* isEmpty) const;
private:
// Write the script name in out.
bool writeCompartmentName(JSCompartment* comp);
// Return the LCovSource entry which matches the given ScriptSourceObject.
LCovSource* lookupOrAdd(JSCompartment* comp, JSObject* sso);
private:
typedef mozilla::Vector<LCovSource, 16, LifoAllocPolicy<Fallible>> LCovSourceVector;
// LifoAlloc backend for all temporary allocations needed to stash the
// strings to be written in the file.
LifoAlloc alloc_;
// LifoAlloc string which hold the name of the compartment.
LSprinter outTN_;
// Vector of all sources which are used in this compartment.
LCovSourceVector* sources_;
};
class LCovRuntime
{
public:
LCovRuntime();
~LCovRuntime();
// If the environment variable JS_CODE_COVERAGE_OUTPUT_DIR is set to a
// directory, create a file inside this directory which uses the process
// ID, the thread ID and a timestamp to ensure the uniqueness of the
// file.
//
// At the end of the execution, this file should contains the LCOV output of
// all the scripts executed in the current JSRuntime.
void init();
// Check if we should collect code coverage information.
bool isEnabled() const { return out_.isInitialized(); }
// Write the aggregated result of the code coverage of a compartment
// into a file.
void writeLCovResult(LCovCompartment& comp);
private:
// When a process forks, the file will remain open, but 2 processes will
// have the same file. To avoid conflicting writes, we open a new file for
// the child process.
void maybeReopenAfterFork();
// Fill an array with the name of the file. Return false if we are unable to
// serialize the filename in this array.
bool fillWithFilename(char *name, size_t length);
// Finish the current opened file, and remove if it does not have any
// content.
void finishFile();
private:
// Output file which is created if code coverage is enabled.
Fprinter out_;
// The process' PID is used to watch for fork. When the process fork,
// we want to close the current file and open a new one.
size_t pid_;
// Flag used to report if the generated file is empty or not. If it is empty
// when the runtime is destroyed, then the file would be removed as an empty
// file is not a valid LCov file.
bool isEmpty_;
};
} // namespace coverage
} // namespace js
#endif // vm_Printer_h