diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/CodeCoverage.h | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/js/src/vm/CodeCoverage.h b/js/src/vm/CodeCoverage.h new file mode 100644 index 0000000000..724e2ef35d --- /dev/null +++ b/js/src/vm/CodeCoverage.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "js/AllocPolicy.h" +#include "js/HashTable.h" +#include "js/TypeDecls.h" +#include "js/Utility.h" + +#include "vm/Printer.h" + +namespace js { +namespace coverage { + +class LCovSource { + public: + LCovSource(LifoAlloc* alloc, JS::UniqueChars name); + + // Whether the given script name matches this LCovSource. + bool match(const char* name) const { return strcmp(name_.get(), name) == 0; } + + // Whether an OOM was seen recording coverage information. This indicates + // that the resulting coverage information is incomplete. + bool hadOutOfMemory() const { return hadOOM_; } + + // Whether the current source is complete and if it can be flushed. + bool isComplete() const { return hasTopLevelScript_; } + + // Iterate over the bytecode and collect the lcov output based on the + // ScriptCounts counters. + void writeScript(JSScript* script, const char* scriptName); + + // Write the Lcov output in a buffer, such as the one associated with + // the runtime code coverage trace file. + void exportInto(GenericPrinter& out); + + private: + // Name of the source file. + JS::UniqueChars name_; + + // 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_; + + // Holds lines statistics. When processing a line hit count, the hit count + // is added to any hit count already in the hash map so that we handle + // lines that belong to more than one JSScript or function in the same + // source file. + HashMap<size_t, uint64_t, DefaultHasher<size_t>, SystemAllocPolicy> linesHit_; + size_t numLinesInstrumented_; + size_t numLinesHit_; + size_t maxLineHit_; + + // Status flags. + bool hasTopLevelScript_ : 1; + bool hadOOM_ : 1; +}; + +class LCovRealm { + public: + explicit LCovRealm(JS::Realm* realm); + ~LCovRealm(); + + // 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; + + friend bool InitScriptCoverage(JSContext* cx, JSScript* script); + + private: + // Write the realm name in outTN_. + void writeRealmName(JS::Realm* realm); + + // Return the LCovSource entry which matches the given ScriptSourceObject. + LCovSource* lookupOrAdd(const char* name); + + // Generate escaped form of script atom and allocate inside our LifoAlloc if + // necessary. + const char* getScriptName(JSScript* script); + + 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 realm. + LSprinter outTN_; + + // Vector of all sources which are used in this realm. The entries are + // allocated within the LifoAlloc. + 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(); + + // Write the aggregated result of the code coverage of a realm + // into a file. + void writeLCovResult(LCovRealm& realm); + + 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. + uint32_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_; +}; + +void InitLCov(); + +void EnableLCov(); + +inline bool IsLCovEnabled() { + extern bool gLCovIsEnabled; + return gLCovIsEnabled; +} + +// Initialize coverage info to track code coverage for a JSScript. +bool InitScriptCoverage(JSContext* cx, JSScript* script); + +// Collect the code-coverage data from a script into relevant LCovSource. +bool CollectScriptCoverage(JSScript* script, bool finalizing); + +} // namespace coverage +} // namespace js + +#endif // vm_Printer_h |