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