/* -*- 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