CodeCoverage.h (5274B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_CodeCoverage_h 8 #define vm_CodeCoverage_h 9 10 #include "mozilla/Vector.h" 11 12 #include "ds/LifoAlloc.h" 13 14 #include "js/AllocPolicy.h" 15 #include "js/HashTable.h" 16 #include "js/Printer.h" 17 #include "js/TypeDecls.h" 18 #include "js/Utility.h" 19 20 namespace js { 21 namespace coverage { 22 23 class LCovSource { 24 public: 25 LCovSource(LifoAlloc* alloc, JS::UniqueChars name); 26 27 // Whether the given script name matches this LCovSource. 28 bool match(const char* name) const { return strcmp(name_.get(), name) == 0; } 29 30 // Whether an OOM was seen recording coverage information. This indicates 31 // that the resulting coverage information is incomplete. 32 bool hadOutOfMemory() const { return hadOOM_; } 33 34 // Whether the current source is complete and if it can be flushed. 35 bool isComplete() const { return hasTopLevelScript_; } 36 37 // Iterate over the bytecode and collect the lcov output based on the 38 // ScriptCounts counters. 39 void writeScript(JSScript* script, const char* scriptName); 40 41 // Write the Lcov output in a buffer, such as the one associated with 42 // the runtime code coverage trace file. 43 void exportInto(GenericPrinter& out); 44 45 private: 46 // Name of the source file. 47 JS::UniqueChars name_; 48 49 // LifoAlloc strings which hold the filename of each function as 50 // well as the number of hits for each function. 51 LSprinter outFN_; 52 LSprinter outFNDA_; 53 size_t numFunctionsFound_; 54 size_t numFunctionsHit_; 55 56 // LifoAlloc string which hold branches statistics. 57 LSprinter outBRDA_; 58 size_t numBranchesFound_; 59 size_t numBranchesHit_; 60 61 // Holds lines statistics. When processing a line hit count, the hit count 62 // is added to any hit count already in the hash map so that we handle 63 // lines that belong to more than one JSScript or function in the same 64 // source file. 65 HashMap<size_t, uint64_t, DefaultHasher<size_t>, SystemAllocPolicy> linesHit_; 66 size_t numLinesInstrumented_; 67 size_t numLinesHit_; 68 size_t maxLineHit_; 69 70 // Status flags. 71 bool hasTopLevelScript_ : 1; 72 bool hadOOM_ : 1; 73 }; 74 75 class LCovRealm { 76 public: 77 explicit LCovRealm(JS::Realm* realm); 78 ~LCovRealm(); 79 80 // Write the Lcov output in a buffer, such as the one associated with 81 // the runtime code coverage trace file. 82 void exportInto(GenericPrinter& out, bool* isEmpty) const; 83 84 friend bool InitScriptCoverage(JSContext* cx, JSScript* script); 85 86 private: 87 // Write the realm name in outTN_. 88 void writeRealmName(JS::Realm* realm); 89 90 // Return the LCovSource entry which matches the given ScriptSourceObject. 91 LCovSource* lookupOrAdd(const char* name); 92 93 // Generate escaped form of script atom and allocate inside our LifoAlloc if 94 // necessary. 95 const char* getScriptName(JSScript* script); 96 97 private: 98 using LCovSourceVector = 99 mozilla::Vector<LCovSource*, 16, LifoAllocPolicy<Fallible>>; 100 101 // LifoAlloc backend for all temporary allocations needed to stash the 102 // strings to be written in the file. 103 LifoAlloc alloc_; 104 105 // LifoAlloc string which hold the name of the realm. 106 LSprinter outTN_; 107 108 // Vector of all sources which are used in this realm. The entries are 109 // allocated within the LifoAlloc. 110 LCovSourceVector sources_; 111 }; 112 113 class LCovRuntime { 114 public: 115 LCovRuntime(); 116 ~LCovRuntime(); 117 118 // If the environment variable JS_CODE_COVERAGE_OUTPUT_DIR is set to a 119 // directory, create a file inside this directory which uses the process 120 // ID, the thread ID and a timestamp to ensure the uniqueness of the 121 // file. 122 // 123 // At the end of the execution, this file should contains the LCOV output of 124 // all the scripts executed in the current JSRuntime. 125 void init(); 126 127 // Write the aggregated result of the code coverage of a realm 128 // into a file. 129 void writeLCovResult(LCovRealm& realm); 130 131 private: 132 // Fill an array with the name of the file. Return false if we are unable to 133 // serialize the filename in this array. 134 bool fillWithFilename(char* name, size_t length); 135 136 // Finish the current opened file, and remove if it does not have any 137 // content. 138 void finishFile(); 139 140 private: 141 // Output file which is created if code coverage is enabled. 142 Fprinter out_; 143 144 // The process' PID is used to watch for fork. When the process fork, 145 // we want to close the current file and open a new one. 146 uint32_t pid_; 147 148 // Flag used to report if the generated file is empty or not. If it is empty 149 // when the runtime is destroyed, then the file would be removed as an empty 150 // file is not a valid LCov file. 151 bool isEmpty_; 152 }; 153 154 void InitLCov(); 155 156 void EnableLCov(); 157 158 inline bool IsLCovEnabled() { 159 extern bool gLCovIsEnabled; 160 return gLCovIsEnabled; 161 } 162 163 // Initialize coverage info to track code coverage for a JSScript. 164 bool InitScriptCoverage(JSContext* cx, JSScript* script); 165 166 // Collect the code-coverage data from a script into relevant LCovSource. 167 bool CollectScriptCoverage(JSScript* script, bool finalizing); 168 169 } // namespace coverage 170 } // namespace js 171 172 #endif // vm_CodeCoverage_h